Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,44 @@ The example searches Brave on a weekly cron schedule, clusters findings, and
upserts a GitHub issue. See
[`examples/weekly-digest`](./examples/weekly-digest/).

## Simulate an invocation (dry-run the handler)

`deploy --dry-run` validates the persona and exits **without invoking the
handler**. To answer "if this agent received this event, what would happen?",
use `invoke`: it executes the handler against fixture event envelope(s) with
**every external side effect recorded, not executed** — no harness spawn, no
shell, no cloud writes, no scheduling.

```bash
agentworkforce invoke ./persona.json --fixture ./event.json
```

The fixture is a raw gateway envelope (the runner's stdin line shape): a
single JSON object, a JSON array, or NDJSON — one envelope per line:

```json
{ "id": "e1", "workspace": "ws-dev", "type": "cron.tick",
"occurredAt": "2026-06-03T09:00:00Z", "name": "weekly", "cron": "0 9 * * 6" }
```

Provider events use `"type": "<provider>.<event>"` (e.g.
`github.pull_request.opened`) with the payload under `"resource"`.

`invoke` prints a human summary to stderr and a machine-readable run record
to stdout (or `--output run.json`). One record is emitted per envelope, in
the same compact shape Cloud's hosted run API serves (`runId`, `status`,
`exitCode`, `summary`, `error`, timings, `trigger`, `failureClass`, `logs`)
with `origin: "local_dry_run"`, so simulated runs can later be ingested and
displayed alongside hosted ones. Captured `ctx.log(...)` output lands in
`logs.stdout`/`logs.stderr`; every intercepted call (`harness.run`,
`sandbox.exec`, `memory.save`, `workflow.run`, `schedule.at`, …) is listed
under `simulation.sideEffects` with the inert result the handler received.

Useful flags: `--input KEY=value` overrides declared persona inputs;
`--seed /slack/channels/_index.json=./channels.json` seeds the simulated
filesystem with provider data the handler reads. Exit code is 0 when every
envelope succeeded, 1 when any handler invocation failed.

## Persona vs agent

A deployable agent is two files. The **persona** says *what the agent is*
Expand Down
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@agent-relay/cloud": "^6.0.17",
"@agentworkforce/deploy": "workspace:*",
"@agentworkforce/persona-kit": "workspace:*",
"@agentworkforce/runtime": "workspace:*",
"@agentworkforce/workload-router": "workspace:*",
"@relayburn/sdk": "^2.5.2",
"@relayfile/local-mount": "^0.7.24",
Expand Down
20 changes: 20 additions & 0 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
} from '@relayfile/local-mount';
import ora, { type Ora } from 'ora';
import { runDeploy, runLogin, runLogout } from './deploy-command.js';
import { runInvoke } from './invoke-command.js';
import { runDestroy } from './destroy-command.js';
import { runDeploymentList, runDeploymentLogs } from './list-command.js';
import {
Expand Down Expand Up @@ -243,6 +244,20 @@ Commands:
--cloud-url <url> override the workforce cloud URL
--input KEY=value override a declared persona input
(repeat for multiple)
invoke <persona-path> --fixture <file> [flags]
Simulate an invocation: run the persona's handler
against fixture event envelope(s) with every external
side effect recorded, NOT executed. Emits a
Cloud-compatible run record (origin "local_dry_run")
to stdout. Distinct from \`deploy --dry-run\`, which
validates without invoking the handler.
Flags:
--fixture <file> JSON envelope, JSON array, or
NDJSON of raw gateway envelopes
--output <file> write the run record to a file
--input KEY=value override a declared persona input
--seed PATH=file seed the simulated filesystem
--workspace <id> workspace id for the simulated ctx
deployments list List deployed cloud agents in the active workspace.
deployments logs Show structured logs for a deployed cloud agent.
destroy <persona-or-agent-id> [flags]
Expand Down Expand Up @@ -4368,6 +4383,11 @@ export async function main(): Promise<void> {
return;
}

if (subcommand === 'invoke') {
await runInvoke(rest);
return;
}

if (subcommand === 'deployments') {
const [action, ...extra] = rest;
if (!action || action === '-h' || action === '--help') {
Expand Down
Loading
Loading