@sema-agent/core 2.1.0 → 2.3.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/dist/agents/observer.d.ts +14 -0
- package/dist/agents/observer.js +58 -9
- package/dist/agents/send-message-tool.js +55 -10
- package/dist/agents/subagent.d.ts +1 -0
- package/dist/agents/subagent.js +69 -15
- package/dist/agents/teacher.js +51 -23
- package/dist/core/context-edit.js +16 -3
- package/dist/core/file-snapshot-store.js +10 -1
- package/dist/core/runner/assemble-result.d.ts +1 -0
- package/dist/core/runner/assemble-result.js +12 -8
- package/dist/core/runner/prepare-task.d.ts +1 -0
- package/dist/core/runner/prepare-task.js +40 -10
- package/dist/core/runner/runtask.js +27 -5
- package/dist/core/runner/synthetic-tools.d.ts +1 -0
- package/dist/core/runner/synthetic-tools.js +18 -15
- package/dist/core/runner/turn-attachments.d.ts +12 -2
- package/dist/core/runner/turn-attachments.js +33 -3
- package/dist/core/task-registry-agent.d.ts +11 -1
- package/dist/core/task-registry-agent.js +40 -3
- package/dist/core/task-registry-monitor.js +149 -29
- package/dist/core/task-registry-shared.d.ts +25 -2
- package/dist/core/task-registry-shared.js +26 -2
- package/dist/core/task-registry.d.ts +5 -0
- package/dist/core/task-registry.js +25 -26
- package/dist/core/tools.d.ts +2 -0
- package/dist/core/tools.js +9 -0
- package/dist/core/types.d.ts +3 -1
- package/dist/core/workflow-journal-store.d.ts +16 -0
- package/dist/core/workflow-journal-store.js +28 -0
- package/dist/engine/session/memory-repo.js +5 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/orchestration/workflow-size-guideline.d.ts +6 -1
- package/dist/orchestration/workflow-size-guideline.js +19 -9
- package/dist/orchestration/workflow.d.ts +16 -0
- package/dist/orchestration/workflow.js +80 -20
- package/dist/prompt-assembly/assemble.js +3 -7
- package/dist/prompt-assembly/packs/sema-default.js +8 -5
- package/dist/prompts/coordinator.d.ts +1 -1
- package/dist/prompts/coordinator.js +45 -0
- package/dist/prompts/default.d.ts +4 -5
- package/dist/prompts/default.js +16 -18
- package/dist/prompts/simple-sections.d.ts +3 -1
- package/dist/prompts/simple-sections.js +11 -1
- package/dist/stores/file/workflow-journal-store.d.ts +7 -1
- package/dist/stores/file/workflow-journal-store.js +70 -33
- package/dist/tools/fs/bash-readonly-classifier.js +20 -1
- package/dist/tools/fs/fs-bash.d.ts +1 -0
- package/dist/tools/fs/fs-bash.js +10 -3
- package/dist/tools/fs/fs-read.js +10 -10
- package/dist/tools/fs/fs-search-tools.js +42 -7
- package/dist/tools/fs/fs-write.js +18 -6
- package/dist/tools/fs/index.d.ts +1 -0
- package/dist/tools/fs/index.js +2 -1
- package/dist/tools/fs/safety.d.ts +4 -0
- package/dist/tools/fs/safety.js +102 -6
- package/dist/tools/fs/search.d.ts +1 -0
- package/dist/tools/fs/search.js +23 -3
- package/dist/tools/monitor.d.ts +2 -0
- package/dist/tools/monitor.js +3 -1
- package/package.json +3 -2
|
@@ -6,10 +6,10 @@ import { canAccessAgentRecord, } from "./background-agent-store.js";
|
|
|
6
6
|
import { delimitUntrusted } from "./untrusted-text.js";
|
|
7
7
|
import { registerMonitorLane, pollMonitorLane, stopMonitorLane } from "./task-registry-monitor.js";
|
|
8
8
|
import { registerWorkflowLane, pollWorkflowLane, stopWorkflowLane } from "./task-registry-workflow.js";
|
|
9
|
-
import { mintCompletionId, canAccessWorkflowRun, formatWorkflowRun, clipTaskOutput, assertOwnership, sleepPollStep, statusFromBackground, rollSpoolText, accountDroppedBytes, droppedGapNote, alreadyTerminalStopNote, TASK_OUTPUT_MAX_CHARS, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE, canAccess, DURABLE_AGENT_HANDLE_RE, } from "./task-registry-shared.js";
|
|
9
|
+
import { mintCompletionId, canAccessWorkflowRun, formatWorkflowRun, clipTaskOutput, assertOwnership, sleepPollStep, statusFromBackground, rollSpoolText, accountDroppedBytes, droppedGapNote, alreadyTerminalStopNote, terminalTaskSummary, TASK_OUTPUT_MAX_CHARS, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE, canAccess, DURABLE_AGENT_HANDLE_RE, } from "./task-registry-shared.js";
|
|
10
10
|
export { normalizeAgentName, DURABLE_AGENT_HEARTBEAT_MS, DURABLE_AGENT_HANDLE_RE, BG_AGENT_REAP_STOP_ERROR } from "./task-registry-shared.js";
|
|
11
11
|
import { TASK_OUTPUT_TOOL_NAME, TASK_STOP_TOOL_NAME, TASK_OUTPUT_ALIASES, TASK_STOP_ALIASES, TASK_OUTPUT_CONTRACT, TASK_STOP_CONTRACT, TASK_OUTPUT_MISSING_ID_MESSAGE, TASK_STOP_MISSING_ID_MESSAGE, TASK_STOP_PARAMS, resolveTaskIdArg, REGISTRY_TASK_TOOL_CAPS, composeTaskOutputDescription, composeTaskOutputParams, composeTaskStopDescription, } from "./task-tool-shape.js";
|
|
12
|
-
import { durableAgentArmedLane, durableAgentRowProbeLane, beginDurableClaimLane, endDurableClaimLane, reapDurableAgentsLane, releaseDurableTranscriptAnchorLane, bindBackgroundAgentSessionLane, registerBackgroundAgentLane, parkBackgroundAgentLane, reconcileParkedAgentsLane, claimParkedAgentLane, rollbackParkedClaimLane, consumeParkedFlipLane, finalizeParkedResumeLane, settleBackgroundAgentLane, abortBackgroundAgentsForOwnerLane, serveDurableAgentRowLane, resolveBackgroundAgentByNameLane, markRetainedContinuationLane, reviveBackgroundAgentLane, settleRevivedAgentLane, unmarkRetainedContinuationLane, attachAgentNotifyLane, deliverToRunningAgentLane, runningBackgroundAgentLabelsLane, pollBackgroundAgentLane, stopBackgroundAgentLane, } from "./task-registry-agent.js";
|
|
12
|
+
import { durableAgentArmedLane, durableAgentRowProbeLane, beginDurableClaimLane, endDurableClaimLane, reapDurableAgentsLane, releaseDurableTranscriptAnchorLane, bindBackgroundAgentSessionLane, registerBackgroundAgentLane, parkBackgroundAgentLane, reconcileParkedAgentsLane, claimParkedAgentLane, rollbackParkedClaimLane, consumeParkedFlipLane, finalizeParkedResumeLane, settleBackgroundAgentLane, abortBackgroundAgentsForOwnerLane, serveDurableAgentRowLane, resolveBackgroundAgentByNameLane, markRetainedContinuationLane, reviveBackgroundAgentLane, settleRevivedAgentLane, unmarkRetainedContinuationLane, attachAgentNotifyLane, deliverToRunningAgentLane, runningBackgroundAgentLabelsLane, runningAgentFooterLane, notFoundRunningAgentsTail, pollBackgroundAgentLane, stopBackgroundAgentLane, } from "./task-registry-agent.js";
|
|
13
13
|
export { canAccessWorkflowRun, clipTaskOutput, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE };
|
|
14
14
|
const BLOCK_DEFAULT_TIMEOUT_MS = 30_000;
|
|
15
15
|
const BLOCK_MAX_TIMEOUT_MS = 600_000;
|
|
@@ -24,19 +24,18 @@ const TASK_PREFIX = {
|
|
|
24
24
|
background_agent: "a",
|
|
25
25
|
monitor: "m",
|
|
26
26
|
};
|
|
27
|
-
function noTask(taskId) {
|
|
27
|
+
function noTask(taskId, runningAgents) {
|
|
28
28
|
return {
|
|
29
|
-
content:
|
|
29
|
+
content: `No task found with ID: ${taskId}${runningAgents ? notFoundRunningAgentsTail(runningAgents) : ""}`,
|
|
30
30
|
details: { task_id: taskId, type: "unknown", status: "not_found", retrieval_status: "not_ready", error: "not_found" },
|
|
31
31
|
isError: true,
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
function noTaskForStop(taskId, enrich) {
|
|
35
35
|
let content = `No task found with ID: ${taskId}`;
|
|
36
|
-
if (enrich
|
|
36
|
+
if (enrich.suggestion !== undefined)
|
|
37
37
|
content += `. Did you mean: ${enrich.suggestion}?`;
|
|
38
|
-
|
|
39
|
-
content += `. Running background agents: ${enrich.runningAgents.join(", ")}`;
|
|
38
|
+
content += notFoundRunningAgentsTail(enrich.runningAgents);
|
|
40
39
|
return {
|
|
41
40
|
content,
|
|
42
41
|
details: { task_id: taskId, type: "unknown", status: "not_found", retrieval_status: "not_ready", error: "not_found" },
|
|
@@ -152,8 +151,8 @@ export class TaskRegistry {
|
|
|
152
151
|
runningBackgroundAgentLabels(access) {
|
|
153
152
|
return runningBackgroundAgentLabelsLane(this.core, access);
|
|
154
153
|
}
|
|
155
|
-
async pollBackgroundAgent(handle, deadline, signal) {
|
|
156
|
-
return pollBackgroundAgentLane(handle, deadline, signal);
|
|
154
|
+
async pollBackgroundAgent(handle, deadline, signal, oneShot, toolResultStore, sessionId) {
|
|
155
|
+
return pollBackgroundAgentLane(handle, deadline, signal, oneShot, toolResultStore, sessionId);
|
|
157
156
|
}
|
|
158
157
|
async stopBackgroundAgent(handle) {
|
|
159
158
|
return stopBackgroundAgentLane(this.core, handle);
|
|
@@ -387,7 +386,7 @@ export class TaskRegistry {
|
|
|
387
386
|
...(handle.outputFile !== undefined ? { output_file: handle.outputFile } : {}),
|
|
388
387
|
status: "killed",
|
|
389
388
|
stoppedBy: handle.stoppedBy,
|
|
390
|
-
summary: `${(handle.description ?? "background command").slice(0, 200)} — killed before completion (${clause(handle.stoppedBy)})${killFailed ? ` — kill attempt reported ${kill.ok ? "" : kill.error.code}: the process may still be terminating; the environment teardown retries` : ""}${bashMirrorGapNote(handle)}`,
|
|
389
|
+
summary: `${terminalTaskSummary("bash", (handle.description ?? "background command").slice(0, 200), "killed")} — killed before completion (${clause(handle.stoppedBy)})${killFailed ? ` — kill attempt reported ${kill.ok ? "" : kill.error.code}: the process may still be terminating; the environment teardown retries` : ""}${bashMirrorGapNote(handle)}`,
|
|
391
390
|
...(resultText.length > 0 ? { result: resultText, partial: true } : {}),
|
|
392
391
|
...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
|
|
393
392
|
}));
|
|
@@ -413,7 +412,7 @@ export class TaskRegistry {
|
|
|
413
412
|
...(handle.toolUseId !== undefined ? { toolUseId: handle.toolUseId } : {}),
|
|
414
413
|
status: "killed",
|
|
415
414
|
stoppedBy: handle.stoppedBy,
|
|
416
|
-
summary: `${(handle.description ?? "monitor").slice(0, 200)} — killed before completion (${clause(handle.stoppedBy)}); watch ended.${killFailed ? ` Kill attempt reported ${kill.ok ? "" : kill.error.code}: the process may still be terminating; the environment teardown retries.` : ""}`,
|
|
415
|
+
summary: `${terminalTaskSummary("monitor", (handle.description ?? "monitor").slice(0, 200), "killed")} — killed before completion (${clause(handle.stoppedBy)}); watch ended.${killFailed ? ` Kill attempt reported ${kill.ok ? "" : kill.error.code}: the process may still be terminating; the environment teardown retries.` : ""}`,
|
|
417
416
|
...(lastLines.length > 0 ? { lines: lastLines, result: lastLines.join("\n"), partial: true } : {}),
|
|
418
417
|
...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
|
|
419
418
|
}));
|
|
@@ -624,7 +623,7 @@ export class TaskRegistry {
|
|
|
624
623
|
...(handle.toolUseId !== undefined ? { toolUseId: handle.toolUseId } : {}),
|
|
625
624
|
...(handle.outputFile !== undefined ? { output_file: handle.outputFile } : {}),
|
|
626
625
|
status: "failed",
|
|
627
|
-
summary: `${(handle.description ?? "background command").slice(0, 200)} — lost its background process (${r.error.message}); no further output will arrive.${bashMirrorGapNote(handle)}`,
|
|
626
|
+
summary: `${terminalTaskSummary("bash", (handle.description ?? "background command").slice(0, 200), "failed")} — lost its background process (${r.error.message}); no further output will arrive.${bashMirrorGapNote(handle)}`,
|
|
628
627
|
...(lostResult.length > 0 ? { result: lostResult } : {}),
|
|
629
628
|
...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
|
|
630
629
|
}));
|
|
@@ -666,14 +665,12 @@ export class TaskRegistry {
|
|
|
666
665
|
...(handle.status === "killed" && handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
|
|
667
666
|
...(r.value.status === "exited" && r.value.exitCode !== undefined ? { exitCode: r.value.exitCode } : {}),
|
|
668
667
|
summary: (r.value.status === "exited"
|
|
669
|
-
? r.value.exitCode === 0
|
|
670
|
-
? `Background command "${desc}" completed (exit code 0)`
|
|
671
|
-
: `Background command "${desc}" failed (exit code ${r.value.exitCode})`
|
|
668
|
+
? terminalTaskSummary("bash", desc, r.value.exitCode === 0 ? "completed" : "failed", r.value.exitCode)
|
|
672
669
|
: r.value.timedOut === true
|
|
673
670
|
? r.value.timeoutSec !== undefined
|
|
674
|
-
? `${desc} — timed out after ${r.value.timeoutSec}s (its own background time budget); the process was killed`
|
|
675
|
-
: `${desc} — timed out (its own background time budget); the process was killed`
|
|
676
|
-
: `${desc} — ${r.value.status}`) +
|
|
671
|
+
? `${terminalTaskSummary("bash", desc, "killed")} — timed out after ${r.value.timeoutSec}s (its own background time budget); the process was killed`
|
|
672
|
+
: `${terminalTaskSummary("bash", desc, "killed")} — timed out (its own background time budget); the process was killed`
|
|
673
|
+
: `${terminalTaskSummary("bash", desc, handle.status === "completed" ? "completed" : handle.status === "killed" ? "killed" : "failed")} — ${r.value.status}`) +
|
|
677
674
|
bashMirrorGapNote(handle) +
|
|
678
675
|
droppedGapNote(spool),
|
|
679
676
|
...(resultText.length > 0 ? { result: resultText } : {}),
|
|
@@ -698,7 +695,7 @@ export class TaskRegistry {
|
|
|
698
695
|
...(handle.toolUseId !== undefined ? { toolUseId: handle.toolUseId } : {}),
|
|
699
696
|
...(handle.outputFile !== undefined ? { output_file: handle.outputFile } : {}),
|
|
700
697
|
status: "failed",
|
|
701
|
-
summary: `${(handle.description ?? "background command").slice(0, 200)} — lost its background process (poll failed: ${e instanceof Error ? e.message : String(e)}); no further output will arrive.${bashMirrorGapNote(handle)}`,
|
|
698
|
+
summary: `${terminalTaskSummary("bash", (handle.description ?? "background command").slice(0, 200), "failed")} — lost its background process (poll failed: ${e instanceof Error ? e.message : String(e)}); no further output will arrive.${bashMirrorGapNote(handle)}`,
|
|
702
699
|
...(crashedResult.length > 0 ? { result: crashedResult } : {}),
|
|
703
700
|
...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
|
|
704
701
|
}));
|
|
@@ -749,9 +746,9 @@ export class TaskRegistry {
|
|
|
749
746
|
const handle = this.resolveInternal(taskId, access);
|
|
750
747
|
if (handle) {
|
|
751
748
|
if (handle.type === "background_bash")
|
|
752
|
-
return this.pollBackgroundBash(handle, taskId, opts.filter, deadline, opts.signal);
|
|
749
|
+
return this.pollBackgroundBash(handle, taskId, access, opts.filter, deadline, opts.signal);
|
|
753
750
|
if (handle.type === "background_agent")
|
|
754
|
-
return this.pollBackgroundAgent(handle, deadline, opts.signal);
|
|
751
|
+
return this.pollBackgroundAgent(handle, deadline, opts.signal, opts.oneShot, opts.toolResultStore, access.sessionId);
|
|
755
752
|
if (handle.type === "monitor")
|
|
756
753
|
return this.pollMonitor(handle, opts.filter, deadline, opts.signal);
|
|
757
754
|
return this.pollWorkflow(handle, access, deadline, opts.signal);
|
|
@@ -803,7 +800,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
|
|
|
803
800
|
await sleepPollStep(deadline, opts.signal);
|
|
804
801
|
}
|
|
805
802
|
}
|
|
806
|
-
return noTask(taskId);
|
|
803
|
+
return noTask(taskId, runningAgentFooterLane(this.core, access));
|
|
807
804
|
}
|
|
808
805
|
async stopTask(id, access, opts = {}) {
|
|
809
806
|
const taskId = id.trim();
|
|
@@ -876,7 +873,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
|
|
|
876
873
|
}
|
|
877
874
|
return noTaskForStop(taskId, {
|
|
878
875
|
...(byName.suggestion !== undefined ? { suggestion: byName.suggestion } : {}),
|
|
879
|
-
runningAgents: this.
|
|
876
|
+
runningAgents: runningAgentFooterLane(this.core, access),
|
|
880
877
|
});
|
|
881
878
|
}
|
|
882
879
|
deliverConfirmTimeoutMs = 10_000;
|
|
@@ -974,7 +971,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
|
|
|
974
971
|
return undefined;
|
|
975
972
|
return handle;
|
|
976
973
|
}
|
|
977
|
-
async pollBackgroundBash(handle, requestedId, filter, deadline, signal) {
|
|
974
|
+
async pollBackgroundBash(handle, requestedId, access, filter, deadline, signal) {
|
|
978
975
|
let rx;
|
|
979
976
|
if (filter !== undefined) {
|
|
980
977
|
try {
|
|
@@ -989,7 +986,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
|
|
|
989
986
|
}
|
|
990
987
|
}
|
|
991
988
|
if (!hasBackgroundShell(handle.env))
|
|
992
|
-
return noTask(requestedId);
|
|
989
|
+
return noTask(requestedId, runningAgentFooterLane(this.core, access));
|
|
993
990
|
if (handle.spool !== undefined) {
|
|
994
991
|
while (handle.status === "running" && deadline !== undefined && Date.now() < deadline && !signal?.aborted) {
|
|
995
992
|
await sleepPollStep(deadline, signal);
|
|
@@ -1043,7 +1040,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
|
|
|
1043
1040
|
const r = await handle.env.pollBackground(handle.shellId);
|
|
1044
1041
|
if (!r.ok) {
|
|
1045
1042
|
if (r.error.code === "not_found")
|
|
1046
|
-
return noTask(requestedId);
|
|
1043
|
+
return noTask(requestedId, runningAgentFooterLane(this.core, access));
|
|
1047
1044
|
const retrieval_status = r.error.code === "timeout" ? "timeout" : "not_ready";
|
|
1048
1045
|
const salvaged = stdout.length > 0 || stderr.length > 0
|
|
1049
1046
|
? `\n--- output accumulated before the error ---\n${clipTaskOutput(stdout, handle.outputFile)}${stderr ? `\n--- stderr ---\n${clipTaskOutput(stderr, handle.outputFile)}` : ""}`
|
|
@@ -1216,6 +1213,8 @@ export function createTaskOutputTool(opts) {
|
|
|
1216
1213
|
block: args.block !== false,
|
|
1217
1214
|
timeoutMs: effectiveTimeoutMs,
|
|
1218
1215
|
signal: ctx.signal,
|
|
1216
|
+
oneShot: opts.oneShot,
|
|
1217
|
+
toolResultStore: opts.toolResultStore,
|
|
1219
1218
|
});
|
|
1220
1219
|
const { type: taskType, ...rest } = r.details;
|
|
1221
1220
|
return {
|
package/dist/core/tools.d.ts
CHANGED
|
@@ -6,4 +6,6 @@ export declare function errorResult(text: string, details?: unknown): {
|
|
|
6
6
|
isError: true;
|
|
7
7
|
details?: unknown;
|
|
8
8
|
};
|
|
9
|
+
export declare function isDefineToolProduct(x: unknown): x is AgentTool;
|
|
10
|
+
export declare function stampDefineToolBrand<T extends object>(tool: T): T;
|
|
9
11
|
export declare function defineTool<TParams extends TSchema = TSchema>(spec: ToolSpec<TParams>): AgentTool<TParams>;
|
package/dist/core/tools.js
CHANGED
|
@@ -25,6 +25,14 @@ function isEmptyToolContent(content) {
|
|
|
25
25
|
export function errorResult(text, details) {
|
|
26
26
|
return details === undefined ? { content: text, isError: true } : { content: text, isError: true, details };
|
|
27
27
|
}
|
|
28
|
+
const DEFINE_TOOL_BRAND = Symbol("sema.core.defineTool.product");
|
|
29
|
+
export function isDefineToolProduct(x) {
|
|
30
|
+
return typeof x === "object" && x !== null && x[DEFINE_TOOL_BRAND] === true;
|
|
31
|
+
}
|
|
32
|
+
export function stampDefineToolBrand(tool) {
|
|
33
|
+
Object.defineProperty(tool, DEFINE_TOOL_BRAND, { value: true, enumerable: false });
|
|
34
|
+
return tool;
|
|
35
|
+
}
|
|
28
36
|
export function defineTool(spec) {
|
|
29
37
|
const executionMode = spec.executionMode ?? (spec.effect === "read" ? "parallel" : "sequential");
|
|
30
38
|
const tool = {
|
|
@@ -72,5 +80,6 @@ export function defineTool(spec) {
|
|
|
72
80
|
...(spec.aliases && spec.aliases.length > 0 ? { aliases: spec.aliases } : {}),
|
|
73
81
|
});
|
|
74
82
|
}
|
|
83
|
+
stampDefineToolBrand(tool);
|
|
75
84
|
return tool;
|
|
76
85
|
}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -92,6 +92,7 @@ export interface ToolExecuteContext {
|
|
|
92
92
|
model?: Model;
|
|
93
93
|
thinkingLevel?: ThinkingLevel;
|
|
94
94
|
principal?: string;
|
|
95
|
+
oneShot?: boolean;
|
|
95
96
|
clientContext?: TaskSpec["clientContext"];
|
|
96
97
|
excludeTools?: readonly string[];
|
|
97
98
|
deferTools?: readonly string[];
|
|
@@ -344,6 +345,7 @@ export interface TaskSpec {
|
|
|
344
345
|
attachments?: {
|
|
345
346
|
todoReminder?: true;
|
|
346
347
|
todoReminderMode?: "baseline" | "off";
|
|
348
|
+
toolSearchReminder?: true;
|
|
347
349
|
changedFiles?: true | {
|
|
348
350
|
maxFiles?: number;
|
|
349
351
|
};
|
|
@@ -534,7 +536,7 @@ export type TaskEvent = ({
|
|
|
534
536
|
phaseDurations?: import("./auto-compaction.js").CompactionPhaseDurations;
|
|
535
537
|
} | ({
|
|
536
538
|
type: "steering_injected";
|
|
537
|
-
source: "deadline_nudge" | "finalize" | "todo_reminder" | "task_reminder" | "changed_files" | "plan_mode" | "date_change" | "instructions_change" | "budget_usd" | "background_tasks" | "tools_delta" | "agent_listing" | "skills_listing" | "mcp_instructions" | "mcp_dropped_tools";
|
|
539
|
+
source: "deadline_nudge" | "finalize" | "todo_reminder" | "task_reminder" | "tool_search_usage_reminder" | "changed_files" | "plan_mode" | "date_change" | "instructions_change" | "budget_usd" | "background_tasks" | "tools_delta" | "agent_listing" | "skills_listing" | "mcp_instructions" | "mcp_dropped_tools";
|
|
538
540
|
preview: string;
|
|
539
541
|
} & TaskEventIdentity) | ({
|
|
540
542
|
type: "diagnostics";
|
|
@@ -26,8 +26,24 @@ export declare function oversizeJournalResult(serialized: string): boolean;
|
|
|
26
26
|
export declare function callKeyOrdinal(callKey: string): number;
|
|
27
27
|
export declare const JOURNAL_OVERSIZE_ERROR_CODE = "workflow.journal_oversize";
|
|
28
28
|
export declare function journalOversizeTombstone(result: TaskResult, bytes: number): TaskResult;
|
|
29
|
+
export declare const IN_MEMORY_RESUME_CLAIM_TTL_MS: number;
|
|
29
30
|
export declare class InMemoryWorkflowJournalStore implements WorkflowJournalStore {
|
|
30
31
|
private readonly runs;
|
|
32
|
+
private readonly resumeClaims;
|
|
33
|
+
private claimsForScope;
|
|
34
|
+
resumeClaim(input: {
|
|
35
|
+
sourceRunId: string;
|
|
36
|
+
newRunId: string;
|
|
37
|
+
scope: string;
|
|
38
|
+
}): Promise<{
|
|
39
|
+
granted: boolean;
|
|
40
|
+
holder?: string;
|
|
41
|
+
}>;
|
|
42
|
+
releaseResumeClaim(input: {
|
|
43
|
+
sourceRunId: string;
|
|
44
|
+
newRunId: string;
|
|
45
|
+
scope: string;
|
|
46
|
+
}): Promise<void>;
|
|
31
47
|
load(runId: string, scope: string): Promise<WorkflowJournalEntry[]>;
|
|
32
48
|
append(runId: string, scope: string, entry: WorkflowJournalEntry): Promise<void>;
|
|
33
49
|
}
|
|
@@ -29,8 +29,36 @@ function snapshot(entry) {
|
|
|
29
29
|
return JSON.parse(JSON.stringify(entry));
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
+
export const IN_MEMORY_RESUME_CLAIM_TTL_MS = 60 * 60 * 1000;
|
|
32
33
|
export class InMemoryWorkflowJournalStore {
|
|
33
34
|
runs = new Map();
|
|
35
|
+
resumeClaims = new Map();
|
|
36
|
+
claimsForScope(scope) {
|
|
37
|
+
let m = this.resumeClaims.get(scope);
|
|
38
|
+
if (m === undefined) {
|
|
39
|
+
m = new Map();
|
|
40
|
+
this.resumeClaims.set(scope, m);
|
|
41
|
+
}
|
|
42
|
+
return m;
|
|
43
|
+
}
|
|
44
|
+
async resumeClaim(input) {
|
|
45
|
+
const table = this.claimsForScope(input.scope);
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
const existing = table.get(input.sourceRunId);
|
|
48
|
+
if (existing !== undefined && existing.expiresAt > now && existing.holder !== input.newRunId) {
|
|
49
|
+
return { granted: false, holder: existing.holder };
|
|
50
|
+
}
|
|
51
|
+
table.set(input.sourceRunId, { holder: input.newRunId, expiresAt: now + IN_MEMORY_RESUME_CLAIM_TTL_MS });
|
|
52
|
+
return { granted: true };
|
|
53
|
+
}
|
|
54
|
+
async releaseResumeClaim(input) {
|
|
55
|
+
const table = this.resumeClaims.get(input.scope);
|
|
56
|
+
if (table === undefined)
|
|
57
|
+
return;
|
|
58
|
+
const existing = table.get(input.sourceRunId);
|
|
59
|
+
if (existing !== undefined && existing.holder === input.newRunId)
|
|
60
|
+
table.delete(input.sourceRunId);
|
|
61
|
+
}
|
|
34
62
|
async load(runId, scope) {
|
|
35
63
|
const rec = this.runs.get(runId);
|
|
36
64
|
if (!rec || rec.scope !== scope)
|
|
@@ -5,6 +5,11 @@ import { createSessionId, createTimestamp, getEntriesToFork, toSession } from ".
|
|
|
5
5
|
export class InMemorySessionRepo {
|
|
6
6
|
sessions = new Map();
|
|
7
7
|
async create(options = {}) {
|
|
8
|
+
if (options.id !== undefined) {
|
|
9
|
+
const existing = this.sessions.get(options.id);
|
|
10
|
+
if (existing)
|
|
11
|
+
return existing;
|
|
12
|
+
}
|
|
8
13
|
const metadata = {
|
|
9
14
|
id: options.id ?? createSessionId(),
|
|
10
15
|
createdAt: createTimestamp(),
|
package/dist/index.d.ts
CHANGED
|
@@ -113,7 +113,7 @@ export { encodeSurfacedKey, buildManifestText, validateSelectedIds, composeSelec
|
|
|
113
113
|
export { runMemoryConsolidation, CONSOLIDATION_SYSTEM_PROMPT, DEFAULT_CONSOLIDATION_BAND, DEFAULT_CONSOLIDATION_SEARCH_LIMIT, DEFAULT_CONSOLIDATION_MAX_NOTES, DEFAULT_CONSOLIDATION_TIMEOUT_SEC, normalizeForExactMatch, type ConsolidationParams, type ConsolidationStats, type ConsolidationNote, type ConsolidationLLM, } from "./core/runner/memory-consolidation.js";
|
|
114
114
|
export { consolidateScope, advanceCursorAfterInline, type ConsolidateScopeDeps, type ConsolidateScopeOptions, } from "./core/consolidate-scope.js";
|
|
115
115
|
export { DEFAULT_COMPACTION_INSTRUCTIONS } from "./core/auto-compaction.js";
|
|
116
|
-
export { DEFAULT_SYSTEM_PROMPT, CODE_AGENT_PROMPT, SUBAGENT_PROMPT,
|
|
116
|
+
export { DEFAULT_SYSTEM_PROMPT, CODE_AGENT_PROMPT, SUBAGENT_PROMPT, CODE_SYSTEM_PROMPT, MEMORY_GUIDANCE, MEMORY_SAFETY, MEMORY_HYGIENE, OUTPUT_EFFICIENCY, CYBER_RISK, HARNESS_SECTION_ANCHOR, URL_SAFETY, SUMMARIZE_TOOL_RESULTS, EXECUTION_ENVIRONMENT, harnessContext, buildEnvironmentContext, buildGitSnapshot, buildScratchpadSection, GIT_STATUS_MAX_CHARS, formatLocalDate, type EnvironmentFacts, defaultPromptProvider, composeSystemPrompt, composeConstitution, constitutionBlocks, type PromptBlock, analyzePromptCacheFriendliness, assertPromptCacheFriendly, type PromptProvider, type StablePromptContext, type PromptCacheReport, type PromptTextDeclaration, } from "./prompts/default.js";
|
|
117
117
|
export { SUPERVISOR_PROMPT, ORCHESTRATION_GUIDANCE, ORCHESTRATION_AWARENESS, GOAL_COMPLETION_GUIDANCE } from "./prompts/supervisor.js";
|
|
118
118
|
export { compose, validatePack } from "./prompt-assembly/composer.js";
|
|
119
119
|
export { SEMA_DEFAULT_PACK } from "./prompt-assembly/packs/sema-default.js";
|
package/dist/index.js
CHANGED
|
@@ -101,7 +101,7 @@ export { encodeSurfacedKey, buildManifestText, validateSelectedIds, composeSelec
|
|
|
101
101
|
export { runMemoryConsolidation, CONSOLIDATION_SYSTEM_PROMPT, DEFAULT_CONSOLIDATION_BAND, DEFAULT_CONSOLIDATION_SEARCH_LIMIT, DEFAULT_CONSOLIDATION_MAX_NOTES, DEFAULT_CONSOLIDATION_TIMEOUT_SEC, normalizeForExactMatch, } from "./core/runner/memory-consolidation.js";
|
|
102
102
|
export { consolidateScope, advanceCursorAfterInline, } from "./core/consolidate-scope.js";
|
|
103
103
|
export { DEFAULT_COMPACTION_INSTRUCTIONS } from "./core/auto-compaction.js";
|
|
104
|
-
export { DEFAULT_SYSTEM_PROMPT, CODE_AGENT_PROMPT, SUBAGENT_PROMPT,
|
|
104
|
+
export { DEFAULT_SYSTEM_PROMPT, CODE_AGENT_PROMPT, SUBAGENT_PROMPT, CODE_SYSTEM_PROMPT, MEMORY_GUIDANCE, MEMORY_SAFETY, MEMORY_HYGIENE, OUTPUT_EFFICIENCY, CYBER_RISK, HARNESS_SECTION_ANCHOR, URL_SAFETY, SUMMARIZE_TOOL_RESULTS, EXECUTION_ENVIRONMENT, harnessContext, buildEnvironmentContext, buildGitSnapshot, buildScratchpadSection, GIT_STATUS_MAX_CHARS, formatLocalDate, defaultPromptProvider, composeSystemPrompt, composeConstitution, constitutionBlocks, analyzePromptCacheFriendliness, assertPromptCacheFriendly, } from "./prompts/default.js";
|
|
105
105
|
export { SUPERVISOR_PROMPT, ORCHESTRATION_GUIDANCE, ORCHESTRATION_AWARENESS, GOAL_COMPLETION_GUIDANCE } from "./prompts/supervisor.js";
|
|
106
106
|
export { compose, validatePack } from "./prompt-assembly/composer.js";
|
|
107
107
|
export { SEMA_DEFAULT_PACK } from "./prompt-assembly/packs/sema-default.js";
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export type WorkflowSizeGuideline = "small" | "medium" | "large" | "unrestricted";
|
|
2
2
|
export declare const WORKFLOW_SIZE_GUIDELINES: readonly WorkflowSizeGuideline[];
|
|
3
|
+
export declare const WORKFLOW_SIZE_GUIDELINE_DEFAULT: WorkflowSizeGuideline;
|
|
3
4
|
export declare const WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS: Readonly<Record<"small" | "medium" | "large", number>>;
|
|
4
|
-
export declare function normalizeWorkflowSizeGuideline(value: unknown): WorkflowSizeGuideline;
|
|
5
|
+
export declare function normalizeWorkflowSizeGuideline(value: unknown): WorkflowSizeGuideline | undefined;
|
|
6
|
+
export declare function resolveWorkflowSizeGuideline(value: unknown): {
|
|
7
|
+
size: WorkflowSizeGuideline;
|
|
8
|
+
isDefault: boolean;
|
|
9
|
+
};
|
|
5
10
|
export declare function workflowSizeGuidelineAgentCap(g: WorkflowSizeGuideline): number | undefined;
|
|
6
11
|
export declare function describeWorkflowSizeGuideline(g: WorkflowSizeGuideline): string;
|
|
7
12
|
export declare function formatWorkflowSizeGuidelineLabel(g: WorkflowSizeGuideline): string;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
export const WORKFLOW_SIZE_GUIDELINES = ["unrestricted", "small", "medium", "large"];
|
|
2
|
+
export const WORKFLOW_SIZE_GUIDELINE_DEFAULT = "medium";
|
|
2
3
|
export const WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS = {
|
|
3
4
|
small: 5,
|
|
4
5
|
medium: 15,
|
|
5
6
|
large: 50,
|
|
6
7
|
};
|
|
7
8
|
export function normalizeWorkflowSizeGuideline(value) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
return WORKFLOW_SIZE_GUIDELINES.includes(value) ? value : undefined;
|
|
10
|
+
}
|
|
11
|
+
export function resolveWorkflowSizeGuideline(value) {
|
|
12
|
+
const configured = normalizeWorkflowSizeGuideline(value);
|
|
13
|
+
return configured === undefined ? { size: WORKFLOW_SIZE_GUIDELINE_DEFAULT, isDefault: true } : { size: configured, isDefault: false };
|
|
11
14
|
}
|
|
12
15
|
export function workflowSizeGuidelineAgentCap(g) {
|
|
13
16
|
return g === "small" || g === "medium" || g === "large" ? WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS[g] : undefined;
|
|
@@ -24,14 +27,21 @@ export function formatWorkflowSizeGuidelineLabel(g) {
|
|
|
24
27
|
const cap = workflowSizeGuidelineAgentCap(g);
|
|
25
28
|
return cap === undefined ? g : `${g} (aim for <${cap} agents)`;
|
|
26
29
|
}
|
|
30
|
+
function workflowSizeGuidelineSentence(g, isDefault) {
|
|
31
|
+
const lead = isDefault
|
|
32
|
+
? "This session has the default workflow size guideline:"
|
|
33
|
+
: "A workflow size guideline is configured for this session:";
|
|
34
|
+
const escape = isDefault ? ' The user can raise or remove it with "Dynamic workflow size" in /config.' : "";
|
|
35
|
+
return `${lead} ${describeWorkflowSizeGuideline(g)}. ${guidelineNotHardLimit()}${escape}`;
|
|
36
|
+
}
|
|
27
37
|
export function workflowSizeGuidelineSection(value) {
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
30
|
-
return
|
|
31
|
-
return
|
|
38
|
+
const { size, isDefault } = resolveWorkflowSizeGuideline(value);
|
|
39
|
+
if (size === "unrestricted")
|
|
40
|
+
return "";
|
|
41
|
+
return `\n\n${workflowSizeGuidelineSentence(size, isDefault)}`;
|
|
32
42
|
}
|
|
33
43
|
export function workflowSizeGuidelineChangeNotice(g) {
|
|
34
44
|
if (g === "unrestricted")
|
|
35
|
-
return "
|
|
36
|
-
return `The
|
|
45
|
+
return "Workflow size is now unrestricted — no size guideline applies.";
|
|
46
|
+
return `The workflow size guideline for this session changed: ${describeWorkflowSizeGuideline(g)}. ${guidelineNotHardLimit()}`;
|
|
37
47
|
}
|
|
@@ -124,9 +124,25 @@ export declare const MAX_WORKFLOW_ITEMS = 4096;
|
|
|
124
124
|
export declare const WORKFLOW_AGENT_STALL_MS = 180000;
|
|
125
125
|
export declare const WORKFLOW_AGENT_MAX_RETRIES = 5;
|
|
126
126
|
export declare const WORKFLOW_AGENT_THROTTLE_BACKOFF_MS = 45000;
|
|
127
|
+
export declare const WORKFLOW_RESUME_CLAIM_FINALIZE_TIMEOUT_MS = 10000;
|
|
127
128
|
export interface WorkflowTimers {
|
|
128
129
|
setTimeout(fn: () => void, ms: number): unknown;
|
|
129
130
|
clearTimeout(handle: unknown): void;
|
|
130
131
|
}
|
|
132
|
+
export declare const workflowResumeClaimFallback: {
|
|
133
|
+
acquire(store: WorkflowJournalStore, input: {
|
|
134
|
+
sourceRunId: string;
|
|
135
|
+
newRunId: string;
|
|
136
|
+
scope: string;
|
|
137
|
+
}): {
|
|
138
|
+
granted: boolean;
|
|
139
|
+
holder?: string;
|
|
140
|
+
};
|
|
141
|
+
release(store: WorkflowJournalStore, input: {
|
|
142
|
+
sourceRunId: string;
|
|
143
|
+
newRunId: string;
|
|
144
|
+
scope: string;
|
|
145
|
+
}): void;
|
|
146
|
+
};
|
|
131
147
|
export declare function startWorkflow<T>(runner: Runner, fn: (ctx: WorkflowRunContext) => Promise<T>, opts?: RunWorkflowOptions, internals?: WorkflowInternals): WorkflowHandle<T>;
|
|
132
148
|
export declare function runWorkflow<T>(runner: Runner, fn: (ctx: WorkflowRunContext) => Promise<T>, opts?: RunWorkflowOptions, internals?: WorkflowInternals): Promise<RunWorkflowResult<T>>;
|
|
@@ -20,6 +20,7 @@ const MAX_TRANSCRIPT_CHARS = 4000;
|
|
|
20
20
|
const WORKFLOW_RESULT_MAX = 4000;
|
|
21
21
|
const WORKFLOW_RESULT_FULL_MAX = 200_000;
|
|
22
22
|
const MAX_ACTIVITY = 30;
|
|
23
|
+
const RUNNING_AGENT_PERSIST_EVERY_BEATS = 4;
|
|
23
24
|
function workflowModelLabel(spec) {
|
|
24
25
|
const model = spec.model;
|
|
25
26
|
if (model === undefined)
|
|
@@ -132,6 +133,7 @@ export const MAX_WORKFLOW_ITEMS = 4096;
|
|
|
132
133
|
export const WORKFLOW_AGENT_STALL_MS = 180_000;
|
|
133
134
|
export const WORKFLOW_AGENT_MAX_RETRIES = 5;
|
|
134
135
|
export const WORKFLOW_AGENT_THROTTLE_BACKOFF_MS = 45_000;
|
|
136
|
+
export const WORKFLOW_RESUME_CLAIM_FINALIZE_TIMEOUT_MS = 10_000;
|
|
135
137
|
const REAL_WORKFLOW_TIMERS = {
|
|
136
138
|
setTimeout(fn, ms) {
|
|
137
139
|
const t = setTimeout(fn, ms);
|
|
@@ -142,6 +144,21 @@ const REAL_WORKFLOW_TIMERS = {
|
|
|
142
144
|
clearTimeout(handle);
|
|
143
145
|
},
|
|
144
146
|
};
|
|
147
|
+
async function finalizeWithin(timers, p, fallback) {
|
|
148
|
+
let handle;
|
|
149
|
+
try {
|
|
150
|
+
return await Promise.race([
|
|
151
|
+
p,
|
|
152
|
+
new Promise((resolve) => {
|
|
153
|
+
handle = timers.setTimeout(() => resolve(fallback), WORKFLOW_RESUME_CLAIM_FINALIZE_TIMEOUT_MS);
|
|
154
|
+
}),
|
|
155
|
+
]);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
if (handle !== undefined)
|
|
159
|
+
timers.clearTimeout(handle);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
145
162
|
function defaultConcurrency() {
|
|
146
163
|
let cores = 4;
|
|
147
164
|
try {
|
|
@@ -190,6 +207,33 @@ function createSemaphore(max) {
|
|
|
190
207
|
function clampRunIdText(raw) {
|
|
191
208
|
return raw.replace(/[^A-Za-z0-9_.:-]/g, "").slice(0, 64);
|
|
192
209
|
}
|
|
210
|
+
const fallbackResumeClaimTables = new WeakMap();
|
|
211
|
+
function fallbackResumeClaimKey(scope, sourceRunId) {
|
|
212
|
+
return JSON.stringify([scope, sourceRunId]);
|
|
213
|
+
}
|
|
214
|
+
export const workflowResumeClaimFallback = {
|
|
215
|
+
acquire(store, input) {
|
|
216
|
+
let table = fallbackResumeClaimTables.get(store);
|
|
217
|
+
if (table === undefined) {
|
|
218
|
+
table = new Map();
|
|
219
|
+
fallbackResumeClaimTables.set(store, table);
|
|
220
|
+
}
|
|
221
|
+
const key = fallbackResumeClaimKey(input.scope, input.sourceRunId);
|
|
222
|
+
const holder = table.get(key);
|
|
223
|
+
if (holder !== undefined && holder !== input.newRunId)
|
|
224
|
+
return { granted: false, holder };
|
|
225
|
+
table.set(key, input.newRunId);
|
|
226
|
+
return { granted: true };
|
|
227
|
+
},
|
|
228
|
+
release(store, input) {
|
|
229
|
+
const table = fallbackResumeClaimTables.get(store);
|
|
230
|
+
if (table === undefined)
|
|
231
|
+
return;
|
|
232
|
+
const key = fallbackResumeClaimKey(input.scope, input.sourceRunId);
|
|
233
|
+
if (table.get(key) === input.newRunId)
|
|
234
|
+
table.delete(key);
|
|
235
|
+
},
|
|
236
|
+
};
|
|
193
237
|
export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
194
238
|
const alsDepth = currentWorkflowDepth();
|
|
195
239
|
const internalDepth = internals?.workflowDepth;
|
|
@@ -550,8 +594,9 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
550
594
|
throw err;
|
|
551
595
|
}
|
|
552
596
|
};
|
|
553
|
-
const createActivityCapture = (callKey, label, groupId) => {
|
|
597
|
+
const createActivityCapture = (callKey, label, groupId, rec) => {
|
|
554
598
|
const tail = [];
|
|
599
|
+
let beatCount = 0;
|
|
555
600
|
const onActivity = (a) => {
|
|
556
601
|
if (finalized)
|
|
557
602
|
return;
|
|
@@ -571,6 +616,13 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
571
616
|
tail.push(a);
|
|
572
617
|
if (tail.length > MAX_ACTIVITY)
|
|
573
618
|
tail.shift();
|
|
619
|
+
if (a.phase === "start")
|
|
620
|
+
rec.toolCalls = (rec.toolCalls ?? 0) + 1;
|
|
621
|
+
rec.activity = tail;
|
|
622
|
+
beatCount += 1;
|
|
623
|
+
if (beatCount === 1 || beatCount % RUNNING_AGENT_PERSIST_EVERY_BEATS === 0) {
|
|
624
|
+
void persist("update");
|
|
625
|
+
}
|
|
574
626
|
};
|
|
575
627
|
return { tail, onActivity };
|
|
576
628
|
};
|
|
@@ -718,8 +770,8 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
718
770
|
if (budgetTotal !== null && spent() >= budgetTotal) {
|
|
719
771
|
throw new WorkflowBudgetExceededError(spent(), budgetTotal);
|
|
720
772
|
}
|
|
721
|
-
const { tail: activityTail, onActivity } = createActivityCapture(callKey, label, groupId);
|
|
722
773
|
const rec = { label, callKey, ...(groupId !== undefined ? { groupId } : {}), phase, prompt, ...(model !== undefined ? { model } : {}), status: "running", queuedAt: now() };
|
|
774
|
+
const { tail: activityTail, onActivity } = createActivityCapture(callKey, label, groupId, rec);
|
|
723
775
|
run.agents.push(rec);
|
|
724
776
|
if (phaseInstance)
|
|
725
777
|
agentPhaseOf.set(rec, phaseInstance);
|
|
@@ -1031,8 +1083,8 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
1031
1083
|
const model = workflowModelLabel(specForIdentity);
|
|
1032
1084
|
noteDivergence(run.agents.length, "ctx.agentStream results are never replayed");
|
|
1033
1085
|
diverged = true;
|
|
1034
|
-
const { tail: activityTail, onActivity } = createActivityCapture(callKey, label, groupId);
|
|
1035
1086
|
const rec = { label, callKey, ...(groupId !== undefined ? { groupId } : {}), phase, prompt, ...(model !== undefined ? { model } : {}), status: "running", queuedAt: now() };
|
|
1087
|
+
const { tail: activityTail, onActivity } = createActivityCapture(callKey, label, groupId, rec);
|
|
1036
1088
|
run.agents.push(rec);
|
|
1037
1089
|
if (phaseInstance)
|
|
1038
1090
|
agentPhaseOf.set(rec, phaseInstance);
|
|
@@ -1372,18 +1424,19 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
1372
1424
|
try {
|
|
1373
1425
|
const runBody = async () => {
|
|
1374
1426
|
if (opts.resumeFromRunId !== undefined && journalStore) {
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1427
|
+
const usingFallback = journalStore.resumeClaim === undefined;
|
|
1428
|
+
const verdict = usingFallback
|
|
1429
|
+
? Promise.resolve(workflowResumeClaimFallback.acquire(journalStore, { sourceRunId: opts.resumeFromRunId, newRunId: runId, scope }))
|
|
1430
|
+
: journalStore.resumeClaim({ sourceRunId: opts.resumeFromRunId, newRunId: runId, scope });
|
|
1431
|
+
resumeClaim = { sourceRunId: opts.resumeFromRunId, verdict, fallback: usingFallback };
|
|
1432
|
+
const decision = await verdict;
|
|
1433
|
+
if (!decision.granted) {
|
|
1434
|
+
const holder = decision.holder !== undefined ? clampRunIdText(decision.holder) : "";
|
|
1435
|
+
throw new Error(`startWorkflow: resume from "${clampRunIdText(opts.resumeFromRunId)}" was REFUSED — ` +
|
|
1436
|
+
`another run already holds the resume claim on it${holder ? ` (holder: ${holder})` : " (holder unknown to the store)"}. ` +
|
|
1437
|
+
"Two concurrent resumes of one source run fork its execution: both replay the same prefix and then " +
|
|
1438
|
+
"re-run the whole suffix live, duplicating every side effect. Wait for the holder to reach a terminal " +
|
|
1439
|
+
"state (or stop it) and resume again.");
|
|
1387
1440
|
}
|
|
1388
1441
|
const entries = await journalStore.load(opts.resumeFromRunId, scope);
|
|
1389
1442
|
for (const e of entries)
|
|
@@ -1485,13 +1538,20 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
1485
1538
|
finally {
|
|
1486
1539
|
finalized = true;
|
|
1487
1540
|
await journalTail.catch(() => undefined);
|
|
1488
|
-
if (resumeClaim !== undefined
|
|
1489
|
-
const granted = await resumeClaim.verdict.then((v) => v.granted, () => false);
|
|
1541
|
+
if (resumeClaim !== undefined) {
|
|
1542
|
+
const granted = await finalizeWithin(timers, resumeClaim.verdict.then((v) => v.granted, () => false), false);
|
|
1490
1543
|
if (granted) {
|
|
1491
|
-
|
|
1492
|
-
|
|
1544
|
+
if (resumeClaim.fallback) {
|
|
1545
|
+
if (journalStore) {
|
|
1546
|
+
workflowResumeClaimFallback.release(journalStore, { sourceRunId: resumeClaim.sourceRunId, newRunId: runId, scope });
|
|
1547
|
+
}
|
|
1493
1548
|
}
|
|
1494
|
-
|
|
1549
|
+
else if (journalStore?.releaseResumeClaim) {
|
|
1550
|
+
try {
|
|
1551
|
+
await finalizeWithin(timers, journalStore.releaseResumeClaim({ sourceRunId: resumeClaim.sourceRunId, newRunId: runId, scope }), undefined);
|
|
1552
|
+
}
|
|
1553
|
+
catch {
|
|
1554
|
+
}
|
|
1495
1555
|
}
|
|
1496
1556
|
}
|
|
1497
1557
|
}
|