@tt-a1i/openpi 0.1.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.
Files changed (114) hide show
  1. package/README.md +643 -0
  2. package/SETUP.md +74 -0
  3. package/THIRD_PARTY_NOTICES.md +16 -0
  4. package/assets/openpi-package.png +0 -0
  5. package/assets/readme-hero-mobile.svg +72 -0
  6. package/assets/readme-hero.svg +118 -0
  7. package/assets/readme-runtime-mobile.svg +91 -0
  8. package/assets/readme-runtime.svg +111 -0
  9. package/extensions/ask-user/handoff.ts +205 -0
  10. package/extensions/ask-user/index.ts +1110 -0
  11. package/extensions/ask-user/limits.ts +89 -0
  12. package/extensions/ask-user/prompt.ts +76 -0
  13. package/extensions/background-terminals/index.ts +653 -0
  14. package/extensions/background-terminals/src/domain.ts +99 -0
  15. package/extensions/background-terminals/src/manager.ts +989 -0
  16. package/extensions/background-terminals/src/output.ts +84 -0
  17. package/extensions/background-terminals/src/prompt.ts +195 -0
  18. package/extensions/background-terminals/src/result-delivery.ts +43 -0
  19. package/extensions/background-terminals/src/runtime.ts +36 -0
  20. package/extensions/background-terminals/src/ui/output-view.ts +55 -0
  21. package/extensions/background-terminals/src/ui/ps.ts +642 -0
  22. package/extensions/background-terminals/src/ui/tool-result.ts +146 -0
  23. package/extensions/background-terminals/src/watch.ts +192 -0
  24. package/extensions/context-pivot/index.ts +222 -0
  25. package/extensions/copy-all/index.ts +65 -0
  26. package/extensions/cron/index.ts +173 -0
  27. package/extensions/cron/schedule.ts +127 -0
  28. package/extensions/file-mutation-display/index.ts +105 -0
  29. package/extensions/file-mutation-display/render.ts +107 -0
  30. package/extensions/file-search/index.ts +515 -0
  31. package/extensions/file-search/src/args.ts +129 -0
  32. package/extensions/file-search/src/binaries.ts +419 -0
  33. package/extensions/file-search/src/output.ts +142 -0
  34. package/extensions/file-search/src/process.ts +309 -0
  35. package/extensions/file-search/src/prompt.ts +53 -0
  36. package/extensions/git-info/index.ts +272 -0
  37. package/extensions/git-info/src/changed-files-view.ts +414 -0
  38. package/extensions/git-info/src/process.ts +107 -0
  39. package/extensions/git-info/src/refresh-coordinator.ts +13 -0
  40. package/extensions/git-info/src/runtime.ts +28 -0
  41. package/extensions/goal/controller.ts +794 -0
  42. package/extensions/goal/index.ts +521 -0
  43. package/extensions/goal/prompts.ts +122 -0
  44. package/extensions/goal/state.ts +763 -0
  45. package/extensions/goal/ui.ts +158 -0
  46. package/extensions/model-info/index.ts +234 -0
  47. package/extensions/plan-mode/bash-policy.ts +313 -0
  48. package/extensions/plan-mode/index.ts +539 -0
  49. package/extensions/post-edit/index.ts +129 -0
  50. package/extensions/sessions/LICENSE.upstream +21 -0
  51. package/extensions/sessions/git-stats.ts +226 -0
  52. package/extensions/sessions/index.ts +1092 -0
  53. package/extensions/sessions/sessions.ts +385 -0
  54. package/extensions/setup/index.ts +408 -0
  55. package/extensions/shared/activity-status.ts +65 -0
  56. package/extensions/shared/below-editor-navigation.ts +343 -0
  57. package/extensions/shared/child-session.ts +352 -0
  58. package/extensions/shared/context-utilization.ts +47 -0
  59. package/extensions/shared/dashboard-state.ts +102 -0
  60. package/extensions/shared/plan-mode-state.ts +65 -0
  61. package/extensions/shared/setup-config.ts +971 -0
  62. package/extensions/shared/subagent-roles.ts +22 -0
  63. package/extensions/shared/terminal-text.ts +38 -0
  64. package/extensions/shared/tool-call-timeout.ts +104 -0
  65. package/extensions/shared/worktree.ts +526 -0
  66. package/extensions/subagents/index.ts +1225 -0
  67. package/extensions/subagents/navigation.ts +121 -0
  68. package/extensions/subagents/src/agent-types.ts +543 -0
  69. package/extensions/subagents/src/backend.ts +63 -0
  70. package/extensions/subagents/src/backends/pi.ts +493 -0
  71. package/extensions/subagents/src/backends/stub.ts +296 -0
  72. package/extensions/subagents/src/by-the-way.ts +21 -0
  73. package/extensions/subagents/src/domain.ts +271 -0
  74. package/extensions/subagents/src/format.ts +48 -0
  75. package/extensions/subagents/src/manager.ts +769 -0
  76. package/extensions/subagents/src/prompt.ts +190 -0
  77. package/extensions/subagents/src/result-delivery.ts +20 -0
  78. package/extensions/subagents/src/runtime.ts +51 -0
  79. package/extensions/subagents/src/ui/takeover.ts +615 -0
  80. package/extensions/subagents/src/ui/transcript.ts +293 -0
  81. package/extensions/subagents/src/ui/wait-result.ts +89 -0
  82. package/extensions/suggestions/index.ts +172 -0
  83. package/extensions/suggestions/src/config.ts +12 -0
  84. package/extensions/suggestions/src/predictor.ts +147 -0
  85. package/extensions/suggestions/src/prompt.ts +20 -0
  86. package/extensions/suggestions/src/transcript.ts +233 -0
  87. package/extensions/suggestions/src/ui.ts +224 -0
  88. package/extensions/tasks/index.ts +512 -0
  89. package/extensions/tasks/tasks.ts +649 -0
  90. package/extensions/tasks/ui.ts +421 -0
  91. package/extensions/turn-time/index.ts +61 -0
  92. package/extensions/ui-customization/footer.ts +512 -0
  93. package/extensions/ui-customization/index.ts +217 -0
  94. package/extensions/workflows/acceptance.ts +298 -0
  95. package/extensions/workflows/artifacts.ts +225 -0
  96. package/extensions/workflows/controller.ts +210 -0
  97. package/extensions/workflows/dashboard.ts +1226 -0
  98. package/extensions/workflows/index.ts +1884 -0
  99. package/extensions/workflows/journal.ts +188 -0
  100. package/extensions/workflows/meta.ts +250 -0
  101. package/extensions/workflows/model.ts +423 -0
  102. package/extensions/workflows/navigation.ts +93 -0
  103. package/extensions/workflows/prompt.ts +212 -0
  104. package/extensions/workflows/replay-safety.ts +577 -0
  105. package/extensions/workflows/runner.ts +786 -0
  106. package/extensions/workflows/sandbox-child.cjs +402 -0
  107. package/extensions/workflows/sandbox.ts +397 -0
  108. package/extensions/workflows/serialization.ts +162 -0
  109. package/extensions/workflows/worktree-handoff.ts +216 -0
  110. package/package.json +87 -0
  111. package/scripts/prepare-effect-tsgo.mjs +16 -0
  112. package/skills/background-terminals/SKILL.md +30 -0
  113. package/skills/subagents/SKILL.md +15 -0
  114. package/themes/github-dark-default.json +89 -0
@@ -0,0 +1,212 @@
1
+ import { sanitizeTerminalText } from "../shared/terminal-text.ts";
2
+ import {
3
+ countStates,
4
+ formatElapsed,
5
+ resultJson,
6
+ shortenHome,
7
+ type WorkflowDetails,
8
+ } from "./model.ts";
9
+
10
+ /** Model-facing schema descriptions for workflow source, arguments, and background mode. */
11
+ export const WORKFLOW_PARAMETER_DESCRIPTIONS = {
12
+ script:
13
+ "JavaScript workflow script. May start with `export const meta = {...}`, then use phase(), agent(), parallel(), args, and a final `return`.",
14
+ args: "Optional JSON string exposed to the script as `args` (parsed when valid JSON, otherwise passed through as the raw string).",
15
+ background:
16
+ "Run in the background: the tool returns a run id immediately and you receive a follow-up message when the workflow finishes. Defaults to false (blocking with live progress).",
17
+ resumeFromRunId:
18
+ 'Optional run id of a previous workflow (e.g. "wf_1a2b3c4d5e6f", or a unique suffix) to replay cached read-only agent results. A call replays only when its prompt, resolved agent type/schema/model/provider/effort, canonical cwd, repository state, loaded resources, and trust context match. Unrestricted/no-type agents, writable or unknown tool lists, worktree-isolated agents, failed calls, and calls whose context cannot be fingerprinted always run for real. Matching remains content-based and order-independent. Old or unknown journals simply run everything fresh.',
19
+ };
20
+
21
+ /** Describes stopping a running background workflow, mirroring subagent_cancel/bg_kill. */
22
+ export const WORKFLOW_STOP_TOOL_DESCRIPTION =
23
+ "Cancel a running background workflow by its run id (from the workflow launch result). This aborts its remaining agents and settles the run; partial results and artifacts are preserved. Only background runs need this — a blocking workflow is already cancelled by interrupting the turn.";
24
+
25
+ /** Model-facing schema description for the workflow run id to stop. */
26
+ export const WORKFLOW_STOP_PARAMETER_DESCRIPTIONS = {
27
+ runId: 'Workflow run id to cancel, e.g. "wf_1a2b3c4d5e6f".',
28
+ };
29
+
30
+ /** Describes nonblocking inspection of workflow runs, mirroring subagent_check/subagent_list. */
31
+ export const WORKFLOW_STATUS_TOOL_DESCRIPTION =
32
+ "Peek at background workflow runs without blocking. With a run id, returns that run's phases, per-agent status, and result if finished; without one, lists this session's active and recently finished runs. Does not wait — use background:false when you need the result inline.";
33
+
34
+ /** Model-facing schema description for the optional workflow run id to inspect. */
35
+ export const WORKFLOW_STATUS_PARAMETER_DESCRIPTIONS = {
36
+ runId:
37
+ "Optional workflow run id to inspect. Omit to list active and recently finished runs.",
38
+ };
39
+
40
+ /** Adds workflow lifecycle inspection/cancellation to the parent model's tools prompt. */
41
+ export const WORKFLOW_LIFECYCLE_PROMPT_SNIPPET =
42
+ "Inspect (workflow_status) or cancel (workflow_stop) a background workflow by run id";
43
+
44
+ /** Defines the workflow DSL, constraints, reliability guidance, and model-authored task examples. */
45
+ export const WORKFLOW_TOOL_DESCRIPTION = [
46
+ "Use the workflow tool when the user explicitly requests a workflow run or when the task clearly requires multi-phase dynamic orchestration.",
47
+ "Run a multi-agent workflow from a JavaScript orchestration script you write inline. Use this when a task benefits from fanning work out across several isolated subagents in ordered phases (research fan-out, per-file review, verify-then-synthesize pipelines).",
48
+ "The script runs as an async function body with these primitives:",
49
+ "• export const meta = { name, description, phases: [{ title, detail? }] } — metadata for the progress UI. Declare all phases up front.",
50
+ "• phase(title) — mark the current phase at runtime (use titles from meta.phases).",
51
+ "• log(message) — emit one progress line to the user and to your own final report. This is the run's narrator: use it for anything the reader needs while the run is still going, or that the return value would not capture — round counts, how many agents were dropped, why a branch was skipped. Unlike phase(), it does not touch the phase list. Lines are one row each (newlines are flattened); the most recent 100 are kept and any earlier ones are reported as dropped.",
52
+ "• usage() — read this run's cumulative token spend so far: { input, output, cacheRead, cacheWrite, total, cost, agents }. The reading refreshes as each agent settles, so evaluating it right after an `await` reflects that agent. `total` never decreases, but it is a LOWER BOUND rather than an exact figure: a child session that compacts drops the tokens of the messages it discarded. Use it to report or adapt cost — e.g. log a running total, or stop a discovery loop once the spend stops paying for itself — and expect a long run to have spent somewhat more than it says. It is a reading, not a limit: nothing is enforced for you.",
53
+ "• await agent(prompt, { agent_type?, label?, phase?, schema?, acceptance?, model?, provider?, effort?, isolation? }) — run ONE subagent in an isolated context and wait for it. `agent_type` applies the same named preset and enforced capabilities as subagent_spawn: specialized system prompt, tool allowlist, model assignment, and default effort. Prefer a matching type when one exists. Model precedence is explicit model/provider > type-file model > configured built-in role model > parent model; effort precedence is explicit effort > type default > parent effort. Omit `agent_type` for a general-purpose child. Always resolves to { ok, output, structured?, acceptance?, error? }. Check `ok` before using the result. When you pass a JSON `schema`, `structured` holds the validated object on success. Optional `acceptance: { criteria: [{ id, description, requiredEvidence? }] }` is explicit and adds no extra agent: the same structured result must include an evidence ledger; missing, malformed, or rejected criteria make `ok:false` while preserving output and ledger. Children receive normal built-ins and trust-appropriate extensions, settings, skills, and AGENTS.md context, but cannot recursively orchestrate or ask the user.",
54
+ "• isolation: 'worktree' runs that one agent in its own git worktree on its own branch, instead of the shared working directory. Use it for any fan-out where agents WRITE — without it, concurrent agents share one checkout and one git index, so their edits and `git add`s silently overwrite each other. Tell such an agent to COMMIT its work: on completion the worktree directory is reclaimed and its branch is kept for you to merge (an empty branch is deleted; uncommitted changes keep the directory instead). The branch name comes back in the run artifacts. Costs a fresh checkout, needs a git repo, and starts without gitignored files, so leave it off for read-only agents.",
55
+ "• await parallel([() => agent(...), () => agent(...)], { concurrency? }) — run zero-argument agent thunks concurrently and return results in order. This is a BARRIER: nothing after it starts until every thunk settles. A thunk that throws settles to null (filter it out) rather than failing the whole batch, so one bad item never discards the others' results. The package default is 8 concurrent agents per workflow and can be changed with /my-pi-setup (hard maximum 64).",
56
+ "• await pipeline(items, stage1, stage2, ...) — run each item through every stage independently, with NO barrier between stages: item A can be in stage 3 while item B is still in stage 1. Results come back in input order. Each stage receives (previousResult, originalItem, index), so a later stage can label its work without threading context through the earlier stage's return value. A stage that throws drops that item to null and skips its remaining stages, leaving siblings untouched.",
57
+ "PREFER pipeline() for multi-stage work. parallel() forces every item to wait for the slowest one in each stage, so wall-clock becomes the sum of per-stage worst cases (max stage1 + max stage2) instead of the slowest single chain. The gap is widest when different items are slow in different stages; when one item is slowest everywhere it is the critical path either way. Reach for a barrier only when a stage genuinely needs cross-item context from ALL of the previous one: deduping or merging the full result set, exiting early when the total count is zero, or a prompt that compares one finding against the others. Needing to flatten/map/filter in between is NOT a reason — do that inside a pipeline stage.",
58
+
59
+ "• args — the parsed value of the `args` tool parameter (or undefined).",
60
+ "Workflow JavaScript runs in a restricted, killable child with no imports, eval, timers, filesystem, network, or process APIs. The package default permits 128 agent calls per run and can be changed with /my-pi-setup (hard maximum 1024); there is no overall deadline. Each agent must receive its first assistant response event within 45 seconds so silent provider requests fail clearly; after that, agent() has no wall-clock deadline. Each individual child tool call times out independently after 3 minutes, becomes an error tool result, and leaves the agent loop free to recover. Use map/filter/if/await/template strings to orchestrate, and `return` a JSON-serializable aggregate.",
61
+ "Pass a `schema` to agent() whenever a later step branches on the result, so you get typed fields instead of prose. Artifacts are saved under ~/.pi/agent/workflows/<runId>/ for inspection. To re-run an edited workflow cheaply, pass `resume_from_run_id` with the previous run id: only provably read-only calls whose content and project/resource context are unchanged can replay; writable, unrestricted, unknown-tool, and worktree calls always run for real.",
62
+ "Example — each file is verified as soon as ITS OWN scan lands, instead of waiting for every scan:",
63
+ "export const meta = { name: 'reliability-review', description: 'Review modules for reliability risks, then report', phases: [{ title: 'Scan' }, { title: 'Verify' }, { title: 'Report' }] }",
64
+ "const FINDINGS = { type: 'object', properties: { issues: { type: 'array', items: { type: 'string' } }, ok: { type: 'boolean' } }, required: ['issues', 'ok'] }",
65
+ "phase('Scan')",
66
+ "const checked = await pipeline(args.files,",
67
+ " (f) => agent(`Trace ${f} for candidate reliability risks with file:line evidence.`, { agent_type: 'explorer', label: `scan:${f}`, phase: 'Scan', schema: FINDINGS }),",
68
+ " (scan, f) => scan.ok ? agent(`Review whether these candidate issues in ${f} are real: ${JSON.stringify(scan.structured.issues)}`, { agent_type: 'reviewer', label: `verify:${f}`, phase: 'Verify' }) : null)",
69
+ "const verified = checked.filter((r) => r && r.ok)",
70
+ "const dropped = checked.length - verified.length // agents that failed/dropped: surface, never silently swallow",
71
+ "if (dropped) log(`${dropped}/${checked.length} file(s) dropped before verification`)",
72
+ "phase('Report')",
73
+ "const report = await agent(`Synthesize tradeoffs and recommendations from these verified findings: ${JSON.stringify(verified.map((r) => r.output))}`, { agent_type: 'advisor', label: 'report', phase: 'Report' })",
74
+ "log(`done — ${verified.length} verified, ${usage().total} tokens`)",
75
+ "return { verified: verified.length, dropped, report: report.ok ? report.output : report.error }",
76
+ ].join("\n");
77
+
78
+ /** Adds workflow orchestration primitives and background execution to the model's tool prompt. */
79
+ export const WORKFLOW_PROMPT_SNIPPET =
80
+ "Orchestrate isolated subagents from an inline JS script: phase()/agent()/pipeline()/parallel() with structured outputs, log() progress, usage() token readings, and optional background execution";
81
+
82
+ /** Guides the model on appropriate workflow fan-out and mandatory agent result checks. */
83
+ export const WORKFLOW_PROMPT_GUIDELINES = [
84
+ "Use workflow when a task needs several subagents with phase dependencies or dynamic fan-out; keep single small delegations in the main session.",
85
+ "For each workflow agent() call, select a matching agent_type when one exists (explorer, implementer, reviewer, advisor, or a loaded custom type) so its configured model, prompt, effort, and enforced tools apply; do not hardcode that role's model. Omit agent_type only for genuinely general-purpose work.",
86
+ "Default to pipeline() for multi-stage fan-out so each item advances as soon as its own previous stage lands; use parallel() only when a stage truly needs every prior result at once.",
87
+ "In workflow scripts, agent() never throws — check `.ok` before using `.output`/`.structured`; but parallel() and pipeline() settle a throwing thunk or stage to `null`, so guard those with `r && r.ok`.",
88
+ "A filtered-out or null result is a failed agent, not a clean pass: surface how many dropped (e.g. return a count) so a crashed or timed-out agent never reads as success.",
89
+ "log() anything the reader would want before the run ends — round counts, dropped agents, why a branch was skipped. A long run that narrates nothing is indistinguishable from a stalled one, and the return value only arrives at the end.",
90
+ "When several agents will edit files concurrently, give each one isolation: 'worktree' and tell it to commit; otherwise they share one checkout and one git index and overwrite each other. Read-only agents do not need it.",
91
+ ];
92
+
93
+ /** Marks and forwards a workflow script's agent() task as an isolated child-model prompt. */
94
+ export function buildWorkflowAgentPrompt(prompt: string) {
95
+ return prompt;
96
+ }
97
+
98
+ /** Instructs structured workflow children to terminate with exactly one structured_output call. */
99
+ export const STRUCTURED_OUTPUT_SYSTEM_INSTRUCTION =
100
+ "When your task is complete, call the `structured_output` tool exactly once as your final action, with fields matching the required schema. Do not write any other text after it.";
101
+
102
+ /** Describes the terminating structured_output tool and its final-action contract. */
103
+ export const STRUCTURED_OUTPUT_TOOL_DESCRIPTION =
104
+ "Return your final result as structured data matching the required schema. Call this exactly once, as your last action; do not write any other text after it.";
105
+
106
+ /** Builds the workflow completion report returned to the parent model. */
107
+ export function buildWorkflowResultMessage(
108
+ details: WorkflowDetails,
109
+ runDir: string,
110
+ ) {
111
+ const { done, failed } = countStates(details);
112
+ const elapsed = formatElapsed(details.startedAt, details.finishedAt);
113
+ const lines = [
114
+ `Workflow ${details.name ? `"${details.name}"` : details.runId} ${details.status} — ` +
115
+ `${done}/${details.agents.length} agents ok${failed ? `, ${failed} failed` : ""} ` +
116
+ `across ${details.phases.length} phase(s) in ${elapsed}.`,
117
+ `Run dir: ${shortenHome(runDir)}`,
118
+ ];
119
+ // State the hit rate out loud: it is the only way to tell a resume that
120
+ // worked from one that silently replayed nothing.
121
+ const replayed = details.agents.filter((agent) => agent.replayed).length;
122
+ if (details.resumedFrom) {
123
+ lines.push(
124
+ `Resumed from ${details.resumedFrom}: replayed ${replayed}/${details.agents.length} agent call(s), ran ${details.agents.length - replayed} for real.`,
125
+ );
126
+ }
127
+ if (details.resumeNote) lines.push(`Resume: ${details.resumeNote}`);
128
+ if (details.error) lines.push(`Error: ${details.error}`);
129
+ // The script's own narration of what happened, which is often the only
130
+ // record of work that did not make it into the return value.
131
+ if (details.logs && details.logs.length > 0) {
132
+ lines.push("", "Log:");
133
+ if (details.logsDropped) {
134
+ lines.push(` (${details.logsDropped} earlier line(s) dropped)`);
135
+ }
136
+ for (const entry of details.logs) lines.push(` ${entry.text}`);
137
+ }
138
+ // Isolated work lives on a branch or in a kept directory, not in the working
139
+ // tree, so an unreported one is work the parent cannot find.
140
+ const isolated = details.agents.filter(
141
+ (agent) => agent.worktreeBranch || agent.worktreePath,
142
+ );
143
+ if (isolated.length > 0) {
144
+ lines.push("", "Isolated worktrees:");
145
+ for (const agent of isolated) {
146
+ const cleanup = agent.worktreeCleanup;
147
+ const work = cleanup?.commits
148
+ ? `${cleanup.commits} commit${cleanup.commits === 1 ? "" : "s"} on ${cleanup.branch}`
149
+ : agent.worktreeBranch
150
+ ? `committed to branch ${agent.worktreeBranch}`
151
+ : "no commits";
152
+ lines.push(
153
+ `- [${agent.label}] ${work}${
154
+ agent.worktreePath
155
+ ? `; kept at ${shortenHome(agent.worktreePath)} (${cleanup?.reason ?? "uncommitted changes"})`
156
+ : cleanup?.branchDeleted
157
+ ? "; empty branch deleted"
158
+ : cleanup?.reason
159
+ ? `; cleanup warning: ${cleanup.reason}`
160
+ : ""
161
+ }${agent.worktreeHandoffArtifact ? `; handoff ${agent.worktreeHandoffArtifact}` : ""}`,
162
+ );
163
+ }
164
+ }
165
+ if (details.agents.length > 0) {
166
+ lines.push("", "Agents:");
167
+ for (const agent of details.agents) {
168
+ const status =
169
+ agent.state === "done"
170
+ ? agent.replayed
171
+ ? "ok (replayed)"
172
+ : "ok"
173
+ : agent.state === "error"
174
+ ? "FAILED"
175
+ : "running";
176
+ lines.push(
177
+ `- [${agent.label}]${agent.phase ? ` (${agent.phase})` : ""} ${status}` +
178
+ (agent.acceptance ? ` · acceptance ${agent.acceptance.status}` : "") +
179
+ (agent.error ? ` — ${agent.error}` : ""),
180
+ );
181
+ }
182
+ }
183
+ if (details.result !== undefined)
184
+ lines.push("", "Result:", resultJson(details.result));
185
+ return sanitizeTerminalText(lines.join("\n"));
186
+ }
187
+
188
+ /** Builds the follow-up message that delivers a settled background workflow to the parent model. */
189
+ export function buildBackgroundWorkflowFollowUp(options: {
190
+ runId: string;
191
+ name?: string;
192
+ status: WorkflowDetails["status"];
193
+ result: string;
194
+ }) {
195
+ // Sentence lead-in matching the subagent/terminal completion messages.
196
+ const label = options.name ? `"${options.name}"` : options.runId;
197
+ const verb = options.status === "completed" ? "finished" : options.status;
198
+ return `Background workflow ${label} (${options.runId}) ${verb}.\n\n${options.result}`;
199
+ }
200
+
201
+ /** Builds the background-launch result and tells the parent model how to inspect or stop the run. */
202
+ export function buildBackgroundWorkflowLaunchResult(options: {
203
+ runId: string;
204
+ name?: string;
205
+ runDir: string;
206
+ }) {
207
+ return [
208
+ `Workflow ${options.name ? `"${options.name}"` : options.runId} launched in background (run ${options.runId}).`,
209
+ `Artifacts: ${shortenHome(options.runDir)}`,
210
+ `Its result will be delivered to you when it finishes, or use workflow_status(runId: "${options.runId}") to peek and workflow_stop(runId: "${options.runId}") to cancel; /workflows shows progress.`,
211
+ ].join("\n");
212
+ }