TechFabricTechFabricHarness
CLI

fh run

Execute a workspace agent, or any standalone agent file, against a local Node runtime or a Temporal worker.

fabric-harness run <agent|file> [options]

Runs the named agent. The CLI loads the agent module, validates the input payload, calls run({ init, input, payload }), and validates the output.

<agent> is either a workspace agent name (resolved from .fabricharness/jobs/) or a path to an agent file (see Single-file mode).

Single-file mode

When <agent> resolves to an existing .ts, .mts, .js, or .mjs file, fh run executes that file directly — no .fabricharness/ workspace, no config.ts, no credentials required:

fh run ./agent.ts --message "hello"
  • The file is transpiled on the fly. Package imports (including @fabric-harness/sdk) resolve from the nearest node_modules to the file, falling back to the CLI's own install.
  • Every exported defineAgent()/createAgent() value is discovered. With exactly one export it runs directly; with several, pass --agent <name> to select one (the error lists the exported names). Persistent createAgent() agents still require a workspace.
  • When no model credentials resolve (--model, FABRIC_MODEL, or provider keys), the run falls back to the mock model and says so on stderr, so the command round-trips offline. --mock keeps its explicit meaning.
  • The reply (and any structured output) prints on stdout; the session id prints on stderr as [fabric-harness] session <id> completed (memory store — not persisted). — single-file runs use an in-memory store, so there is nothing to resume.
  • An explicit path that does not exist (./missing.ts) is a hard error; a bare name still falls back to workspace agent resolution.

Options

FlagDescription
--agent <name>Single-file mode: select one export when the file exports several agents.
--id <id>Session identifier. Reusing an existing id resumes that persisted session — new turns append to its history. If omitted, a session id is generated using ${idPrefix}-${uuid}.
--resume <id>Explicit alias for --id <id>: continue the persisted session <id>.
--fork <id>Fork persisted session <id> into a new session: copies its history into a fresh id (pass --id to name the fork), then continues the copy. Requires a persisted store.
--target <node|temporal-worker>Where the agent runs. Defaults to node. temporal-worker switches --runtime to temporal.
--runtime <inline|temporal>Equivalent lower-level flag. Usually set via --target.
--model <provider/model-id>Override the agent's default model, e.g. openai/gpt-5.5.
--mockUse the deterministic mock model provider — no credentials needed.
--mock-script <path>Scripted mock responses from a JSON fixture file (aimock-style { match, response } entries). Implies --mock; see Test Without Credentials.
--cwd <dir>Default sandbox/session working directory for this run. Relative paths stay scoped inside the sandbox workspace.
--uid <uid>Persistent agents: require an existing incarnation with this id.
--newPersistent agents: require creation of a new instance.
--data <json>Persistent agents: immutable initial data for a new instance.
--tenant <id>Tenant identifier for this run.
--prompt <text>Message text for persistent agents; also the prompt for --runtime temporal mode.
--payload '<json>'Pass an inline JSON payload.
--payload-file <path>Read JSON payload from a file.
--stdinRead JSON payload from stdin.
--set key=valueSet a payload field. May be repeated.
--<field> <value>Shortcut for setting a payload field, e.g. --question "...".
<field>=<value>Positional shortcut for setting a payload field, e.g. question="...".
--env <file>Load .env-style variables. Repeatable; shell env wins.

Resuming and forking sessions

Every successful workspace run prints its session id on stderr with a resume hint:

[fabric-harness] session sess-1 completed. Resume: fh run hello --resume sess-1

With a durable store (store: { backend: 'file' }, the default), passing the same id again — via --id or the explicit --resume alias — continues the session: the model sees the prior history and new entries append to it. --fork <id> instead copies a persisted session into a new id and continues the copy, leaving the original untouched:

fh run hello --id sess-1 --message "first"
fh run hello --resume sess-1 --message "second"        # continues sess-1
fh run hello --fork sess-1 --id sess-1-copy --message "third"  # branches a copy

With store: { backend: 'memory' } nothing is persisted, and the run says so explicitly (session <id> completed (memory store — not persisted).) — fh sessions staying empty after a memory-store run is expected, not data loss. Resume and fork require a persisted store.

Payload precedence

When more than one source is given, fields merge in this order (later wins):

--payload  →  --payload-file  →  --stdin  →  --set / --<field> / key=value

Examples

Single file, no workspace

fh run ./agent.ts --message "hello"
fh run ./multi.ts --agent beta --message "hello"

Inline JSON

fh run ask --payload '{"question":"What is Temporal?"}'

Field shortcut

fh run ask --question "What is Temporal?"
fh run ask question="What is Temporal?"
fh run ask --set question="What is Temporal?"

Working directory

fh run code --cwd /workspace/project --prompt "Run tests and summarize failures"
fh run code --cwd packages/core --payload '{"prompt":"Inspect this package"}'

--cwd is a sandbox cwd, not a host directory switch. Use it to make file/shell tools default to a repository subdirectory.

From a file or stdin

fh run ask --payload-file input.json
echo '{"question":"hi"}' | fh run ask --stdin

Real model

Put provider keys once in a repo/workspace .env.local; TechFabric Harness auto-loads it and shell env still wins.

cp .env.example .env.local
# edit .env.local and set OPENAI_API_KEY=...
fh run ask --model openai/gpt-5.5 --question "What is Temporal?"

Use explicit --env <file> only for test/CI overrides.

Temporal worker target

# In a separate terminal:
fh temporal-worker

# Then:
fh run ask --target temporal-worker --id ask-001 --prompt "What is Temporal?"

What happens during run

  1. Resolve workspace root by walking up to find .fabricharness/. (Single-file mode skips this and the next step — the file is loaded directly with SDK defaults.)
  2. Load .fabricharness/config.ts and merge with env and CLI flags.
  3. Resolve agent path, target, runtime, model, id, and sandbox cwd.
  4. Apply env model provider (e.g. set provider credentials).
  5. Validate input payload against the agent's input schema (metadata agents only).
  6. Call the agent's run({ init, input, payload }).
  7. Validate output against the output schema (metadata agents only).
  8. Persist session and print the session id on stderr (with a resume hint, or a not persisted notice on the memory store).

See also