@automatalabs/workflows 1.1.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -15
- package/dist/mcp-server.js +391 -402
- package/package.json +4 -4
package/dist/mcp-server.js
CHANGED
|
@@ -30908,7 +30908,7 @@ var permissionResponseSchema = external_exports.object({
|
|
|
30908
30908
|
var WORKFLOW_RESULT_CHUNK_BYTES_DEFAULT = 16384;
|
|
30909
30909
|
var WORKFLOW_RESULT_CHUNK_BYTES_MAX = 16384;
|
|
30910
30910
|
var WORKFLOW_RESULT_CHUNK_BYTES_MIN = 4;
|
|
30911
|
-
var actionSchema = external_exports.enum(["config", "run", "resume", "status", "result", "permissions-response", "stop"]).describe("Workflow operation.
|
|
30911
|
+
var actionSchema = external_exports.enum(["config", "run", "resume", "status", "result", "permissions-response", "stop"]).describe("Workflow operation. Activate the agentprism-workflow-authoring skill for the action guide.");
|
|
30912
30912
|
var scriptSchema = external_exports.string().min(1).describe("Run only: raw JavaScript workflow source, without Markdown fences.");
|
|
30913
30913
|
var scriptPathSchema = external_exports.string().min(1).refine((value) => isAbsolute(value), "scriptPath must be an absolute path").describe("Run only: absolute server-side script path, read once at admission.");
|
|
30914
30914
|
var projectDirSchema = external_exports.string().min(1).refine((value) => isAbsolute(value), "projectDir must be an absolute path").describe("Config/run project directory; required by the shared daemon.");
|
|
@@ -32625,6 +32625,7 @@ function createProgressReporter(extra) {
|
|
|
32625
32625
|
|
|
32626
32626
|
// ../mcp-server/src/authoring-prompt.ts
|
|
32627
32627
|
var AUTHORING_PROMPT_NAME = "author-workflow";
|
|
32628
|
+
var WORKFLOW_AUTHORING_SKILL_URI = "skill://agentprism-workflow-authoring/SKILL.md";
|
|
32628
32629
|
function buildAuthoringPromptText(task) {
|
|
32629
32630
|
const trimmed = task?.trim();
|
|
32630
32631
|
const taskSection = trimmed ? `## Your task
|
|
@@ -32633,9 +32634,9 @@ ${trimmed}` : "## Next step\n\nAuthor the workflow script the user asks for, the
|
|
|
32633
32634
|
return [
|
|
32634
32635
|
"# Author an AgentPrism workflow",
|
|
32635
32636
|
"",
|
|
32636
|
-
|
|
32637
|
+
`Activate the connected server's Agent Skill at \`${WORKFLOW_AUTHORING_SKILL_URI}\` through the host's skill-loading path. Follow its workflow-script guidance and read only the supporting references needed for this task. Do not use the separate REPL skill: workflow scripts and REPL evals have different \`agent()\` signatures and lifecycle semantics.`,
|
|
32637
32638
|
"",
|
|
32638
|
-
'When the script pins a model, mode, or configOptions, call the `workflow` tool with `action:"config"` first; after choosing a model, use `modelSpecs` to read its exact option domain. Read the harness-owned mode names and descriptions before pinning an
|
|
32639
|
+
'When the script pins a model, mode, or configOptions, call the `workflow` tool with `action:"config"` first; after choosing a model, use `modelSpecs` to read its exact option domain. Read the harness-owned mode names and descriptions before pinning an advertised id. The run action automatically performs static validation, a mocked dry run, and routed no-prompt config checks before admission. Correct any direct rejection diagnostic and re-run.',
|
|
32639
32640
|
"",
|
|
32640
32641
|
taskSection,
|
|
32641
32642
|
""
|
|
@@ -32646,7 +32647,7 @@ function registerAuthoringPrompt(mcp) {
|
|
|
32646
32647
|
AUTHORING_PROMPT_NAME,
|
|
32647
32648
|
{
|
|
32648
32649
|
title: "Author an AgentPrism workflow script",
|
|
32649
|
-
description: "Frame a workflow-authoring task and direct the assistant to
|
|
32650
|
+
description: "Frame a workflow-authoring task and direct the assistant to activate the connected server's version-matched Agent Skill.",
|
|
32650
32651
|
argsSchema: external_exports.object({
|
|
32651
32652
|
task: external_exports.string().optional().describe("What the workflow should accomplish (optional).")
|
|
32652
32653
|
})
|
|
@@ -32662,400 +32663,377 @@ function registerAuthoringPrompt(mcp) {
|
|
|
32662
32663
|
);
|
|
32663
32664
|
}
|
|
32664
32665
|
|
|
32665
|
-
// ../mcp-server/src/generated/authoring-
|
|
32666
|
-
var
|
|
32667
|
-
var
|
|
32668
|
-
var AUTHORING_DOC_TOPICS = [
|
|
32666
|
+
// ../mcp-server/src/generated/authoring-skills-content.ts
|
|
32667
|
+
var AUTHORING_SKILLS_SCHEMA_VERSION = 2;
|
|
32668
|
+
var AUTHORING_SKILLS = [
|
|
32669
32669
|
{
|
|
32670
|
-
"
|
|
32671
|
-
"
|
|
32672
|
-
"
|
|
32673
|
-
|
|
32674
|
-
|
|
32675
|
-
|
|
32676
|
-
|
|
32677
|
-
|
|
32678
|
-
|
|
32679
|
-
|
|
32680
|
-
|
|
32681
|
-
|
|
32682
|
-
|
|
32683
|
-
|
|
32684
|
-
|
|
32685
|
-
|
|
32686
|
-
|
|
32687
|
-
|
|
32688
|
-
|
|
32689
|
-
|
|
32690
|
-
|
|
32691
|
-
|
|
32692
|
-
|
|
32693
|
-
|
|
32694
|
-
|
|
32695
|
-
|
|
32696
|
-
|
|
32697
|
-
|
|
32698
|
-
|
|
32699
|
-
{
|
|
32700
|
-
|
|
32701
|
-
|
|
32702
|
-
|
|
32703
|
-
|
|
32704
|
-
|
|
32705
|
-
|
|
32706
|
-
|
|
32707
|
-
|
|
32708
|
-
|
|
32709
|
-
|
|
32710
|
-
|
|
32711
|
-
|
|
32712
|
-
|
|
32713
|
-
|
|
32714
|
-
|
|
32715
|
-
"
|
|
32716
|
-
|
|
32717
|
-
|
|
32718
|
-
|
|
32719
|
-
|
|
32720
|
-
|
|
32721
|
-
|
|
32722
|
-
|
|
32723
|
-
|
|
32724
|
-
|
|
32725
|
-
|
|
32726
|
-
|
|
32727
|
-
"text": '## Choosing the agent for each call\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\nThe backend is selected **per `agent()` call** from its effective `model` string. One script can plan on one vendor\'s agent, implement on another\'s, and review on a third\'s, handing structured results between them.\n\nThe built-in names (`claude`, `codex`, `opencode`, `pi`) come from the runtime backend registry. Registered custom names extend that set.\n\n- **Omit `model` entirely** for maximum portability. Before an MCP run, a form-elicitation-capable client receives one structured request covering every dry-run-observed `agent()` call. The user selects each call\'s provider/model from the live advertised catalogs, so a script with no model specs remains backend-portable. Clients without form elicitation retain the fallback policy: an explicitly present `AGENTPRISM_DEFAULT_BACKEND` wins; when it is truly unset, a model-less run performs zero-token readiness probes and pins one backend. The SDK runner itself retains its configured default (`AGENTPRISM_DEFAULT_BACKEND`, historical fallback Claude).\n- **Route by one registered first segment.** Split on the first `/`; ASCII-case-insensitive `claude`, `codex`, `opencode`, `pi`, or a registered custom backend name selects that harness and is stripped exactly once. A custom registration wins on a built-in-name collision.\n- **Use a backend name alone** (`claude`, `codex`, `opencode`, `pi`, or a custom name) to preserve the harness\'s configured default model. No model config call is made.\n- **Everything else goes intact to the default backend.** `anthropic/\u2026`, `openai/\u2026`, bare `opus`, and bare `gpt-\u2026` are not routing aliases. When an id remains after routing, it is sent byte-for-byte: no catalog matching, case folding, bracket parsing, effort/Fast option driving, retry, or fallback. Harness rejection is an agent error.\n- **`tier`** (`"small" | "medium" | "big"`) is a coarse alternative resolved from the host\'s tier config \u2014 use it for "a cheap model" without naming a vendor.\n\nThe MCP pre-execution request includes the resolved label, phase title/detail, and a bounded credential-redacted task preview for each call, so the user can see what each selected model will do. It offers one required provider/model field per call and optional provider-scoped mode and non-model config fields; fields for providers the user did not select are ignored, and omitted optional fields use the selected provider\'s defaults. The server validates accepted values against the exact probed form, reruns the complete zero-token preflight, and atomically persists the canonical effective occurrence map before any live agent starts. Raw form fields are never persisted. Decline or cancel starts no run. If live control flow reaches an occurrence the mock preflight did not observe, the occurrence is durably recorded and strict host selection fails closed. Same-ID continuation reuses the canonical snapshot without eliciting again. Legacy MCP elicitation and modern `2026-07-28` `input_required` retries use the same signed selection contract.\n\nThe published examples use ids verified against live harness catalogs: `claude/opus[1m]`, `codex/gpt-5.6-sol`, and `opencode/zai/glm-5.2`. For Pi, `pi/openrouter/vendor/model-id` strips only `pi/`; Pi then splits provider `openrouter` from model id `vendor/model-id`. Prefer backend-only forms when the desired model is configured inside the harness.\n\nNever guess model ids, mode ids, effort values, or option names from memory. With MCP, call the `workflow` tool using `action:"config"` and optional `harnesses` / `modelFilter`; it returns the live catalog without starting a workflow.\n\nOne no-prompt session per harness, zero tokens: each successful harness entry contains `modes`, `defaultModeId`, and its config-option catalog. A non-null `modes` object carries every available mode\'s raw id, name, description, and `_meta`; only exact advertised ids are valid. Omission applies Claude `auto`, Codex `agent`, OpenCode `build`, or no Pi mode. For trusted implementation/review work, explicitly choose Claude `bypassPermissions` or Codex `agent` when the catalog advertises it. Claude `auto` delegates permission policy to a model classifier and may ask the user; it is not fully autonomous. `modes:null` means the backend supports no mode. `probed:true` proves session/config discovery, not universal first-prompt authentication. The bare config probe reads the default model; option domains are model-specific, so use `modelSpecs` for the selected model and confirm every pinned value against its own echoed entry.\n\n```js\nconst plan = await agent(PLAN_PROMPT, { label: "plan", model: "opencode/zai/glm-5.2", schema: PLAN });\nconst impl = await agent(implPrompt(plan), { label: "implement", model: "codex/gpt-5.6-sol", mode: "agent" });\nconst review = await agent(reviewPrompt(impl), { label: "review", model: "claude/opus[1m]", mode: "bypassPermissions", schema: REVIEW });\n```\n\nUse `configOptions` only for exact ACP session options advertised by that routed harness. With MCP, read the selected harness\'s `action:"config"` result before choosing ids or select values; catalogs vary by harness version, login, and machine.\n\n```js\nconst impl = await agent(implPrompt(plan), {\n label: "implement",\n model: "codex",\n mode: "agent",\n configOptions: { "fast-mode": true, reasoning_effort: "high" },\n});\n```\n\nIds and string/boolean values pass through verbatim in ascending id order, after model selection and before the prompt. There are no aliases, coercion, client-side vocabulary, defaults, or cached catalogs. Copy option ids character-for-character from the catalog, punctuation included \u2014 `"fast-mode"`, not `fast_mode` \u2014 and quote ids that are not valid identifiers. Never put `"model"` in `configOptions`; use the dedicated `model` field. A harness rejection follows the ordinary agent-error path.\n\nPi\'s thought-level option is named `thinkingLevel`, and its choices depend on the exact model in the same call:\n\n```js\nconst review = await agent(REVIEW_PROMPT, {\n label: "pi-review",\n model: "pi/openrouter/vendor/model-id",\n configOptions: { thinkingLevel: "high" },\n});\n```\n\nValidation selects `openrouter/vendor/model-id` before reading Pi\'s choices. A listed value passes unchanged. A recognized value above an ordered model\'s ceiling, or in a model-specific gap, passes with a warning that names the effective clamp target. Pi advertises its SDK-derived domain directly. Claude and Codex are also ordered: when their options omit domain metadata, validation enumerates the advertised models and merges their per-model effort orders. A Claude model without an `effort` option does not support effort, and `default` never becomes a ceiling target. OpenCode and custom backends have no declared value order, so validation is exact-set. An unrecognized or unadvertised value fails with exit code `2`. Enumeration stops at 32 advertised models; a larger or inconsistently ordered catalog warns and falls back to exact advertised-value validation.\n\n**The harness is authoritative.** The client never substitutes a nearby model or silently falls back. A rejected id follows the existing agent-error path; a harness that accepts or ignores it determines the outcome. The public `fallbacks`/`onModelFallback` fields remain for compatibility but model resolution does not emit them.\n\n## Structured output\n\nPass `schema` \u2014 a **plain JSON Schema object literal** (no schema builders exist inside the realm) \u2014 and the call resolves to a **validated object** instead of text:\n\n```js\nconst FINDINGS = {\n type: "object",\n additionalProperties: false,\n required: ["findings"],\n properties: {\n findings: {\n type: "array",\n items: {\n type: "object",\n additionalProperties: false,\n required: ["file", "line", "summary"],\n properties: {\n file: { type: "string", description: "Repo-relative path \u2014 copy it exactly, never invent one" },\n line: { type: "number", description: "1-indexed line the finding anchors to" },\n summary: { type: "string", description: "One sentence stating the defect, grounded in code you actually read" },\n },\n },\n },\n },\n};\n\nconst report = await agent("Review the diff on this branch for correctness bugs.", {\n label: "review", schema: FINDINGS,\n});\nreport.findings.forEach((f) => log(`${f.file}:${f.line} ${f.summary}`));\n```\n\nThe same schema works on **every** backend; only the fulfillment channel differs, and the runner picks it for you: Claude uses its `outputFormat`, Codex its strict `outputSchema`, while Pi, OpenCode, and eligible custom ACP agents receive a client-hosted `StructuredOutput` MCP tool when they advertise HTTP MCP support. Pi accepts stdio, Streamable HTTP, and SSE MCP servers. If no valid tool capture exists, Pi retains the runner\'s common prompt-embedded schema and validated final-text JSON fallback. In every channel the runner validates the value client-side (with type coercion) and re-prompts a bounded number of times before failing the call with non-recoverable `SCHEMA_NONCOMPLIANCE`.\n\nSchema authoring rules that keep all channels healthy:\n\n- Root must be an object; set `additionalProperties: false` and list every property in `required`.\n- Put a `description` on every field \u2014 descriptions are the per-field prompt.\n- Keep schemas structurally simple. Exotic keywords (`oneOf`, `patternProperties`, unusual `format`s, backreference regexes) are normalized or stripped on the wire for some backends \u2014 validation still enforces them client-side, which shows up as re-prompt churn. Prefer `anyOf`, `enum`, and plain types.\n- Keep free-text fields small (tens of lines). An oversized structured output can exhaust schema repair and fail the call.\n- Validation checks structure, not truth. Check load-bearing values in script code (for example, reject findings whose `file` is not in a known file list) before spending more agents on them.\n'
|
|
32728
|
-
|
|
32729
|
-
|
|
32730
|
-
|
|
32731
|
-
"
|
|
32732
|
-
|
|
32733
|
-
|
|
32734
|
-
|
|
32735
|
-
|
|
32736
|
-
|
|
32737
|
-
|
|
32738
|
-
|
|
32739
|
-
],
|
|
32740
|
-
|
|
32741
|
-
|
|
32742
|
-
|
|
32743
|
-
|
|
32744
|
-
|
|
32745
|
-
|
|
32746
|
-
|
|
32747
|
-
"description": "gate, retry, verify, judgePanel, loopUntilDry, completenessCheck, and human checkpoints.",
|
|
32748
|
-
|
|
32749
|
-
|
|
32750
|
-
|
|
32751
|
-
|
|
32752
|
-
|
|
32753
|
-
|
|
32754
|
-
|
|
32755
|
-
|
|
32756
|
-
|
|
32757
|
-
|
|
32758
|
-
|
|
32759
|
-
|
|
32760
|
-
|
|
32761
|
-
|
|
32762
|
-
|
|
32763
|
-
|
|
32764
|
-
|
|
32765
|
-
"relatedTopics": [
|
|
32766
|
-
"workflow/api-agents",
|
|
32767
|
-
"workflow/api-resume-and-backends",
|
|
32768
|
-
"workflow/models-and-config"
|
|
32769
|
-
],
|
|
32770
|
-
"bytes": 5429,
|
|
32771
|
-
"sha256": "a53fafb94a3001efeee3c8e62293764f9ecb5303d50e84aa27761a98d22229f2",
|
|
32772
|
-
"text": '## Working directory, isolation, confinement\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\n- Every agent session runs in the run\'s base `cwd` unless the call narrows it: `agent({ cwd: "packages/api" })` (relative resolves against the base).\n- `isolation: "worktree"` runs the agent in a **throwaway git worktree** (`<repoRoot>/.agentprism/worktrees/\u2026`) so parallel agents can edit without colliding. The worktree and its branch are **always deleted when the call ends \u2014 an isolated agent\'s file edits are discarded**. Have isolated agents *return their work as data* (a unified diff, a file map, a report) and apply it in a later non-isolated step; use worktrees for experiments, builds, and verification, not for persistent edits. Outside a git repo, isolation degrades to the shared tree with a logged notice.\n- `mode` requests an exact agent-advertised ACP session mode. Config returns raw names, descriptions, and `_meta`; use those backend-owned explanations instead of inferring from an id. For trusted implementation/review workflows use Claude `bypassPermissions` or Codex `agent` when advertised. Claude `auto` uses a model classifier and may still request permission, so it is not the full-access autonomous mode. Automatic preflight rejects a mode the selected backend/model does not advertise. Only set `mode` on calls whose `model` you also pin. Use an advertised read-only/plan mode for reviewers that must not write.\n- `agentType: "<name>"` binds a reusable subagent definition \u2014 a Markdown file at `<cwd>/.agentprism/agents/<name>.md` (project) or `~/.agentprism/agents/<name>.md` (user; project wins) whose frontmatter sets tool allow/deny lists, a model, and isolation, and whose body is the role prompt. An unknown name logs a warning and degrades to defaults.\n\n## Where a mutating workflow runs\n\nThe run\'s base `cwd` is the USER\'S checkout \u2014 the working copy they launched the host from. Treat it as borrowed: committing onto whatever branch is checked out, switching branches, or resetting it are defects unless the user asked for exactly that. A script that commits should verify its target workspace in a preflight step, or create its own workspace idempotently, and refuse on a mismatch rather than adapt. `isolation: "worktree"` is NOT such a workspace \u2014 it is per-call and throwaway. Note also that a throwaway worktree branches from the run cwd\'s repository: an isolated agent sees another agent\'s commits only when they are reachable there.\n\n## Wiring tools and inputs into a call\n\n- `mcpServers: [{ name, command, args: [], env: [] }]` attaches MCP servers to that agent\'s session \u2014 the portable way to hand any backend a capability (image generation, a browser, a ticket system). The agent sees the server\'s tools natively. Note `env` is a list of `{ name, value }` pairs (ACP shape), not an object map; HTTP/SSE servers use `{ type: "http", name, url, headers: [] }`.\n- `images: [...]` appends base64 image blocks to the prompt (backends without image support receive a bracketed text note instead).\n- `meta` / `promptMeta` pass generic ACP `_meta` through to `session/new` / `session/prompt` \u2014 the escape hatch for driving a custom agent\'s extension surface.\n- `keepSession: true` keeps a successful agent\'s ACP session re-openable after the run: the re-attach record (sessionId, backend, effective pool identity, cwd, reopen capabilities) lands in `WorkflowRunResult.agentSessions`, and the HOST can continue that conversation later via `runner.loadSession()`. Usage/auth pause failures are kept open automatically so managed resume can continue the interrupted occurrence. Scripts themselves never request reattach.\n\n### Custom ACP backends\n\nAny process that speaks ACP over stdio can serve `agent()` calls \u2014 an in-house browser-QA agent, an image generator, a domain-specific executor. Two ways in:\n\n1. **Host-registered** (preferred): the embedder passes `createAcpRunner({ backends: { browser: { command: "/abs/browser-acp" } } })`; the script just routes with `model: "browser"`.\n2. **Script-declared**: the script itself declares the backend in `meta.backends` \u2014 but declarations are **inert until the host approves them** (an elicitation in the MCP server; `allowScriptBackends` in the SDK), because they spawn commands on the host machine. Don\'t rely on them silently working.\n\n```js\nexport const meta = {\n name: "checkout-qa",\n description: "Implement, then QA the checkout flow in a real browser",\n backends: {\n browser: { command: "browser-acp", args: ["--headless"] }, // requires host approval\n },\n};\n\nconst change = await agent("Implement the coupon-code field per the spec in docs/coupon.md.",\n { label: "implement" }); // default backend\nconst verdict = await agent(\n `Open the app, walk through checkout with coupon SAVE20, and verify the discount line. Change summary:\\n${change}`,\n { label: "qa", model: "browser", // the custom agent\n schema: { type: "object", additionalProperties: false, required: ["passed"],\n properties: { passed: { type: "boolean" }, notes: { type: "string" } } } },\n);\nreturn { change, qa: verdict };\n```\n\nStructured output works on custom backends through the same injected-tool/fallback ladder as OpenCode \u2014 no special-casing in the script.\n'
|
|
32773
|
-
},
|
|
32774
|
-
{
|
|
32775
|
-
"id": "workflow/determinism-and-resume",
|
|
32776
|
-
"title": "Workflow determinism and resume",
|
|
32777
|
-
"description": "Identity/input fingerprints, content-addressed replay, eligibility diagnostics, checkpoints, and stop-patch-resume.",
|
|
32778
|
-
"uri": "agentprism://docs/workflow/determinism-and-resume",
|
|
32779
|
-
"mimeType": "text/markdown",
|
|
32780
|
-
"relatedTopics": [
|
|
32781
|
-
"workflow/run-lifecycle",
|
|
32782
|
-
"workflow/api-resume-and-backends",
|
|
32783
|
-
"workflow/examples"
|
|
32784
|
-
],
|
|
32785
|
-
"bytes": 3084,
|
|
32786
|
-
"sha256": "0afcdc404ac0aa5c48c92499684852ddd4c823cebdb56bc29f73780b3143fa9e",
|
|
32787
|
-
"text": '## Determinism and same-run continuation\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\nOne MCP run owns one immutable logical execution. Every `agent()` and `checkpoint()` result is\njournaled under a deterministic call index. `{ action:"resume", runId }` reconstructs and continues\nthat exact run; it never forks a child execution and never accepts changed script, args, or agent\nconfiguration.\n\n- Direct `Date.now()`, `Math.random()`, and no-arg `new Date()` / `Date()` fail validation. Pass nondeterministic values through the original Run `args`.\n- An agent identity hashes the prompt, resolved model, authored mode, non-empty sorted `configOptions`, tier, phase, agent type/definition, and schema. A separate fingerprint covers label, cwd/isolation, session retention, images, MCP servers, metadata, and approved script backends.\n- At admission the host atomically stores a versioned canonical effective occurrence map, default model, approved script backends, stable selection hash, source, and timestamp. Raw elicitation form fields are not stored.\n- Strict coverage is permanent. If live control flow reaches an occurrence the admission pass did not cover, that occurrence fails before ACP dispatch and is recorded durably. Later continuation refuses; it never shifts a configuration to another ordinal.\n- Exact index/hash journal hits rebuild script state without spawning a provider session, adding provider usage, or appending duplicate journal entries. Live usage is added to the run\'s existing cumulative total.\n- A usage/auth-interrupted root call may reattach its recorded ACP session when its call identity, inputs, cwd, backend pool identity, and reopen capability agree. Failed eligibility falls back to a fresh live call within the same run, never a child run.\n- The persisted event stream remains one stream for the run. A continuation appends a `resumed` event and new execution observations at the existing durable cursor.\n- MCP status is an immediate snapshot. Reissue it or consume the event resource for later progress.\n\n### Durable checkpoints\n\nFor a `headless:"pause"` checkpoint, resume with\n`{ action:"resume", runId, checkpointReplies:{ [checkpointContext.callIndex]: decision } }`.\nThe decision must be strict JSON. Under the run lease, the first answer is journaled before\ncontinuation. An identical repeat is idempotent. A different later answer is ignored and reported\nagainst the durable first answer. Cold reconstruction replays the decision forever.\n\n### Failure and restart\n\nA paused or failed run with valid admission metadata can continue. A completed or aborted run is\nterminal. A pre-contract record without the required canonical admission may remain observable but\nmust be replaced with a fresh `{ action:"run", ... }`; no migration or inferred mapping exists.\n\nGive repeated calls stable labels and narrate decisions with `log()`. Retain the original run ID:\nthe same ID addresses its script, event stream, cumulative usage, status, and result.\n'
|
|
32788
|
-
},
|
|
32789
|
-
{
|
|
32790
|
-
"id": "workflow/api-agents",
|
|
32791
|
-
"title": "Workflow agent API reference",
|
|
32792
|
-
"description": "Every agent() option, exact model grammar, timeout behavior, and structured-output semantics.",
|
|
32793
|
-
"uri": "agentprism://docs/workflow/api-agents",
|
|
32794
|
-
"mimeType": "text/markdown",
|
|
32795
|
-
"relatedTopics": [
|
|
32796
|
-
"workflow/models-and-config",
|
|
32797
|
-
"workflow/environment-and-tools",
|
|
32798
|
-
"workflow/api-control-flow"
|
|
32799
|
-
],
|
|
32800
|
-
"bytes": 7565,
|
|
32801
|
-
"sha256": "9f6d10510812b4cd13846eca369d9c763cefb42051c376c08d08e3f84cc93a79",
|
|
32802
|
-
"text": '# Workflow agent API reference\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\n## `agent(prompt, options?)` \u2014 full option table\n\nReturns the agent\'s final assistant text, or the schema-validated object when `schema` is set. Resolves to `null` when a *recoverable* failure survives all retries.\n\n| option | type | meaning |\n|---|---|---|\n| `label` | `string` | Display/telemetry name; also stamped on every live ACP event for this call. Always set it. Not part of the resume hash. |\n| `phase` | `string` | Assign this call to a phase explicitly (needed inside concurrent stages where the global `phase()` state would race). |\n| `schema` | JSON Schema object | Structured output. Plain object literal only \u2014 no schema builders exist in the realm. Part of the resume hash. |\n| `model` | `string` | Model spec: optional registered harness prefix plus a verbatim id, or a backend-only name. See [Model specs & routing](#model-specs--routing). Part of the resume hash. |\n| `tier` | `"small" \\| "medium" \\| "big"` | Coarse tier resolved from host config; beats phase/meta model, loses to explicit `model`. Part of the resume hash. |\n| `mode` | `string` | Exact ACP session mode id advertised by the selected backend/model. For trusted implementation/review work use Claude `bypassPermissions` or Codex `agent` when advertised. Claude `auto` is classifier-driven and may request permission; it is not full-access autonomy. Config preserves raw names/descriptions/metadata, and every selected id is validated before prompting. Part of call identity only when authored. |\n| `configOptions` | `Record<string, string \\| boolean>` | Exact ACP session option ids and authored values. Applied in ascending id order after model and before the prompt, with no aliases or coercion. `"model"` is reserved for the dedicated `model` field. Part of the resume hash only when non-empty, with sorted keys. With MCP, read the advertised-options table from `workflow` action `config` before choosing values. |\n| `agentType` | `string` | Bind a named subagent definition (tools allow/deny, model, isolation, role prompt). See [agentType definitions](#agenttype-definitions). Part of the resume hash. |\n| `isolation` | `"worktree"` | Run in a throwaway git worktree branched from the run cwd. **Always removed (worktree + branch) when the call ends** \u2014 edits are discarded; return work as data. Degrades to the shared tree outside a git repo (logged). |\n| `cwd` | `string` | Per-session working directory; relative resolves against the run\'s base cwd. Overridden by worktree isolation. Not hashed. |\n| `retries` | `number` | Retries after *recoverable* failures (default 0, host-overridable). Exhausted retries \u21D2 the call resolves `null`. |\n| `mcpServers` | `McpServerConfig[]` | MCP servers attached to this session. Stdio shape: `{ name, command, args: [], env: [{ name, value }] }` (`args`/`env` required, `env` is name/value pairs, not a map); `{ type: "http" \\| "sse", name, url, headers: [] }` also accepted. Not hashed. |\n| `images` | `PromptImage[]` | Base64 image blocks appended to the prompt; backends without image support get a bracketed text note. Not hashed. |\n| `meta` | `object` | ACP `_meta` merged into `session/new` \u2014 session-scoped extension passthrough (pairs with custom backends). Not hashed. |\n| `promptMeta` | `object` | ACP `_meta` merged into `session/prompt` \u2014 turn-scoped passthrough. Backend-computed keys win on conflict. Not hashed. |\n| `keepSession` | `boolean` | Skip release-time best-effort `session/close`; the non-secret re-attach record lands in `WorkflowRunResult.agentSessions` for host-side `loadSession()` / `resumeSession()`. Usage/auth pause failures are kept open automatically for managed continuation. Not identity-hashed; included in the input fingerprint. |\n\nAgent attempts have no model-facing wall-clock or idle timeout. They remain live until they complete,\nfail, or the host explicitly cancels the call or run. Same-ID MCP continuation may apply new runtime\nlimits, but it cannot change the persisted script, args, or effective agent configuration.\n\n## Model specs & routing\n\nA `model` string is resolved solely from its first segment, then delegated to the harness:\n\n| spec shape | routes to | notes |\n|---|---|---|\n| *(omitted)* | host-pinned/default backend | MCP: explicit `AGENTPRISM_DEFAULT_BACKEND` wins; when truly unset, zero-token readiness discovery pins one project default before validation/execution and preserves it across resume. SDK runner: configured default, historical fallback `claude`. The selected harness keeps its session default model. Most portable. |\n| `claude`, `codex`, `opencode`, `pi`, or `<custom-name>` | that registered harness | Backend-only: no model config call; the harness default remains active. |\n| `claude/<id>`, `codex/<id>`, `opencode/<id>`, `pi/<id>`, or `<custom-name>/<id>` | that registered harness | Match the first segment ASCII-case-insensitively and strip exactly one segment. Custom names take priority on collision. The remaining `<id>` is sent verbatim, including further `/` characters. For Pi, that remainder is its `<provider>/<model-id>` and Pi preserves any further slashes in the model id. |\n| any other string, including `anthropic/\u2026`, `openai/\u2026`, bare `opus`, or bare `gpt-\u2026` | host default backend | The **entire** authored string is sent verbatim; these are not routing aliases. |\n\nSelection is a single `session/set_config_option` with `configId: "model"` and the exact remaining string. There is no catalog matching, case folding, normalization, bracket parsing, nearest-neighbor selection, sibling effort/Fast option driving, retry, or echo verification. Brackets, dots, and provider-style prefixes are ordinary model-id characters.\n\nWhatever the harness returns is the outcome. A rejection follows the existing agent-error path with no resolution-specific code or model fallback event. `onModelFallback` and `WorkflowRunResult.fallbacks` remain public compatibility surfaces; model resolution does not emit entries, while pause recovery emits `kind: "continuation"` reattach/skip notices.\n\n## Structured output channels\n\nOne author API (`schema`), four fulfillment paths \u2014 chosen automatically per backend:\n\n| backend | channel |\n|---|---|\n| Claude | native `outputFormat`, schema normalized to Anthropic\'s structured-outputs subset (e.g. `oneOf` \u2192 `anyOf`; unsupported keywords/formats stripped on the wire) |\n| Codex | native strict `outputSchema` (OpenAI strict subset normalization) |\n| Pi | a client-hosted `StructuredOutput` MCP tool injected when the agent advertises HTTP MCP support; common prompt-embedded schema and validated final-text JSON fallback |\n| OpenCode / custom ACP | a client-hosted **`StructuredOutput` MCP tool** injected into the session when the agent advertises HTTP MCP support (an agent may show it as `structured_output_StructuredOutput`); otherwise prompt-embedded schema + JSON parse of the final message. Custom backends can opt out of tool injection with `structuredOutputTool: false`. |\n\nPi accepts stdio, Streamable HTTP, and SSE MCP servers; ACP-transport MCP hosting remains client-side.\n\nIn every channel the runner coerces + validates client-side and re-prompts a bounded number of times; the final miss fails the call with non-recoverable `SCHEMA_NONCOMPLIANCE`. Constraints stripped from the wire are still enforced client-side \u2014 an exotic schema keyword shows up as re-prompt churn, so keep schemas simple.\n'
|
|
32803
|
-
},
|
|
32804
|
-
{
|
|
32805
|
-
"id": "workflow/api-control-flow",
|
|
32806
|
-
"title": "Workflow control-flow API reference",
|
|
32807
|
-
"description": "Complete DSL global signatures, checkpoint options, gate verdicts, and workflow error taxonomy.",
|
|
32808
|
-
"uri": "agentprism://docs/workflow/api-control-flow",
|
|
32809
|
-
"mimeType": "text/markdown",
|
|
32810
|
-
"relatedTopics": [
|
|
32811
|
-
"workflow/composition-and-failure",
|
|
32812
|
-
"workflow/checkpoints-and-quality",
|
|
32813
|
-
"workflow/api-agents"
|
|
32814
|
-
],
|
|
32815
|
-
"bytes": 6212,
|
|
32816
|
-
"sha256": "8ea5a881325679c70830bf7284a06de3bb8418bee20b2f009aa9a8d34aef0e77",
|
|
32817
|
-
"text": '# Workflow control-flow API reference\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\n## DSL globals \u2014 complete signatures\n\n```\nagent(prompt, options?) \u2192 Promise<string | object | null>\nparallel(thunks) \u2192 Promise<results[]> // barrier; input order; failed slot = null\npipeline(items, ...stages) \u2192 Promise<results[]> // no inter-stage barrier; stage(prev, original, index); failed item = null\nworkflow(nameOrScript, args?) \u2192 Promise<unknown> // one nesting level; names resolve from the host\'s workflows folder, inline scripts always work\ngate(thunk, validator, { attempts = 3 }) \u2192 { ok, value, verdict, attempts }\n // thunk(feedback, attempt); validator(result) \u2192 { ok, feedback?, ... } | boolean | null (may be async / an agent call)\nretry(thunk, { attempts = 3, until? }) \u2192 last result // thunk(attempt); stops early when until(result)\nverify(item, { reviewers = 2, threshold = 0.5, lens? })\n \u2192 { real, realCount, total, votes: [{ real?, reason? }] }\n // N adversarial reviewers prompted to REFUTE; lens (string | string[]) rotates focus per reviewer\njudgePanel(attempts, { judges = 3, rubric = "overall quality and correctness" })\n \u2192 { index, attempt, score, judgments } // mean 0\u20131 score per candidate; stable tie-break by index\nloopUntilDry({ round, key = JSON.stringify, consecutiveEmpty = 2, maxRounds = 50 })\n \u2192 unique items[] // round(i) returns items; stops after N dry rounds; agent-limit exhaustion returns the partial result\ncompletenessCheck(taskArgs, results) \u2192 { complete, missing?: string[] }\ncheckpoint(promptText, options?) \u2192 Promise<reply> // journaled human gate; zero tokens\nphase(title) \u2192 void // open a named phase\nlog(message) \u2192 void // console.log/info/warn/error route here too\nargs // the host-provided input value, verbatim\ncwd // the run\'s base working directory (string); process.cwd() returns it too\n```\n\nFor `gate()`, `value` is the final producer result and `verdict` is the exact last completed\nvalidator return, including any extra structured fields. `{ ok: true }` and bare `true` pass;\n`{ ok: false, feedback? }`, bare `false`, and `null` reject. Only object feedback is threaded into\nthe next producer attempt. A producer result of `null` is still passed to the validator. Producer\nor validator exceptions propagate immediately, so no partial gate result is returned and no later\nattempt runs. An explicit unsupported `undefined` validator return is a rejection represented as\n`verdict: null`. If the script returns the gate result, its complete verdict is persisted and may\nreach the host; keep evidence concise and never put credentials or other secrets in verdict data.\n\n`verify`, `judgePanel`, and `completenessCheck` spawn their subagents on the run\'s default model \u2014 hand-roll with `parallel` + `agent` to pin panel members to specific backends.\n\n## `checkpoint()` options\n\n| option | type | meaning |\n|---|---|---|\n| `kind` | `"confirm" \\| "input" \\| "select"` | Reply shape: boolean-ish / free text / one of `choices`. Affects the journal hash and the host UI widget. |\n| `choices` | `string[]` | For `kind: "select"`. |\n| `default` | `unknown` | Reply taken in the default headless mode \u2014 journaled like a real reply. Defaults to `true`. |\n| `headless` | `"default" \\| "abort" \\| "pause"` | No live channel: `"default"` takes `default ?? true`, `"abort"` aborts, and `"pause"` creates a persisted `checkpoint_required` pause. Default `"default"`. |\n| `timeoutMs` | `number` | Deadline for the interactive prompt. |\n\nThe host supplies the live human channel (elicitation in the MCP server; `ExecOptions.confirm` in the SDK), and that channel wins even when `headless:"pause"` is declared. A durable pause carries non-secret `checkpointContext`; MCP continues the same run with `checkpointReplies:{ [context.callIndex]: decision }`. The first strict-JSON decision stored under the run lease is authoritative forever: repeats are idempotent and conflicts are ignored. Detached runs never pause for a checkpoint unless the author opts into `"pause"`.\n\n## Error codes (`WorkflowError.code`)\n\n| code | recoverable | engine behavior |\n|---|---|---|\n| `AGENT_CANCELLED` | yes | The host selected this in-flight call for cancellation. It resolves `null` immediately through an engine race, skips retries, leaves the run live, and is recorded as a failed call rather than a replayable journal result. |\n| `AGENT_EMPTY_OUTPUT` | yes | No assistant text on a schema-less call; same retry-then-`null`. |\n| `AGENT_EXECUTION_ERROR` | yes* | Generic agent failure (*refusal/truncation variants are non-recoverable). |\n| `SCHEMA_NONCOMPLIANCE` | no | Structured output never validated after the re-prompt ladder. Halts the run (catchable in-script). |\n| `PROVIDER_USAGE_LIMIT` | no | Quota/rate wall \u2014 the run **pauses** (journaled, resumable), with the provider\'s reset hint. |\n| `AGENT_LIMIT_EXCEEDED` | no | `maxAgents` cap hit. |\n| `AUTH_REQUIRED` | no | Backend needs authentication. `WorkflowManager` returns a resumable pause with `reason: "auth_required"` and redacted `authContext`; a direct runner throws. The host completes auth before resuming/retrying. |\n| `CHECKPOINT_REQUIRED` | no | `headless: "pause"` reached without a live channel. `WorkflowManager` returns `reason: "checkpoint_required"` plus non-secret `checkpointContext`; resume with `checkpointReplies` or live confirm. |\n| `SCRIPT_VALIDATION_ERROR` | no | Script failed parse/validation (bad meta, nondeterministic API, bad `meta.backends` shape). |\n| `SCRIPT_ERROR` | no | The script itself crashed (uncaught throw, floated rejection). |\n| `WORKFLOW_ABORTED` | \u2014 | Real cancellation (pause/stop/host signal) \u2014 never used for crashes. |\n\n`loopUntilDry` absorbs `AGENT_LIMIT_EXCEEDED` from its rounds and returns the partial result; everywhere else it propagates.\n'
|
|
32818
|
-
},
|
|
32819
|
-
{
|
|
32820
|
-
"id": "workflow/api-resume-and-backends",
|
|
32821
|
-
"title": "Workflow resume and extension reference",
|
|
32822
|
-
"description": "Journal matching details, replay diagnostics, script-declared backends, and agentType definitions.",
|
|
32823
|
-
"uri": "agentprism://docs/workflow/api-resume-and-backends",
|
|
32824
|
-
"mimeType": "text/markdown",
|
|
32825
|
-
"relatedTopics": [
|
|
32826
|
-
"workflow/determinism-and-resume",
|
|
32827
|
-
"workflow/environment-and-tools",
|
|
32828
|
-
"workflow/run-lifecycle"
|
|
32829
|
-
],
|
|
32830
|
-
"bytes": 2892,
|
|
32831
|
-
"sha256": "a58fe072eb7289a74057e3a57489512b9e9547435a3ffdc29e968aaf853085bb",
|
|
32832
|
-
"text": '# Workflow continuation and extension reference\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\n## Same-run continuation journal\n\nEach `agent()` and `checkpoint()` result is journaled under a monotonic call index and identity hash.\nMCP `{ action:"resume", runId }` reloads the same run\'s persisted script, args, canonical effective\nagent configuration, journal, events, cumulative usage, and checkpoint decisions. It returns the\nsame `runId`; there is no public execution-attempt or child-run model.\n\nThe canonical agent identity fields are `prompt`, resolved `model`, authored `mode`, non-empty\nsorted `configOptions`, `tier`, `phase`, `agentType`, resolved agent definition, and `schema`.\nExact journal hits reconstruct completed calls without current provider usage. Eligible interrupted\nACP calls may reattach at the live boundary. New live usage is added to the prior cumulative total.\n\nThe host persists a versioned canonical admission snapshot before execution. Same-ID continuation\nuses it without new provider/model elicitation. Missing, invalid, or uncovered admission metadata\nfails closed. Checkpoint replies are first-writer-wins under the run lease and become permanent\njournal facts.\n\n## <a name="custom-backends-metabackends"></a>Custom backends \u2014 `meta.backends`\n\n```js\nexport const meta = {\n name: "\u2026", description: "\u2026",\n backends: {\n browser: {\n command: "browser-acp", // required: executable (absolute or on PATH)\n args: ["--headless"], // default []\n env: { BROWSER_PROFILE: "qa" }, // merged over the child\'s inherited env\n sessionMeta: { viewport: "desktop" },\n structuredOutputTool: true,\n },\n },\n};\n```\n\nScript-declared backends spawn commands on the host and are trust-gated before admission. The MCP\nserver obtains explicit approval; SDK hosts use `allowScriptBackends`, `ExecOptions.scriptBackends`,\nor their configured environment policy. A declined backend aborts admission rather than rerouting.\nHost-registered names win. Approved canonical backend definitions are stored in the run\'s admission\nsnapshot so continuation never re-elicits or changes them.\n\n## <a name="agenttype-definitions"></a>`agentType` definitions\n\nMarkdown files at `<runCwd>/.agentprism/agents/<name>.md` and\n`~/.agentprism/agents/<name>.md`; project wins:\n\n```markdown\n---\ndescription: Read-only security auditor\ntools: [read, grep, glob]\ndisallowedTools: [bash]\nmodel: claude/opus[1m]\nisolation: worktree\n---\nYou are a security auditor. Report findings; never modify files.\n```\n\nThe body is prepended to the task. An unknown type warns and degrades to defaults. The resolved\ndefinition participates in agent identity, so a same-run continuation only replays the exact\ndefinition captured by its journal and admitted configuration.\n'
|
|
32833
|
-
},
|
|
32834
|
-
{
|
|
32835
|
-
"id": "workflow/examples",
|
|
32836
|
-
"title": "Workflow composition examples",
|
|
32837
|
-
"description": "Cross-vendor build, backend-agnostic audit, bounded loops, schemas, and automatic validation patterns.",
|
|
32838
|
-
"uri": "agentprism://docs/workflow/examples",
|
|
32839
|
-
"mimeType": "text/markdown",
|
|
32840
|
-
"relatedTopics": [
|
|
32841
|
-
"workflow/quickstart",
|
|
32842
|
-
"workflow/composition-and-failure",
|
|
32843
|
-
"workflow/checkpoints-and-quality"
|
|
32844
|
-
],
|
|
32845
|
-
"bytes": 6292,
|
|
32846
|
-
"sha256": "d916530fc55e0dbcdc2e9ee686b7d923f4cecac70501ebdb366145e0d62dbe90",
|
|
32847
|
-
"text": '## Worked example \u2014 cross-vendor build with every major primitive\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\n```js\nexport const meta = {\n name: "feature-build",\n description: "Plan, gate on approval, implement, cross-vendor review, fix until green",\n phases: [{ title: "Plan" }, { title: "Implement" }, { title: "Review" }],\n};\n\nconst PLAN = { type: "object", additionalProperties: false, required: ["steps", "risks"],\n properties: {\n steps: { type: "array", items: { type: "string", description: "One concrete implementation step" } },\n risks: { type: "array", items: { type: "string" } } } };\nconst VERDICT = { type: "object", additionalProperties: false, required: ["ok"],\n properties: { ok: { type: "boolean" },\n feedback: { type: "string", description: "Required when ok=false: concretely what to change" } } };\n\nphase("Plan");\nconst plan = await agent(\n `Study this repo, then write an implementation plan for: ${args.feature}. Keep steps concrete.`,\n { label: "plan", model: "opencode/zai/glm-5.2", schema: PLAN },\n);\n\nconst approved = await checkpoint(\n `Implement "${args.feature}" with this plan?\\n- ${plan.steps.join("\\n- ")}\\nRisks: ${plan.risks.join("; ")}`,\n { kind: "confirm", default: true },\n);\nif (!approved) return { implemented: false, plan };\n\nphase("Implement");\nconst outcome = await gate(\n (feedback, attempt) => agent(\n `Implement: ${args.feature}\\nPlan:\\n- ${plan.steps.join("\\n- ")}\\n` +\n `Run the project\'s tests before finishing and report results.` +\n (feedback ? `\\n\\nReviewer feedback on attempt ${attempt}:\\n${feedback}\\nAddress every point.` : ""),\n { label: `implement:${attempt + 1}`, model: "codex/gpt-5.6-sol", mode: "agent", retries: 1 },\n ),\n async (report) => {\n if (!report) return { ok: false, feedback: "implementation agent produced no result" };\n phase("Review");\n const reviews = (await parallel([ // two reviewers on different vendors\n () => agent(`Review the working-tree diff for correctness. Implementer\'s report:\\n${report}`,\n { label: "review:correctness", model: "claude/opus[1m]", mode: "bypassPermissions", schema: VERDICT }),\n () => agent(`Review the working-tree diff for regressions and missing tests. Report:\\n${report}`,\n { label: "review:coverage", model: "opencode/zai/glm-5.2", schema: VERDICT }),\n ])).filter(Boolean);\n const rejections = reviews.filter((r) => !r.ok);\n return rejections.length\n ? { ok: false, feedback: rejections.map((r) => r.feedback).join("\\n"), reviews }\n : { ok: true, reviews };\n },\n { attempts: 3 },\n);\n\nreturn { implemented: outcome.ok, attempts: outcome.attempts, reviewVerdict: outcome.verdict, plan };\n```\n\nThese trusted implementation/review calls pin Codex `agent` and Claude `bypassPermissions` for\nfull tool autonomy. Confirm both ids in the live catalog first. Claude `auto` uses a model classifier\nand may request permission; it is not the full-access mode. For a read-only planner, select the\nexact advertised read-only/plan mode instead.\n\n## Worked example \u2014 fully backend-agnostic audit\n\nNo `model` anywhere: this script runs unchanged on whatever backend the host defaults to.\n\n```js\nexport const meta = {\n name: "edge-case-audit",\n description: "Exhaustively hunt edge-case bugs in a target dir, verify each, report gaps",\n phases: [{ title: "Hunt" }, { title: "Verify" }],\n};\n\nconst BUGS = { type: "object", additionalProperties: false, required: ["bugs"],\n properties: { bugs: { type: "array", items: { type: "object", additionalProperties: false,\n required: ["file", "scenario"], properties: {\n file: { type: "string", description: "Repo-relative path you actually opened" },\n scenario: { type: "string", description: "Concrete input/state \u2192 wrong behavior" } } } } } };\n\nphase("Hunt");\nconst seen = []; // what earlier rounds reported, threaded into each new prompt\nconst candidates = await loopUntilDry({\n round: async (i) => {\n const r = await agent(\n `Round ${i + 1}: find edge-case bugs in ${args.target} not already in this list:\\n` +\n JSON.stringify(seen) + `\\nOnly report what you can ground in code you read.`,\n { label: `hunt:${i + 1}`, schema: BUGS },\n );\n const bugs = r ? r.bugs : [];\n seen.push(...bugs);\n return bugs; // loopUntilDry dedups these by `key` across rounds\n },\n key: (b) => `${b.file}:${b.scenario}`,\n consecutiveEmpty: 2,\n maxRounds: 8,\n});\n\nphase("Verify");\nconst confirmed = (await pipeline(\n candidates,\n (bug) => verify(bug, { reviewers: 3, threshold: 0.66, lens: ["correctness", "reproducibility"] }),\n (v, bug) => (v.real ? bug : null),\n)).filter(Boolean);\n\nconst gaps = await completenessCheck(args, confirmed);\nlog(`${confirmed.length}/${candidates.length} confirmed; complete=${gaps.complete}`);\nreturn { confirmed, missing: gaps.missing ?? [] };\n```\n\n## Automatic validation before admission\n\nThe MCP `workflow` tool validates every run automatically before admission: static parse, mocked dry run, then routed no-prompt config checks. Invalid scripts return `status:"rejected"` diagnostics without creating a run ID, reserving a background slot, or spending tokens. When pinning model, mode, or `configOptions`, use `action:"config"` first.\n\nThe mocked pass executes reachable script control flow with schema-conforming fabricated agent results. It can prove that syntax, metadata, helper calls, and reachable branches are structurally executable, but it cannot prove prompt quality, real-world judgment, or convergence through every branch. Keep loops bounded in script code and inspect validation warnings for declared phases that the default fabricated path did not reach.\n\nThe routed config pass probes each distinct backend/model pair without prompting. Unknown option ids, invalid select values, wrong value types, and the reserved `"model"` config key reject the script with direct alternatives. A backend that cannot be probed produces an explicit warning and leaves only that backend\'s option domain unverified.\n\nFor model/config details, read `workflow/models-and-config`. For exact-run recovery semantics, read `workflow/determinism-and-resume`.\n'
|
|
32848
|
-
},
|
|
32849
|
-
{
|
|
32850
|
-
"id": "repl/quickstart",
|
|
32851
|
-
"title": "REPL orchestration: quickstart",
|
|
32852
|
-
"description": "Persistent eval basics, correct agent signature, handles, polling, structured output, and interrupt semantics.",
|
|
32853
|
-
"uri": "agentprism://docs/repl/quickstart",
|
|
32854
|
-
"mimeType": "text/markdown",
|
|
32855
|
-
"relatedTopics": [
|
|
32856
|
-
"repl/state-and-bindings",
|
|
32857
|
-
"repl/agent-handles",
|
|
32858
|
-
"repl/api-reference",
|
|
32859
|
-
"repl/examples"
|
|
32860
|
-
],
|
|
32861
|
-
"bytes": 3174,
|
|
32862
|
-
"sha256": "253f3e465fc3172b686ea7f1a4a9f989e8559c2dc9010fdc355418379271c968",
|
|
32863
|
-
"text": '# REPL orchestration: quickstart\n\n**Context:** JavaScript sent to the MCP `repl` tool. This is not workflow-script code: REPL `agent()` takes `(modelSpec, task, options?)`, top-level `return` is a syntax error, and named bindings persist between calls.\n\nThe REPL is one persistent QuickJS-in-WASM workspace per absolute `projectDir`. Use it when you want to inspect intermediate results and decide the next step interactively. Use `workflow` when the full orchestration is known up front and should be repeatable as one deterministic script.\n\n## First eval\n\n```js\nconst audit = agent("codex", "Inspect the parser for correctness bugs");\n```\n\nSend that code using:\n\n```json\n{ "action": "eval", "projectDir": "/absolute/project", "code": "..." }\n```\n\n`agent()` returns a persistent promise-handle immediately. Storing the handle before awaiting preserves its `id`, `queue()`, `steer()`, and `cancel()` methods for later evals.\n\nInspect or await it in another eval:\n\n```js\nagents()\n```\n\n```js\nconst report = await audit;\nreport\n```\n\nA completed eval returns `{ output, result? }`. If the soft hold bound expires while the eval remains suspended, the tool returns `{ output, running: [callIds] }`; execution continues server-side. Poll without running new code by evaluating the empty string:\n\n```json\n{ "action": "eval", "projectDir": "/absolute/project", "code": "" }\n```\n\n## Essential semantics\n\n- Top-level `await` works; top-level `return` does not.\n- `let`, `const`, `var`, functions, and classes remain available to later evals.\n- `_` is the previous eval\'s completion value.\n- Console output is returned as text but is not a persistent value. Assign values you need later.\n- There is no filesystem, network, import, or general timer API in the VM. Subagents perform external work; `sleep(ms)` is the one host-backed timer.\n- The default eval hold is 60 seconds and the per-call maximum is 120 seconds. This is a response hold, not cancellation.\n- Use `interrupt` with a call `id` to cancel one subagent/queued turn, or omit `id` to break the currently running eval.\n- `workspace()` shows bindings, in-flight calls, checkpoints, and diagnostics. `agents()` shows live agent lanes and queued turns.\n- `reset()` tears the workspace down after the current eval completes.\n\n## Structured output\n\n```js\nconst schema = {\n type: "object",\n additionalProperties: false,\n required: ["findings"],\n properties: {\n findings: { type: "array", items: { type: "string" } },\n },\n};\nconst review = agent("claude", "Review error handling", { schema });\nconst result = await review;\nresult.findings\n```\n\nAgent options are exactly `schema`, `cwd`, `configOptions`, and `mode`. Unknown keys reject. Model, mode, and option ids are backend-specific; use the `workflow` tool\'s zero-token `action:"config"` discovery before pinning them.\n\n## What to read next\n\n- `repl/state-and-bindings` \u2014 persistence, completion values, polling, and output.\n- `repl/agent-handles` \u2014 `agent()` options, failures, and handle identity.\n- `repl/steering-queueing-and-cancellation` \u2014 strict active-turn control and durable future turns.\n- `repl/api-reference` \u2014 every guest global and tool action.\n'
|
|
32864
|
-
},
|
|
32865
|
-
{
|
|
32866
|
-
"id": "repl/state-and-bindings",
|
|
32867
|
-
"title": "REPL state, bindings, and eval results",
|
|
32868
|
-
"description": "Persistent lexical bindings, completion values, output rendering, soft-bound results, polling, and cleanup.",
|
|
32869
|
-
"uri": "agentprism://docs/repl/state-and-bindings",
|
|
32870
|
-
"mimeType": "text/markdown",
|
|
32871
|
-
"relatedTopics": [
|
|
32872
|
-
"repl/quickstart",
|
|
32873
|
-
"repl/checkpoints-and-introspection",
|
|
32874
|
-
"repl/persistence-and-reset"
|
|
32875
|
-
],
|
|
32876
|
-
"bytes": 2837,
|
|
32877
|
-
"sha256": "02b4040fc3a76c9f07c234ce59c95a66b19b1b8eccf384362dece9642762115b",
|
|
32878
|
-
"text": '# REPL state, bindings, and eval results\n\nEvery project has one persistent JavaScript VM. A later `eval` sees the same global lexical environment and the same unresolved promise-handles as earlier evals.\n\n## Persist values explicitly\n\n```js\nconst target = "src/parser";\nconst findings = [];\n```\n\nBoth names remain available later. Console output does not create a value:\n\n```js\nconsole.log({ target }); // emits output only\n```\n\nThe special `_` binding contains the previous eval\'s completion value, similar to an interactive language shell:\n\n```js\n[1, 2, 3].map((x) => x * 2)\n// later:\n_.reduce((sum, x) => sum + x, 0)\n```\n\nAssign important results to descriptive names instead of depending on `_`, because each completed eval replaces it.\n\n## Top-level syntax\n\nTop-level `await` is supported:\n\n```js\nconst answer = await worker;\nanswer\n```\n\nTop-level `return` is a syntax error. The final expression becomes the eval completion value. Standard JavaScript control flow works, including async functions and `for await`.\n\n## Eval result shapes\n\nFinished:\n\n```js\n{ output: "zero or more console/error lines", result: "value repr when present" }\n```\n\nStill running after the soft hold bound:\n\n```js\n{ output: "lines emitted so far", running: ["c1", "c2"] }\n```\n\nThe eval continues after the second shape. Any later eval drains settlements first. An empty code string is the idempotent poll and runs no user code.\n\n`output` is newline-joined console, checkpoint, and uncaught-error rendering. Direct strings passed to console print in full. Objects and arrays use a depth-limited preview; evaluate a narrower property or slice to inspect more. Preserve large values in bindings rather than repeatedly printing them into model context.\n\n## Concurrency and serialization\n\nVM operations serialize, so concurrent clients cannot reorder individual eval operations. Subagents run concurrently under the workspace\'s host limit. The eval call pumps promise settlements until completion or its response bound; the bound does not terminate the underlying eval.\n\nUse `sleep(ms)` for a host-backed delay:\n\n```js\nawait sleep(250);\n```\n\nNo other timer API is available.\n\n## Introspection\n\n```js\nworkspace()\n```\n\nreturns a plain object containing `bindings`, `inFlight`, `checkpoints`, and `diagnostics`. Binding rows include the name, type/size preview, provenance, task/call id where relevant, and settlement status.\n\n```js\nagents()\n```\n\nreturns live agent/queued-turn rows with call ids, model specs, task previews, state, steering support, and queued-turn counts. Use these functions instead of guessing whether a handle is active.\n\n## Cleanup\n\n```js\nreset()\n```\n\nrequests teardown after the current eval completes. All bindings and pending work in that project workspace are discarded. It returns `undefined`; the next eval creates a fresh workspace.\n'
|
|
32879
|
-
},
|
|
32880
|
-
{
|
|
32881
|
-
"id": "repl/agent-handles",
|
|
32882
|
-
"title": "REPL agent calls and persistent handles",
|
|
32883
|
-
"description": "Exact agent(modelSpec, task, options) API, routing, option vocabulary, handle identity, and failures.",
|
|
32884
|
-
"uri": "agentprism://docs/repl/agent-handles",
|
|
32885
|
-
"mimeType": "text/markdown",
|
|
32886
|
-
"relatedTopics": [
|
|
32887
|
-
"repl/quickstart",
|
|
32888
|
-
"repl/steering-queueing-and-cancellation",
|
|
32889
|
-
"repl/api-reference"
|
|
32890
|
-
],
|
|
32891
|
-
"bytes": 3565,
|
|
32892
|
-
"sha256": "26f9868ba7d70fac2465cdb8fc0d22726585b61608d852aeca26b12a762dff96",
|
|
32893
|
-
"text": '# REPL agent calls and persistent handles\n\nREPL delegation uses:\n\n```js\nagent(modelSpec, task, options?) -> PromiseHandle\n```\n\nThis differs from workflow scripts, whose signature is `agent(prompt, options?)`.\n\n## Model routing\n\n`modelSpec` is required. Use a registered backend name alone to preserve its configured default model:\n\n```js\nconst worker = agent("codex", "Investigate the failing parser test");\n```\n\nUse `backend/model-id` only after model discovery:\n\n```js\nconst worker = agent("claude/verified-model-id", "Review the implementation");\n```\n\nThe known built-ins are Claude, Codex, OpenCode, and pi, plus host-registered custom backends. Unknown backend names reject and enumerate known backends. Use the `workflow` tool\'s `action:"config"` with `harnesses`/`modelFilter`, then `modelSpecs`, before pinning model, mode, or config-option values. Read the selected entry\'s harness-owned mode names/descriptions before pinning an exact advertised id. Omission uses `defaultModeId` (Claude auto, Codex agent, OpenCode build; none for Pi).\n\n## Exact option vocabulary\n\n```js\nconst worker = agent("codex", "Inspect the parser", {\n schema: {\n type: "object",\n additionalProperties: false,\n required: ["summary"],\n properties: { summary: { type: "string" } },\n },\n cwd: "/absolute/path",\n mode: "advertised-mode-id",\n configOptions: { advertisedOptionId: "advertised-value" },\n});\n```\n\nOptions are exactly:\n\n- `schema`: plain JSON Schema object; the promise resolves to its validated object.\n- `cwd`: absolute worker session working directory.\n- `mode`: exact ACP mode explicitly listed in the selected backend/model\'s discovered `modes.availableModes`.\n- `configOptions`: exact string/boolean ACP option ids and values.\n\nUnknown option keys reject. Options must be JSON-serializable.\n\n## Preserve the handle\n\nThe returned promise is also the live handle:\n\n```js\nconst worker = agent("pi", "Research the issue");\nconst id = worker.id;\nconst answer = await worker;\n```\n\nDo not write this if you intend to reuse the session:\n\n```js\nconst worker = await agent("pi", "Research the issue");\n```\n\nThat variable stores only the answer and loses access to handle methods.\n\nThe founding handle exposes non-enumerable, immutable members:\n\n- `id`: stable call id such as `"c1"`.\n- `queue(prompt, options?)`: create a distinct durable FIFO future turn.\n- `steer(prompt, options?)`: attempt strict control of only the currently active turn.\n- `cancel()`: cancel the session\'s current public turn.\n\nA queued-turn handle exposes its own `id` and `cancel()`.\n\n## Settlement and failures\n\nWithout `schema`, the founding promise resolves to final assistant text. With `schema`, it resolves to the validated object.\n\nA rejected call carries an error with call/backend attribution where available. Errors whose `recoverable` field is not `false` are treated as recoverable by `parallel()` and `pipeline()` and become `null` slots. A non-recoverable error rejects the surrounding combinator/eval.\n\nDirect `await worker` propagates rejection; catch only errors you can handle meaningfully:\n\n```js\nlet answer;\ntry {\n answer = await worker;\n} catch (error) {\n console.error(error);\n answer = null;\n}\n```\n\n## Session continuity\n\nThe founding answer settling does not erase the handle binding. Queue later prompts on the founding handle to continue the same ACP session. Session continuity depends on the backend\'s continuation capability and the workspace\'s durable lane state. Never fabricate a new handle from a saved id; retain the actual promise-handle binding.\n'
|
|
32894
|
-
},
|
|
32895
|
-
{
|
|
32896
|
-
"id": "repl/steering-queueing-and-cancellation",
|
|
32897
|
-
"title": "REPL steering, queueing, and cancellation",
|
|
32898
|
-
"description": "Strict active-turn steering, durable FIFO future turns, exact handle cancellation, ordering, and recovery.",
|
|
32899
|
-
"uri": "agentprism://docs/repl/steering-queueing-and-cancellation",
|
|
32900
|
-
"mimeType": "text/markdown",
|
|
32901
|
-
"relatedTopics": [
|
|
32902
|
-
"repl/agent-handles",
|
|
32903
|
-
"repl/checkpoints-and-introspection",
|
|
32904
|
-
"repl/persistence-and-reset"
|
|
32905
|
-
],
|
|
32906
|
-
"bytes": 3681,
|
|
32907
|
-
"sha256": "c3af06afe4c969b5512da0ed7d4f7e1ef02dffc63b00444c55b8ef1b4c8b6ffb",
|
|
32908
|
-
"text": '# REPL steering, queueing, and exact cancellation\n\nA founding agent handle separates transient control of a running turn from durable future work.\n\n## Strict steering\n\n```js\nconst worker = agent("codex", "Investigate the parser failure");\n```\n\nOnly while `agents()` reports its active turn as running:\n\n```js\nconst outcome = await worker.steer("Focus on the parser state machine");\n```\n\nThe result is exactly:\n\n- `"injected"`: the instruction was delivered into the active turn.\n- `"idle"`: no turn was active; the instruction was not retained.\n- `"unsupported"`: the backend does not advertise strict steering.\n\nTransport or protocol failures reject. Steering never starts a new turn and never queues work. An `"idle"` result intentionally loses the instruction. Check `agents()` immediately before steering when timing matters.\n\nSteering support is based on the backend\'s raw ACP steering advertisement. Do not infer it from the backend name.\n\n## Durable queued turns\n\nAfter retaining the founding handle, queue future public turns:\n\n```js\nconst first = await worker;\nconst implement = worker.queue("Implement the fix");\nconst test = worker.queue("Run the focused tests");\nconsole.log(implement.id, test.id);\nconst implemented = await implement;\nconst tested = await test;\n```\n\nEach queue call synchronously returns a distinct promise-handle with its own stable id. Queued turns execute FIFO on the founding session using ordinary public prompts. Queueing is broker-owned and works on every backend that can continue the session; it does not depend on a backend-native queue API.\n\nQueued-turn options are exactly:\n\n```js\n{ promptMeta?: object }\n```\n\nThe prompt must be a string. Malformed queue requests still receive a durable call record and reject directly.\n\n## Cancellation\n\nCancel the founding session\'s currently active public turn:\n\n```js\nawait worker.cancel();\n```\n\nCancel one queued turn exactly:\n\n```js\nawait test.cancel();\n```\n\nOr cancel by stable call id outside the VM through the tool:\n\n```json\n{ "action": "interrupt", "projectDir": "/absolute/project", "id": "c4" }\n```\n\nA targeted cancellation rejects that call recoverably and leaves unrelated work live. Cancelling a queued handle does not cancel its founding turn or siblings. Cancellation settlement is first-wins; an already-settled target cannot be retroactively cancelled.\n\nOmit `id` only to break the currently running eval itself:\n\n```json\n{ "action": "interrupt", "projectDir": "/absolute/project" }\n```\n\nThat is eval control, not agent-call selection. It breaks an executing eval or terminates a suspended eval that cannot be safely resumed. It returns an honest idle refusal only when nothing is running.\n\n## Ordering and persistence\n\nQueue admission, handoff, settlement, and cancellation are persisted. Pending queue turns survive daemon restart and reattach lazily to their founding session when eligible. Queue delivery has the documented narrow at-least-once crash window around remote acceptance and local handoff persistence; prompts that cause external side effects should therefore be idempotent or guarded.\n\nA lane-fatal persistence/session failure rejects the active and queued turns on that lane. The broker never opens a blank replacement session and pretends context survived.\n\n## Recommended pattern\n\n```js\nconst worker = agent("claude", "Analyze the issue; do not edit yet");\n// While running, optionally steer after checking agents().\nconst analysis = await worker;\nconst fix = worker.queue("Implement the agreed fix");\nconst verify = worker.queue("Run tests and report the exact results");\nconst fixed = await fix;\nconst verified = await verify;\n({ analysis, fixed, verified })\n```\n'
|
|
32909
|
-
},
|
|
32910
|
-
{
|
|
32911
|
-
"id": "repl/checkpoints-and-introspection",
|
|
32912
|
-
"title": "REPL checkpoints and introspection",
|
|
32913
|
-
"description": "Raising and answering durable checkpoints plus workspace(), agents(), and error diagnostics.",
|
|
32914
|
-
"uri": "agentprism://docs/repl/checkpoints-and-introspection",
|
|
32915
|
-
"mimeType": "text/markdown",
|
|
32916
|
-
"relatedTopics": [
|
|
32917
|
-
"repl/state-and-bindings",
|
|
32918
|
-
"repl/steering-queueing-and-cancellation",
|
|
32919
|
-
"repl/api-reference"
|
|
32920
|
-
],
|
|
32921
|
-
"bytes": 2667,
|
|
32922
|
-
"sha256": "62c5c8a50ca429c0573e3ce227348a4fb8133ccabc197da455a63fd6ad0a9f7d",
|
|
32923
|
-
"text": '# REPL checkpoints and introspection\n\nREPL checkpoints park a promise until a later eval explicitly supplies the human answer.\n\n## Raise a checkpoint\n\n```js\nconst approval = checkpoint("Proceed with the destructive migration?", {\n choices: ["approve", "reject"],\n default: "reject",\n});\n```\n\nThe eval output includes a line such as:\n\n```text\ncheckpoint c3: Proceed with the destructive migration?\n```\n\nThe promise remains live in the workspace. The host does not infer an answer from conversation text; answer delivery is an explicit data-plane operation.\n\n## Inspect pending checkpoints\n\n```js\nworkspace().checkpoints\n```\n\nEach pending row identifies the call id, question, and available option metadata. Retain the promise binding or recover its call id through `workspace()`.\n\n## Answer from a later eval\n\n```js\ncheckpoint.answer("c3", "approve")\n```\n\nThis returns `true` if that checkpoint was pending and the answer settled it, or `false` if the id was unknown/already settled. Delivery is first-wins and idempotent. The answer must be JSON-serializable; `undefined` is normalized to `null`.\n\nThe original continuation resumes during the same eval\'s settlement drain:\n\n```js\nconst decision = await approval;\ndecision\n```\n\nCheckpoint promises and questions survive daemon restart through the workspace snapshot.\n\n## Workspace introspection\n\n```js\nconst state = workspace();\n```\n\nThe returned plain object contains:\n\n- `bindings`: persistent user bindings with bounded type/size/status previews and call provenance where applicable.\n- `inFlight`: all unsettled bridge calls, including agents, queue/steer/cancel controls, sleeps, and checkpoints.\n- `checkpoints`: currently pending human questions.\n- `diagnostics`: restore reconciliation notes and retained settlement-drain faults.\n\nInspect it narrowly to avoid unnecessary context:\n\n```js\nworkspace().bindings.map(({ name, type, status }) => ({ name, type, status }))\n```\n\n## Agent-lane introspection\n\n```js\nagents()\n```\n\nreturns only live agent/queued-turn entries, including call id, model spec, task preview, state, strict-steering support, and queued-turn count. This is the authority for deciding whether `handle.steer()` has an active target.\n\n## Error diagnosis\n\nUncaught errors render into eval output. Errors associated with an agent/queued call include its stable call id and resolved backend when known. A drain or reconciliation problem that did not lose state is retained under `workspace().diagnostics`; state loss additionally produces a notice in the next eval output.\n\nUse the empty eval to drain late settlements, then inspect `workspace()` and `agents()` before deciding to cancel or reset.\n'
|
|
32924
|
-
},
|
|
32925
|
-
{
|
|
32926
|
-
"id": "repl/persistence-and-reset",
|
|
32927
|
-
"title": "REPL persistence, restore, and reset",
|
|
32928
|
-
"description": "Snapshot boundaries, restart reconciliation, queue recovery, snapshot refusal, disconnect drain, and reset.",
|
|
32929
|
-
"uri": "agentprism://docs/repl/persistence-and-reset",
|
|
32930
|
-
"mimeType": "text/markdown",
|
|
32931
|
-
"relatedTopics": [
|
|
32932
|
-
"repl/state-and-bindings",
|
|
32933
|
-
"repl/steering-queueing-and-cancellation",
|
|
32934
|
-
"repl/checkpoints-and-introspection"
|
|
32935
|
-
],
|
|
32936
|
-
"bytes": 2619,
|
|
32937
|
-
"sha256": "47bacb8c2ce0c640d2167d369a5f5ae487d164e058e324f7e0ee9bdf1fdd56f8",
|
|
32938
|
-
"text": "# REPL persistence, restore, and reset\n\nThe REPL workspace is durable per project. Named bindings, pending agent and checkpoint promises, queue state, and call-id sequencing persist across MCP client disconnects and daemon restarts.\n\n## Snapshot boundaries\n\nEvery state-changing eval and settlement drain persists the workspace. On first touch after restart, the server restores the QuickJS snapshot, re-registers host callbacks, and reconciles outstanding calls. The guest library itself is not re-evaluated into a restored VM.\n\nA later call can therefore continue with prior bindings:\n\n```js\nworkspace().bindings\n```\n\nand await a handle created before restart:\n\n```js\nconst answer = await worker;\n```\n\n## Session and queued-turn recovery\n\nPending founding turns and queued turns are reconciled from durable records. Eligible queued turns reattach lazily to their founding ACP session. The broker never substitutes a blank session if continuity cannot be preserved; lane-fatal recovery faults reject the lane instead.\n\nStrict steering is transient. A pending steering control cannot be replayed as a future prompt after restart and rejects rather than changing semantics. Durable queue turns remain future work.\n\n## Snapshot refusal\n\nSnapshot compatibility is tied to the snapshot format and the exact QuickJS WASM binary hash. Corrupt, incompatible-format, or mismatched-binary snapshots are not restored silently.\n\nA refused snapshot is renamed aside with a `.refused-<timestamp>` suffix and the workspace automatically resets. The next successful eval output begins with a notice naming the file and refusal reason. The refused file is never silently deleted.\n\nReconciliation summaries and retained drain errors are available under:\n\n```js\nworkspace().diagnostics\n```\n\n## Client disconnect drain\n\nWhen the last MCP client for a project disconnects, the workspace drains in-flight subagent turns and closes idle children. Persistent workspace state remains. The next eligible queued turn reattaches its founding session lazily.\n\n## Explicit reset\n\n```js\nreset()\n```\n\nrequests teardown after the current eval completes. It discards bindings, pending calls, checkpoints, and the active snapshot for that workspace. The eval that calls `reset()` still completes normally; the next touch creates a fresh VM.\n\nPrefer targeted cleanup first:\n\n- cancel a queued handle with `queued.cancel()`;\n- cancel an active call with `handle.cancel()` or `repl` interrupt by id;\n- break only a runaway eval with interrupt and no id.\n\nUse `reset()` when the whole interactive state is intentionally disposable or no longer trustworthy.\n"
|
|
32939
|
-
},
|
|
32940
|
-
{
|
|
32941
|
-
"id": "repl/api-reference",
|
|
32942
|
-
"title": "REPL API reference",
|
|
32943
|
-
"description": "Exact repl tool actions, every guest global, handle methods, combinator semantics, and environment limits.",
|
|
32944
|
-
"uri": "agentprism://docs/repl/api-reference",
|
|
32945
|
-
"mimeType": "text/markdown",
|
|
32946
|
-
"relatedTopics": [
|
|
32947
|
-
"repl/quickstart",
|
|
32948
|
-
"repl/agent-handles",
|
|
32949
|
-
"repl/checkpoints-and-introspection"
|
|
32670
|
+
"directory": "agentprism-workflow-authoring",
|
|
32671
|
+
"uri": "skill://agentprism-workflow-authoring/SKILL.md",
|
|
32672
|
+
"frontmatter": {
|
|
32673
|
+
"name": "agentprism-workflow-authoring",
|
|
32674
|
+
"description": "Write and run deterministic AgentPrism workflow scripts through the MCP workflow tool. Use for workflow DSL syntax, agent routing, structured output, checkpoints, composition, validation, background runs, status, stop, result retrieval, and same-run resume."
|
|
32675
|
+
},
|
|
32676
|
+
"resources": [
|
|
32677
|
+
{
|
|
32678
|
+
"path": "references/api-agents.md",
|
|
32679
|
+
"uri": "skill://agentprism-workflow-authoring/references/api-agents.md",
|
|
32680
|
+
"mimeType": "text/markdown",
|
|
32681
|
+
"digest": "sha256:9f6d10510812b4cd13846eca369d9c763cefb42051c376c08d08e3f84cc93a79",
|
|
32682
|
+
"size": 7565,
|
|
32683
|
+
"text": '# Workflow agent API reference\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\n## `agent(prompt, options?)` \u2014 full option table\n\nReturns the agent\'s final assistant text, or the schema-validated object when `schema` is set. Resolves to `null` when a *recoverable* failure survives all retries.\n\n| option | type | meaning |\n|---|---|---|\n| `label` | `string` | Display/telemetry name; also stamped on every live ACP event for this call. Always set it. Not part of the resume hash. |\n| `phase` | `string` | Assign this call to a phase explicitly (needed inside concurrent stages where the global `phase()` state would race). |\n| `schema` | JSON Schema object | Structured output. Plain object literal only \u2014 no schema builders exist in the realm. Part of the resume hash. |\n| `model` | `string` | Model spec: optional registered harness prefix plus a verbatim id, or a backend-only name. See [Model specs & routing](#model-specs--routing). Part of the resume hash. |\n| `tier` | `"small" \\| "medium" \\| "big"` | Coarse tier resolved from host config; beats phase/meta model, loses to explicit `model`. Part of the resume hash. |\n| `mode` | `string` | Exact ACP session mode id advertised by the selected backend/model. For trusted implementation/review work use Claude `bypassPermissions` or Codex `agent` when advertised. Claude `auto` is classifier-driven and may request permission; it is not full-access autonomy. Config preserves raw names/descriptions/metadata, and every selected id is validated before prompting. Part of call identity only when authored. |\n| `configOptions` | `Record<string, string \\| boolean>` | Exact ACP session option ids and authored values. Applied in ascending id order after model and before the prompt, with no aliases or coercion. `"model"` is reserved for the dedicated `model` field. Part of the resume hash only when non-empty, with sorted keys. With MCP, read the advertised-options table from `workflow` action `config` before choosing values. |\n| `agentType` | `string` | Bind a named subagent definition (tools allow/deny, model, isolation, role prompt). See [agentType definitions](#agenttype-definitions). Part of the resume hash. |\n| `isolation` | `"worktree"` | Run in a throwaway git worktree branched from the run cwd. **Always removed (worktree + branch) when the call ends** \u2014 edits are discarded; return work as data. Degrades to the shared tree outside a git repo (logged). |\n| `cwd` | `string` | Per-session working directory; relative resolves against the run\'s base cwd. Overridden by worktree isolation. Not hashed. |\n| `retries` | `number` | Retries after *recoverable* failures (default 0, host-overridable). Exhausted retries \u21D2 the call resolves `null`. |\n| `mcpServers` | `McpServerConfig[]` | MCP servers attached to this session. Stdio shape: `{ name, command, args: [], env: [{ name, value }] }` (`args`/`env` required, `env` is name/value pairs, not a map); `{ type: "http" \\| "sse", name, url, headers: [] }` also accepted. Not hashed. |\n| `images` | `PromptImage[]` | Base64 image blocks appended to the prompt; backends without image support get a bracketed text note. Not hashed. |\n| `meta` | `object` | ACP `_meta` merged into `session/new` \u2014 session-scoped extension passthrough (pairs with custom backends). Not hashed. |\n| `promptMeta` | `object` | ACP `_meta` merged into `session/prompt` \u2014 turn-scoped passthrough. Backend-computed keys win on conflict. Not hashed. |\n| `keepSession` | `boolean` | Skip release-time best-effort `session/close`; the non-secret re-attach record lands in `WorkflowRunResult.agentSessions` for host-side `loadSession()` / `resumeSession()`. Usage/auth pause failures are kept open automatically for managed continuation. Not identity-hashed; included in the input fingerprint. |\n\nAgent attempts have no model-facing wall-clock or idle timeout. They remain live until they complete,\nfail, or the host explicitly cancels the call or run. Same-ID MCP continuation may apply new runtime\nlimits, but it cannot change the persisted script, args, or effective agent configuration.\n\n## Model specs & routing\n\nA `model` string is resolved solely from its first segment, then delegated to the harness:\n\n| spec shape | routes to | notes |\n|---|---|---|\n| *(omitted)* | host-pinned/default backend | MCP: explicit `AGENTPRISM_DEFAULT_BACKEND` wins; when truly unset, zero-token readiness discovery pins one project default before validation/execution and preserves it across resume. SDK runner: configured default, historical fallback `claude`. The selected harness keeps its session default model. Most portable. |\n| `claude`, `codex`, `opencode`, `pi`, or `<custom-name>` | that registered harness | Backend-only: no model config call; the harness default remains active. |\n| `claude/<id>`, `codex/<id>`, `opencode/<id>`, `pi/<id>`, or `<custom-name>/<id>` | that registered harness | Match the first segment ASCII-case-insensitively and strip exactly one segment. Custom names take priority on collision. The remaining `<id>` is sent verbatim, including further `/` characters. For Pi, that remainder is its `<provider>/<model-id>` and Pi preserves any further slashes in the model id. |\n| any other string, including `anthropic/\u2026`, `openai/\u2026`, bare `opus`, or bare `gpt-\u2026` | host default backend | The **entire** authored string is sent verbatim; these are not routing aliases. |\n\nSelection is a single `session/set_config_option` with `configId: "model"` and the exact remaining string. There is no catalog matching, case folding, normalization, bracket parsing, nearest-neighbor selection, sibling effort/Fast option driving, retry, or echo verification. Brackets, dots, and provider-style prefixes are ordinary model-id characters.\n\nWhatever the harness returns is the outcome. A rejection follows the existing agent-error path with no resolution-specific code or model fallback event. `onModelFallback` and `WorkflowRunResult.fallbacks` remain public compatibility surfaces; model resolution does not emit entries, while pause recovery emits `kind: "continuation"` reattach/skip notices.\n\n## Structured output channels\n\nOne author API (`schema`), four fulfillment paths \u2014 chosen automatically per backend:\n\n| backend | channel |\n|---|---|\n| Claude | native `outputFormat`, schema normalized to Anthropic\'s structured-outputs subset (e.g. `oneOf` \u2192 `anyOf`; unsupported keywords/formats stripped on the wire) |\n| Codex | native strict `outputSchema` (OpenAI strict subset normalization) |\n| Pi | a client-hosted `StructuredOutput` MCP tool injected when the agent advertises HTTP MCP support; common prompt-embedded schema and validated final-text JSON fallback |\n| OpenCode / custom ACP | a client-hosted **`StructuredOutput` MCP tool** injected into the session when the agent advertises HTTP MCP support (an agent may show it as `structured_output_StructuredOutput`); otherwise prompt-embedded schema + JSON parse of the final message. Custom backends can opt out of tool injection with `structuredOutputTool: false`. |\n\nPi accepts stdio, Streamable HTTP, and SSE MCP servers; ACP-transport MCP hosting remains client-side.\n\nIn every channel the runner coerces + validates client-side and re-prompts a bounded number of times; the final miss fails the call with non-recoverable `SCHEMA_NONCOMPLIANCE`. Constraints stripped from the wire are still enforced client-side \u2014 an exotic schema keyword shows up as re-prompt churn, so keep schemas simple.\n'
|
|
32684
|
+
},
|
|
32685
|
+
{
|
|
32686
|
+
"path": "references/api-control-flow.md",
|
|
32687
|
+
"uri": "skill://agentprism-workflow-authoring/references/api-control-flow.md",
|
|
32688
|
+
"mimeType": "text/markdown",
|
|
32689
|
+
"digest": "sha256:8ea5a881325679c70830bf7284a06de3bb8418bee20b2f009aa9a8d34aef0e77",
|
|
32690
|
+
"size": 6212,
|
|
32691
|
+
"text": '# Workflow control-flow API reference\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\n## DSL globals \u2014 complete signatures\n\n```\nagent(prompt, options?) \u2192 Promise<string | object | null>\nparallel(thunks) \u2192 Promise<results[]> // barrier; input order; failed slot = null\npipeline(items, ...stages) \u2192 Promise<results[]> // no inter-stage barrier; stage(prev, original, index); failed item = null\nworkflow(nameOrScript, args?) \u2192 Promise<unknown> // one nesting level; names resolve from the host\'s workflows folder, inline scripts always work\ngate(thunk, validator, { attempts = 3 }) \u2192 { ok, value, verdict, attempts }\n // thunk(feedback, attempt); validator(result) \u2192 { ok, feedback?, ... } | boolean | null (may be async / an agent call)\nretry(thunk, { attempts = 3, until? }) \u2192 last result // thunk(attempt); stops early when until(result)\nverify(item, { reviewers = 2, threshold = 0.5, lens? })\n \u2192 { real, realCount, total, votes: [{ real?, reason? }] }\n // N adversarial reviewers prompted to REFUTE; lens (string | string[]) rotates focus per reviewer\njudgePanel(attempts, { judges = 3, rubric = "overall quality and correctness" })\n \u2192 { index, attempt, score, judgments } // mean 0\u20131 score per candidate; stable tie-break by index\nloopUntilDry({ round, key = JSON.stringify, consecutiveEmpty = 2, maxRounds = 50 })\n \u2192 unique items[] // round(i) returns items; stops after N dry rounds; agent-limit exhaustion returns the partial result\ncompletenessCheck(taskArgs, results) \u2192 { complete, missing?: string[] }\ncheckpoint(promptText, options?) \u2192 Promise<reply> // journaled human gate; zero tokens\nphase(title) \u2192 void // open a named phase\nlog(message) \u2192 void // console.log/info/warn/error route here too\nargs // the host-provided input value, verbatim\ncwd // the run\'s base working directory (string); process.cwd() returns it too\n```\n\nFor `gate()`, `value` is the final producer result and `verdict` is the exact last completed\nvalidator return, including any extra structured fields. `{ ok: true }` and bare `true` pass;\n`{ ok: false, feedback? }`, bare `false`, and `null` reject. Only object feedback is threaded into\nthe next producer attempt. A producer result of `null` is still passed to the validator. Producer\nor validator exceptions propagate immediately, so no partial gate result is returned and no later\nattempt runs. An explicit unsupported `undefined` validator return is a rejection represented as\n`verdict: null`. If the script returns the gate result, its complete verdict is persisted and may\nreach the host; keep evidence concise and never put credentials or other secrets in verdict data.\n\n`verify`, `judgePanel`, and `completenessCheck` spawn their subagents on the run\'s default model \u2014 hand-roll with `parallel` + `agent` to pin panel members to specific backends.\n\n## `checkpoint()` options\n\n| option | type | meaning |\n|---|---|---|\n| `kind` | `"confirm" \\| "input" \\| "select"` | Reply shape: boolean-ish / free text / one of `choices`. Affects the journal hash and the host UI widget. |\n| `choices` | `string[]` | For `kind: "select"`. |\n| `default` | `unknown` | Reply taken in the default headless mode \u2014 journaled like a real reply. Defaults to `true`. |\n| `headless` | `"default" \\| "abort" \\| "pause"` | No live channel: `"default"` takes `default ?? true`, `"abort"` aborts, and `"pause"` creates a persisted `checkpoint_required` pause. Default `"default"`. |\n| `timeoutMs` | `number` | Deadline for the interactive prompt. |\n\nThe host supplies the live human channel (elicitation in the MCP server; `ExecOptions.confirm` in the SDK), and that channel wins even when `headless:"pause"` is declared. A durable pause carries non-secret `checkpointContext`; MCP continues the same run with `checkpointReplies:{ [context.callIndex]: decision }`. The first strict-JSON decision stored under the run lease is authoritative forever: repeats are idempotent and conflicts are ignored. Detached runs never pause for a checkpoint unless the author opts into `"pause"`.\n\n## Error codes (`WorkflowError.code`)\n\n| code | recoverable | engine behavior |\n|---|---|---|\n| `AGENT_CANCELLED` | yes | The host selected this in-flight call for cancellation. It resolves `null` immediately through an engine race, skips retries, leaves the run live, and is recorded as a failed call rather than a replayable journal result. |\n| `AGENT_EMPTY_OUTPUT` | yes | No assistant text on a schema-less call; same retry-then-`null`. |\n| `AGENT_EXECUTION_ERROR` | yes* | Generic agent failure (*refusal/truncation variants are non-recoverable). |\n| `SCHEMA_NONCOMPLIANCE` | no | Structured output never validated after the re-prompt ladder. Halts the run (catchable in-script). |\n| `PROVIDER_USAGE_LIMIT` | no | Quota/rate wall \u2014 the run **pauses** (journaled, resumable), with the provider\'s reset hint. |\n| `AGENT_LIMIT_EXCEEDED` | no | `maxAgents` cap hit. |\n| `AUTH_REQUIRED` | no | Backend needs authentication. `WorkflowManager` returns a resumable pause with `reason: "auth_required"` and redacted `authContext`; a direct runner throws. The host completes auth before resuming/retrying. |\n| `CHECKPOINT_REQUIRED` | no | `headless: "pause"` reached without a live channel. `WorkflowManager` returns `reason: "checkpoint_required"` plus non-secret `checkpointContext`; resume with `checkpointReplies` or live confirm. |\n| `SCRIPT_VALIDATION_ERROR` | no | Script failed parse/validation (bad meta, nondeterministic API, bad `meta.backends` shape). |\n| `SCRIPT_ERROR` | no | The script itself crashed (uncaught throw, floated rejection). |\n| `WORKFLOW_ABORTED` | \u2014 | Real cancellation (pause/stop/host signal) \u2014 never used for crashes. |\n\n`loopUntilDry` absorbs `AGENT_LIMIT_EXCEEDED` from its rounds and returns the partial result; everywhere else it propagates.\n'
|
|
32692
|
+
},
|
|
32693
|
+
{
|
|
32694
|
+
"path": "references/api-resume-and-backends.md",
|
|
32695
|
+
"uri": "skill://agentprism-workflow-authoring/references/api-resume-and-backends.md",
|
|
32696
|
+
"mimeType": "text/markdown",
|
|
32697
|
+
"digest": "sha256:a58fe072eb7289a74057e3a57489512b9e9547435a3ffdc29e968aaf853085bb",
|
|
32698
|
+
"size": 2892,
|
|
32699
|
+
"text": '# Workflow continuation and extension reference\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\n## Same-run continuation journal\n\nEach `agent()` and `checkpoint()` result is journaled under a monotonic call index and identity hash.\nMCP `{ action:"resume", runId }` reloads the same run\'s persisted script, args, canonical effective\nagent configuration, journal, events, cumulative usage, and checkpoint decisions. It returns the\nsame `runId`; there is no public execution-attempt or child-run model.\n\nThe canonical agent identity fields are `prompt`, resolved `model`, authored `mode`, non-empty\nsorted `configOptions`, `tier`, `phase`, `agentType`, resolved agent definition, and `schema`.\nExact journal hits reconstruct completed calls without current provider usage. Eligible interrupted\nACP calls may reattach at the live boundary. New live usage is added to the prior cumulative total.\n\nThe host persists a versioned canonical admission snapshot before execution. Same-ID continuation\nuses it without new provider/model elicitation. Missing, invalid, or uncovered admission metadata\nfails closed. Checkpoint replies are first-writer-wins under the run lease and become permanent\njournal facts.\n\n## <a name="custom-backends-metabackends"></a>Custom backends \u2014 `meta.backends`\n\n```js\nexport const meta = {\n name: "\u2026", description: "\u2026",\n backends: {\n browser: {\n command: "browser-acp", // required: executable (absolute or on PATH)\n args: ["--headless"], // default []\n env: { BROWSER_PROFILE: "qa" }, // merged over the child\'s inherited env\n sessionMeta: { viewport: "desktop" },\n structuredOutputTool: true,\n },\n },\n};\n```\n\nScript-declared backends spawn commands on the host and are trust-gated before admission. The MCP\nserver obtains explicit approval; SDK hosts use `allowScriptBackends`, `ExecOptions.scriptBackends`,\nor their configured environment policy. A declined backend aborts admission rather than rerouting.\nHost-registered names win. Approved canonical backend definitions are stored in the run\'s admission\nsnapshot so continuation never re-elicits or changes them.\n\n## <a name="agenttype-definitions"></a>`agentType` definitions\n\nMarkdown files at `<runCwd>/.agentprism/agents/<name>.md` and\n`~/.agentprism/agents/<name>.md`; project wins:\n\n```markdown\n---\ndescription: Read-only security auditor\ntools: [read, grep, glob]\ndisallowedTools: [bash]\nmodel: claude/opus[1m]\nisolation: worktree\n---\nYou are a security auditor. Report findings; never modify files.\n```\n\nThe body is prepended to the task. An unknown type warns and degrades to defaults. The resolved\ndefinition participates in agent identity, so a same-run continuation only replays the exact\ndefinition captured by its journal and admitted configuration.\n'
|
|
32700
|
+
},
|
|
32701
|
+
{
|
|
32702
|
+
"path": "references/checkpoints-and-quality.md",
|
|
32703
|
+
"uri": "skill://agentprism-workflow-authoring/references/checkpoints-and-quality.md",
|
|
32704
|
+
"mimeType": "text/markdown",
|
|
32705
|
+
"digest": "sha256:dccdb201fd4545e3aacc6c39be618624b9031005dd481509a49c823661b6a12d",
|
|
32706
|
+
"size": 3958,
|
|
32707
|
+
"text": '## Built-in quality loops\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\nThese helpers spawn their own subagents on the default model. Hand-roll with `parallel` + `agent` when you want panel members on specific backends. Full signatures are in [`api-control-flow.md`](api-control-flow.md).\n\n| helper | shape | use for |\n|---|---|---|\n| `gate(produce, validate, { attempts })` | produce \u2192 validate \u2192 feed `feedback` back; return `{ ok, value, verdict, attempts }` | produce-until-a-reviewer-approves loops that need the final review evidence |\n| `retry(thunk, { attempts, until })` | bounded retry until `until(result)` holds | flaky single steps |\n| `verify(item, { reviewers, threshold, lens })` | N adversarial reviewers vote `real`/not | killing plausible-but-wrong findings |\n| `judgePanel(attempts, { judges, rubric })` | score candidates 0\u20131 against a rubric, return the best | picking among independent solutions |\n| `loopUntilDry({ round, key, consecutiveEmpty, maxRounds })` | repeat a round, dedup by `key`, stop when dry | unknown-size discovery (bugs, edge cases) |\n| `completenessCheck(args, results)` | one critic lists what\'s still missing | a final "what did we not cover?" pass |\n\nThe `gate` pattern, spelled out \u2014 note how the producer thunk threads the validator\'s feedback into a *fresh* agent\'s prompt (sessions have no memory):\n\n```js\nconst outcome = await gate(\n (feedback, attempt) => agent(\n `Implement the fix described here:\\n${JSON.stringify(plan)}\\n` +\n (feedback ? `\\nA reviewer rejected attempt ${attempt}: ${feedback}\\nAddress every point.` : ""),\n { label: `fix:${attempt + 1}`, model: "codex/gpt-5.6-sol", mode: "agent" },\n ),\n (result) => agent(\n `Run the test suite and review this change summary:\\n${result}\\n` +\n `Return ok=true only if tests pass and the fix is correct; include the reviewed commit SHA.`,\n { label: "gate-review", model: "claude/opus[1m]", mode: "bypassPermissions", schema: { type: "object", additionalProperties: false,\n required: ["ok"], properties: { ok: { type: "boolean" }, feedback: { type: "string" },\n commitSha: { type: "string" } } } },\n ),\n { attempts: 3 },\n);\nif (!outcome.ok) log(`reviewer never approved after ${outcome.attempts} attempts`);\nelse log(`reviewer approved commit ${outcome.verdict?.commitSha ?? "(unspecified)"}`);\n```\n\nFeedback is the producer\'s only context for the next attempt. Interpolate everything it needs, and name only files that provably exist.\n\n## Human gates: `checkpoint()`\n\n`checkpoint(promptText, options?)` is a zero-token, journaled human gate. With MCP elicitation (or a live SDK `confirm` callback) it waits for that reply; without a live channel, its default mode takes `default ?? true` immediately, so detached runs never hang.\n\n```js\nconst proceed = await checkpoint(`Apply this plan?\\n${JSON.stringify(plan, null, 2)}`, {\n kind: "confirm", // "confirm" | "input" | "select"\n default: false, // default headless mode takes this (or true)\n // headless: "abort", // abort when no live human is attached\n // headless: "pause", // or persist a resumable human-decision pause\n});\nif (!proceed) return { applied: false, plan };\n```\n\n`kind: "input"` resolves to free text, `kind: "select"` to one of `choices`. How the question reaches a human is the host\'s job (elicitation in the MCP server; `ExecOptions.confirm` in the SDK). With no live channel, `headless: "default"` (the default) takes `default ?? true`, `"abort"` aborts, and `"pause"` returns the run with `reason:"checkpoint_required"` plus non-secret `checkpointContext`. Continue that same run with `checkpointReplies: { [context.callIndex]: decision }`. The first strict-JSON decision persisted under the lease wins forever; repeats are idempotent and conflicts are ignored. Put a checkpoint before anything hard to reverse.\n'
|
|
32708
|
+
},
|
|
32709
|
+
{
|
|
32710
|
+
"path": "references/composition-and-failure.md",
|
|
32711
|
+
"uri": "skill://agentprism-workflow-authoring/references/composition-and-failure.md",
|
|
32712
|
+
"mimeType": "text/markdown",
|
|
32713
|
+
"digest": "sha256:b29542b9ff475729a58e7217df84a01a5fa5d7d13ba13c68c80f2d9e15a06f5f",
|
|
32714
|
+
"size": 6290,
|
|
32715
|
+
"text": '## The `meta` header\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\nEvery script must **begin** with `export const meta = {...}` as a plain object literal (no computed values \u2014 it is parsed from the source text before anything runs):\n\n```js\nexport const meta = {\n name: "fix-flaky-tests", // required\n description: "Find flaky tests and fix them", // required\n phases: [ // optional; one { title, detail?, model? } entry\n { title: "Find", model: "opencode/zai/glm-5.2" }, // per phase() call, matched by exact title;\n { title: "Fix" }, // a phase model is that phase\'s default\n ],\n model: "claude/sonnet", // optional run-wide default model\n backends: { /* optional custom ACP agents \u2014 see "Custom ACP backends" */ },\n};\n```\n\nPer-agent model resolution order: explicit `agent({ model })` > `agent({ tier })` > the current phase\'s `model` > `meta.model` > the host session\'s default. So `meta.phases[].model` gives a whole phase a backend without repeating it on every call.\n\n## Fan-out: `parallel` and `pipeline`\n\n```js\n// parallel: an array of THUNKS (not promises!) run concurrently \u2014 a barrier that\n// resolves in input order. A failed slot resolves to null; filter before use.\nconst sweeps = (await parallel([\n () => agent("Audit error handling in src/server", { label: "sweep:errors", schema: FINDINGS }),\n () => agent("Audit input validation in src/api", { label: "sweep:input", schema: FINDINGS }),\n])).filter(Boolean);\n\n// pipeline: each item flows through the stages independently \u2014 NO barrier between\n// stages, so item A can be in stage 2 while item B is still in stage 1.\n// Stages receive (previousResult, originalItem, index).\nconst verified = (await pipeline(\n sweeps.flatMap((s) => s.findings),\n (f) => agent(`Adversarially verify this finding \u2014 try to refute it:\\n${JSON.stringify(f)}`,\n { label: `verify:${f.file}`, schema: VERDICT }),\n (verdict, f) => ({ ...f, real: verdict.real }),\n)).filter(Boolean).filter((f) => f.real);\n```\n\n**Default to `pipeline`** for multi-stage work. Add a `parallel` barrier only when the next stage needs *all* prior results at once: dedup across the full set, early-exit on a zero count, or prompts that compare "the other findings". The test is the **information dependency** \u2014 a barrier\'s cost is real, because the fastest worker idles for the slowest. All coordination lives in script code: agents cannot see each other, so never ask an agent to "check with the other reviewers" or "spawn helpers". Passing a promise instead of a thunk to `parallel` is a `TypeError` \u2014 wrap every call: `() => agent(...)`.\n\nFan-out also contends for the **working tree**, not just the concurrency limiter. Two agents running builds or test suites in the same checkout collide on build outputs, caches, and lockfiles, and concurrent `git fetch`es contend on the same `.git`. Give run-things agents `isolation: "worktree"` when the commits they must inspect are reachable from the run cwd\'s repository, or serialize them; fan out freely only the agents that just read.\n\nThe host caps concurrent agents per run (default 8); hand `parallel`/`pipeline` as many items as the task needs and let the limiter schedule them. The cap counts active agent attempts, not authored branches: queued branches begin as other attempts finish, and a branch that exhausts its timeout settles to `null` and frees its slot. `workflow(nameOrScript, args)` nests another workflow inline (one level deep, sharing this run\'s limiter) \u2014 inline script strings always work; saved names resolve when the host serves a workflows folder.\n\n## Failure semantics \u2014 design for `null`\n\n- A **recoverable** failure (timeout, empty output, transient execution error) is retried per the call\'s `retries` (default 0), then the call **resolves to `null`** \u2014 inside `parallel`/`pipeline` *and* as a bare `await agent(...)`. Null-check anything load-bearing, and set `retries: 1\u20132` on steps you can\'t afford to lose.\n- A host can settle one runaway in-flight call with MCP `{ action: "stop", runId, callIndex }` or SDK `manager.cancelAgentCall(runId, callIndex)`. The call resolves to `null` with `AGENT_CANCELLED`, skips every configured retry, and does not abort the run or its siblings. Its failed call record is not cached as a journal result, so a later resume runs that occurrence live.\n- A **non-recoverable** failure (schema never validated, script bug) throws and fails the run. You *may* `try/catch` around an `agent()` call to degrade gracefully \u2014 rethrow anything you can\'t meaningfully handle. In particular, **always rethrow pause-class errors** (`err.code === "PROVIDER_USAGE_LIMIT"` or `"AUTH_REQUIRED"`): they must propagate out of the script so the engine can pause the run resumably \u2014 swallowing one converts that pause into a fake, lossy completion.\n- A **provider quota wall, missing backend authentication, or opted-in durable checkpoint pauses a managed run instead of failing it** \u2014 the journal checkpoints and the host can resume after the provider quota refills, authentication completes, or a checkpoint decision is supplied. Direct `runner.run()` calls still receive the `AUTH_REQUIRED` error because they have no manager lifecycle.\n- Per-call `retries` can override the host retry default. Agent attempts otherwise remain live until they complete, fail, or the host explicitly cancels the call or run.\n\n## Phases\n\n```js\nphase("Explore"); // open a named phase: subsequent agents group under it\n\nconst found = [];\nwhile (found.length < 20) {\n const r = await agent("Find one more edge case not in: " + JSON.stringify(found.map((f) => f.name)),\n { label: `edge:${found.length}`, schema: EDGE });\n if (!r) break;\n found.push(r);\n}\n```\n\nTerminate every loop on a bound the script controls. The agent-count limit (`maxAgents`) is hard: once exhausted, further `agent()` calls throw `AGENT_LIMIT_EXCEEDED`. `phase()` groups agents in progress UIs and run logs; `log(msg)` (and `console.log`) append to the run log \u2014 narrate what matters, especially anything you drop.\n'
|
|
32716
|
+
},
|
|
32717
|
+
{
|
|
32718
|
+
"path": "references/determinism-and-resume.md",
|
|
32719
|
+
"uri": "skill://agentprism-workflow-authoring/references/determinism-and-resume.md",
|
|
32720
|
+
"mimeType": "text/markdown",
|
|
32721
|
+
"digest": "sha256:0afcdc404ac0aa5c48c92499684852ddd4c823cebdb56bc29f73780b3143fa9e",
|
|
32722
|
+
"size": 3084,
|
|
32723
|
+
"text": '## Determinism and same-run continuation\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\nOne MCP run owns one immutable logical execution. Every `agent()` and `checkpoint()` result is\njournaled under a deterministic call index. `{ action:"resume", runId }` reconstructs and continues\nthat exact run; it never forks a child execution and never accepts changed script, args, or agent\nconfiguration.\n\n- Direct `Date.now()`, `Math.random()`, and no-arg `new Date()` / `Date()` fail validation. Pass nondeterministic values through the original Run `args`.\n- An agent identity hashes the prompt, resolved model, authored mode, non-empty sorted `configOptions`, tier, phase, agent type/definition, and schema. A separate fingerprint covers label, cwd/isolation, session retention, images, MCP servers, metadata, and approved script backends.\n- At admission the host atomically stores a versioned canonical effective occurrence map, default model, approved script backends, stable selection hash, source, and timestamp. Raw elicitation form fields are not stored.\n- Strict coverage is permanent. If live control flow reaches an occurrence the admission pass did not cover, that occurrence fails before ACP dispatch and is recorded durably. Later continuation refuses; it never shifts a configuration to another ordinal.\n- Exact index/hash journal hits rebuild script state without spawning a provider session, adding provider usage, or appending duplicate journal entries. Live usage is added to the run\'s existing cumulative total.\n- A usage/auth-interrupted root call may reattach its recorded ACP session when its call identity, inputs, cwd, backend pool identity, and reopen capability agree. Failed eligibility falls back to a fresh live call within the same run, never a child run.\n- The persisted event stream remains one stream for the run. A continuation appends a `resumed` event and new execution observations at the existing durable cursor.\n- MCP status is an immediate snapshot. Reissue it or consume the event resource for later progress.\n\n### Durable checkpoints\n\nFor a `headless:"pause"` checkpoint, resume with\n`{ action:"resume", runId, checkpointReplies:{ [checkpointContext.callIndex]: decision } }`.\nThe decision must be strict JSON. Under the run lease, the first answer is journaled before\ncontinuation. An identical repeat is idempotent. A different later answer is ignored and reported\nagainst the durable first answer. Cold reconstruction replays the decision forever.\n\n### Failure and restart\n\nA paused or failed run with valid admission metadata can continue. A completed or aborted run is\nterminal. A pre-contract record without the required canonical admission may remain observable but\nmust be replaced with a fresh `{ action:"run", ... }`; no migration or inferred mapping exists.\n\nGive repeated calls stable labels and narrate decisions with `log()`. Retain the original run ID:\nthe same ID addresses its script, event stream, cumulative usage, status, and result.\n'
|
|
32724
|
+
},
|
|
32725
|
+
{
|
|
32726
|
+
"path": "references/environment-and-tools.md",
|
|
32727
|
+
"uri": "skill://agentprism-workflow-authoring/references/environment-and-tools.md",
|
|
32728
|
+
"mimeType": "text/markdown",
|
|
32729
|
+
"digest": "sha256:a53fafb94a3001efeee3c8e62293764f9ecb5303d50e84aa27761a98d22229f2",
|
|
32730
|
+
"size": 5429,
|
|
32731
|
+
"text": '## Working directory, isolation, confinement\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\n- Every agent session runs in the run\'s base `cwd` unless the call narrows it: `agent({ cwd: "packages/api" })` (relative resolves against the base).\n- `isolation: "worktree"` runs the agent in a **throwaway git worktree** (`<repoRoot>/.agentprism/worktrees/\u2026`) so parallel agents can edit without colliding. The worktree and its branch are **always deleted when the call ends \u2014 an isolated agent\'s file edits are discarded**. Have isolated agents *return their work as data* (a unified diff, a file map, a report) and apply it in a later non-isolated step; use worktrees for experiments, builds, and verification, not for persistent edits. Outside a git repo, isolation degrades to the shared tree with a logged notice.\n- `mode` requests an exact agent-advertised ACP session mode. Config returns raw names, descriptions, and `_meta`; use those backend-owned explanations instead of inferring from an id. For trusted implementation/review workflows use Claude `bypassPermissions` or Codex `agent` when advertised. Claude `auto` uses a model classifier and may still request permission, so it is not the full-access autonomous mode. Automatic preflight rejects a mode the selected backend/model does not advertise. Only set `mode` on calls whose `model` you also pin. Use an advertised read-only/plan mode for reviewers that must not write.\n- `agentType: "<name>"` binds a reusable subagent definition \u2014 a Markdown file at `<cwd>/.agentprism/agents/<name>.md` (project) or `~/.agentprism/agents/<name>.md` (user; project wins) whose frontmatter sets tool allow/deny lists, a model, and isolation, and whose body is the role prompt. An unknown name logs a warning and degrades to defaults.\n\n## Where a mutating workflow runs\n\nThe run\'s base `cwd` is the USER\'S checkout \u2014 the working copy they launched the host from. Treat it as borrowed: committing onto whatever branch is checked out, switching branches, or resetting it are defects unless the user asked for exactly that. A script that commits should verify its target workspace in a preflight step, or create its own workspace idempotently, and refuse on a mismatch rather than adapt. `isolation: "worktree"` is NOT such a workspace \u2014 it is per-call and throwaway. Note also that a throwaway worktree branches from the run cwd\'s repository: an isolated agent sees another agent\'s commits only when they are reachable there.\n\n## Wiring tools and inputs into a call\n\n- `mcpServers: [{ name, command, args: [], env: [] }]` attaches MCP servers to that agent\'s session \u2014 the portable way to hand any backend a capability (image generation, a browser, a ticket system). The agent sees the server\'s tools natively. Note `env` is a list of `{ name, value }` pairs (ACP shape), not an object map; HTTP/SSE servers use `{ type: "http", name, url, headers: [] }`.\n- `images: [...]` appends base64 image blocks to the prompt (backends without image support receive a bracketed text note instead).\n- `meta` / `promptMeta` pass generic ACP `_meta` through to `session/new` / `session/prompt` \u2014 the escape hatch for driving a custom agent\'s extension surface.\n- `keepSession: true` keeps a successful agent\'s ACP session re-openable after the run: the re-attach record (sessionId, backend, effective pool identity, cwd, reopen capabilities) lands in `WorkflowRunResult.agentSessions`, and the HOST can continue that conversation later via `runner.loadSession()`. Usage/auth pause failures are kept open automatically so managed resume can continue the interrupted occurrence. Scripts themselves never request reattach.\n\n### Custom ACP backends\n\nAny process that speaks ACP over stdio can serve `agent()` calls \u2014 an in-house browser-QA agent, an image generator, a domain-specific executor. Two ways in:\n\n1. **Host-registered** (preferred): the embedder passes `createAcpRunner({ backends: { browser: { command: "/abs/browser-acp" } } })`; the script just routes with `model: "browser"`.\n2. **Script-declared**: the script itself declares the backend in `meta.backends` \u2014 but declarations are **inert until the host approves them** (an elicitation in the MCP server; `allowScriptBackends` in the SDK), because they spawn commands on the host machine. Don\'t rely on them silently working.\n\n```js\nexport const meta = {\n name: "checkout-qa",\n description: "Implement, then QA the checkout flow in a real browser",\n backends: {\n browser: { command: "browser-acp", args: ["--headless"] }, // requires host approval\n },\n};\n\nconst change = await agent("Implement the coupon-code field per the spec in docs/coupon.md.",\n { label: "implement" }); // default backend\nconst verdict = await agent(\n `Open the app, walk through checkout with coupon SAVE20, and verify the discount line. Change summary:\\n${change}`,\n { label: "qa", model: "browser", // the custom agent\n schema: { type: "object", additionalProperties: false, required: ["passed"],\n properties: { passed: { type: "boolean" }, notes: { type: "string" } } } },\n);\nreturn { change, qa: verdict };\n```\n\nStructured output works on custom backends through the same injected-tool/fallback ladder as OpenCode \u2014 no special-casing in the script.\n'
|
|
32732
|
+
},
|
|
32733
|
+
{
|
|
32734
|
+
"path": "references/examples.md",
|
|
32735
|
+
"uri": "skill://agentprism-workflow-authoring/references/examples.md",
|
|
32736
|
+
"mimeType": "text/markdown",
|
|
32737
|
+
"digest": "sha256:b903620ba5356cc9863d5a9af8e126f29f063177216a41ce27ee95d9c88cc55d",
|
|
32738
|
+
"size": 6333,
|
|
32739
|
+
"text": '## Worked example \u2014 cross-vendor build with every major primitive\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\n```js\nexport const meta = {\n name: "feature-build",\n description: "Plan, gate on approval, implement, cross-vendor review, fix until green",\n phases: [{ title: "Plan" }, { title: "Implement" }, { title: "Review" }],\n};\n\nconst PLAN = { type: "object", additionalProperties: false, required: ["steps", "risks"],\n properties: {\n steps: { type: "array", items: { type: "string", description: "One concrete implementation step" } },\n risks: { type: "array", items: { type: "string" } } } };\nconst VERDICT = { type: "object", additionalProperties: false, required: ["ok"],\n properties: { ok: { type: "boolean" },\n feedback: { type: "string", description: "Required when ok=false: concretely what to change" } } };\n\nphase("Plan");\nconst plan = await agent(\n `Study this repo, then write an implementation plan for: ${args.feature}. Keep steps concrete.`,\n { label: "plan", model: "opencode/zai/glm-5.2", schema: PLAN },\n);\n\nconst approved = await checkpoint(\n `Implement "${args.feature}" with this plan?\\n- ${plan.steps.join("\\n- ")}\\nRisks: ${plan.risks.join("; ")}`,\n { kind: "confirm", default: true },\n);\nif (!approved) return { implemented: false, plan };\n\nphase("Implement");\nconst outcome = await gate(\n (feedback, attempt) => agent(\n `Implement: ${args.feature}\\nPlan:\\n- ${plan.steps.join("\\n- ")}\\n` +\n `Run the project\'s tests before finishing and report results.` +\n (feedback ? `\\n\\nReviewer feedback on attempt ${attempt}:\\n${feedback}\\nAddress every point.` : ""),\n { label: `implement:${attempt + 1}`, model: "codex/gpt-5.6-sol", mode: "agent", retries: 1 },\n ),\n async (report) => {\n if (!report) return { ok: false, feedback: "implementation agent produced no result" };\n phase("Review");\n const reviews = (await parallel([ // two reviewers on different vendors\n () => agent(`Review the working-tree diff for correctness. Implementer\'s report:\\n${report}`,\n { label: "review:correctness", model: "claude/opus[1m]", mode: "bypassPermissions", schema: VERDICT }),\n () => agent(`Review the working-tree diff for regressions and missing tests. Report:\\n${report}`,\n { label: "review:coverage", model: "opencode/zai/glm-5.2", schema: VERDICT }),\n ])).filter(Boolean);\n const rejections = reviews.filter((r) => !r.ok);\n return rejections.length\n ? { ok: false, feedback: rejections.map((r) => r.feedback).join("\\n"), reviews }\n : { ok: true, reviews };\n },\n { attempts: 3 },\n);\n\nreturn { implemented: outcome.ok, attempts: outcome.attempts, reviewVerdict: outcome.verdict, plan };\n```\n\nThese trusted implementation/review calls pin Codex `agent` and Claude `bypassPermissions` for\nfull tool autonomy. Confirm both ids in the live catalog first. Claude `auto` uses a model classifier\nand may request permission; it is not the full-access mode. For a read-only planner, select the\nexact advertised read-only/plan mode instead.\n\n## Worked example \u2014 fully backend-agnostic audit\n\nNo `model` anywhere: this script runs unchanged on whatever backend the host defaults to.\n\n```js\nexport const meta = {\n name: "edge-case-audit",\n description: "Exhaustively hunt edge-case bugs in a target dir, verify each, report gaps",\n phases: [{ title: "Hunt" }, { title: "Verify" }],\n};\n\nconst BUGS = { type: "object", additionalProperties: false, required: ["bugs"],\n properties: { bugs: { type: "array", items: { type: "object", additionalProperties: false,\n required: ["file", "scenario"], properties: {\n file: { type: "string", description: "Repo-relative path you actually opened" },\n scenario: { type: "string", description: "Concrete input/state \u2192 wrong behavior" } } } } } };\n\nphase("Hunt");\nconst seen = []; // what earlier rounds reported, threaded into each new prompt\nconst candidates = await loopUntilDry({\n round: async (i) => {\n const r = await agent(\n `Round ${i + 1}: find edge-case bugs in ${args.target} not already in this list:\\n` +\n JSON.stringify(seen) + `\\nOnly report what you can ground in code you read.`,\n { label: `hunt:${i + 1}`, schema: BUGS },\n );\n const bugs = r ? r.bugs : [];\n seen.push(...bugs);\n return bugs; // loopUntilDry dedups these by `key` across rounds\n },\n key: (b) => `${b.file}:${b.scenario}`,\n consecutiveEmpty: 2,\n maxRounds: 8,\n});\n\nphase("Verify");\nconst confirmed = (await pipeline(\n candidates,\n (bug) => verify(bug, { reviewers: 3, threshold: 0.66, lens: ["correctness", "reproducibility"] }),\n (v, bug) => (v.real ? bug : null),\n)).filter(Boolean);\n\nconst gaps = await completenessCheck(args, confirmed);\nlog(`${confirmed.length}/${candidates.length} confirmed; complete=${gaps.complete}`);\nreturn { confirmed, missing: gaps.missing ?? [] };\n```\n\n## Automatic validation before admission\n\nThe MCP `workflow` tool validates every run automatically before admission: static parse, mocked dry run, then routed no-prompt config checks. Invalid scripts return `status:"rejected"` diagnostics without creating a run ID, reserving a background slot, or spending tokens. When pinning model, mode, or `configOptions`, use `action:"config"` first.\n\nThe mocked pass executes reachable script control flow with schema-conforming fabricated agent results. It can prove that syntax, metadata, helper calls, and reachable branches are structurally executable, but it cannot prove prompt quality, real-world judgment, or convergence through every branch. Keep loops bounded in script code and inspect validation warnings for declared phases that the default fabricated path did not reach.\n\nThe routed config pass probes each distinct backend/model pair without prompting. Unknown option ids, invalid select values, wrong value types, and the reserved `"model"` config key reject the script with direct alternatives. A backend that cannot be probed produces an explicit warning and leaves only that backend\'s option domain unverified.\n\nFor model/config details, read [`models-and-config.md`](models-and-config.md). For exact-run recovery semantics, read [`determinism-and-resume.md`](determinism-and-resume.md).\n'
|
|
32740
|
+
},
|
|
32741
|
+
{
|
|
32742
|
+
"path": "references/models-and-config.md",
|
|
32743
|
+
"uri": "skill://agentprism-workflow-authoring/references/models-and-config.md",
|
|
32744
|
+
"mimeType": "text/markdown",
|
|
32745
|
+
"digest": "sha256:c46099e7f47768c6ab16fab864fdae6ed8d47fb77c9d260295aafcb2ce222c99",
|
|
32746
|
+
"size": 10119,
|
|
32747
|
+
"text": '## Choosing the agent for each call\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\nThe backend is selected **per `agent()` call** from its effective `model` string. One script can plan on one vendor\'s agent, implement on another\'s, and review on a third\'s, handing structured results between them.\n\nThe built-in names (`claude`, `codex`, `opencode`, `pi`) come from the runtime backend registry. Registered custom names extend that set.\n\n- **Omit `model` entirely** for maximum portability. Before an MCP run, a form-elicitation-capable client receives one structured request covering every dry-run-observed `agent()` call. The user selects each call\'s provider/model from the live advertised catalogs, so a script with no model specs remains backend-portable. Clients without form elicitation retain the fallback policy: an explicitly present `AGENTPRISM_DEFAULT_BACKEND` wins; when it is truly unset, a model-less run performs zero-token readiness probes and pins one backend. The SDK runner itself retains its configured default (`AGENTPRISM_DEFAULT_BACKEND`, historical fallback Claude).\n- **Route by one registered first segment.** Split on the first `/`; ASCII-case-insensitive `claude`, `codex`, `opencode`, `pi`, or a registered custom backend name selects that harness and is stripped exactly once. A custom registration wins on a built-in-name collision.\n- **Use a backend name alone** (`claude`, `codex`, `opencode`, `pi`, or a custom name) to preserve the harness\'s configured default model. No model config call is made.\n- **Everything else goes intact to the default backend.** `anthropic/\u2026`, `openai/\u2026`, bare `opus`, and bare `gpt-\u2026` are not routing aliases. When an id remains after routing, it is sent byte-for-byte: no catalog matching, case folding, bracket parsing, effort/Fast option driving, retry, or fallback. Harness rejection is an agent error.\n- **`tier`** (`"small" | "medium" | "big"`) is a coarse alternative resolved from the host\'s tier config \u2014 use it for "a cheap model" without naming a vendor.\n\nThe MCP pre-execution request includes the resolved label, phase title/detail, and a bounded credential-redacted task preview for each call, so the user can see what each selected model will do. It offers one required provider/model field per call and optional provider-scoped mode and non-model config fields; fields for providers the user did not select are ignored, and omitted optional fields use the selected provider\'s defaults. The server validates accepted values against the exact probed form, reruns the complete zero-token preflight, and atomically persists the canonical effective occurrence map before any live agent starts. Raw form fields are never persisted. Decline or cancel starts no run. If live control flow reaches an occurrence the mock preflight did not observe, the occurrence is durably recorded and strict host selection fails closed. Same-ID continuation reuses the canonical snapshot without eliciting again. Legacy MCP elicitation and modern `2026-07-28` `input_required` retries use the same signed selection contract.\n\nThe published examples use ids verified against live harness catalogs: `claude/opus[1m]`, `codex/gpt-5.6-sol`, and `opencode/zai/glm-5.2`. For Pi, `pi/openrouter/vendor/model-id` strips only `pi/`; Pi then splits provider `openrouter` from model id `vendor/model-id`. Prefer backend-only forms when the desired model is configured inside the harness.\n\nNever guess model ids, mode ids, effort values, or option names from memory. With MCP, call the `workflow` tool using `action:"config"` and optional `harnesses` / `modelFilter`; it returns the live catalog without starting a workflow.\n\nOne no-prompt session per harness, zero tokens: each successful harness entry contains `modes`, `defaultModeId`, and its config-option catalog. A non-null `modes` object carries every available mode\'s raw id, name, description, and `_meta`; only exact advertised ids are valid. Omission applies Claude `auto`, Codex `agent`, OpenCode `build`, or no Pi mode. For trusted implementation/review work, explicitly choose Claude `bypassPermissions` or Codex `agent` when the catalog advertises it. Claude `auto` delegates permission policy to a model classifier and may ask the user; it is not fully autonomous. `modes:null` means the backend supports no mode. `probed:true` proves session/config discovery, not universal first-prompt authentication. The bare config probe reads the default model; option domains are model-specific, so use `modelSpecs` for the selected model and confirm every pinned value against its own echoed entry.\n\n```js\nconst plan = await agent(PLAN_PROMPT, { label: "plan", model: "opencode/zai/glm-5.2", schema: PLAN });\nconst impl = await agent(implPrompt(plan), { label: "implement", model: "codex/gpt-5.6-sol", mode: "agent" });\nconst review = await agent(reviewPrompt(impl), { label: "review", model: "claude/opus[1m]", mode: "bypassPermissions", schema: REVIEW });\n```\n\nUse `configOptions` only for exact ACP session options advertised by that routed harness. With MCP, read the selected harness\'s `action:"config"` result before choosing ids or select values; catalogs vary by harness version, login, and machine.\n\n```js\nconst impl = await agent(implPrompt(plan), {\n label: "implement",\n model: "codex",\n mode: "agent",\n configOptions: { "fast-mode": true, reasoning_effort: "high" },\n});\n```\n\nIds and string/boolean values pass through verbatim in ascending id order, after model selection and before the prompt. There are no aliases, coercion, client-side vocabulary, defaults, or cached catalogs. Copy option ids character-for-character from the catalog, punctuation included \u2014 `"fast-mode"`, not `fast_mode` \u2014 and quote ids that are not valid identifiers. Never put `"model"` in `configOptions`; use the dedicated `model` field. A harness rejection follows the ordinary agent-error path.\n\nPi\'s thought-level option is named `thinkingLevel`, and its choices depend on the exact model in the same call:\n\n```js\nconst review = await agent(REVIEW_PROMPT, {\n label: "pi-review",\n model: "pi/openrouter/vendor/model-id",\n configOptions: { thinkingLevel: "high" },\n});\n```\n\nValidation selects `openrouter/vendor/model-id` before reading Pi\'s choices. A listed value passes unchanged. A recognized value above an ordered model\'s ceiling, or in a model-specific gap, passes with a warning that names the effective clamp target. Pi advertises its SDK-derived domain directly. Claude and Codex are also ordered: when their options omit domain metadata, validation enumerates the advertised models and merges their per-model effort orders. A Claude model without an `effort` option does not support effort, and `default` never becomes a ceiling target. OpenCode and custom backends have no declared value order, so validation is exact-set. An unrecognized or unadvertised value fails with exit code `2`. Enumeration stops at 32 advertised models; a larger or inconsistently ordered catalog warns and falls back to exact advertised-value validation.\n\n**The harness is authoritative.** The client never substitutes a nearby model or silently falls back. A rejected id follows the existing agent-error path; a harness that accepts or ignores it determines the outcome. The public `fallbacks`/`onModelFallback` fields remain for compatibility but model resolution does not emit them.\n\n## Structured output\n\nPass `schema` \u2014 a **plain JSON Schema object literal** (no schema builders exist inside the realm) \u2014 and the call resolves to a **validated object** instead of text:\n\n```js\nconst FINDINGS = {\n type: "object",\n additionalProperties: false,\n required: ["findings"],\n properties: {\n findings: {\n type: "array",\n items: {\n type: "object",\n additionalProperties: false,\n required: ["file", "line", "summary"],\n properties: {\n file: { type: "string", description: "Repo-relative path \u2014 copy it exactly, never invent one" },\n line: { type: "number", description: "1-indexed line the finding anchors to" },\n summary: { type: "string", description: "One sentence stating the defect, grounded in code you actually read" },\n },\n },\n },\n },\n};\n\nconst report = await agent("Review the diff on this branch for correctness bugs.", {\n label: "review", schema: FINDINGS,\n});\nreport.findings.forEach((f) => log(`${f.file}:${f.line} ${f.summary}`));\n```\n\nThe same schema works on **every** backend; only the fulfillment channel differs, and the runner picks it for you: Claude uses its `outputFormat`, Codex its strict `outputSchema`, while Pi, OpenCode, and eligible custom ACP agents receive a client-hosted `StructuredOutput` MCP tool when they advertise HTTP MCP support. Pi accepts stdio, Streamable HTTP, and SSE MCP servers. If no valid tool capture exists, Pi retains the runner\'s common prompt-embedded schema and validated final-text JSON fallback. In every channel the runner validates the value client-side (with type coercion) and re-prompts a bounded number of times before failing the call with non-recoverable `SCHEMA_NONCOMPLIANCE`.\n\nSchema authoring rules that keep all channels healthy:\n\n- Root must be an object; set `additionalProperties: false` and list every property in `required`.\n- Put a `description` on every field \u2014 descriptions are the per-field prompt.\n- Keep schemas structurally simple. Exotic keywords (`oneOf`, `patternProperties`, unusual `format`s, backreference regexes) are normalized or stripped on the wire for some backends \u2014 validation still enforces them client-side, which shows up as re-prompt churn. Prefer `anyOf`, `enum`, and plain types.\n- Keep free-text fields small (tens of lines). An oversized structured output can exhaust schema repair and fail the call.\n- Validation checks structure, not truth. Check load-bearing values in script code (for example, reject findings whose `file` is not in a known file list) before spending more agents on them.\n'
|
|
32748
|
+
},
|
|
32749
|
+
{
|
|
32750
|
+
"path": "references/run-lifecycle.md",
|
|
32751
|
+
"uri": "skill://agentprism-workflow-authoring/references/run-lifecycle.md",
|
|
32752
|
+
"mimeType": "text/markdown",
|
|
32753
|
+
"digest": "sha256:fd60617c776467fcabbd252c6e790919d6d97e72742e891a37bd47cc5032ea3f",
|
|
32754
|
+
"size": 7776,
|
|
32755
|
+
"text": '## Running workflows \u2014 the MCP `workflow` tool\n\n**Context:** JavaScript passed to the MCP `workflow` tool. Workflow scripts use `agent(prompt, options?)`; REPL evals use a different API.\n\nUse the connected `workflow` tool for deterministic batch orchestration. The shared daemon owns\nexecution, so admitted runs survive client-session churn and request timeouts. Runs, canonical\nadmission data, journals, event streams, cumulative usage, logs, and stop intents persist per\nproject namespace. Both the legacy 2025 transport and modern `2026-07-28` transport expose the same\nlifecycle.\n\nEvery `config` and `run` call on the shared daemon names its project with absolute `projectDir`.\n`resume`, `status`, `result`, and `stop` take a `runId`, which locates that store. In a\nsingle-project server, `projectDir` defaults to the server project.\n\nTool discovery publishes a strict seven-action `oneOf`. Always send the required canonical\n`action`; each branch accepts only its documented fields. There are no omitted-action defaults,\nretired action aliases, waiting status inputs, or hidden cross-action inputs.\n\n### The `workflow` tool, by action\n\n- **Config** (`{ action:"config", projectDir, harnesses?, modelSpecs?, modelFilter? }`): discover live model, mode, effort, and `configOptions` from no-prompt backend sessions. Use `modelSpecs` for the selected model\'s option domain. Raw mode ids, names, descriptions, and `_meta` are preserved. For trusted implementation/review work, choose Claude `bypassPermissions` or Codex `agent` when those exact modes are advertised. Claude `auto` uses a model classifier and may still request permission; it is not the full-access autonomous mode. Config starts no workflow and spends no prompt tokens.\n- **Run** (`{ action:"run", script | scriptPath, projectDir, ... }`): provide exactly one content source. The server statically validates, mock-runs, probes routed configuration, and\u2014when supported\u2014presents one form covering every observed agent. Each row shows phase title/detail, label, and a bounded credential-redacted task preview so the user knows what the selected model will do. Accepted effective occurrence configurations are validated and atomically persisted as a versioned host-owned admission snapshot before live dispatch. Raw form fields are not persisted. Strict occurrence coverage remains enabled; an unobserved live occurrence fails closed and is recorded. A path is snapshotted at admission. `args` becomes the script\'s `args` global. `background:true` acknowledges only after durable admission. A form-capable foreground run presents each live ACP permission in that same call; the accepted response continues the same run and can reach later permissions or checkpoints before the call completes.\n- **Resume** (`{ action:"resume", runId, checkpointReplies?, background?, maxAgents?, concurrency?, agentRetries? }`): continue that exact run ID using its persisted script, args, canonical agent configuration, journal, event stream, cumulative usage, and checkpoint decisions. Resume never creates a child run and never re-runs configuration elicitation. Script/args/config cannot be replaced. Old records without valid canonical admission remain observable but cannot continue; start a fresh Run. A checkpoint reply names this run\'s call index. The first strict-JSON answer is durable before continuation, the same answer is idempotent, and later conflicts are ignored in favor of the durable first answer. A repeat or conflict never stands in for a still-pending checkpoint: the run stays paused and the response shows what is pending. A foreground resume from a form-capable client is asked pending and newly reached checkpoints or live ACP permissions directly; clients without forms receive the paused/running observation and use `checkpointReplies` or `permissions-response`.\n- **Status** (`{ action:"status", runId, lastN?, labelGlob?, logLines? }`): return an immediate bounded snapshot. Status never waits, elicits, or changes execution. Request another snapshot only when an on-demand sample is needed. It includes calls, compact durable `latestActivity`, logs, cumulative usage, safe pending-permission state, and resource links; terminal snapshots add `outcome`. A permission projection includes run, phase, agent, backend, tool title/kind, bounded credential-redacted `rawInput`, `content`, and `locations`, plus the exact one-request/session scope of each advertised option. Private ACP session IDs and unredacted secrets never appear.\n- **Result** (`{ action:"result", runId, offset?, maxBytes? }`): retrieve a completed exact JSON result in chunks of at most 16,384 UTF-8 bytes. Continue at the prior `endOffset`; code points are never split.\n- **Permission response** (`{ action:"permissions-response", runId, permissionId, response }`): clients without form elicitation select one exact advertised option id or cancel. Caller-supplied response `_meta` is forbidden. The request must still belong to the live execution owner.\n- **Stop**: `{ action:"stop", runId }` durably aborts a whole run; `{ action:"stop", runId, callIndex }` cancels one live agent call and leaves the run alive. Across daemon succession, signed forwarding targets the execution owner. `forceOwner:true` is an explicit whole-run authorization and cannot accompany `callIndex`.\n\n### Operating rules\n\n- **Keep the `runId`.** One ID names the immutable script resource, one event stream, cumulative usage, and final result across every continuation. MCP exposes no separate attempt identity. A completed result is available at `workflow://runs/{runId}/result`; large results remain out of bounded summary text and are paged with `result`.\n- **Admission is immutable.** The host persists a canonical effective occurrence map, default model, approved script backends, and selection hash before execution. Same-ID continuation inherits it. Missing/invalid metadata or an uncovered occurrence fails closed; no migration or mapping guess is attempted.\n- **Journal replay is same-run reconstruction.** Exact index/hash hits are reused while execution rebuilds state; they add no new provider usage and are not appended as new journal entries. An interrupted eligible ACP call may reattach and charge only new usage.\n- **Checkpoint answers are first-writer-wins under the lease.** A durable answer is replayed forever. Repeats are idempotent; conflicts are reported but cannot replace it.\n- **Runtime controls are not logical inputs.** `maxAgents`, `concurrency`, and `agentRetries` may be supplied for the continuing execution. Agent attempts otherwise remain live until completion, failure, or explicit cancellation.\n- **A background start returns after durable admission.** It emits no progress after the request returns. Request an immediate status snapshot when you need machine-readable state, or consume the events resource. Background runs have no live checkpoint channel, so authored `headless` behavior applies.\n- A run paused for authentication continues with the same `{ action:"resume", runId }` after credentials are configured; it never switches provider.\n\n### Execution logs and the multi-run App\n\nEvery journaling run publishes `workflow://runs/{runId}/events`. Use its cursor and `streamId` for\ndurable redacted detail; `status.latestActivity` is the compact model-facing projection. MCP\nApps-capable hosts also receive the run-monitor panel. Because a host may replace a panel, every\nsurviving panel is a multi-run dashboard: an app-only, capability-gated `workflow-runs` query returns\na bounded active/recent list from the anchor run\'s authoritative project manager. The initiating\ntool run is selected by default, and the selector navigates active and recent runs. Incapable\nclients never see the app-only tools.\n'
|
|
32756
|
+
},
|
|
32757
|
+
{
|
|
32758
|
+
"path": "SKILL.md",
|
|
32759
|
+
"uri": "skill://agentprism-workflow-authoring/SKILL.md",
|
|
32760
|
+
"mimeType": "text/markdown",
|
|
32761
|
+
"digest": "sha256:6502ee157c4af5dfc36c72a492bf316956cb9377c1e3f03a3753af9e34cffa75",
|
|
32762
|
+
"size": 7097,
|
|
32763
|
+
"text": '---\nname: agentprism-workflow-authoring\ndescription: Write and run deterministic AgentPrism workflow scripts through the MCP workflow tool. Use for workflow DSL syntax, agent routing, structured output, checkpoints, composition, validation, background runs, status, stop, result retrieval, and same-run resume.\n---\n\n# Workflow scripts: quickstart\n\n**Context:** JavaScript passed to the MCP `workflow` tool. This is not REPL code: workflow scripts use `agent(prompt, options?)`, allow top-level `return`, and start from a required metadata export.\n\nA workflow script is a deterministic orchestrator. Script code owns loops, fan-out, conditionals, aggregation, and checkpoints; `agent()` workers perform repository or research tasks. Workers start fresh sessions and do not share memory, so interpolate every prior result a later worker needs into its prompt.\n\n## Minimal valid script\n\n```js\nexport const meta = {\n name: "review-target",\n description: "Review a target and return concrete findings",\n phases: [{ title: "Review" }],\n};\n\nphase("Review");\nconst report = await agent(\n `Review ${args.target}. Read the relevant files and report concrete findings.`,\n { label: "review" },\n);\nreturn { report };\n```\n\nThe metadata export must be the first statement and a pure object literal. `name` and `description` are required non-empty strings. `phases`, when present, is an array of objects shaped `{ title: string, detail?: string, model?: string }`, never strings.\n\nSubmit the source without Markdown fences using the `workflow` tool\'s run form, with an absolute `projectDir` on the shared daemon. `args` is the JSON value supplied by the tool call. Some hosts may carry caller data as a JSON string, so harden scripts that accept external input:\n\n```js\nconst raw = typeof args === "string" ? (() => {\n try { return JSON.parse(args); } catch { return {}; }\n})() : args;\nconst input = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};\n```\n\n## Core rules\n\n- The DSL primitives are injected globals; do not import them.\n- Top-level `await` and top-level `return` are supported.\n- Scripts are JavaScript, not TypeScript.\n- No `require`, imports, filesystem API, network API, timers, `Date.now()`, `Math.random()`, or no-argument `Date` construction. Pass nondeterministic values through `args`.\n- Every `agent()` call should have a stable descriptive `label`.\n- A recoverable worker failure resolves to `null` after retries. Null-check load-bearing results.\n- `parallel()` takes thunks, not already-started promises:\n\n```js\nconst results = (await parallel([\n () => agent("Review correctness", { label: "review:correctness" }),\n () => agent("Review test coverage", { label: "review:coverage" }),\n])).filter(Boolean);\n```\n\n- Use a plain JSON Schema object in `schema` when script control flow depends on a worker result.\n- Return a compact JSON-serializable result; do not return a transcript.\n\n## Model selection\n\nOmit `model` for the server default, or use a backend-only value such as `"codex"` to retain that backend\'s configured default model. When `AGENTPRISM_DEFAULT_BACKEND` is truly unset, the MCP server probes backend readiness without prompting and pins one project default at admission; an explicit environment default wins. Before pinning a model id, `mode`, or `configOptions`, call `workflow` with `action:"config"` and use `modelSpecs` for that model\'s exact domain. For trusted implementation/review work, select Claude `bypassPermissions` or Codex `agent` when advertised. Claude `auto` is classifier-driven and may request permission; do not treat it as full-access autonomy. Pin only exact advertised ids and never guess model or option ids. The effective choices are persisted canonically for the run and reused unchanged by continuation.\n\n## Validation and execution\n\nEvery `{ action:"run", ... }` request is statically parsed, mock-executed, and checked against no-prompt backend configuration before admission. A rejection creates no run ID, reserves no background slot, and spends no tokens. Read the diagnostic, correct the script, and submit it again.\n\nUse foreground execution for short work. Use `background:true` for work that may outlive one tool request; retain the returned `runId`, then use immediate `status` snapshots or `stop` calls.\n\nThe input is a strict action union: send only fields belonging to the selected action. In particular, `projectDir` belongs to `config` and `run`, not `status`, `result`, `resume`, or `stop`. Some MCP clients report every rejected union branch; when that happens, first check the branch matching your `action` and remove cross-action fields.\n\n## Minimal MCP lifecycle\n\nThis is the complete long-running loop. First admit a background run and retain its `runId`:\n\n```json\n{\n "action": "run",\n "projectDir": "/absolute/project",\n "background": true,\n "script": "export const meta = { name: \'review\', description: \'Review a target\' }; return await agent(`Review ${args.target}`, { label: \'review\' });",\n "args": { "target": "packages/core" }\n}\n```\n\nObserve the current state. Status is always an immediate snapshot; issue it again for a later sample:\n\n```json\n{ "action": "status", "runId": "RUN_ID" }\n```\n\nAfter completion, retrieve the exact result. If `hasMore` is true, repeat with `offset` set to the previous `endOffset`:\n\n```json\n{ "action": "result", "runId": "RUN_ID", "offset": 0, "maxBytes": 16384 }\n```\n\nContinue an incomplete run in place; do not resend `script` or `args`:\n\n```json\n{ "action": "resume", "runId": "RUN_ID", "background": true }\n```\n\nThe response keeps the same `runId` without exposing an execution-attempt identity. It reuses the\nadmitted script, args, effective agent configuration, journal, event stream, cumulative usage, and\ndurable checkpoint decisions. Use `status` on that same ID, then `result` after completion.\n\n## What to read next\n\nRead only the references needed for the task:\n\n- [`references/composition-and-failure.md`](references/composition-and-failure.md) \u2014 metadata, fan-out, phases, and null semantics.\n- [`references/api-agents.md`](references/api-agents.md) \u2014 every `agent()` option and structured output.\n- [`references/run-lifecycle.md`](references/run-lifecycle.md) \u2014 config, run, status, stop, and resume.\n- [`references/models-and-config.md`](references/models-and-config.md) \u2014 backend routing and live model/config discovery.\n- [`references/checkpoints-and-quality.md`](references/checkpoints-and-quality.md) \u2014 quality loops and human checkpoints.\n- [`references/environment-and-tools.md`](references/environment-and-tools.md) \u2014 execution roots, isolation, tools, and custom backends.\n- [`references/determinism-and-resume.md`](references/determinism-and-resume.md) \u2014 replay identity and continuation.\n- [`references/api-control-flow.md`](references/api-control-flow.md) \u2014 complete control-flow signatures.\n- [`references/api-resume-and-backends.md`](references/api-resume-and-backends.md) \u2014 detailed resume and backend-extension contracts.\n- [`references/examples.md`](references/examples.md) \u2014 complete composition patterns.\n'
|
|
32764
|
+
}
|
|
32950
32765
|
],
|
|
32951
|
-
"
|
|
32952
|
-
"sha256": "3a9342f8e7862d0ba53c830b9fe51f3606f6131f0386a8b2c9cf16044d565712",
|
|
32953
|
-
"text": '# REPL API reference\n\n**Context:** code evaluated by the MCP `repl` tool. Workflow scripts use a different `agent(prompt, options?)` signature and have additional run/journal APIs.\n\n## MCP tool actions\n\n```text\nrepl({ action: "eval", projectDir, code, timeoutMs? })\nrepl({ action: "interrupt", projectDir, id? })\n```\n\n`projectDir` is required on the shared daemon and optional in a single-project server. `eval` requires a code string; an empty string is the idempotent settlement poll. `timeoutMs` is an integer from 0 through 120000, default 60000, and bounds only how long the tool call pumps settlements. `interrupt` with `id` cancels exactly that live call; without `id` it breaks the running eval. Fields from the other action are rejected.\n\nFinished eval result:\n\n```ts\n{ output: string; result?: string }\n```\n\nStill-running eval result:\n\n```ts\n{ output: string; running: string[] }\n```\n\n## Guest globals\n\n```text\nagent(modelSpec, task, options?) -> PromiseHandle\ncheckpoint(question, options?) -> Promise\ncheckpoint.answer(callId, value) -> boolean\nparallel(thunks) -> Promise<results[]>\npipeline(items, ...stages) -> Promise<results[]>\nverify(item, { reviewers = 2, threshold = 0.5, lens? })\n -> { real, realCount, total, votes }\njudgePanel(attempts, { judges = 3, rubric = "overall quality and correctness" })\n -> { index, attempt, score, judgments }\ngate(thunk, validator, { attempts = 3 })\n -> { ok, value, verdict, attempts }\nretry(thunk, { attempts = 3, until? }) -> last result\nloopUntilDry({ round, key = JSON.stringify, consecutiveEmpty = 2, maxRounds = 50 })\n -> unique items[]\nsleep(ms) -> Promise<undefined>\nworkspace() -> { bindings, inFlight, checkpoints, diagnostics }\nagents() -> live agent/queued-turn rows\nreset() -> undefined\nconsole.log/info/warn/error/debug(...values) -> undefined\n_ -> previous eval completion value\n```\n\nTop-level `await` is accepted. Top-level `return` is a syntax error.\n\n## `agent()`\n\n```js\nagent(modelSpec, task, {\n schema?: object,\n cwd?: string,\n configOptions?: Record<string, string | boolean>,\n mode?: string,\n})\n```\n\nAll arguments except options are required strings. The option vocabulary is exact. Options cross the bridge as JSON. Without `schema`, the handle resolves to assistant text; with `schema`, to the validated object.\n\nFounding promise-handle members:\n\n```text\nhandle.id: string\nhandle.queue(prompt, { promptMeta?: object }?) -> QueuedPromiseHandle\nhandle.steer(prompt, { promptMeta?: object }?) -> Promise<"injected" | "idle" | "unsupported">\nhandle.cancel() -> Promise\n```\n\nQueued promise-handle members:\n\n```text\nqueued.id: string\nqueued.cancel() -> Promise\n```\n\n## Combinator semantics\n\n`parallel` requires functions, not promises, and preserves input order. A recoverable rejection becomes `null` in its slot; a non-recoverable rejection propagates.\n\n`pipeline` runs each item through each stage in order while items progress concurrently. A stage receives `(previousValue, originalItem, index)`. Recoverable item failure yields `null` for that item.\n\n`verify` uses the host\'s configured default backend for adversarial votes. Failed reviewers are dropped. `lens` may be one string or an array rotated across reviewers.\n\n`judgePanel` uses the host default backend to score every candidate from 0 to 1 and returns the highest mean; ties prefer the lower input index.\n\n`retry` calls `thunk(attempt)` up to the bound. Without `until`, the first result is accepted. With `until`, the last result is returned if no attempt passes.\n\n`gate` calls `thunk(feedback, attempt)`, then awaits `validator(result)`. The verdict may be boolean or `{ ok, feedback?, ... }`; object feedback enters the next producer attempt. The complete last verdict is returned.\n\n`loopUntilDry` repeatedly calls `round(index)`, deduplicates non-null items using `key`, and stops after the configured consecutive empty rounds or maximum rounds.\n\n## Checkpoints\n\n`checkpoint(question, options?)` accepts a string question and any JSON-serializable options object. The promise parks until a later eval calls `checkpoint.answer(id, value)`. Answer delivery is explicit, first-wins, and returns whether a pending checkpoint was settled.\n\n## Environment\n\nThe VM has ordinary deterministic JavaScript data/control APIs but no imports, `require`, filesystem, network, or general timers. `sleep(ms)` is host-backed. Console calls never throw and return output only. Bind values explicitly when they must persist.\n'
|
|
32766
|
+
"totalSize": 66755
|
|
32954
32767
|
},
|
|
32955
32768
|
{
|
|
32956
|
-
"
|
|
32957
|
-
"
|
|
32958
|
-
"
|
|
32959
|
-
|
|
32960
|
-
|
|
32961
|
-
|
|
32962
|
-
|
|
32963
|
-
|
|
32964
|
-
|
|
32769
|
+
"directory": "agentprism-repl-orchestration",
|
|
32770
|
+
"uri": "skill://agentprism-repl-orchestration/SKILL.md",
|
|
32771
|
+
"frontmatter": {
|
|
32772
|
+
"name": "agentprism-repl-orchestration",
|
|
32773
|
+
"description": "Drive AgentPrism's persistent MCP REPL for interactive multi-agent orchestration. Use for incremental evals, persistent bindings and agent handles, polling, steering, queueing, cancellation, checkpoints, workspace inspection, restore, and reset."
|
|
32774
|
+
},
|
|
32775
|
+
"resources": [
|
|
32776
|
+
{
|
|
32777
|
+
"path": "references/agent-handles.md",
|
|
32778
|
+
"uri": "skill://agentprism-repl-orchestration/references/agent-handles.md",
|
|
32779
|
+
"mimeType": "text/markdown",
|
|
32780
|
+
"digest": "sha256:26f9868ba7d70fac2465cdb8fc0d22726585b61608d852aeca26b12a762dff96",
|
|
32781
|
+
"size": 3565,
|
|
32782
|
+
"text": '# REPL agent calls and persistent handles\n\nREPL delegation uses:\n\n```js\nagent(modelSpec, task, options?) -> PromiseHandle\n```\n\nThis differs from workflow scripts, whose signature is `agent(prompt, options?)`.\n\n## Model routing\n\n`modelSpec` is required. Use a registered backend name alone to preserve its configured default model:\n\n```js\nconst worker = agent("codex", "Investigate the failing parser test");\n```\n\nUse `backend/model-id` only after model discovery:\n\n```js\nconst worker = agent("claude/verified-model-id", "Review the implementation");\n```\n\nThe known built-ins are Claude, Codex, OpenCode, and pi, plus host-registered custom backends. Unknown backend names reject and enumerate known backends. Use the `workflow` tool\'s `action:"config"` with `harnesses`/`modelFilter`, then `modelSpecs`, before pinning model, mode, or config-option values. Read the selected entry\'s harness-owned mode names/descriptions before pinning an exact advertised id. Omission uses `defaultModeId` (Claude auto, Codex agent, OpenCode build; none for Pi).\n\n## Exact option vocabulary\n\n```js\nconst worker = agent("codex", "Inspect the parser", {\n schema: {\n type: "object",\n additionalProperties: false,\n required: ["summary"],\n properties: { summary: { type: "string" } },\n },\n cwd: "/absolute/path",\n mode: "advertised-mode-id",\n configOptions: { advertisedOptionId: "advertised-value" },\n});\n```\n\nOptions are exactly:\n\n- `schema`: plain JSON Schema object; the promise resolves to its validated object.\n- `cwd`: absolute worker session working directory.\n- `mode`: exact ACP mode explicitly listed in the selected backend/model\'s discovered `modes.availableModes`.\n- `configOptions`: exact string/boolean ACP option ids and values.\n\nUnknown option keys reject. Options must be JSON-serializable.\n\n## Preserve the handle\n\nThe returned promise is also the live handle:\n\n```js\nconst worker = agent("pi", "Research the issue");\nconst id = worker.id;\nconst answer = await worker;\n```\n\nDo not write this if you intend to reuse the session:\n\n```js\nconst worker = await agent("pi", "Research the issue");\n```\n\nThat variable stores only the answer and loses access to handle methods.\n\nThe founding handle exposes non-enumerable, immutable members:\n\n- `id`: stable call id such as `"c1"`.\n- `queue(prompt, options?)`: create a distinct durable FIFO future turn.\n- `steer(prompt, options?)`: attempt strict control of only the currently active turn.\n- `cancel()`: cancel the session\'s current public turn.\n\nA queued-turn handle exposes its own `id` and `cancel()`.\n\n## Settlement and failures\n\nWithout `schema`, the founding promise resolves to final assistant text. With `schema`, it resolves to the validated object.\n\nA rejected call carries an error with call/backend attribution where available. Errors whose `recoverable` field is not `false` are treated as recoverable by `parallel()` and `pipeline()` and become `null` slots. A non-recoverable error rejects the surrounding combinator/eval.\n\nDirect `await worker` propagates rejection; catch only errors you can handle meaningfully:\n\n```js\nlet answer;\ntry {\n answer = await worker;\n} catch (error) {\n console.error(error);\n answer = null;\n}\n```\n\n## Session continuity\n\nThe founding answer settling does not erase the handle binding. Queue later prompts on the founding handle to continue the same ACP session. Session continuity depends on the backend\'s continuation capability and the workspace\'s durable lane state. Never fabricate a new handle from a saved id; retain the actual promise-handle binding.\n'
|
|
32783
|
+
},
|
|
32784
|
+
{
|
|
32785
|
+
"path": "references/api-reference.md",
|
|
32786
|
+
"uri": "skill://agentprism-repl-orchestration/references/api-reference.md",
|
|
32787
|
+
"mimeType": "text/markdown",
|
|
32788
|
+
"digest": "sha256:3a9342f8e7862d0ba53c830b9fe51f3606f6131f0386a8b2c9cf16044d565712",
|
|
32789
|
+
"size": 4455,
|
|
32790
|
+
"text": '# REPL API reference\n\n**Context:** code evaluated by the MCP `repl` tool. Workflow scripts use a different `agent(prompt, options?)` signature and have additional run/journal APIs.\n\n## MCP tool actions\n\n```text\nrepl({ action: "eval", projectDir, code, timeoutMs? })\nrepl({ action: "interrupt", projectDir, id? })\n```\n\n`projectDir` is required on the shared daemon and optional in a single-project server. `eval` requires a code string; an empty string is the idempotent settlement poll. `timeoutMs` is an integer from 0 through 120000, default 60000, and bounds only how long the tool call pumps settlements. `interrupt` with `id` cancels exactly that live call; without `id` it breaks the running eval. Fields from the other action are rejected.\n\nFinished eval result:\n\n```ts\n{ output: string; result?: string }\n```\n\nStill-running eval result:\n\n```ts\n{ output: string; running: string[] }\n```\n\n## Guest globals\n\n```text\nagent(modelSpec, task, options?) -> PromiseHandle\ncheckpoint(question, options?) -> Promise\ncheckpoint.answer(callId, value) -> boolean\nparallel(thunks) -> Promise<results[]>\npipeline(items, ...stages) -> Promise<results[]>\nverify(item, { reviewers = 2, threshold = 0.5, lens? })\n -> { real, realCount, total, votes }\njudgePanel(attempts, { judges = 3, rubric = "overall quality and correctness" })\n -> { index, attempt, score, judgments }\ngate(thunk, validator, { attempts = 3 })\n -> { ok, value, verdict, attempts }\nretry(thunk, { attempts = 3, until? }) -> last result\nloopUntilDry({ round, key = JSON.stringify, consecutiveEmpty = 2, maxRounds = 50 })\n -> unique items[]\nsleep(ms) -> Promise<undefined>\nworkspace() -> { bindings, inFlight, checkpoints, diagnostics }\nagents() -> live agent/queued-turn rows\nreset() -> undefined\nconsole.log/info/warn/error/debug(...values) -> undefined\n_ -> previous eval completion value\n```\n\nTop-level `await` is accepted. Top-level `return` is a syntax error.\n\n## `agent()`\n\n```js\nagent(modelSpec, task, {\n schema?: object,\n cwd?: string,\n configOptions?: Record<string, string | boolean>,\n mode?: string,\n})\n```\n\nAll arguments except options are required strings. The option vocabulary is exact. Options cross the bridge as JSON. Without `schema`, the handle resolves to assistant text; with `schema`, to the validated object.\n\nFounding promise-handle members:\n\n```text\nhandle.id: string\nhandle.queue(prompt, { promptMeta?: object }?) -> QueuedPromiseHandle\nhandle.steer(prompt, { promptMeta?: object }?) -> Promise<"injected" | "idle" | "unsupported">\nhandle.cancel() -> Promise\n```\n\nQueued promise-handle members:\n\n```text\nqueued.id: string\nqueued.cancel() -> Promise\n```\n\n## Combinator semantics\n\n`parallel` requires functions, not promises, and preserves input order. A recoverable rejection becomes `null` in its slot; a non-recoverable rejection propagates.\n\n`pipeline` runs each item through each stage in order while items progress concurrently. A stage receives `(previousValue, originalItem, index)`. Recoverable item failure yields `null` for that item.\n\n`verify` uses the host\'s configured default backend for adversarial votes. Failed reviewers are dropped. `lens` may be one string or an array rotated across reviewers.\n\n`judgePanel` uses the host default backend to score every candidate from 0 to 1 and returns the highest mean; ties prefer the lower input index.\n\n`retry` calls `thunk(attempt)` up to the bound. Without `until`, the first result is accepted. With `until`, the last result is returned if no attempt passes.\n\n`gate` calls `thunk(feedback, attempt)`, then awaits `validator(result)`. The verdict may be boolean or `{ ok, feedback?, ... }`; object feedback enters the next producer attempt. The complete last verdict is returned.\n\n`loopUntilDry` repeatedly calls `round(index)`, deduplicates non-null items using `key`, and stops after the configured consecutive empty rounds or maximum rounds.\n\n## Checkpoints\n\n`checkpoint(question, options?)` accepts a string question and any JSON-serializable options object. The promise parks until a later eval calls `checkpoint.answer(id, value)`. Answer delivery is explicit, first-wins, and returns whether a pending checkpoint was settled.\n\n## Environment\n\nThe VM has ordinary deterministic JavaScript data/control APIs but no imports, `require`, filesystem, network, or general timers. `sleep(ms)` is host-backed. Console calls never throw and return output only. Bind values explicitly when they must persist.\n'
|
|
32791
|
+
},
|
|
32792
|
+
{
|
|
32793
|
+
"path": "references/checkpoints-and-introspection.md",
|
|
32794
|
+
"uri": "skill://agentprism-repl-orchestration/references/checkpoints-and-introspection.md",
|
|
32795
|
+
"mimeType": "text/markdown",
|
|
32796
|
+
"digest": "sha256:62c5c8a50ca429c0573e3ce227348a4fb8133ccabc197da455a63fd6ad0a9f7d",
|
|
32797
|
+
"size": 2667,
|
|
32798
|
+
"text": '# REPL checkpoints and introspection\n\nREPL checkpoints park a promise until a later eval explicitly supplies the human answer.\n\n## Raise a checkpoint\n\n```js\nconst approval = checkpoint("Proceed with the destructive migration?", {\n choices: ["approve", "reject"],\n default: "reject",\n});\n```\n\nThe eval output includes a line such as:\n\n```text\ncheckpoint c3: Proceed with the destructive migration?\n```\n\nThe promise remains live in the workspace. The host does not infer an answer from conversation text; answer delivery is an explicit data-plane operation.\n\n## Inspect pending checkpoints\n\n```js\nworkspace().checkpoints\n```\n\nEach pending row identifies the call id, question, and available option metadata. Retain the promise binding or recover its call id through `workspace()`.\n\n## Answer from a later eval\n\n```js\ncheckpoint.answer("c3", "approve")\n```\n\nThis returns `true` if that checkpoint was pending and the answer settled it, or `false` if the id was unknown/already settled. Delivery is first-wins and idempotent. The answer must be JSON-serializable; `undefined` is normalized to `null`.\n\nThe original continuation resumes during the same eval\'s settlement drain:\n\n```js\nconst decision = await approval;\ndecision\n```\n\nCheckpoint promises and questions survive daemon restart through the workspace snapshot.\n\n## Workspace introspection\n\n```js\nconst state = workspace();\n```\n\nThe returned plain object contains:\n\n- `bindings`: persistent user bindings with bounded type/size/status previews and call provenance where applicable.\n- `inFlight`: all unsettled bridge calls, including agents, queue/steer/cancel controls, sleeps, and checkpoints.\n- `checkpoints`: currently pending human questions.\n- `diagnostics`: restore reconciliation notes and retained settlement-drain faults.\n\nInspect it narrowly to avoid unnecessary context:\n\n```js\nworkspace().bindings.map(({ name, type, status }) => ({ name, type, status }))\n```\n\n## Agent-lane introspection\n\n```js\nagents()\n```\n\nreturns only live agent/queued-turn entries, including call id, model spec, task preview, state, strict-steering support, and queued-turn count. This is the authority for deciding whether `handle.steer()` has an active target.\n\n## Error diagnosis\n\nUncaught errors render into eval output. Errors associated with an agent/queued call include its stable call id and resolved backend when known. A drain or reconciliation problem that did not lose state is retained under `workspace().diagnostics`; state loss additionally produces a notice in the next eval output.\n\nUse the empty eval to drain late settlements, then inspect `workspace()` and `agents()` before deciding to cancel or reset.\n'
|
|
32799
|
+
},
|
|
32800
|
+
{
|
|
32801
|
+
"path": "references/examples.md",
|
|
32802
|
+
"uri": "skill://agentprism-repl-orchestration/references/examples.md",
|
|
32803
|
+
"mimeType": "text/markdown",
|
|
32804
|
+
"digest": "sha256:dbae758165a803a32807952f0caa12bbd2ece22f86e1d9e65f9ead1ca3e3ba9f",
|
|
32805
|
+
"size": 3176,
|
|
32806
|
+
"text": '# REPL orchestration examples\n\n## Interactive investigate, steer, implement, verify\n\nFirst eval\u2014retain the founding handle:\n\n```js\nconst parser = agent("codex", "Investigate the parser test failure. Analyze only; do not edit yet.");\n```\n\nWhile it is actually running:\n\n```js\nagents()\n```\n\n```js\nconst steering = await parser.steer("Focus on token recovery after malformed metadata");\nsteering\n```\n\nAfter the founding answer:\n\n```js\nconst analysis = await parser;\nconst fix = parser.queue("Implement the smallest correct fix and add focused tests");\nconst verify = parser.queue("Run the focused tests and report exact results");\nconst fixed = await fix;\nconst tested = await verify;\n({ analysis, fixed, tested })\n```\n\n## Parallel structured reviews\n\n```js\nconst verdictSchema = {\n type: "object",\n additionalProperties: false,\n required: ["ok", "reason"],\n properties: {\n ok: { type: "boolean" },\n reason: { type: "string" },\n },\n};\n\nconst reviews = (await parallel([\n () => agent("claude", "Review the current diff for correctness", { schema: verdictSchema }),\n () => agent("codex", "Review the current diff for regressions", { schema: verdictSchema }),\n () => agent("opencode", "Review whether tests cover the changed behavior", { schema: verdictSchema }),\n])).filter(Boolean);\nreviews\n```\n\n## Decide after inspection\n\n```js\nconst candidates = agent("pi", "Find up to five concrete reliability improvements", {\n schema: {\n type: "object",\n additionalProperties: false,\n required: ["items"],\n properties: {\n items: { type: "array", items: { type: "string" } },\n },\n },\n});\n```\n\nLater:\n\n```js\nconst found = await candidates;\nfound.items\n```\n\nChoose one after discussing it with the user, then continue the same session:\n\n```js\nconst chosen = found.items[0];\nconst implementation = candidates.queue(`Implement only this item: ${chosen}`);\nawait implementation\n```\n\n## Human checkpoint\n\n```js\nconst decision = checkpoint("Which candidate should be implemented?", {\n choices: found.items,\n});\n```\n\nAfter receiving the human response:\n\n```js\nworkspace().checkpoints\n```\n\n```js\ncheckpoint.answer("c4", found.items[1]);\nconst selected = await decision;\nselected\n```\n\n## Recover from a long-running eval\n\nIf a tool result reports `running`, poll without side effects:\n\n```json\n{ "action": "eval", "projectDir": "/absolute/project", "code": "" }\n```\n\nInspect state:\n\n```js\n({ agents: agents(), diagnostics: workspace().diagnostics })\n```\n\nCancel only one identified call through an out-of-band tool call:\n\n```json\n{ "action": "interrupt", "projectDir": "/absolute/project", "id": "c7" }\n```\n\nUse interrupt without an id only for a runaway eval, not as a substitute for targeted call cancellation.\n\n## Bounded hunt loop\n\n```js\nconst seen = [];\nconst findings = await loopUntilDry({\n round: async (i) => {\n const h = agent("claude", `Round ${i + 1}: find new bugs not in ${JSON.stringify(seen)}`);\n const text = await h;\n const rows = text ? [text] : [];\n seen.push(...rows);\n return rows;\n },\n consecutiveEmpty: 2,\n maxRounds: 5,\n});\nfindings\n```\n\nKeep interactive loops bounded even though the workspace persists indefinitely.\n'
|
|
32807
|
+
},
|
|
32808
|
+
{
|
|
32809
|
+
"path": "references/persistence-and-reset.md",
|
|
32810
|
+
"uri": "skill://agentprism-repl-orchestration/references/persistence-and-reset.md",
|
|
32811
|
+
"mimeType": "text/markdown",
|
|
32812
|
+
"digest": "sha256:47bacb8c2ce0c640d2167d369a5f5ae487d164e058e324f7e0ee9bdf1fdd56f8",
|
|
32813
|
+
"size": 2619,
|
|
32814
|
+
"text": "# REPL persistence, restore, and reset\n\nThe REPL workspace is durable per project. Named bindings, pending agent and checkpoint promises, queue state, and call-id sequencing persist across MCP client disconnects and daemon restarts.\n\n## Snapshot boundaries\n\nEvery state-changing eval and settlement drain persists the workspace. On first touch after restart, the server restores the QuickJS snapshot, re-registers host callbacks, and reconciles outstanding calls. The guest library itself is not re-evaluated into a restored VM.\n\nA later call can therefore continue with prior bindings:\n\n```js\nworkspace().bindings\n```\n\nand await a handle created before restart:\n\n```js\nconst answer = await worker;\n```\n\n## Session and queued-turn recovery\n\nPending founding turns and queued turns are reconciled from durable records. Eligible queued turns reattach lazily to their founding ACP session. The broker never substitutes a blank session if continuity cannot be preserved; lane-fatal recovery faults reject the lane instead.\n\nStrict steering is transient. A pending steering control cannot be replayed as a future prompt after restart and rejects rather than changing semantics. Durable queue turns remain future work.\n\n## Snapshot refusal\n\nSnapshot compatibility is tied to the snapshot format and the exact QuickJS WASM binary hash. Corrupt, incompatible-format, or mismatched-binary snapshots are not restored silently.\n\nA refused snapshot is renamed aside with a `.refused-<timestamp>` suffix and the workspace automatically resets. The next successful eval output begins with a notice naming the file and refusal reason. The refused file is never silently deleted.\n\nReconciliation summaries and retained drain errors are available under:\n\n```js\nworkspace().diagnostics\n```\n\n## Client disconnect drain\n\nWhen the last MCP client for a project disconnects, the workspace drains in-flight subagent turns and closes idle children. Persistent workspace state remains. The next eligible queued turn reattaches its founding session lazily.\n\n## Explicit reset\n\n```js\nreset()\n```\n\nrequests teardown after the current eval completes. It discards bindings, pending calls, checkpoints, and the active snapshot for that workspace. The eval that calls `reset()` still completes normally; the next touch creates a fresh VM.\n\nPrefer targeted cleanup first:\n\n- cancel a queued handle with `queued.cancel()`;\n- cancel an active call with `handle.cancel()` or `repl` interrupt by id;\n- break only a runaway eval with interrupt and no id.\n\nUse `reset()` when the whole interactive state is intentionally disposable or no longer trustworthy.\n"
|
|
32815
|
+
},
|
|
32816
|
+
{
|
|
32817
|
+
"path": "references/state-and-bindings.md",
|
|
32818
|
+
"uri": "skill://agentprism-repl-orchestration/references/state-and-bindings.md",
|
|
32819
|
+
"mimeType": "text/markdown",
|
|
32820
|
+
"digest": "sha256:02b4040fc3a76c9f07c234ce59c95a66b19b1b8eccf384362dece9642762115b",
|
|
32821
|
+
"size": 2837,
|
|
32822
|
+
"text": '# REPL state, bindings, and eval results\n\nEvery project has one persistent JavaScript VM. A later `eval` sees the same global lexical environment and the same unresolved promise-handles as earlier evals.\n\n## Persist values explicitly\n\n```js\nconst target = "src/parser";\nconst findings = [];\n```\n\nBoth names remain available later. Console output does not create a value:\n\n```js\nconsole.log({ target }); // emits output only\n```\n\nThe special `_` binding contains the previous eval\'s completion value, similar to an interactive language shell:\n\n```js\n[1, 2, 3].map((x) => x * 2)\n// later:\n_.reduce((sum, x) => sum + x, 0)\n```\n\nAssign important results to descriptive names instead of depending on `_`, because each completed eval replaces it.\n\n## Top-level syntax\n\nTop-level `await` is supported:\n\n```js\nconst answer = await worker;\nanswer\n```\n\nTop-level `return` is a syntax error. The final expression becomes the eval completion value. Standard JavaScript control flow works, including async functions and `for await`.\n\n## Eval result shapes\n\nFinished:\n\n```js\n{ output: "zero or more console/error lines", result: "value repr when present" }\n```\n\nStill running after the soft hold bound:\n\n```js\n{ output: "lines emitted so far", running: ["c1", "c2"] }\n```\n\nThe eval continues after the second shape. Any later eval drains settlements first. An empty code string is the idempotent poll and runs no user code.\n\n`output` is newline-joined console, checkpoint, and uncaught-error rendering. Direct strings passed to console print in full. Objects and arrays use a depth-limited preview; evaluate a narrower property or slice to inspect more. Preserve large values in bindings rather than repeatedly printing them into model context.\n\n## Concurrency and serialization\n\nVM operations serialize, so concurrent clients cannot reorder individual eval operations. Subagents run concurrently under the workspace\'s host limit. The eval call pumps promise settlements until completion or its response bound; the bound does not terminate the underlying eval.\n\nUse `sleep(ms)` for a host-backed delay:\n\n```js\nawait sleep(250);\n```\n\nNo other timer API is available.\n\n## Introspection\n\n```js\nworkspace()\n```\n\nreturns a plain object containing `bindings`, `inFlight`, `checkpoints`, and `diagnostics`. Binding rows include the name, type/size preview, provenance, task/call id where relevant, and settlement status.\n\n```js\nagents()\n```\n\nreturns live agent/queued-turn rows with call ids, model specs, task previews, state, steering support, and queued-turn counts. Use these functions instead of guessing whether a handle is active.\n\n## Cleanup\n\n```js\nreset()\n```\n\nrequests teardown after the current eval completes. All bindings and pending work in that project workspace are discarded. It returns `undefined`; the next eval creates a fresh workspace.\n'
|
|
32823
|
+
},
|
|
32824
|
+
{
|
|
32825
|
+
"path": "references/steering-queueing-and-cancellation.md",
|
|
32826
|
+
"uri": "skill://agentprism-repl-orchestration/references/steering-queueing-and-cancellation.md",
|
|
32827
|
+
"mimeType": "text/markdown",
|
|
32828
|
+
"digest": "sha256:c3af06afe4c969b5512da0ed7d4f7e1ef02dffc63b00444c55b8ef1b4c8b6ffb",
|
|
32829
|
+
"size": 3681,
|
|
32830
|
+
"text": '# REPL steering, queueing, and exact cancellation\n\nA founding agent handle separates transient control of a running turn from durable future work.\n\n## Strict steering\n\n```js\nconst worker = agent("codex", "Investigate the parser failure");\n```\n\nOnly while `agents()` reports its active turn as running:\n\n```js\nconst outcome = await worker.steer("Focus on the parser state machine");\n```\n\nThe result is exactly:\n\n- `"injected"`: the instruction was delivered into the active turn.\n- `"idle"`: no turn was active; the instruction was not retained.\n- `"unsupported"`: the backend does not advertise strict steering.\n\nTransport or protocol failures reject. Steering never starts a new turn and never queues work. An `"idle"` result intentionally loses the instruction. Check `agents()` immediately before steering when timing matters.\n\nSteering support is based on the backend\'s raw ACP steering advertisement. Do not infer it from the backend name.\n\n## Durable queued turns\n\nAfter retaining the founding handle, queue future public turns:\n\n```js\nconst first = await worker;\nconst implement = worker.queue("Implement the fix");\nconst test = worker.queue("Run the focused tests");\nconsole.log(implement.id, test.id);\nconst implemented = await implement;\nconst tested = await test;\n```\n\nEach queue call synchronously returns a distinct promise-handle with its own stable id. Queued turns execute FIFO on the founding session using ordinary public prompts. Queueing is broker-owned and works on every backend that can continue the session; it does not depend on a backend-native queue API.\n\nQueued-turn options are exactly:\n\n```js\n{ promptMeta?: object }\n```\n\nThe prompt must be a string. Malformed queue requests still receive a durable call record and reject directly.\n\n## Cancellation\n\nCancel the founding session\'s currently active public turn:\n\n```js\nawait worker.cancel();\n```\n\nCancel one queued turn exactly:\n\n```js\nawait test.cancel();\n```\n\nOr cancel by stable call id outside the VM through the tool:\n\n```json\n{ "action": "interrupt", "projectDir": "/absolute/project", "id": "c4" }\n```\n\nA targeted cancellation rejects that call recoverably and leaves unrelated work live. Cancelling a queued handle does not cancel its founding turn or siblings. Cancellation settlement is first-wins; an already-settled target cannot be retroactively cancelled.\n\nOmit `id` only to break the currently running eval itself:\n\n```json\n{ "action": "interrupt", "projectDir": "/absolute/project" }\n```\n\nThat is eval control, not agent-call selection. It breaks an executing eval or terminates a suspended eval that cannot be safely resumed. It returns an honest idle refusal only when nothing is running.\n\n## Ordering and persistence\n\nQueue admission, handoff, settlement, and cancellation are persisted. Pending queue turns survive daemon restart and reattach lazily to their founding session when eligible. Queue delivery has the documented narrow at-least-once crash window around remote acceptance and local handoff persistence; prompts that cause external side effects should therefore be idempotent or guarded.\n\nA lane-fatal persistence/session failure rejects the active and queued turns on that lane. The broker never opens a blank replacement session and pretends context survived.\n\n## Recommended pattern\n\n```js\nconst worker = agent("claude", "Analyze the issue; do not edit yet");\n// While running, optionally steer after checking agents().\nconst analysis = await worker;\nconst fix = worker.queue("Implement the agreed fix");\nconst verify = worker.queue("Run tests and report the exact results");\nconst fixed = await fix;\nconst verified = await verify;\n({ analysis, fixed, verified })\n```\n'
|
|
32831
|
+
},
|
|
32832
|
+
{
|
|
32833
|
+
"path": "SKILL.md",
|
|
32834
|
+
"uri": "skill://agentprism-repl-orchestration/SKILL.md",
|
|
32835
|
+
"mimeType": "text/markdown",
|
|
32836
|
+
"digest": "sha256:45156e0c239ce7aedb50fce414df4187a22357a9a2f66ac35358f15118d55fbf",
|
|
32837
|
+
"size": 4086,
|
|
32838
|
+
"text": '---\nname: agentprism-repl-orchestration\ndescription: Drive AgentPrism\'s persistent MCP REPL for interactive multi-agent orchestration. Use for incremental evals, persistent bindings and agent handles, polling, steering, queueing, cancellation, checkpoints, workspace inspection, restore, and reset.\n---\n\n# REPL orchestration: quickstart\n\n**Context:** JavaScript sent to the MCP `repl` tool. This is not workflow-script code: REPL `agent()` takes `(modelSpec, task, options?)`, top-level `return` is a syntax error, and named bindings persist between calls.\n\nThe REPL is one persistent QuickJS-in-WASM workspace per absolute `projectDir`. Use it when you want to inspect intermediate results and decide the next step interactively. Use `workflow` when the full orchestration is known up front and should be repeatable as one deterministic script.\n\n## First eval\n\n```js\nconst audit = agent("codex", "Inspect the parser for correctness bugs");\n```\n\nSend that code using:\n\n```json\n{ "action": "eval", "projectDir": "/absolute/project", "code": "..." }\n```\n\n`agent()` returns a persistent promise-handle immediately. Storing the handle before awaiting preserves its `id`, `queue()`, `steer()`, and `cancel()` methods for later evals.\n\nInspect or await it in another eval:\n\n```js\nagents()\n```\n\n```js\nconst report = await audit;\nreport\n```\n\nA completed eval returns `{ output, result? }`. If the soft hold bound expires while the eval remains suspended, the tool returns `{ output, running: [callIds] }`; execution continues server-side. Poll without running new code by evaluating the empty string:\n\n```json\n{ "action": "eval", "projectDir": "/absolute/project", "code": "" }\n```\n\n## Essential semantics\n\n- Top-level `await` works; top-level `return` does not.\n- `let`, `const`, `var`, functions, and classes remain available to later evals.\n- `_` is the previous eval\'s completion value.\n- Console output is returned as text but is not a persistent value. Assign values you need later.\n- There is no filesystem, network, import, or general timer API in the VM. Subagents perform external work; `sleep(ms)` is the one host-backed timer.\n- The default eval hold is 60 seconds and the per-call maximum is 120 seconds. This is a response hold, not cancellation.\n- Use `interrupt` with a call `id` to cancel one subagent/queued turn, or omit `id` to break the currently running eval.\n- `workspace()` shows bindings, in-flight calls, checkpoints, and diagnostics. `agents()` shows live agent lanes and queued turns.\n- `reset()` tears the workspace down after the current eval completes.\n\n## Structured output\n\n```js\nconst schema = {\n type: "object",\n additionalProperties: false,\n required: ["findings"],\n properties: {\n findings: { type: "array", items: { type: "string" } },\n },\n};\nconst review = agent("claude", "Review error handling", { schema });\nconst result = await review;\nresult.findings\n```\n\nAgent options are exactly `schema`, `cwd`, `configOptions`, and `mode`. Unknown keys reject. Model, mode, and option ids are backend-specific; use the `workflow` tool\'s zero-token `action:"config"` discovery before pinning them.\n\n## What to read next\n\nRead only the references needed for the task:\n\n- [`references/state-and-bindings.md`](references/state-and-bindings.md) \u2014 persistence, completion values, polling, and output.\n- [`references/agent-handles.md`](references/agent-handles.md) \u2014 `agent()` options, failures, and handle identity.\n- [`references/steering-queueing-and-cancellation.md`](references/steering-queueing-and-cancellation.md) \u2014 strict active-turn control and durable future turns.\n- [`references/checkpoints-and-introspection.md`](references/checkpoints-and-introspection.md) \u2014 durable checkpoints and workspace inspection.\n- [`references/persistence-and-reset.md`](references/persistence-and-reset.md) \u2014 snapshot restoration, disconnect behavior, and reset.\n- [`references/api-reference.md`](references/api-reference.md) \u2014 every guest global and tool action.\n- [`references/examples.md`](references/examples.md) \u2014 interactive orchestration examples.\n'
|
|
32839
|
+
}
|
|
32965
32840
|
],
|
|
32966
|
-
"
|
|
32967
|
-
"sha256": "dbae758165a803a32807952f0caa12bbd2ece22f86e1d9e65f9ead1ca3e3ba9f",
|
|
32968
|
-
"text": '# REPL orchestration examples\n\n## Interactive investigate, steer, implement, verify\n\nFirst eval\u2014retain the founding handle:\n\n```js\nconst parser = agent("codex", "Investigate the parser test failure. Analyze only; do not edit yet.");\n```\n\nWhile it is actually running:\n\n```js\nagents()\n```\n\n```js\nconst steering = await parser.steer("Focus on token recovery after malformed metadata");\nsteering\n```\n\nAfter the founding answer:\n\n```js\nconst analysis = await parser;\nconst fix = parser.queue("Implement the smallest correct fix and add focused tests");\nconst verify = parser.queue("Run the focused tests and report exact results");\nconst fixed = await fix;\nconst tested = await verify;\n({ analysis, fixed, tested })\n```\n\n## Parallel structured reviews\n\n```js\nconst verdictSchema = {\n type: "object",\n additionalProperties: false,\n required: ["ok", "reason"],\n properties: {\n ok: { type: "boolean" },\n reason: { type: "string" },\n },\n};\n\nconst reviews = (await parallel([\n () => agent("claude", "Review the current diff for correctness", { schema: verdictSchema }),\n () => agent("codex", "Review the current diff for regressions", { schema: verdictSchema }),\n () => agent("opencode", "Review whether tests cover the changed behavior", { schema: verdictSchema }),\n])).filter(Boolean);\nreviews\n```\n\n## Decide after inspection\n\n```js\nconst candidates = agent("pi", "Find up to five concrete reliability improvements", {\n schema: {\n type: "object",\n additionalProperties: false,\n required: ["items"],\n properties: {\n items: { type: "array", items: { type: "string" } },\n },\n },\n});\n```\n\nLater:\n\n```js\nconst found = await candidates;\nfound.items\n```\n\nChoose one after discussing it with the user, then continue the same session:\n\n```js\nconst chosen = found.items[0];\nconst implementation = candidates.queue(`Implement only this item: ${chosen}`);\nawait implementation\n```\n\n## Human checkpoint\n\n```js\nconst decision = checkpoint("Which candidate should be implemented?", {\n choices: found.items,\n});\n```\n\nAfter receiving the human response:\n\n```js\nworkspace().checkpoints\n```\n\n```js\ncheckpoint.answer("c4", found.items[1]);\nconst selected = await decision;\nselected\n```\n\n## Recover from a long-running eval\n\nIf a tool result reports `running`, poll without side effects:\n\n```json\n{ "action": "eval", "projectDir": "/absolute/project", "code": "" }\n```\n\nInspect state:\n\n```js\n({ agents: agents(), diagnostics: workspace().diagnostics })\n```\n\nCancel only one identified call through an out-of-band tool call:\n\n```json\n{ "action": "interrupt", "projectDir": "/absolute/project", "id": "c7" }\n```\n\nUse interrupt without an id only for a runaway eval, not as a substitute for targeted call cancellation.\n\n## Bounded hunt loop\n\n```js\nconst seen = [];\nconst findings = await loopUntilDry({\n round: async (i) => {\n const h = agent("claude", `Round ${i + 1}: find new bugs not in ${JSON.stringify(seen)}`);\n const text = await h;\n const rows = text ? [text] : [];\n seen.push(...rows);\n return rows;\n },\n consecutiveEmpty: 2,\n maxRounds: 5,\n});\nfindings\n```\n\nKeep interactive loops bounded even though the workspace persists indefinitely.\n'
|
|
32841
|
+
"totalSize": 27086
|
|
32969
32842
|
}
|
|
32970
32843
|
];
|
|
32844
|
+
var AUTHORING_SKILL_URIS = AUTHORING_SKILLS.map((skill) => skill.uri);
|
|
32971
32845
|
|
|
32972
|
-
// ../mcp-server/src/
|
|
32973
|
-
var
|
|
32974
|
-
var
|
|
32975
|
-
var
|
|
32976
|
-
var
|
|
32977
|
-
|
|
32978
|
-
|
|
32979
|
-
|
|
32980
|
-
|
|
32981
|
-
|
|
32982
|
-
|
|
32983
|
-
|
|
32984
|
-
|
|
32985
|
-
|
|
32986
|
-
|
|
32987
|
-
|
|
32988
|
-
|
|
32846
|
+
// ../mcp-server/src/authoring-skills.ts
|
|
32847
|
+
var SKILLS_EXTENSION_ID = "io.modelcontextprotocol/skills";
|
|
32848
|
+
var SKILLS_LIST_METHOD = "skills/list";
|
|
32849
|
+
var SKILLS_GET_METHOD = "skills/get";
|
|
32850
|
+
var DIRECTORY_READ_METHOD = "resources/directory/read";
|
|
32851
|
+
var INODE_DIRECTORY_MIME_TYPE = "inode/directory";
|
|
32852
|
+
var digestSchema = external_exports.string().regex(/^sha256:[0-9a-f]{64}$/);
|
|
32853
|
+
var resultMetaSchema = external_exports.record(external_exports.string(), external_exports.unknown()).optional();
|
|
32854
|
+
var skillResourceRefSchema = external_exports.object({
|
|
32855
|
+
uri: external_exports.string().min(1),
|
|
32856
|
+
digest: digestSchema,
|
|
32857
|
+
size: external_exports.number().int().nonnegative()
|
|
32858
|
+
}).strict();
|
|
32859
|
+
var skillEntrySchema = external_exports.object({
|
|
32860
|
+
uri: external_exports.string().min(1),
|
|
32861
|
+
frontmatter: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
32862
|
+
resources: external_exports.union([external_exports.array(skillResourceRefSchema), external_exports.literal("dynamic")])
|
|
32863
|
+
}).strict();
|
|
32864
|
+
var skillsListParamsSchema = external_exports.object({
|
|
32865
|
+
cursor: external_exports.string().optional()
|
|
32866
|
+
}).loose();
|
|
32867
|
+
var skillsListResultSchema = external_exports.object({
|
|
32868
|
+
// The 2026 codec preserves resultType; the legacy 2025 codec projects it away.
|
|
32869
|
+
resultType: external_exports.literal("complete").optional(),
|
|
32870
|
+
skills: external_exports.array(skillEntrySchema),
|
|
32871
|
+
nextCursor: external_exports.string().optional(),
|
|
32872
|
+
ttlMs: external_exports.number().int().nonnegative().optional(),
|
|
32873
|
+
cacheScope: external_exports.enum(["public", "private"]).optional(),
|
|
32874
|
+
_meta: resultMetaSchema
|
|
32989
32875
|
}).strict();
|
|
32990
|
-
var
|
|
32991
|
-
|
|
32992
|
-
);
|
|
32993
|
-
|
|
32994
|
-
|
|
32995
|
-
|
|
32996
|
-
|
|
32997
|
-
|
|
32998
|
-
|
|
32876
|
+
var skillsGetParamsSchema = external_exports.object({
|
|
32877
|
+
uri: external_exports.string().min(1)
|
|
32878
|
+
}).loose();
|
|
32879
|
+
var skillsGetResultSchema = external_exports.object({
|
|
32880
|
+
// The 2026 codec preserves resultType; the legacy 2025 codec projects it away.
|
|
32881
|
+
resultType: external_exports.literal("complete").optional(),
|
|
32882
|
+
skill: skillEntrySchema,
|
|
32883
|
+
_meta: resultMetaSchema
|
|
32884
|
+
}).strict();
|
|
32885
|
+
var directoryReadParamsSchema = external_exports.object({
|
|
32886
|
+
uri: external_exports.string().min(1),
|
|
32887
|
+
cursor: external_exports.string().optional()
|
|
32888
|
+
}).loose();
|
|
32889
|
+
var directoryReadResultSchema = external_exports.object({
|
|
32890
|
+
// The 2026 codec preserves resultType; the legacy 2025 codec projects it away.
|
|
32891
|
+
resultType: external_exports.literal("complete").optional(),
|
|
32892
|
+
resources: external_exports.array(external_exports.object({
|
|
32893
|
+
uri: external_exports.string().min(1),
|
|
32894
|
+
name: external_exports.string().min(1),
|
|
32895
|
+
mimeType: external_exports.string().min(1),
|
|
32896
|
+
size: external_exports.number().int().nonnegative().optional()
|
|
32897
|
+
}).strict()),
|
|
32898
|
+
nextCursor: external_exports.string().optional(),
|
|
32899
|
+
_meta: resultMetaSchema
|
|
32900
|
+
}).strict();
|
|
32901
|
+
function skillEntry(skill) {
|
|
32999
32902
|
return {
|
|
33000
|
-
|
|
32903
|
+
uri: skill.uri,
|
|
32904
|
+
frontmatter: { ...skill.frontmatter },
|
|
32905
|
+
resources: skill.resources.map(({ uri, digest, size }) => ({ uri, digest, size }))
|
|
33001
32906
|
};
|
|
33002
32907
|
}
|
|
33003
|
-
|
|
32908
|
+
var AUTHORING_SKILL_ENTRIES = AUTHORING_SKILLS.map(skillEntry);
|
|
32909
|
+
var entriesByUri = new Map(AUTHORING_SKILL_ENTRIES.map((entry) => [entry.uri, entry]));
|
|
32910
|
+
var resourcesByUri = /* @__PURE__ */ new Map();
|
|
32911
|
+
for (const skill of AUTHORING_SKILLS) {
|
|
32912
|
+
for (const resource of skill.resources) resourcesByUri.set(resource.uri, resource);
|
|
32913
|
+
}
|
|
32914
|
+
function cloneEntry(entry) {
|
|
33004
32915
|
return {
|
|
33005
|
-
|
|
33006
|
-
|
|
33007
|
-
|
|
33008
|
-
|
|
33009
|
-
|
|
33010
|
-
|
|
33011
|
-
|
|
33012
|
-
|
|
33013
|
-
}
|
|
33014
|
-
function registerAuthoringDocs(mcp, options) {
|
|
33015
|
-
for (const topic of AUTHORING_DOC_TOPICS) {
|
|
33016
|
-
const read = () => authoringDocResource(topic);
|
|
33017
|
-
mcp.registerResource(
|
|
33018
|
-
`agentprism-docs-${topic.id.replaceAll("/", "-")}`,
|
|
33019
|
-
topic.uri,
|
|
33020
|
-
{
|
|
33021
|
-
title: topic.title,
|
|
33022
|
-
description: topic.description,
|
|
33023
|
-
mimeType: AUTHORING_DOC_MIME_TYPE
|
|
33024
|
-
},
|
|
33025
|
-
read
|
|
33026
|
-
);
|
|
33027
|
-
options.registerResourceReader(topic.uri, read);
|
|
32916
|
+
uri: entry.uri,
|
|
32917
|
+
frontmatter: { ...entry.frontmatter },
|
|
32918
|
+
resources: entry.resources.map((resource) => ({ ...resource }))
|
|
32919
|
+
};
|
|
32920
|
+
}
|
|
32921
|
+
function authoringSkillResource(uri) {
|
|
32922
|
+
const resource = resourcesByUri.get(uri);
|
|
32923
|
+
if (!resource) {
|
|
32924
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Authoring skill resource not found: ${uri}`);
|
|
33028
32925
|
}
|
|
33029
|
-
|
|
33030
|
-
|
|
33031
|
-
|
|
33032
|
-
|
|
33033
|
-
|
|
33034
|
-
|
|
33035
|
-
|
|
33036
|
-
|
|
33037
|
-
|
|
33038
|
-
|
|
33039
|
-
|
|
33040
|
-
|
|
32926
|
+
return {
|
|
32927
|
+
contents: [{ uri: resource.uri, mimeType: resource.mimeType, text: resource.text }]
|
|
32928
|
+
};
|
|
32929
|
+
}
|
|
32930
|
+
function addDirectoryChild(directories, parentUri, child) {
|
|
32931
|
+
let children = directories.get(parentUri);
|
|
32932
|
+
if (!children) {
|
|
32933
|
+
children = /* @__PURE__ */ new Map();
|
|
32934
|
+
directories.set(parentUri, children);
|
|
32935
|
+
}
|
|
32936
|
+
children.set(child.uri, child);
|
|
32937
|
+
}
|
|
32938
|
+
function buildDirectoryIndex() {
|
|
32939
|
+
const directories = /* @__PURE__ */ new Map();
|
|
32940
|
+
for (const skill of AUTHORING_SKILLS) {
|
|
32941
|
+
const rootUri = `skill://${skill.directory}`;
|
|
32942
|
+
if (!directories.has(rootUri)) directories.set(rootUri, /* @__PURE__ */ new Map());
|
|
32943
|
+
for (const resource of skill.resources) {
|
|
32944
|
+
const segments = resource.path.split("/");
|
|
32945
|
+
let parentUri = rootUri;
|
|
32946
|
+
for (let index = 0; index < segments.length - 1; index += 1) {
|
|
32947
|
+
const name = segments[index];
|
|
32948
|
+
const childUri = `${parentUri}/${encodeURIComponent(name)}`;
|
|
32949
|
+
addDirectoryChild(directories, parentUri, {
|
|
32950
|
+
uri: childUri,
|
|
32951
|
+
name,
|
|
32952
|
+
mimeType: INODE_DIRECTORY_MIME_TYPE
|
|
32953
|
+
});
|
|
32954
|
+
if (!directories.has(childUri)) directories.set(childUri, /* @__PURE__ */ new Map());
|
|
32955
|
+
parentUri = childUri;
|
|
32956
|
+
}
|
|
32957
|
+
addDirectoryChild(directories, parentUri, {
|
|
32958
|
+
uri: resource.uri,
|
|
32959
|
+
name: segments.at(-1),
|
|
32960
|
+
mimeType: resource.mimeType,
|
|
32961
|
+
size: resource.size
|
|
32962
|
+
});
|
|
32963
|
+
}
|
|
32964
|
+
}
|
|
32965
|
+
return new Map(
|
|
32966
|
+
[...directories].map(([uri, children]) => [
|
|
32967
|
+
uri,
|
|
32968
|
+
[...children.values()].sort((left, right) => left.uri.localeCompare(right.uri))
|
|
32969
|
+
])
|
|
32970
|
+
);
|
|
32971
|
+
}
|
|
32972
|
+
var directoryIndex = buildDirectoryIndex();
|
|
32973
|
+
function invalidCursor(method, cursor) {
|
|
32974
|
+
throw new ProtocolError(
|
|
32975
|
+
ProtocolErrorCode.InvalidParams,
|
|
32976
|
+
`${method} issued no pagination cursor; received unknown cursor: ${cursor}`
|
|
32977
|
+
);
|
|
32978
|
+
}
|
|
32979
|
+
function registerAuthoringSkills(mcp, options) {
|
|
32980
|
+
for (const skill of AUTHORING_SKILLS) {
|
|
32981
|
+
for (const resource of skill.resources) {
|
|
32982
|
+
const isSkillDocument = resource.uri === skill.uri;
|
|
32983
|
+
const read = () => authoringSkillResource(resource.uri);
|
|
32984
|
+
mcp.registerResource(
|
|
32985
|
+
isSkillDocument ? skill.directory : `${skill.directory}:${resource.path}`,
|
|
32986
|
+
resource.uri,
|
|
32987
|
+
{
|
|
32988
|
+
title: isSkillDocument ? String(skill.frontmatter.name) : resource.path,
|
|
32989
|
+
description: isSkillDocument ? String(skill.frontmatter.description) : `Supporting file for the ${skill.directory} Agent Skill.`,
|
|
32990
|
+
mimeType: resource.mimeType,
|
|
32991
|
+
size: resource.size,
|
|
32992
|
+
annotations: {
|
|
32993
|
+
audience: ["assistant"],
|
|
32994
|
+
priority: isSkillDocument ? 1 : 0.5
|
|
32995
|
+
}
|
|
32996
|
+
},
|
|
32997
|
+
read
|
|
32998
|
+
);
|
|
32999
|
+
options.registerResourceReader(resource.uri, read);
|
|
33000
|
+
}
|
|
33001
|
+
}
|
|
33002
|
+
mcp.server.setRequestHandler(
|
|
33003
|
+
SKILLS_LIST_METHOD,
|
|
33004
|
+
{ params: skillsListParamsSchema, result: skillsListResultSchema },
|
|
33005
|
+
async ({ cursor }) => {
|
|
33006
|
+
if (cursor !== void 0) invalidCursor(SKILLS_LIST_METHOD, cursor);
|
|
33007
|
+
return {
|
|
33008
|
+
resultType: "complete",
|
|
33009
|
+
skills: AUTHORING_SKILL_ENTRIES.map(cloneEntry),
|
|
33010
|
+
cacheScope: "public"
|
|
33011
|
+
};
|
|
33012
|
+
}
|
|
33013
|
+
);
|
|
33014
|
+
mcp.server.setRequestHandler(
|
|
33015
|
+
SKILLS_GET_METHOD,
|
|
33016
|
+
{ params: skillsGetParamsSchema, result: skillsGetResultSchema },
|
|
33017
|
+
async ({ uri }) => {
|
|
33018
|
+
const entry = entriesByUri.get(uri);
|
|
33019
|
+
if (!entry) {
|
|
33020
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Not a skill served by this server: ${uri}`);
|
|
33021
|
+
}
|
|
33022
|
+
return { resultType: "complete", skill: cloneEntry(entry) };
|
|
33023
|
+
}
|
|
33024
|
+
);
|
|
33025
|
+
mcp.server.setRequestHandler(
|
|
33026
|
+
DIRECTORY_READ_METHOD,
|
|
33027
|
+
{ params: directoryReadParamsSchema, result: directoryReadResultSchema },
|
|
33028
|
+
async ({ uri, cursor }) => {
|
|
33029
|
+
if (cursor !== void 0) invalidCursor(DIRECTORY_READ_METHOD, cursor);
|
|
33030
|
+
const resources = directoryIndex.get(uri);
|
|
33031
|
+
if (!resources) {
|
|
33032
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Not a directory resource: ${uri}`);
|
|
33041
33033
|
}
|
|
33042
|
-
},
|
|
33043
|
-
({ topic = "index" }) => {
|
|
33044
|
-
const document = authoringDocTopic(topic);
|
|
33045
|
-
const structuredContent = docsResult(document);
|
|
33046
33034
|
return {
|
|
33047
|
-
|
|
33048
|
-
|
|
33049
|
-
{
|
|
33050
|
-
type: "resource",
|
|
33051
|
-
resource: {
|
|
33052
|
-
uri: document.uri,
|
|
33053
|
-
mimeType: AUTHORING_DOC_MIME_TYPE,
|
|
33054
|
-
text: document.text
|
|
33055
|
-
}
|
|
33056
|
-
}
|
|
33057
|
-
],
|
|
33058
|
-
isError: false
|
|
33035
|
+
resultType: "complete",
|
|
33036
|
+
resources: resources.map((resource) => ({ ...resource }))
|
|
33059
33037
|
};
|
|
33060
33038
|
}
|
|
33061
33039
|
);
|
|
@@ -33242,7 +33220,7 @@ function registerReplTool(mcp, options) {
|
|
|
33242
33220
|
mcp.registerTool(
|
|
33243
33221
|
"repl",
|
|
33244
33222
|
{
|
|
33245
|
-
description: 'A persistent QuickJS-in-WASM JavaScript VM you drive interactively to orchestrate subagents \u2014 one VM per projectDir, addressed by the same project model as the workflow tool. Two actions: eval runs code and holds the call open pumping settlements; interrupt cancels one subagent call (by id) or breaks the running eval (no id). Named bindings, pending subagent calls, raised checkpoints, and `_` (the previous eval\'s completion value) PERSIST in the VM between calls \u2014 a later eval sees the same variables and awaits the same promises. Console logging produces output text only and creates no persistent value; nothing lives in the transcript. For deeper syntax and examples,
|
|
33223
|
+
description: 'A persistent QuickJS-in-WASM JavaScript VM you drive interactively to orchestrate subagents \u2014 one VM per projectDir, addressed by the same project model as the workflow tool. Two actions: eval runs code and holds the call open pumping settlements; interrupt cancels one subagent call (by id) or breaks the running eval (no id). Named bindings, pending subagent calls, raised checkpoints, and `_` (the previous eval\'s completion value) PERSIST in the VM between calls \u2014 a later eval sees the same variables and awaits the same promises. Console logging produces output text only and creates no persistent value; nothing lives in the transcript. For deeper syntax and examples, activate skill://agentprism-repl-orchestration/SKILL.md through the host\'s skill-loading path and read only the needed references. Inside code (JavaScript; top-level await is allowed, top-level return is a syntax error; console output is captured) the host bridge provides agent(modelSpec, task, opts?) \u2192 Promise: spawn an ACP subagent on a registry built-in (currently Claude, Codex, OpenCode, and pi) or a registered custom agent. The spec is "backend/model" (a bare "backend" runs its default model); an unknown backend rejects the call immediately, naming the known backends. The opts keys are schema (a structured-output JSON schema, validated per call), cwd, configOptions (backend-specific knobs, validated at admission), and mode. Before setting mode, use workflow action:"config" for that exact modelSpec and read the harness-owned mode descriptions. Trusted autonomous implementation/review uses advertised Claude bypassPermissions or Codex agent; Claude auto uses a model classifier and may request permission. Pin only an exact advertised id. Unknown option keys reject synchronously. agent() returns a persistent promise-handle. Assign the handle before awaiting it: `const a = agent("codex", "inspect the failure"); const first = await a`. a.steer(text) targets only the currently running turn. It never starts or queues another turn and resolves "injected", "idle", or "unsupported"; transport and protocol failures reject. Steering while idle returns "idle" and loses the instruction by design. `const q = a.queue(text)` creates a distinct FIFO turn on the same session. q.id is available immediately, await q returns that turn\'s answer, and q.cancel() or an out-of-band interrupt of q.id cancels that exact turn. Queueing works on every backend that can continue the session; steering requires the ACP server\'s raw steering advertisement. Do not write `const a = await agent(...)` when you intend to reuse the handle, because that stores only the answer. Persistent-workspace example \u2014 first eval: `const a = agent("codex", "Investigate the parser failure")`; a later eval, only while agents() reports a\'s turn as running: `const steering = await a.steer("Focus on the parser state machine")`; after the founding answer settles: `const first = await a; const q1 = a.queue("Implement the fix"); const q2 = a.queue("Run the focused tests"); console.log(q1.id, q2.id, steering); const fixed = await q1; const tested = await q2`. checkpoint(question) parks a promise for a human answer, resolved by checkpoint.answer(id, value) in a later eval. parallel, pipeline, verify, judgePanel, gate, retry, loopUntilDry, and sleep(ms) round out the guest library. Introspection is in-band: workspace() returns { bindings, inFlight, checkpoints, diagnostics }; agents() lists live agents with their call ids and states; reset() tears the workspace down. `_` holds the previous eval\'s completion value. No fs, no net, no timers beyond sleep. Subagents (6 concurrent per workspace) take stable ids c1, c2, \u2026 used by interrupt and reported by agents(). eval { code } runs the code, then HOLDS THE CALL OPEN pumping settlements up to a soft bound (default 60 000 ms; per-call timeoutMs override; hard cap 120 000 ms). If everything the code waits on settles within the bound the result is the finished shape { output, result? } \u2014 output is ONE newline-joined string (console lines, checkpoint lines like "checkpoint c9: <question>", error renderings), result the completion value\'s repr. If the bound elapses first the result is the still-running shape { output, running: [call ids] } and the eval continues server-side \u2014 any later eval drains what settled, and eval with "" (the empty script) is the documented idempotent poll: it re-executes nothing, only reports. State survives MCP-session churn and daemon restarts: every eval and every settlement drain that changed state persists the workspace to the daemon\'s per-project repl store, and the first touch of a stored workspace restores it and reconciles every outstanding call. A stored snapshot that refuses (corrupt, a format upgrade, a wasm-binary mismatch) AUTO-RESETS \u2014 the file is renamed aside, never deleted, and the next eval\'s output leads with a notice naming the file and reason. Reconcile reports and drain errors live in workspace().diagnostics. On last-client disconnect the workspace drains in-flight subagent turns to completion and closes idle children; the next eligible queued turn re-attaches its founding session lazily. Subagent output passes through UNFILTERED \u2014 backend harness noise (e.g. codex\'s "Warning: Skill descriptions were shortened\u2026") is forwarded verbatim, never curated away. Every result carries the machine-readable shape (see the output schema) as structuredContent alongside the human text.',
|
|
33246
33224
|
// STRICT at the wire too: the MCP SDK strips unknown keys from a
|
|
33247
33225
|
// non-strict object schema before the handler runs, so a deleted
|
|
33248
33226
|
// surface like `refs` would be silently discarded instead of
|
|
@@ -34917,13 +34895,13 @@ var DEFAULT_REQUEST_STATE_CODEC = createRequestStateCodec({
|
|
|
34917
34895
|
bind: (ctx) => ctx.mcpReq.method
|
|
34918
34896
|
});
|
|
34919
34897
|
var require2 = createRequire(import.meta.url);
|
|
34920
|
-
var SERVER_VERSION = true ? "
|
|
34898
|
+
var SERVER_VERSION = true ? "3.0.0" : require2("../package.json").version;
|
|
34921
34899
|
var SERVER_INSTRUCTIONS = [
|
|
34922
|
-
"This server exposes
|
|
34923
|
-
|
|
34924
|
-
'\u2022 workflow \u2014 DETERMINISTIC BATCH orchestration. Use action:"run" with a JavaScript workflow script
|
|
34925
|
-
'\u2022 repl \u2014 INTERACTIVE STATEFUL orchestration. A persistent per-project JavaScript VM
|
|
34926
|
-
"Rule of thumb: use workflow when you can script the whole plan ahead of time; use repl when you want a live
|
|
34900
|
+
"This server exposes two model-facing tools for orchestrating multi-agent work. workflow and repl spawn subagents over the same ACP backends \u2014 the registry built-ins Claude, Codex, OpenCode, and pi, plus any registered custom agents \u2014 and key durable state by an absolute projectDir (required on the shared daemon; defaulted by a single-project server). Backend credentials come from each agent's own login, so there is nothing auth-shaped to configure here.",
|
|
34901
|
+
"Version-matched authoring guidance is available through the server's Agent Skills. Activate skill://agentprism-workflow-authoring/SKILL.md for deterministic workflow scripts, or skill://agentprism-repl-orchestration/SKILL.md for the persistent REPL. Load a skill through the host's skill-loading path, then read only the supporting resources it references as needed.",
|
|
34902
|
+
'\u2022 workflow \u2014 DETERMINISTIC BATCH orchestration. Use action:"run" with a JavaScript workflow script that fans out agent() subagents and optional checkpoint() gates. background:true returns a durable runId for bounded status, permissions-response, result, and stop calls; resume continues the exact run from its durable admission and journal. action:"config" discovers the live backend and model option catalog. Every run is statically checked, mock-executed, and config-probed before admission.',
|
|
34903
|
+
'\u2022 repl \u2014 INTERACTIVE STATEFUL orchestration. A persistent per-project JavaScript VM driven with action:"eval". Named bindings, pending subagent handles, queued turns, checkpoints, and `_` persist between calls and survive daemon restarts. Use it when the next orchestration step depends on inspecting intermediate results.',
|
|
34904
|
+
"Rule of thumb: use workflow when you can script the whole plan ahead of time; use repl when you want a live session that evolves call by call."
|
|
34927
34905
|
].join("\n\n");
|
|
34928
34906
|
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["paused", "completed", "failed", "aborted"]);
|
|
34929
34907
|
function createExecutionAdmissionLatch() {
|
|
@@ -35852,13 +35830,16 @@ function createWorkflowServer(runner, options = {}) {
|
|
|
35852
35830
|
});
|
|
35853
35831
|
mcp.server.registerCapabilities({
|
|
35854
35832
|
resources: { subscribe: true, listChanged: true },
|
|
35855
|
-
extensions: {
|
|
35833
|
+
extensions: {
|
|
35834
|
+
[EXTENSION_ID]: {},
|
|
35835
|
+
[SKILLS_EXTENSION_ID]: { directoryRead: true }
|
|
35836
|
+
}
|
|
35856
35837
|
});
|
|
35857
35838
|
const requireProjectDir = options.requireProjectDir === true;
|
|
35858
35839
|
const projects = options.projects ?? new WorkflowProjectRegistry(runner);
|
|
35859
35840
|
const defaultContext = requireProjectDir ? void 0 : projects.adopt(options.manager ?? new WorkflowManager3({ agent: runner }), options.backgroundRuns);
|
|
35860
35841
|
const scriptResources = new WorkflowScriptResources(mcp, { router: projects }, options.modernNotifier);
|
|
35861
|
-
|
|
35842
|
+
registerAuthoringSkills(mcp, {
|
|
35862
35843
|
registerResourceReader: (uri, read) => scriptResources.registerExternalResourceReader(uri, read)
|
|
35863
35844
|
});
|
|
35864
35845
|
const replPresence = options.replPresence ?? new ReplPresenceLedger(options.replDrainBoundMs ?? REPL_DRAIN_BOUND_MS);
|
|
@@ -35891,7 +35872,7 @@ function createWorkflowServer(runner, options = {}) {
|
|
|
35891
35872
|
const workflowToolOutputSchema = workflowToolOutputShape;
|
|
35892
35873
|
const workflowToolConfig = {
|
|
35893
35874
|
title: "Run and manage deterministic agent workflows",
|
|
35894
|
-
description: "Validate, run, resume, observe, and control deterministic JavaScript agent workflows. Use config before pinning live model, mode, or config-option ids; run validates explicit script or scriptPath content; resume continues the exact runId from durable state. Use status for an immediate snapshot, result for exact completed JSON, permissions-response for a pending ACP choice, and stop for a run or one live call. " + (requireProjectDir ? "Config and run require an absolute projectDir on this shared daemon. " : "Config and run may omit projectDir on this single-project server. ") + "For
|
|
35875
|
+
description: "Validate, run, resume, observe, and control deterministic JavaScript agent workflows. Use config before pinning live model, mode, or config-option ids; run validates explicit script or scriptPath content; resume continues the exact runId from durable state. Use status for an immediate snapshot, result for exact completed JSON, permissions-response for a pending ACP choice, and stop for a run or one live call. " + (requireProjectDir ? "Config and run require an absolute projectDir on this shared daemon. " : "Config and run may omit projectDir on this single-project server. ") + "For deeper syntax and lifecycle guidance, activate skill://agentprism-workflow-authoring/SKILL.md through the host's skill-loading path and read only the references needed.",
|
|
35895
35876
|
inputSchema: workflowToolInputSchema,
|
|
35896
35877
|
outputSchema: workflowToolOutputSchema,
|
|
35897
35878
|
annotations: void 0
|
|
@@ -53874,19 +53855,20 @@ if (isProcessEntryPoint2()) {
|
|
|
53874
53855
|
});
|
|
53875
53856
|
}
|
|
53876
53857
|
export {
|
|
53877
|
-
AUTHORING_DOCS_SCHEMA_VERSION,
|
|
53878
|
-
AUTHORING_DOC_MIME_TYPE,
|
|
53879
|
-
AUTHORING_DOC_TOPICS,
|
|
53880
|
-
AUTHORING_DOC_TOPIC_IDS,
|
|
53881
53858
|
AUTHORING_PROMPT_NAME,
|
|
53859
|
+
AUTHORING_SKILLS,
|
|
53860
|
+
AUTHORING_SKILLS_SCHEMA_VERSION,
|
|
53861
|
+
AUTHORING_SKILL_ENTRIES,
|
|
53862
|
+
AUTHORING_SKILL_URIS,
|
|
53882
53863
|
BackgroundRunRegistry,
|
|
53883
53864
|
BoundedEventStore,
|
|
53884
53865
|
DAEMON_NAME,
|
|
53885
53866
|
DEFAULT_DAEMON_PORT,
|
|
53886
|
-
|
|
53867
|
+
DIRECTORY_READ_METHOD,
|
|
53887
53868
|
DaemonPortInUseError,
|
|
53888
53869
|
EVENTS_RESOURCE_MIME_TYPE,
|
|
53889
53870
|
EXTENSION_ID,
|
|
53871
|
+
INODE_DIRECTORY_MIME_TYPE,
|
|
53890
53872
|
MAX_BACKGROUND_RUNS,
|
|
53891
53873
|
MCP_ENDPOINT_PATH,
|
|
53892
53874
|
RESOURCE_MIME_TYPE,
|
|
@@ -53897,6 +53879,10 @@ export {
|
|
|
53897
53879
|
SCRIPT_RESOURCE_LIST_LIMIT,
|
|
53898
53880
|
SCRIPT_RESOURCE_MIME_TYPE,
|
|
53899
53881
|
SHUTDOWN_DEADLINE_MS,
|
|
53882
|
+
SKILLS_EXTENSION_ID,
|
|
53883
|
+
SKILLS_GET_METHOD,
|
|
53884
|
+
SKILLS_LIST_METHOD,
|
|
53885
|
+
WORKFLOW_AUTHORING_SKILL_URI,
|
|
53900
53886
|
WORKFLOW_EVENTS_TOOL_NAME,
|
|
53901
53887
|
WORKFLOW_RESULT_CHUNK_BYTES_DEFAULT,
|
|
53902
53888
|
WORKFLOW_RESULT_CHUNK_BYTES_MAX,
|
|
@@ -53906,19 +53892,18 @@ export {
|
|
|
53906
53892
|
WorkflowPermissionBroker,
|
|
53907
53893
|
WorkflowProjectRegistry,
|
|
53908
53894
|
appResourceToolMeta,
|
|
53909
|
-
|
|
53910
|
-
authoringDocTopic,
|
|
53895
|
+
authoringSkillResource,
|
|
53911
53896
|
buildAuthoringPromptText,
|
|
53912
53897
|
clampWorkflowInput,
|
|
53913
53898
|
createDaemon,
|
|
53914
53899
|
createProgressReporter,
|
|
53915
53900
|
createReplProjectState,
|
|
53916
53901
|
createWorkflowServer,
|
|
53902
|
+
directoryReadParamsSchema,
|
|
53903
|
+
directoryReadResultSchema,
|
|
53917
53904
|
dispatch,
|
|
53918
53905
|
disposeReplProjectState,
|
|
53919
53906
|
disposeRunnerWithDeadline,
|
|
53920
|
-
docsToolInputShape,
|
|
53921
|
-
docsToolOutputShape,
|
|
53922
53907
|
ensureDaemonRunning,
|
|
53923
53908
|
ensureReplWorkspace,
|
|
53924
53909
|
envFingerprint,
|
|
@@ -53929,8 +53914,8 @@ export {
|
|
|
53929
53914
|
parseWorkflowToolInput,
|
|
53930
53915
|
probeHealthz,
|
|
53931
53916
|
readDaemonInfo,
|
|
53932
|
-
registerAuthoringDocs,
|
|
53933
53917
|
registerAuthoringPrompt,
|
|
53918
|
+
registerAuthoringSkills,
|
|
53934
53919
|
registerWorkflowAppUi,
|
|
53935
53920
|
renameAsideNeverOverwriting,
|
|
53936
53921
|
replToolInputShape,
|
|
@@ -53940,6 +53925,10 @@ export {
|
|
|
53940
53925
|
runDaemon,
|
|
53941
53926
|
runShim,
|
|
53942
53927
|
singleStoreRouter,
|
|
53928
|
+
skillsGetParamsSchema,
|
|
53929
|
+
skillsGetResultSchema,
|
|
53930
|
+
skillsListParamsSchema,
|
|
53931
|
+
skillsListResultSchema,
|
|
53943
53932
|
supportsMcpApps,
|
|
53944
53933
|
toWorkflowExecutionOutcome,
|
|
53945
53934
|
toWorkflowToolResult,
|