@sema-agent/core 1.452.0 → 2.0.1

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 (167) hide show
  1. package/dist/agents/agent-transcript-tool.d.ts +15 -0
  2. package/dist/agents/agent-transcript-tool.js +122 -0
  3. package/dist/agents/cascade.js +5 -8
  4. package/dist/agents/repair-loop.d.ts +1 -1
  5. package/dist/agents/repair-loop.js +1 -1
  6. package/dist/agents/retain-ledger.d.ts +60 -0
  7. package/dist/agents/retain-ledger.js +225 -0
  8. package/dist/agents/roster-store.d.ts +1 -0
  9. package/dist/agents/roster-store.js +9 -0
  10. package/dist/agents/send-message-tool.d.ts +41 -0
  11. package/dist/agents/send-message-tool.js +469 -0
  12. package/dist/agents/subagent.d.ts +33 -108
  13. package/dist/agents/subagent.js +22 -834
  14. package/dist/agents/teacher.d.ts +0 -1
  15. package/dist/agents/teacher.js +0 -9
  16. package/dist/bin/sema-tb.js +3 -3
  17. package/dist/brain/anthropic.js +15 -40
  18. package/dist/brain/openai.js +15 -40
  19. package/dist/brain/reasoning.d.ts +1 -1
  20. package/dist/brain/stream-engine.js +2 -2
  21. package/dist/brain/stream-shared.d.ts +17 -0
  22. package/dist/brain/stream-shared.js +50 -0
  23. package/dist/brain/timeout.d.ts +1 -1
  24. package/dist/brain/timeout.js +1 -1
  25. package/dist/config/catalog.js +1 -3
  26. package/dist/config/defaults.d.ts +6 -0
  27. package/dist/config/defaults.js +6 -0
  28. package/dist/core/ask-question.d.ts +1 -1
  29. package/dist/core/background-shell.d.ts +1 -1
  30. package/dist/core/checkpoint-store.d.ts +0 -1
  31. package/dist/core/exec-gate.d.ts +1 -1
  32. package/dist/core/exec-output-tail.d.ts +1 -0
  33. package/dist/core/exec-output-tail.js +6 -0
  34. package/dist/core/file-snapshot-store.d.ts +1 -1
  35. package/dist/core/fs-write-gate-policy.d.ts +1 -1
  36. package/dist/core/git-worktree-env.d.ts +1 -1
  37. package/dist/core/hooks.d.ts +1 -1
  38. package/dist/core/image-downsample.d.ts +44 -0
  39. package/dist/core/image-downsample.js +97 -0
  40. package/dist/core/lsp.d.ts +1 -1
  41. package/dist/core/mcp.d.ts +3 -45
  42. package/dist/core/mcp.js +2 -97
  43. package/dist/core/media-byte-cap.d.ts +1 -1
  44. package/dist/core/memory-recall.d.ts +0 -12
  45. package/dist/core/memory-recall.js +0 -222
  46. package/dist/core/memory.d.ts +0 -6
  47. package/dist/core/memory.js +0 -88
  48. package/dist/core/message-utils.d.ts +1 -1
  49. package/dist/core/oracle-isolation.d.ts +1 -1
  50. package/dist/core/remote-env.d.ts +1 -1
  51. package/dist/core/roles.d.ts +1 -1
  52. package/dist/core/runner/active-skill-scope.d.ts +1 -1
  53. package/dist/core/runner/assemble-result.d.ts +0 -2
  54. package/dist/core/runner/memory-consolidation.d.ts +1 -1
  55. package/dist/core/runner/prepare-memory.d.ts +16 -0
  56. package/dist/core/runner/prepare-memory.js +191 -0
  57. package/dist/core/runner/prepare-task.d.ts +4 -28
  58. package/dist/core/runner/prepare-task.js +19 -207
  59. package/dist/core/runner/prompt-suggestions.d.ts +1 -1
  60. package/dist/core/runner/runtask.d.ts +2 -3
  61. package/dist/core/runner/runtask.js +337 -529
  62. package/dist/core/runner/session-rule-policy.d.ts +1 -1
  63. package/dist/core/runner/synthetic-tools.d.ts +5 -5
  64. package/dist/core/runner/synthetic-tools.js +4 -4
  65. package/dist/core/runner/tool-disclosure.d.ts +3 -3
  66. package/dist/core/runner/tool-disclosure.js +2 -2
  67. package/dist/core/runner/tool-output-projection.d.ts +6 -0
  68. package/dist/core/runner/tool-output-projection.js +81 -0
  69. package/dist/core/runtime.d.ts +1 -1
  70. package/dist/core/scheduler.d.ts +1 -1
  71. package/dist/core/sensitive-path-policy.d.ts +1 -1
  72. package/dist/core/session-reconcile.d.ts +1 -1
  73. package/dist/core/session-store.d.ts +2 -1
  74. package/dist/core/session-store.js +2 -1
  75. package/dist/core/session.d.ts +0 -1
  76. package/dist/core/task-registry-agent.d.ts +115 -0
  77. package/dist/core/task-registry-agent.js +1163 -0
  78. package/dist/core/task-registry-monitor.d.ts +13 -0
  79. package/dist/core/task-registry-monitor.js +347 -0
  80. package/dist/core/task-registry-shared.d.ts +294 -0
  81. package/dist/core/task-registry-shared.js +271 -0
  82. package/dist/core/task-registry-workflow.d.ts +5 -0
  83. package/dist/core/task-registry-workflow.js +143 -0
  84. package/dist/core/task-registry.d.ts +51 -216
  85. package/dist/core/task-registry.js +127 -1940
  86. package/dist/core/task-tool-shape.d.ts +32 -0
  87. package/dist/core/task-tool-shape.js +83 -0
  88. package/dist/core/tool-policy.d.ts +1 -2
  89. package/dist/core/tool-result-budget.d.ts +1 -1
  90. package/dist/core/tool-result-store.d.ts +2 -2
  91. package/dist/core/tool-result-store.js +1 -1
  92. package/dist/core/tools.d.ts +2 -3
  93. package/dist/core/tools.js +1 -8
  94. package/dist/core/trace.d.ts +2 -2
  95. package/dist/core/types.d.ts +0 -29
  96. package/dist/core/workflow-run-store-contract.d.ts +1 -1
  97. package/dist/core/workflow-run-store-contract.js +19 -19
  98. package/dist/engine/execution-env/node-execution-env.d.ts +13 -0
  99. package/dist/engine/execution-env/node-execution-env.js +55 -25
  100. package/dist/engine/loop/agent-loop.d.ts +1 -1
  101. package/dist/engine/loop/agent-loop.js +10 -9
  102. package/dist/engine/lsp/frame-decoder.d.ts +1 -1
  103. package/dist/engine/lsp/frame-decoder.js +1 -1
  104. package/dist/engine/lsp/node-lsp-manager.d.ts +1 -1
  105. package/dist/engine/lsp/stdio-lsp-transport.js +2 -2
  106. package/dist/index.d.ts +16 -14
  107. package/dist/index.js +13 -11
  108. package/dist/internal/harness-types.d.ts +7 -0
  109. package/dist/internal/harness-types.js +1 -0
  110. package/dist/internal/harness.d.ts +1 -7
  111. package/dist/internal/harness.js +1 -0
  112. package/dist/orchestration/goal.js +2 -2
  113. package/dist/orchestration/run-workflow-tool.d.ts +1 -1
  114. package/dist/orchestration/workflow-types.d.ts +193 -0
  115. package/dist/orchestration/workflow-types.js +54 -0
  116. package/dist/orchestration/workflow.d.ts +3 -193
  117. package/dist/orchestration/workflow.js +7 -59
  118. package/dist/prompt-assembly/assemble.d.ts +3 -3
  119. package/dist/prompt-assembly/assemble.js +0 -16
  120. package/dist/prompt-assembly/explain.d.ts +1 -1
  121. package/dist/prompt-assembly/tool-catalog.d.ts +1 -1
  122. package/dist/prompts/default.d.ts +1 -6
  123. package/dist/prompts/default.js +3 -11
  124. package/dist/scenarios/env.d.ts +1 -1
  125. package/dist/scenarios/full-body.d.ts +0 -3
  126. package/dist/scenarios/full-body.js +0 -2
  127. package/dist/stores/file/background-agent-store.d.ts +2 -15
  128. package/dist/stores/file/background-agent-store.js +30 -100
  129. package/dist/stores/file/checkpoint-store.d.ts +1 -14
  130. package/dist/stores/file/checkpoint-store.js +47 -119
  131. package/dist/stores/file/file-snapshot-store.d.ts +1 -1
  132. package/dist/stores/file/mailbox-store.js +33 -2
  133. package/dist/stores/file/shared-ledger.d.ts +35 -0
  134. package/dist/stores/file/shared-ledger.js +94 -0
  135. package/dist/stores/file/workflow-run-store.d.ts +1 -14
  136. package/dist/stores/file/workflow-run-store.js +24 -93
  137. package/dist/tools/fs/bash-readonly-classifier.d.ts +8 -0
  138. package/dist/tools/fs/bash-readonly-classifier.js +166 -0
  139. package/dist/tools/fs/fs-bash.d.ts +31 -0
  140. package/dist/tools/fs/fs-bash.js +672 -0
  141. package/dist/tools/fs/fs-pdf.d.ts +22 -0
  142. package/dist/tools/fs/fs-pdf.js +236 -0
  143. package/dist/tools/fs/fs-read.d.ts +8 -0
  144. package/dist/tools/fs/fs-read.js +286 -0
  145. package/dist/tools/fs/fs-search-tools.d.ts +5 -0
  146. package/dist/tools/fs/fs-search-tools.js +175 -0
  147. package/dist/tools/fs/fs-shared.d.ts +56 -0
  148. package/dist/tools/fs/fs-shared.js +148 -0
  149. package/dist/tools/fs/fs-write.d.ts +8 -0
  150. package/dist/tools/fs/fs-write.js +421 -0
  151. package/dist/tools/fs/index.d.ts +12 -65
  152. package/dist/tools/fs/index.js +26 -2090
  153. package/dist/tools/fs/pdf.d.ts +1 -1
  154. package/dist/tools/fs/repo-map.d.ts +3 -3
  155. package/dist/tools/fs/repo-map.js +1 -1
  156. package/dist/tools/fs/safety.d.ts +4 -1
  157. package/dist/tools/fs/safety.js +7 -1
  158. package/dist/tools/fs/search.d.ts +2 -1
  159. package/dist/tools/fs/search.js +21 -3
  160. package/dist/tools/monitor.d.ts +1 -1
  161. package/dist/tools/scheduler-tools.d.ts +1 -1
  162. package/dist/tools/web.d.ts +2 -2
  163. package/dist/tools/web.js +46 -8
  164. package/dist/tools/worktree.d.ts +1 -1
  165. package/package.json +1 -1
  166. package/dist/core/dynamic-recall.d.ts +0 -32
  167. package/dist/core/dynamic-recall.js +0 -76
@@ -0,0 +1,271 @@
1
+ import { uuidv7 } from "../internal/harness.js";
2
+ import { summarizeWorkflowRun } from "./workflow-run-store.js";
3
+ import { delimitUntrusted } from "./untrusted-text.js";
4
+ import { boundedRedactedSummary } from "./untrusted-egress.js";
5
+ import { clipWithFilePointer } from "./tool-errors.js";
6
+ export function mintCompletionId(target) {
7
+ if (target.completionId === undefined)
8
+ target.completionId = uuidv7();
9
+ return target.completionId;
10
+ }
11
+ export const defaultMonitorTimers = {
12
+ setInterval: (fn, ms) => {
13
+ const h = setInterval(() => void fn(), ms);
14
+ h.unref?.();
15
+ return h;
16
+ },
17
+ clearInterval: (h) => clearInterval(h),
18
+ now: () => Date.now(),
19
+ };
20
+ const BLOCK_POLL_INTERVAL_MS = 400;
21
+ export const sleepPollStep = (deadline, signal) => sleepMs(deadline === undefined ? BLOCK_POLL_INTERVAL_MS : Math.max(1, Math.min(BLOCK_POLL_INTERVAL_MS, deadline - Date.now())), signal);
22
+ export const sleepMs = (ms, signal) => new Promise((resolve) => {
23
+ const t = setTimeout(done, ms);
24
+ function done() {
25
+ clearTimeout(t);
26
+ signal?.removeEventListener("abort", done);
27
+ resolve();
28
+ }
29
+ signal?.addEventListener("abort", done, { once: true });
30
+ });
31
+ export const MONITOR_BATCH_WINDOW_MS = 200;
32
+ export const MONITOR_DEFAULT_TIMEOUT_MS = 300_000;
33
+ export const MONITOR_MAX_TIMEOUT_MS = 3_600_000;
34
+ export const MONITOR_MAX_BATCHES_PER_MINUTE = 50;
35
+ const MONITOR_EVENT_RESULT_CAP = 2_000;
36
+ export function clipMonitorEvent(s) {
37
+ return s.length <= MONITOR_EVENT_RESULT_CAP ? s : `${s.slice(0, MONITOR_EVENT_RESULT_CAP)}\n…[+${s.length - MONITOR_EVENT_RESULT_CAP} chars truncated]`;
38
+ }
39
+ const TASK_OUTPUT_DEFAULT_CHARS = 32_000;
40
+ export const TASK_OUTPUT_MAX_CHARS = 160_000;
41
+ const TASK_OUTPUT_MIN_CHARS = 512;
42
+ function taskMaxOutputChars() {
43
+ const raw = process.env.TASK_MAX_OUTPUT_LENGTH;
44
+ if (!raw)
45
+ return TASK_OUTPUT_DEFAULT_CHARS;
46
+ if (!/^\d+$/.test(raw.trim()))
47
+ return TASK_OUTPUT_DEFAULT_CHARS;
48
+ const n = parseInt(raw.trim(), 10);
49
+ if (!Number.isFinite(n) || n <= 0)
50
+ return TASK_OUTPUT_DEFAULT_CHARS;
51
+ return Math.min(Math.max(n, TASK_OUTPUT_MIN_CHARS), TASK_OUTPUT_MAX_CHARS);
52
+ }
53
+ export function clipTaskOutput(s, fullOutputPath) {
54
+ return clipWithFilePointer(s, taskMaxOutputChars(), fullOutputPath);
55
+ }
56
+ export function statusFromBackground(status, exitCode) {
57
+ if (status === "running")
58
+ return "running";
59
+ if (status === "killed")
60
+ return "killed";
61
+ if (status === "failed")
62
+ return "failed";
63
+ if (status === "exited")
64
+ return exitCode === 0 ? "completed" : "failed";
65
+ return "failed";
66
+ }
67
+ export function rollSpoolText(spool, s, cap) {
68
+ if (s.length <= cap)
69
+ return s;
70
+ const half = Math.floor(cap / 2);
71
+ spool.rolledChars += s.length - 2 * half;
72
+ return s.slice(0, half) + s.slice(s.length - half);
73
+ }
74
+ export function accountDroppedBytes(spool, poll) {
75
+ const d = poll.bytesDroppedBeforeCursor ?? 0;
76
+ if (d > 0)
77
+ spool.droppedBytes = (spool.droppedBytes ?? 0) + d;
78
+ else if (poll.truncated === true)
79
+ spool.dropUnknown = true;
80
+ }
81
+ export function droppedGapNote(spool) {
82
+ if ((spool.droppedBytes ?? 0) > 0)
83
+ return ` [!] ${spool.droppedBytes} earlier byte(s) of output were permanently dropped before this window (execution-environment tail buffer evicted) — the event stream and this spool are INCOMPLETE.`;
84
+ if (spool.dropUnknown === true)
85
+ return " [!] An unknown amount of earlier output was permanently dropped before this window (execution-environment tail buffer evicted) — the event stream and this spool are INCOMPLETE.";
86
+ return "";
87
+ }
88
+ export function firstDropNote(spool) {
89
+ if (spool.dropNotified === true)
90
+ return "";
91
+ const note = droppedGapNote(spool);
92
+ if (note !== "")
93
+ spool.dropNotified = true;
94
+ return note;
95
+ }
96
+ export function alreadyTerminalStopNote(id, status) {
97
+ return `${id} already ${status} — nothing to stop; poll TaskOutput for its final state.`;
98
+ }
99
+ export function assertOwnership(input, site) {
100
+ if (input.owner === undefined || input.scope === undefined) {
101
+ throw new Error(`TaskRegistry.${site}: owner AND scope are both required (access is default-deny on both axes; use scope "default" for a single-tenant deployment)`);
102
+ }
103
+ }
104
+ export function canAccessWorkflowRun(run, access) {
105
+ if (access.scope === undefined || run.scope !== access.scope)
106
+ return false;
107
+ if (access.owner !== undefined && run.sourceTaskId !== undefined && run.sourceTaskId === access.owner)
108
+ return true;
109
+ if (access.sessionId !== undefined && run.originatingSessionId !== undefined && run.originatingSessionId === access.sessionId)
110
+ return true;
111
+ return false;
112
+ }
113
+ const WORKFLOW_PARTIAL_RESULTS_MAX = 5;
114
+ const WORKFLOW_AGENT_ROWS_MAX = 60;
115
+ function workflowAgentRow(a, ordinal) {
116
+ return {
117
+ ordinal,
118
+ label: a.label,
119
+ ...(a.phase !== undefined ? { phase: a.phase } : {}),
120
+ status: a.status,
121
+ ...(a.errorCode !== undefined ? { errorCode: a.errorCode } : {}),
122
+ ...(a.errorMessage !== undefined
123
+ ? { error: delimitUntrusted(`workflow agent ${a.label} error`, boundedRedactedSummary(a.errorMessage, 200)) }
124
+ : {}),
125
+ ...(a.startedAt !== undefined && a.endedAt !== undefined ? { elapsedMs: a.endedAt - a.startedAt } : {}),
126
+ ...(a.replayed === true ? { replayed: true } : {}),
127
+ };
128
+ }
129
+ export function formatWorkflowRun(run) {
130
+ const summary = summarizeWorkflowRun(run);
131
+ const done = run.agents.filter((a) => a.status === "completed" || a.status === "failed").length;
132
+ const failed = run.agents.filter((a) => a.status === "failed").length;
133
+ const allRows = run.agents.map((a, i) => ({ a, i }));
134
+ const overCap = allRows.length > WORKFLOW_AGENT_ROWS_MAX;
135
+ const keptRows = overCap
136
+ ? (() => {
137
+ const urgent = allRows.filter((r) => r.a.status !== "completed");
138
+ const budget = Math.max(0, WORKFLOW_AGENT_ROWS_MAX - urgent.length);
139
+ const fill = allRows.filter((r) => r.a.status === "completed").slice(0, budget);
140
+ return [...urgent, ...fill].sort((x, y) => x.i - y.i);
141
+ })()
142
+ : allRows;
143
+ const agentRows = keptRows.map((r) => workflowAgentRow(r.a, r.i));
144
+ const finishedAgents = run.status === "failed" ? run.agents.filter((a) => a.status === "completed" && a.output !== undefined && a.output !== "") : [];
145
+ return {
146
+ content: JSON.stringify({
147
+ task_id: run.id,
148
+ runId: run.id,
149
+ type: "workflow",
150
+ status: run.status,
151
+ ...(summary.name ? { name: summary.name } : {}),
152
+ ...(summary.currentPhase ? { currentPhase: summary.currentPhase } : {}),
153
+ agents: { done, total: run.agents.length, failed },
154
+ agent_runs: agentRows,
155
+ ...(overCap ? { agent_runs_note: `showing ${agentRows.length} of ${run.agents.length} agent rows (every non-completed row kept; completed rows trimmed).` } : {}),
156
+ startedAt: run.startedAt,
157
+ ...(run.endedAt !== undefined ? { endedAt: run.endedAt, elapsedMs: run.endedAt - run.startedAt } : {}),
158
+ ...(run.resume !== undefined
159
+ ? {
160
+ resume: {
161
+ from_run_id: run.resume.fromRunId,
162
+ journal_entries: run.resume.journalEntries,
163
+ replayed: run.resume.replayed,
164
+ ...(run.resume.divergedAtOrdinal !== undefined
165
+ ? { replay_stopped_at: run.resume.divergedAtOrdinal, replay_stopped_reason: run.resume.divergedReason }
166
+ : {}),
167
+ },
168
+ }
169
+ : {}),
170
+ ...(run.journalSkips !== undefined && run.journalSkips > 0 ? { journal_skipped: run.journalSkips } : {}),
171
+ ...(run.status === "failed" && run.error ? { error: delimitUntrusted("workflow error", boundedRedactedSummary(run.error, 500)) } : {}),
172
+ ...(run.status === "completed" && run.result !== undefined
173
+ ? { result: delimitUntrusted("workflow result", run.resultFull ?? run.result) }
174
+ : {}),
175
+ ...(finishedAgents.length > 0
176
+ ? {
177
+ partial_results: finishedAgents.slice(0, WORKFLOW_PARTIAL_RESULTS_MAX).map((a) => ({
178
+ agent: a.label,
179
+ output: delimitUntrusted(`workflow agent ${a.label} output`, a.output),
180
+ })),
181
+ ...(finishedAgents.length > WORKFLOW_PARTIAL_RESULTS_MAX
182
+ ? { partial_results_note: `showing ${WORKFLOW_PARTIAL_RESULTS_MAX} of ${finishedAgents.length} completed agent outputs; the full set remains retrievable from the workflow run record (getWorkflowRun / the workflow journal).` }
183
+ : {}),
184
+ }
185
+ : {}),
186
+ note: run.status === "running"
187
+ ? "still running — poll again shortly."
188
+ : run.status === "completed"
189
+ ? run.result !== undefined
190
+ ? "completed — `result` above is the workflow's return value."
191
+ : "completed — this run predates result persistence; its result was delivered on the completion notification."
192
+ : finishedAgents.length > 0
193
+ ? `failed — see error. ${finishedAgents.length} agent(s) had already completed before the run ended; their outputs are inlined under \`partial_results\`.`
194
+ : "failed — see error.",
195
+ }),
196
+ details: {
197
+ task_id: run.id,
198
+ type: "workflow",
199
+ status: run.status,
200
+ retrieval_status: "success",
201
+ ...(run.completionId !== undefined ? { completionId: run.completionId } : {}),
202
+ details: summary,
203
+ },
204
+ };
205
+ }
206
+ export const DURABLE_AGENT_HEARTBEAT_MS = 60_000;
207
+ export const DURABLE_AGENT_HANDLE_RE = /^a[0-9a-f]{6,32}$/;
208
+ export const BG_AGENT_REAP_STOP_ERROR = "stopped (parent task ended or TaskStop)";
209
+ export function normalizeAgentName(s) {
210
+ return s
211
+ .normalize("NFKC")
212
+ .replace(/[\p{Cc}\p{Cf}]/gu, (t) => (/\s/.test(t) ? t : ""))
213
+ .trim()
214
+ .toLowerCase()
215
+ .replace(/\s+/g, "-");
216
+ }
217
+ export function editDistance(a, b) {
218
+ if (a === b)
219
+ return 0;
220
+ const m = a.length;
221
+ const n = b.length;
222
+ const d = Array.from({ length: m + 1 }, (_, i) => Array.from({ length: n + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)));
223
+ for (let i = 1; i <= m; i++) {
224
+ for (let j = 1; j <= n; j++) {
225
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
226
+ d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
227
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
228
+ d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
229
+ }
230
+ }
231
+ }
232
+ return d[m][n];
233
+ }
234
+ export function closestName(query, candidates) {
235
+ let best;
236
+ let bestDist = 3;
237
+ const seen = new Set();
238
+ for (const c of candidates) {
239
+ if (seen.has(c))
240
+ continue;
241
+ seen.add(c);
242
+ if (Math.abs(c.length - query.length) > 2)
243
+ continue;
244
+ const dist = editDistance(query, c);
245
+ if (dist <= 2 && dist < bestDist) {
246
+ bestDist = dist;
247
+ best = c;
248
+ }
249
+ }
250
+ return best;
251
+ }
252
+ export function canAccess(handle, access) {
253
+ if (handle.scope === undefined || access.scope === undefined || access.scope !== handle.scope)
254
+ return false;
255
+ if (handle.owner === undefined)
256
+ return false;
257
+ if (access.owner === handle.owner)
258
+ return true;
259
+ if (handle.sessionScoped && access.sessionId !== undefined && access.sessionId === handle.owner)
260
+ return true;
261
+ if (handle.type === "workflow" && access.sessionId !== undefined && handle.originatingSessionId === access.sessionId)
262
+ return true;
263
+ if (handle.type === "background_agent" && access.sessionId !== undefined && handle.rootSessionId === access.sessionId)
264
+ return true;
265
+ return false;
266
+ }
267
+ export function commitCompletionIdIfEmpty(target, value) {
268
+ if (target.completionId === undefined)
269
+ target.completionId = value;
270
+ return target.completionId;
271
+ }
@@ -0,0 +1,5 @@
1
+ import type { TaskAccess } from "./task-registry-shared.js";
2
+ import { type RegistryCore, type RegisterWorkflowInput, type WorkflowTaskHandle, type UnifiedTaskResult } from "./task-registry-shared.js";
3
+ export declare function registerWorkflowLane(core: RegistryCore, input: RegisterWorkflowInput): string;
4
+ export declare function pollWorkflowLane(handle: WorkflowTaskHandle, access: TaskAccess, deadline?: number, signal?: AbortSignal): Promise<UnifiedTaskResult>;
5
+ export declare function stopWorkflowLane(handle: WorkflowTaskHandle): Promise<UnifiedTaskResult>;
@@ -0,0 +1,143 @@
1
+ import { boundedRedactedSummary } from "./untrusted-egress.js";
2
+ import { delimitUntrusted } from "./untrusted-text.js";
3
+ import { assertOwnership, canAccessWorkflowRun, formatWorkflowRun, sleepPollStep, } from "./task-registry-shared.js";
4
+ export function registerWorkflowLane(core, input) {
5
+ assertOwnership(input, "registerWorkflow");
6
+ const id = input.runId;
7
+ if (core.handles.has(id))
8
+ throw new Error(`TaskRegistry: task id already registered: ${id}`);
9
+ const now = input.now ?? Date.now();
10
+ const originatingSessionId = input.originatingSessionId;
11
+ const handle = {
12
+ id,
13
+ type: "workflow",
14
+ status: "running",
15
+ ...(input.description !== undefined ? { description: input.description } : {}),
16
+ ...(input.owner !== undefined ? { owner: input.owner } : {}),
17
+ ...(input.scope !== undefined ? { scope: input.scope } : {}),
18
+ ...(input.toolUseId !== undefined ? { toolUseId: input.toolUseId } : {}),
19
+ createdAt: now,
20
+ updatedAt: now,
21
+ runId: id,
22
+ handle: input.handle,
23
+ ...(input.store !== undefined ? { store: input.store } : {}),
24
+ ...(originatingSessionId !== undefined ? { originatingSessionId, sessionScoped: true } : {}),
25
+ ...(input.onServedTerminal !== undefined ? { onServedTerminal: input.onServedTerminal } : {}),
26
+ };
27
+ core.handles.set(id, handle);
28
+ void input.handle.done.then((res) => {
29
+ handle.status = "completed";
30
+ const run = res?.run;
31
+ const r = run?.resultFull ?? run?.result;
32
+ if (typeof r === "string")
33
+ handle.result = r;
34
+ if (run?.completionId !== undefined)
35
+ handle.completionId = run.completionId;
36
+ handle.updatedAt = Date.now();
37
+ }, (err) => {
38
+ if (handle.status !== "cancelled")
39
+ handle.status = "failed";
40
+ handle.error = boundedRedactedSummary(err instanceof Error ? err.message : String(err), 500);
41
+ const rejectedRun = err !== null && (typeof err === "object" || typeof err === "function")
42
+ ? err.workflowRun
43
+ : undefined;
44
+ if (rejectedRun?.completionId !== undefined)
45
+ handle.completionId = rejectedRun.completionId;
46
+ handle.updatedAt = Date.now();
47
+ });
48
+ return id;
49
+ }
50
+ function fireServedTerminalLane(handle) {
51
+ const cb = handle.onServedTerminal;
52
+ if (!cb)
53
+ return;
54
+ handle.onServedTerminal = undefined;
55
+ try {
56
+ cb();
57
+ }
58
+ catch {
59
+ }
60
+ }
61
+ export async function pollWorkflowLane(handle, access, deadline, signal) {
62
+ const store = handle.store;
63
+ if (store) {
64
+ for (;;) {
65
+ const run = await store.get(handle.runId);
66
+ if (!run || !canAccessWorkflowRun(run, access))
67
+ break;
68
+ if (run.status !== "running" || deadline === undefined || Date.now() >= deadline || signal?.aborted) {
69
+ const out = formatWorkflowRun(run);
70
+ if (run.status === "running" && deadline !== undefined && Date.now() >= deadline)
71
+ out.details.retrieval_status = "timeout";
72
+ else if (run.status !== "running")
73
+ fireServedTerminalLane(handle);
74
+ return out;
75
+ }
76
+ await sleepPollStep(deadline, signal);
77
+ }
78
+ }
79
+ while (handle.status === "running" && deadline !== undefined && Date.now() < deadline && !signal?.aborted) {
80
+ await sleepPollStep(deadline, signal);
81
+ }
82
+ if (handle.status !== "running")
83
+ fireServedTerminalLane(handle);
84
+ return {
85
+ content: JSON.stringify({
86
+ task_id: handle.id,
87
+ runId: handle.id,
88
+ type: "workflow",
89
+ status: handle.status,
90
+ ...(handle.status === "completed" && handle.result !== undefined
91
+ ? { result: delimitUntrusted("workflow result", handle.result) }
92
+ : {}),
93
+ ...(handle.status === "failed" && handle.error !== undefined
94
+ ? { error: delimitUntrusted("workflow error", handle.error) }
95
+ : {}),
96
+ note: handle.status === "running"
97
+ ? "still running — poll again shortly."
98
+ : handle.status === "completed"
99
+ ? handle.result !== undefined
100
+ ? "completed — `result` above is the workflow's return value."
101
+ : "completed — its result was delivered on the completion notification."
102
+ : handle.status === "failed" && handle.error !== undefined
103
+ ? "failed — see error."
104
+ : "workflow reached a terminal state.",
105
+ }),
106
+ details: {
107
+ task_id: handle.id,
108
+ type: "workflow",
109
+ status: handle.status,
110
+ retrieval_status: handle.status === "running" && deadline !== undefined && Date.now() >= deadline ? "timeout" : "success",
111
+ ...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
112
+ },
113
+ };
114
+ }
115
+ export async function stopWorkflowLane(handle) {
116
+ if (handle.status !== "running") {
117
+ return {
118
+ content: JSON.stringify({
119
+ task_id: handle.id,
120
+ runId: handle.id,
121
+ type: "workflow",
122
+ status: handle.status,
123
+ stopped: false,
124
+ note: `workflow already ${handle.status} — nothing to stop; poll TaskOutput for its final state.`,
125
+ }),
126
+ details: { task_id: handle.id, type: "workflow", status: handle.status, retrieval_status: "success" },
127
+ };
128
+ }
129
+ handle.status = "cancelled";
130
+ handle.updatedAt = Date.now();
131
+ handle.handle.cancel("TaskStop requested");
132
+ return {
133
+ content: JSON.stringify({
134
+ task_id: handle.id,
135
+ runId: handle.id,
136
+ type: "workflow",
137
+ status: "cancelled",
138
+ stopped: true,
139
+ note: "workflow cancellation requested; durable run status maps cancellation to failed when it finalizes.",
140
+ }),
141
+ details: { task_id: handle.id, type: "workflow", status: "cancelled", retrieval_status: "success" },
142
+ };
143
+ }