@sema-agent/core 1.452.0 → 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.
Files changed (162) 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 +0 -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/lsp/frame-decoder.d.ts +1 -1
  101. package/dist/engine/lsp/frame-decoder.js +1 -1
  102. package/dist/engine/lsp/node-lsp-manager.d.ts +1 -1
  103. package/dist/engine/lsp/stdio-lsp-transport.js +2 -2
  104. package/dist/index.d.ts +16 -14
  105. package/dist/index.js +13 -11
  106. package/dist/internal/harness-types.d.ts +7 -0
  107. package/dist/internal/harness-types.js +1 -0
  108. package/dist/internal/harness.d.ts +1 -7
  109. package/dist/internal/harness.js +1 -0
  110. package/dist/orchestration/goal.js +2 -2
  111. package/dist/orchestration/run-workflow-tool.d.ts +1 -1
  112. package/dist/orchestration/workflow-types.d.ts +193 -0
  113. package/dist/orchestration/workflow-types.js +54 -0
  114. package/dist/orchestration/workflow.d.ts +3 -193
  115. package/dist/orchestration/workflow.js +7 -59
  116. package/dist/prompt-assembly/assemble.d.ts +3 -3
  117. package/dist/prompt-assembly/assemble.js +0 -16
  118. package/dist/prompt-assembly/explain.d.ts +1 -1
  119. package/dist/prompt-assembly/tool-catalog.d.ts +1 -1
  120. package/dist/prompts/default.d.ts +1 -6
  121. package/dist/prompts/default.js +3 -11
  122. package/dist/scenarios/env.d.ts +1 -1
  123. package/dist/scenarios/full-body.d.ts +0 -3
  124. package/dist/scenarios/full-body.js +0 -2
  125. package/dist/stores/file/background-agent-store.d.ts +2 -15
  126. package/dist/stores/file/background-agent-store.js +30 -100
  127. package/dist/stores/file/checkpoint-store.d.ts +1 -14
  128. package/dist/stores/file/checkpoint-store.js +47 -119
  129. package/dist/stores/file/file-snapshot-store.d.ts +1 -1
  130. package/dist/stores/file/shared-ledger.d.ts +35 -0
  131. package/dist/stores/file/shared-ledger.js +94 -0
  132. package/dist/stores/file/workflow-run-store.d.ts +1 -14
  133. package/dist/stores/file/workflow-run-store.js +24 -93
  134. package/dist/tools/fs/bash-readonly-classifier.d.ts +8 -0
  135. package/dist/tools/fs/bash-readonly-classifier.js +166 -0
  136. package/dist/tools/fs/fs-bash.d.ts +31 -0
  137. package/dist/tools/fs/fs-bash.js +672 -0
  138. package/dist/tools/fs/fs-pdf.d.ts +22 -0
  139. package/dist/tools/fs/fs-pdf.js +236 -0
  140. package/dist/tools/fs/fs-read.d.ts +8 -0
  141. package/dist/tools/fs/fs-read.js +286 -0
  142. package/dist/tools/fs/fs-search-tools.d.ts +5 -0
  143. package/dist/tools/fs/fs-search-tools.js +173 -0
  144. package/dist/tools/fs/fs-shared.d.ts +55 -0
  145. package/dist/tools/fs/fs-shared.js +144 -0
  146. package/dist/tools/fs/fs-write.d.ts +8 -0
  147. package/dist/tools/fs/fs-write.js +423 -0
  148. package/dist/tools/fs/index.d.ts +12 -65
  149. package/dist/tools/fs/index.js +26 -2090
  150. package/dist/tools/fs/pdf.d.ts +1 -1
  151. package/dist/tools/fs/repo-map.d.ts +3 -3
  152. package/dist/tools/fs/repo-map.js +1 -1
  153. package/dist/tools/fs/safety.d.ts +1 -1
  154. package/dist/tools/fs/search.d.ts +1 -1
  155. package/dist/tools/monitor.d.ts +1 -1
  156. package/dist/tools/scheduler-tools.d.ts +1 -1
  157. package/dist/tools/web.d.ts +2 -2
  158. package/dist/tools/web.js +1 -1
  159. package/dist/tools/worktree.d.ts +1 -1
  160. package/package.json +1 -1
  161. package/dist/core/dynamic-recall.d.ts +0 -32
  162. package/dist/core/dynamic-recall.js +0 -76
@@ -0,0 +1,13 @@
1
+ import type { TaskNotificationPayload } from "./task-notification.js";
2
+ import { type RegistryCore, type RegisterMonitorInput, type MonitorTaskHandle, type UnifiedTaskResult } from "./task-registry-shared.js";
3
+ export declare function registerMonitorLane(core: RegistryCore, input: RegisterMonitorInput): string;
4
+ export declare function absorbMonitorPollLane(handle: MonitorTaskHandle, v: {
5
+ stdout: string;
6
+ stderr: string;
7
+ truncated?: boolean;
8
+ bytesDroppedBeforeCursor?: number;
9
+ }): string[];
10
+ export declare function emitMonitorEventLane(handle: MonitorTaskHandle, n: TaskNotificationPayload): void;
11
+ export declare function startMonitorWatcherLane(core: RegistryCore, id: string): void;
12
+ export declare function pollMonitorLane(handle: MonitorTaskHandle, filter: string | undefined, deadline?: number, signal?: AbortSignal): Promise<UnifiedTaskResult>;
13
+ export declare function stopMonitorLane(core: RegistryCore, handle: MonitorTaskHandle): Promise<UnifiedTaskResult>;
@@ -0,0 +1,347 @@
1
+ import { delimitUntrusted } from "./untrusted-text.js";
2
+ import { assertOwnership, defaultMonitorTimers, MONITOR_MAX_TIMEOUT_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_BATCH_WINDOW_MS, MONITOR_MAX_BATCHES_PER_MINUTE, TASK_OUTPUT_MAX_CHARS, mintCompletionId, clipMonitorEvent, accountDroppedBytes, rollSpoolText, statusFromBackground, droppedGapNote, firstDropNote, alreadyTerminalStopNote, clipTaskOutput, sleepPollStep, } from "./task-registry-shared.js";
3
+ export function registerMonitorLane(core, input) {
4
+ assertOwnership(input, "registerMonitor");
5
+ const id = core.mintTaskId("monitor");
6
+ const now = input.now ?? Date.now();
7
+ const timers = input.timers ?? defaultMonitorTimers;
8
+ const persistent = input.persistent === true;
9
+ const timeoutMs = persistent
10
+ ? undefined
11
+ : Math.min(MONITOR_MAX_TIMEOUT_MS, Math.max(1, Math.floor(input.timeoutMs ?? MONITOR_DEFAULT_TIMEOUT_MS)));
12
+ const handle = {
13
+ id,
14
+ type: "monitor",
15
+ status: "running",
16
+ ...(input.description !== undefined ? { description: input.description } : {}),
17
+ ...(input.owner !== undefined ? { owner: input.owner } : {}),
18
+ ...(input.scope !== undefined ? { scope: input.scope } : {}),
19
+ ...(input.toolUseId !== undefined ? { toolUseId: input.toolUseId } : {}),
20
+ ...(persistent ? { persistent: true } : {}),
21
+ ...(input.sessionScoped ? { sessionScoped: true } : {}),
22
+ createdAt: now,
23
+ updatedAt: now,
24
+ env: input.env,
25
+ shellId: input.shellId,
26
+ spool: { stdout: "", stderr: "", rolledChars: 0 },
27
+ lineBuf: "",
28
+ seq: 0,
29
+ batchTimes: [],
30
+ batchWindowMs: Math.max(1, Math.floor(input.batchWindowMs ?? MONITOR_BATCH_WINDOW_MS)),
31
+ maxBatchesPerMinute: Math.max(1, Math.floor(input.maxBatchesPerMinute ?? MONITOR_MAX_BATCHES_PER_MINUTE)),
32
+ ...(timeoutMs !== undefined ? { timeoutMs, deadlineAt: timers.now() + timeoutMs } : {}),
33
+ timers,
34
+ ...(input.onEvent !== undefined ? { onEvent: input.onEvent } : {}),
35
+ };
36
+ core.handles.set(id, handle);
37
+ startMonitorWatcherLane(core, id);
38
+ return id;
39
+ }
40
+ export function absorbMonitorPollLane(handle, v) {
41
+ const spool = handle.spool;
42
+ accountDroppedBytes(spool, v);
43
+ const ROLL_CAP = 2 * TASK_OUTPUT_MAX_CHARS;
44
+ spool.stdout = rollSpoolText(spool, spool.stdout + v.stdout, ROLL_CAP);
45
+ spool.stderr = rollSpoolText(spool, spool.stderr + v.stderr, ROLL_CAP);
46
+ handle.lineBuf += v.stdout;
47
+ const parts = handle.lineBuf.split(/\r?\n/);
48
+ handle.lineBuf = parts.pop() ?? "";
49
+ return parts.filter((l) => l !== "");
50
+ }
51
+ export function emitMonitorEventLane(handle, n) {
52
+ try {
53
+ handle.onEvent?.(n);
54
+ }
55
+ catch {
56
+ }
57
+ }
58
+ export function startMonitorWatcherLane(core, id) {
59
+ const handle = core.handles.get(id);
60
+ if (!handle || handle.type !== "monitor")
61
+ return;
62
+ const timers = handle.timers;
63
+ let ticking = false;
64
+ const MAX_RETRYABLE_POLL_FAILURES = 3;
65
+ let pollFailures = 0;
66
+ const stop = () => {
67
+ if (handle.watcher !== undefined) {
68
+ timers.clearInterval(handle.watcher);
69
+ handle.watcher = undefined;
70
+ }
71
+ };
72
+ const emit = (n) => emitMonitorEventLane(handle, n);
73
+ const pendingFinalLines = (lines) => {
74
+ const out = handle.lineBuf !== "" ? [...lines, handle.lineBuf] : lines;
75
+ handle.lineBuf = "";
76
+ return out;
77
+ };
78
+ const terminal = (status, summary, finalLines) => {
79
+ handle.updatedAt = Date.now();
80
+ mintCompletionId(handle);
81
+ stop();
82
+ core.notifyTerminalOnce(handle, () => emit({
83
+ task_id: id,
84
+ task_type: "monitor",
85
+ ...(handle.toolUseId !== undefined ? { toolUseId: handle.toolUseId } : {}),
86
+ status,
87
+ ...(status === "killed" && handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
88
+ summary: `${(handle.description ?? "monitor").slice(0, 200)} — ${summary}${droppedGapNote(handle.spool)}`,
89
+ ...(finalLines.length > 0 ? { lines: finalLines, result: clipMonitorEvent(finalLines.join("\n")), ...(status === "killed" ? { partial: true } : {}) } : {}),
90
+ ...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
91
+ }));
92
+ };
93
+ const tick = async () => {
94
+ if (ticking)
95
+ return;
96
+ ticking = true;
97
+ handle.tickInFlight = true;
98
+ try {
99
+ if (core.handles.get(id) !== handle)
100
+ return stop();
101
+ if (handle.status !== "running")
102
+ return stop();
103
+ const r = await handle.env.pollBackground(handle.shellId);
104
+ if (core.handles.get(id) !== handle || handle.status !== "running")
105
+ return stop();
106
+ if (!r.ok) {
107
+ if ((r.error.code === "timeout" || r.error.code === "io") && ++pollFailures <= MAX_RETRYABLE_POLL_FAILURES)
108
+ return;
109
+ handle.status = "failed";
110
+ return terminal("failed", `monitor lost its process (${r.error.message}); watch ended.`, pendingFinalLines([]));
111
+ }
112
+ pollFailures = 0;
113
+ const spool = handle.spool;
114
+ const lines = absorbMonitorPollLane(handle, r.value);
115
+ handle.status = statusFromBackground(r.value.status, r.value.exitCode);
116
+ if (r.value.status !== "running") {
117
+ spool.exitCode = r.value.exitCode;
118
+ if (handle.status === "killed" && handle.stoppedBy === undefined)
119
+ handle.stoppedBy = handle.stopSource ?? (r.value.timedOut === true ? "timeout" : "system");
120
+ if (r.value.timedOut === true) {
121
+ spool.timedOut = true;
122
+ if (r.value.timeoutSec !== undefined)
123
+ spool.timeoutSec = r.value.timeoutSec;
124
+ }
125
+ const label = r.value.status === "exited"
126
+ ? `exited(code ${r.value.exitCode}); watch ended.`
127
+ : r.value.timedOut === true
128
+ ? `timed out after ${r.value.timeoutSec !== undefined ? `${r.value.timeoutSec}s` : "its background time budget"} (the execution environment's background time budget); the process was killed and the watch ended.`
129
+ : `${r.value.status}; watch ended.`;
130
+ return terminal(handle.status === "completed" ? "completed" : handle.status === "killed" ? "killed" : "failed", label, pendingFinalLines(lines));
131
+ }
132
+ const now = timers.now();
133
+ if (handle.deadlineAt !== undefined && now >= handle.deadlineAt) {
134
+ core.markStopSource(id, "timeout");
135
+ const k = await handle.env.killBackground(handle.shellId);
136
+ if (core.handles.get(id) !== handle || handle.status !== "running")
137
+ return stop();
138
+ if (!k.ok && k.error.code !== "not_found") {
139
+ handle.status = "failed";
140
+ return terminal("failed", `timed out after ${handle.timeoutMs}ms, but the kill failed (${k.error.message}); the process may still be running. Watch ended.`, pendingFinalLines(lines));
141
+ }
142
+ handle.status = "killed";
143
+ handle.stoppedBy = handle.stopSource ?? "timeout";
144
+ return terminal("killed", `timed out after ${handle.timeoutMs}ms; the process was killed and the watch ended.`, pendingFinalLines(lines));
145
+ }
146
+ if (lines.length === 0) {
147
+ if (r.value.stdout.length > 0 || r.value.stderr.length > 0)
148
+ handle.updatedAt = Date.now();
149
+ return;
150
+ }
151
+ const cutoff = now - 60_000;
152
+ handle.batchTimes = handle.batchTimes.filter((t) => t > cutoff);
153
+ handle.batchTimes.push(now);
154
+ if (handle.batchTimes.length > handle.maxBatchesPerMinute) {
155
+ core.markStopSource(id, "system");
156
+ const k = await handle.env.killBackground(handle.shellId);
157
+ if (core.handles.get(id) !== handle || handle.status !== "running")
158
+ return stop();
159
+ if (!k.ok && k.error.code !== "not_found") {
160
+ handle.status = "failed";
161
+ return terminal("failed", `auto-stop triggered (more than ${handle.maxBatchesPerMinute} notification batches within a minute), but the kill failed (${k.error.message}); the process may still be running. Watch ended.`, pendingFinalLines(lines));
162
+ }
163
+ handle.status = "killed";
164
+ handle.stoppedBy = handle.stopSource ?? "system";
165
+ return terminal("killed", `auto-stopped: it produced too many events (more than ${handle.maxBatchesPerMinute} notification batches within a minute). ` +
166
+ `The process was killed; its full output remains readable via TaskOutput. Restart it with a quieter command (filter its output) if you still need the watch.`, pendingFinalLines(lines));
167
+ }
168
+ handle.seq += 1;
169
+ handle.updatedAt = Date.now();
170
+ emit({
171
+ task_id: id,
172
+ task_type: "monitor",
173
+ ...(handle.toolUseId !== undefined ? { toolUseId: handle.toolUseId } : {}),
174
+ status: "event",
175
+ seq: handle.seq,
176
+ lines,
177
+ summary: `${(handle.description ?? "monitor").slice(0, 200)} — ${lines.length} new output line(s)${firstDropNote(spool)}`,
178
+ result: clipMonitorEvent(lines.join("\n")),
179
+ });
180
+ }
181
+ catch (e) {
182
+ if (core.handles.get(id) !== handle || handle.status !== "running")
183
+ return stop();
184
+ if (++pollFailures <= MAX_RETRYABLE_POLL_FAILURES)
185
+ return;
186
+ handle.status = "failed";
187
+ terminal("failed", `monitor watcher crashed (${e instanceof Error ? e.message : String(e)}); watch ended.`, pendingFinalLines([]));
188
+ }
189
+ finally {
190
+ ticking = false;
191
+ handle.tickInFlight = undefined;
192
+ }
193
+ };
194
+ handle.watcher = timers.setInterval(tick, handle.batchWindowMs);
195
+ }
196
+ export async function pollMonitorLane(handle, filter, deadline, signal) {
197
+ let rx;
198
+ if (filter !== undefined) {
199
+ try {
200
+ rx = new RegExp(filter);
201
+ }
202
+ catch {
203
+ return {
204
+ content: `Error (TaskOutput): invalid filter regex: ${filter}`,
205
+ details: { task_id: handle.id, type: "monitor", status: handle.status, retrieval_status: "not_ready" },
206
+ isError: true,
207
+ };
208
+ }
209
+ }
210
+ while (handle.status === "running" && deadline !== undefined && Date.now() < deadline && !signal?.aborted) {
211
+ await sleepPollStep(deadline, signal);
212
+ }
213
+ const spool = handle.spool;
214
+ const shape = (text) => clipTaskOutput(rx ? text.split(/\r?\n/).filter((l) => rx.test(l)).join("\n") : text, handle.outputFile);
215
+ const running = handle.status === "running";
216
+ const retrieval = running && deadline !== undefined && Date.now() >= deadline ? "timeout" : "success";
217
+ const timedOutClause = spool.timedOut === true ? ` (timed out after ${spool.timeoutSec !== undefined ? `${spool.timeoutSec}s` : "its background time budget"})` : "";
218
+ const displayStatus = (spool.exitCode !== undefined ? `exited(code ${spool.exitCode})` : running ? "running" : handle.status) + timedOutClause;
219
+ const rolledNote = spool.rolledChars > 0 ? `[!] ${spool.rolledChars} earlier char(s) dropped from the spool (memory bound)\n` : "";
220
+ const droppedNote = (spool.droppedBytes ?? 0) > 0
221
+ ? `[!] ${spool.droppedBytes} earlier byte(s) permanently dropped before this window (8MB buffer evicted)\n`
222
+ : spool.dropUnknown === true
223
+ ? "[!] an unknown amount of earlier output was permanently dropped before this window (buffer evicted)\n"
224
+ : "";
225
+ const drainNote = spool.drainFailed === true ? "[!] the final output drain at stop failed — the tail of the output may be missing\n" : "";
226
+ const stdoutHeading = droppedNote !== "" || drainNote !== "" ? "stdout (re-readable spool — INCOMPLETE, see note above)" : "stdout (full, re-readable)";
227
+ const body = `status: ${displayStatus}\n${rolledNote}${droppedNote}${drainNote}--- ${stdoutHeading} ---\n${shape(spool.stdout) || "(none)"}\n` +
228
+ `--- stderr ---\n${shape(spool.stderr) || "(none)"}`;
229
+ return {
230
+ content: delimitUntrusted(`TaskOutput ${handle.id}`, body),
231
+ details: {
232
+ task_id: handle.id,
233
+ type: "monitor",
234
+ status: handle.status,
235
+ retrieval_status: retrieval,
236
+ ...(handle.status === "killed" && handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
237
+ ...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
238
+ details: {
239
+ status: running ? "running" : handle.status,
240
+ exitCode: spool.exitCode,
241
+ ...(spool.droppedBytes !== undefined ? { bytesDroppedBeforeCursor: spool.droppedBytes } : {}),
242
+ ...(spool.timedOut === true ? { timedOut: true, ...(spool.timeoutSec !== undefined ? { timeoutSec: spool.timeoutSec } : {}) } : {}),
243
+ },
244
+ },
245
+ };
246
+ }
247
+ export async function stopMonitorLane(core, handle) {
248
+ const wasRunning = handle.status === "running";
249
+ if (handle.watcher !== undefined) {
250
+ handle.timers.clearInterval(handle.watcher);
251
+ handle.watcher = undefined;
252
+ }
253
+ if (wasRunning && handle.tickInFlight !== true) {
254
+ try {
255
+ const drained = await handle.env.pollBackground(handle.shellId);
256
+ if (drained.ok) {
257
+ const lines = absorbMonitorPollLane(handle, drained.value);
258
+ const finalLines = handle.lineBuf !== "" ? [...lines, handle.lineBuf] : lines;
259
+ handle.lineBuf = "";
260
+ if (finalLines.length > 0 && handle.status === "running") {
261
+ handle.seq += 1;
262
+ handle.updatedAt = Date.now();
263
+ emitMonitorEventLane(handle, {
264
+ task_id: handle.id,
265
+ task_type: "monitor",
266
+ ...(handle.toolUseId !== undefined ? { toolUseId: handle.toolUseId } : {}),
267
+ status: "event",
268
+ seq: handle.seq,
269
+ lines: finalLines,
270
+ summary: `${(handle.description ?? "monitor").slice(0, 200)} — ${finalLines.length} final output line(s) drained at stop${firstDropNote(handle.spool)}`,
271
+ result: clipMonitorEvent(finalLines.join("\n")),
272
+ });
273
+ }
274
+ }
275
+ else {
276
+ handle.spool.drainFailed = true;
277
+ }
278
+ }
279
+ catch {
280
+ handle.spool.drainFailed = true;
281
+ }
282
+ }
283
+ const drainNote = handle.spool.drainFailed === true ? " (the final output drain failed — TaskOutput may be missing the last window)" : "";
284
+ core.markStopSource(handle.id, "parent");
285
+ const r = await handle.env.killBackground(handle.shellId);
286
+ if (handle.status !== "running") {
287
+ return {
288
+ content: wasRunning ? `Terminated ${handle.id}.${drainNote}` : alreadyTerminalStopNote(handle.id, handle.status),
289
+ details: {
290
+ task_id: handle.id,
291
+ type: "monitor",
292
+ status: handle.status,
293
+ retrieval_status: "success",
294
+ ...(handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
295
+ ...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
296
+ },
297
+ };
298
+ }
299
+ if (!r.ok) {
300
+ if (r.error.code === "not_found") {
301
+ if (handle.stoppedBy === undefined)
302
+ handle.stoppedBy = handle.stopSource ?? "parent";
303
+ handle.status = "killed";
304
+ handle.updatedAt = Date.now();
305
+ mintCompletionId(handle);
306
+ core.pokeBgQuiescence(handle.owner);
307
+ return {
308
+ content: `Terminated ${handle.id} (the process was already gone).${drainNote}`,
309
+ details: {
310
+ task_id: handle.id,
311
+ type: "monitor",
312
+ status: "killed",
313
+ retrieval_status: "success",
314
+ stoppedBy: handle.stoppedBy,
315
+ ...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
316
+ },
317
+ };
318
+ }
319
+ if (handle.watcher === undefined)
320
+ startMonitorWatcherLane(core, handle.id);
321
+ const retrieval_status = r.error.code === "timeout" ? "timeout" : "not_ready";
322
+ return {
323
+ content: `Error (TaskStop): ${r.error.message} — the kill did not land; the process may still be running. The watch keeps running: events and the terminal notification still fire.`,
324
+ isError: true,
325
+ details: { task_id: handle.id, type: "monitor", status: handle.status, retrieval_status, error: r.error.code },
326
+ };
327
+ }
328
+ if (handle.status === "running") {
329
+ if (handle.stoppedBy === undefined)
330
+ handle.stoppedBy = handle.stopSource ?? "parent";
331
+ handle.status = "killed";
332
+ handle.updatedAt = Date.now();
333
+ mintCompletionId(handle);
334
+ core.pokeBgQuiescence(handle.owner);
335
+ }
336
+ return {
337
+ content: `Terminated ${handle.id}.${drainNote}`,
338
+ details: {
339
+ task_id: handle.id,
340
+ type: "monitor",
341
+ status: handle.status,
342
+ retrieval_status: "success",
343
+ ...(handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
344
+ ...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
345
+ },
346
+ };
347
+ }
@@ -0,0 +1,294 @@
1
+ import type { ExecutionEnv } from "../internal/harness.js";
2
+ import type { BackgroundShellCapability, BackgroundShellId } from "./background-shell.js";
3
+ import { type WorkflowRunStore } from "./workflow-run-store.js";
4
+ import type { BackgroundAgentRecord, BackgroundAgentStore } from "./background-agent-store.js";
5
+ import type { WorkflowHandle, WorkflowRun } from "../orchestration/workflow.js";
6
+ import type { TaskNotificationPayload } from "./task-notification.js";
7
+ export type SemaTaskType = "background_bash" | "workflow" | "background_agent" | "monitor";
8
+ export type StopSource = "user" | "parent" | "system" | (string & {});
9
+ export type SemaTaskStatus = "pending" | "running" | "parked" | "completed" | "failed" | "killed" | "cancelled";
10
+ export type TaskRetrievalStatus = "success" | "not_ready" | "timeout";
11
+ export interface UnifiedTaskOutput {
12
+ task_id: string;
13
+ type?: SemaTaskType | "unknown";
14
+ status?: SemaTaskStatus | string;
15
+ retrieval_status: TaskRetrievalStatus;
16
+ content?: string;
17
+ error?: string;
18
+ stoppedBy?: StopSource;
19
+ seq?: number;
20
+ partial_result?: boolean;
21
+ completionId?: string;
22
+ details?: unknown;
23
+ }
24
+ export type UnifiedTaskResult = {
25
+ content: string;
26
+ details: UnifiedTaskOutput;
27
+ isError?: boolean;
28
+ };
29
+ export interface SemaTaskHandle {
30
+ id: string;
31
+ type: SemaTaskType;
32
+ status: SemaTaskStatus;
33
+ description?: string;
34
+ owner?: string;
35
+ scope?: string;
36
+ toolUseId?: string;
37
+ createdAt: number;
38
+ updatedAt: number;
39
+ outputFile?: string;
40
+ outputOffset?: number;
41
+ completionId?: string;
42
+ }
43
+ export declare function mintCompletionId(target: {
44
+ completionId?: string;
45
+ }): string;
46
+ export interface TaskAccess {
47
+ owner?: string;
48
+ scope?: string;
49
+ sessionId?: string;
50
+ }
51
+ export interface BackgroundBashTaskHandle extends SemaTaskHandle {
52
+ type: "background_bash";
53
+ env: ExecutionEnv & BackgroundShellCapability;
54
+ shellId: BackgroundShellId;
55
+ sessionScoped?: true;
56
+ retained?: true;
57
+ spool?: {
58
+ stdout: string;
59
+ stderr: string;
60
+ rolledChars: number;
61
+ exitCode?: number;
62
+ timedOut?: boolean;
63
+ timeoutSec?: number;
64
+ mirrorFailed?: true;
65
+ droppedBytes?: number;
66
+ dropUnknown?: true;
67
+ };
68
+ watcher?: ReturnType<typeof setInterval>;
69
+ stopSource?: StopSource;
70
+ stoppedBy?: StopSource;
71
+ onTerminal?: (notification: TaskNotificationPayload) => void;
72
+ terminalNotified?: true;
73
+ }
74
+ export interface WorkflowTaskHandle extends SemaTaskHandle {
75
+ type: "workflow";
76
+ runId: string;
77
+ handle: WorkflowHandle<unknown>;
78
+ store?: WorkflowRunStore;
79
+ originatingSessionId?: string;
80
+ sessionScoped?: true;
81
+ onServedTerminal?: () => void;
82
+ result?: string;
83
+ error?: string;
84
+ }
85
+ export interface DurableAgentLane {
86
+ store: BackgroundAgentStore;
87
+ record: BackgroundAgentRecord;
88
+ chain: Promise<void>;
89
+ written: boolean;
90
+ poisoned: boolean;
91
+ flushFailed: boolean;
92
+ redrives?: number;
93
+ }
94
+ export interface BackgroundAgentTaskHandle extends SemaTaskHandle {
95
+ type: "background_agent";
96
+ abort: AbortController;
97
+ parentTaskId?: string;
98
+ parentSessionId?: string;
99
+ rootSessionId?: string;
100
+ sessionId?: string;
101
+ durable?: DurableAgentLane;
102
+ notify?: (notification: import("./task-notification.js").TaskNotificationPayload, opts?: {
103
+ priority?: import("./task-notification.js").SystemInjectionPriority;
104
+ }) => void | "queued" | "parked" | "dropped_duplicate" | Promise<"queued" | "parked" | "dropped_duplicate">;
105
+ retainedContinuation?: boolean;
106
+ reviveCycle?: number;
107
+ cycleSeq?: number;
108
+ parkedCheckpointToken?: string;
109
+ resolveParkedStop?: () => Promise<boolean>;
110
+ inFlightDirect?: Set<(r: {
111
+ ok: true;
112
+ disposition: "queued" | "parked";
113
+ } | {
114
+ ok: false;
115
+ reason: "not_found" | "not_running" | "no_channel";
116
+ }) => void>;
117
+ channelState?: "attaching" | "attached";
118
+ preAttachQueue?: Array<[
119
+ import("./task-notification.js").TaskNotificationPayload,
120
+ {
121
+ priority?: import("./task-notification.js").SystemInjectionPriority;
122
+ } | undefined,
123
+ (((r: {
124
+ ok: true;
125
+ disposition: "queued" | "parked";
126
+ } | {
127
+ ok: false;
128
+ reason: "not_found" | "not_running" | "no_channel";
129
+ }) => void) | undefined)?
130
+ ]>;
131
+ name?: string;
132
+ sessionScoped?: true;
133
+ result?: string;
134
+ resultFull?: string;
135
+ error?: string;
136
+ resultIsPartial?: boolean;
137
+ stopSource?: StopSource;
138
+ stoppedBy?: StopSource;
139
+ }
140
+ export interface MonitorTimers {
141
+ setInterval: (fn: () => unknown, ms: number) => unknown;
142
+ clearInterval: (handle: unknown) => void;
143
+ now: () => number;
144
+ }
145
+ export declare const defaultMonitorTimers: MonitorTimers;
146
+ export interface MonitorTaskHandle extends SemaTaskHandle {
147
+ type: "monitor";
148
+ env: ExecutionEnv & BackgroundShellCapability;
149
+ shellId: BackgroundShellId;
150
+ spool: {
151
+ stdout: string;
152
+ stderr: string;
153
+ rolledChars: number;
154
+ exitCode?: number;
155
+ droppedBytes?: number;
156
+ dropUnknown?: true;
157
+ dropNotified?: true;
158
+ drainFailed?: true;
159
+ timedOut?: boolean;
160
+ timeoutSec?: number;
161
+ };
162
+ lineBuf: string;
163
+ seq: number;
164
+ batchTimes: number[];
165
+ batchWindowMs: number;
166
+ maxBatchesPerMinute: number;
167
+ persistent?: true;
168
+ sessionScoped?: true;
169
+ retained?: true;
170
+ deadlineAt?: number;
171
+ timeoutMs?: number;
172
+ watcher?: unknown;
173
+ tickInFlight?: true;
174
+ timers: MonitorTimers;
175
+ onEvent?: (n: TaskNotificationPayload) => void;
176
+ stopSource?: StopSource;
177
+ stoppedBy?: StopSource;
178
+ terminalNotified?: true;
179
+ }
180
+ export type RegisteredTaskHandle = BackgroundBashTaskHandle | WorkflowTaskHandle | BackgroundAgentTaskHandle | MonitorTaskHandle;
181
+ export interface RegisterMonitorInput extends TaskAccess {
182
+ shellId: BackgroundShellId;
183
+ env: ExecutionEnv & BackgroundShellCapability;
184
+ description?: string;
185
+ toolUseId?: string;
186
+ persistent?: boolean;
187
+ sessionScoped?: true;
188
+ timeoutMs?: number;
189
+ onEvent?: (n: TaskNotificationPayload) => void;
190
+ timers?: MonitorTimers;
191
+ batchWindowMs?: number;
192
+ maxBatchesPerMinute?: number;
193
+ now?: number;
194
+ }
195
+ export interface RegisterWorkflowInput extends TaskAccess {
196
+ runId: string;
197
+ handle: WorkflowHandle<unknown>;
198
+ store?: WorkflowRunStore;
199
+ description?: string;
200
+ toolUseId?: string;
201
+ now?: number;
202
+ originatingSessionId?: string;
203
+ onServedTerminal?: () => void;
204
+ }
205
+ export declare const sleepPollStep: (deadline: number | undefined, signal?: AbortSignal) => Promise<void>;
206
+ export declare const sleepMs: (ms: number, signal?: AbortSignal) => Promise<void>;
207
+ export declare const MONITOR_BATCH_WINDOW_MS = 200;
208
+ export declare const MONITOR_DEFAULT_TIMEOUT_MS = 300000;
209
+ export declare const MONITOR_MAX_TIMEOUT_MS = 3600000;
210
+ export declare const MONITOR_MAX_BATCHES_PER_MINUTE = 50;
211
+ export declare function clipMonitorEvent(s: string): string;
212
+ export declare const TASK_OUTPUT_MAX_CHARS = 160000;
213
+ export declare function clipTaskOutput(s: string, fullOutputPath?: string): string;
214
+ export declare function statusFromBackground(status: string, exitCode?: number): SemaTaskStatus;
215
+ export declare function rollSpoolText(spool: {
216
+ rolledChars: number;
217
+ }, s: string, cap: number): string;
218
+ export declare function accountDroppedBytes(spool: {
219
+ droppedBytes?: number;
220
+ dropUnknown?: true;
221
+ }, poll: {
222
+ truncated?: boolean;
223
+ bytesDroppedBeforeCursor?: number;
224
+ }): void;
225
+ export declare function droppedGapNote(spool: {
226
+ droppedBytes?: number;
227
+ dropUnknown?: true;
228
+ }): string;
229
+ export declare function firstDropNote(spool: {
230
+ droppedBytes?: number;
231
+ dropUnknown?: true;
232
+ dropNotified?: true;
233
+ }): string;
234
+ export declare function alreadyTerminalStopNote(id: string, status: string): string;
235
+ export declare function assertOwnership(input: TaskAccess, site: string): void;
236
+ export declare function canAccessWorkflowRun(run: Pick<WorkflowRun, "scope" | "sourceTaskId" | "originatingSessionId">, access: TaskAccess): boolean;
237
+ export declare function formatWorkflowRun(run: WorkflowRun): UnifiedTaskResult;
238
+ export interface RegistryCore {
239
+ handles: Map<string, RegisteredTaskHandle>;
240
+ mintTaskId(type: SemaTaskType): string;
241
+ markStopSource(id: string, source: StopSource): void;
242
+ notifyTerminalOnce(handle: BackgroundBashTaskHandle | MonitorTaskHandle, send: () => void): void;
243
+ pokeBgQuiescence(owner: string | undefined): void;
244
+ }
245
+ export declare const DURABLE_AGENT_HEARTBEAT_MS = 60000;
246
+ export declare const DURABLE_AGENT_HANDLE_RE: RegExp;
247
+ export declare const BG_AGENT_REAP_STOP_ERROR = "stopped (parent task ended or TaskStop)";
248
+ export declare function normalizeAgentName(s: string): string;
249
+ export declare function editDistance(a: string, b: string): number;
250
+ export declare function closestName(query: string, candidates: Iterable<string>): string | undefined;
251
+ export declare function canAccess(handle: SemaTaskHandle, access: TaskAccess): boolean;
252
+ export interface DurableAgentCore extends RegistryCore {
253
+ readonly writerId: string;
254
+ readonly reapingHandles: Set<string>;
255
+ readonly claimingHandles: Set<string>;
256
+ durableHeartbeatTimer: ReturnType<typeof setInterval> | undefined;
257
+ readonly deliverConfirmTimeoutMs: number;
258
+ }
259
+ export interface ParkedClaimTicket {
260
+ handle: string;
261
+ scope: string;
262
+ token: string;
263
+ claimId: string;
264
+ reservedRev: number;
265
+ row: import("./background-agent-store.js").BackgroundAgentRecord;
266
+ }
267
+ export declare function commitCompletionIdIfEmpty(target: {
268
+ completionId?: string;
269
+ }, value: string): string;
270
+ export interface RegisterBackgroundAgentInput extends TaskAccess {
271
+ initialStatus?: "parked";
272
+ parkedCheckpointToken?: string;
273
+ resolveParkedStop?: () => Promise<boolean>;
274
+ deliveryChannel?: "attaching";
275
+ retainedContinuation?: boolean;
276
+ description?: string;
277
+ name?: string;
278
+ agentType?: string;
279
+ parentTaskId?: string;
280
+ parentSessionId?: string;
281
+ rootSessionId?: string;
282
+ model?: string;
283
+ teamName?: string;
284
+ toolUseId?: string;
285
+ abort: AbortController;
286
+ now?: number;
287
+ sessionScoped?: true;
288
+ id?: string;
289
+ store?: BackgroundAgentStore;
290
+ durableSeed?: {
291
+ record: BackgroundAgentRecord;
292
+ rev: number;
293
+ };
294
+ }