@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.
Files changed (61) hide show
  1. package/dist/agents/observer.d.ts +14 -0
  2. package/dist/agents/observer.js +58 -9
  3. package/dist/agents/send-message-tool.js +55 -10
  4. package/dist/agents/subagent.d.ts +1 -0
  5. package/dist/agents/subagent.js +69 -15
  6. package/dist/agents/teacher.js +51 -23
  7. package/dist/core/context-edit.js +16 -3
  8. package/dist/core/file-snapshot-store.js +10 -1
  9. package/dist/core/runner/assemble-result.d.ts +1 -0
  10. package/dist/core/runner/assemble-result.js +12 -8
  11. package/dist/core/runner/prepare-task.d.ts +1 -0
  12. package/dist/core/runner/prepare-task.js +40 -10
  13. package/dist/core/runner/runtask.js +27 -5
  14. package/dist/core/runner/synthetic-tools.d.ts +1 -0
  15. package/dist/core/runner/synthetic-tools.js +18 -15
  16. package/dist/core/runner/turn-attachments.d.ts +12 -2
  17. package/dist/core/runner/turn-attachments.js +33 -3
  18. package/dist/core/task-registry-agent.d.ts +11 -1
  19. package/dist/core/task-registry-agent.js +40 -3
  20. package/dist/core/task-registry-monitor.js +149 -29
  21. package/dist/core/task-registry-shared.d.ts +25 -2
  22. package/dist/core/task-registry-shared.js +26 -2
  23. package/dist/core/task-registry.d.ts +5 -0
  24. package/dist/core/task-registry.js +25 -26
  25. package/dist/core/tools.d.ts +2 -0
  26. package/dist/core/tools.js +9 -0
  27. package/dist/core/types.d.ts +3 -1
  28. package/dist/core/workflow-journal-store.d.ts +16 -0
  29. package/dist/core/workflow-journal-store.js +28 -0
  30. package/dist/engine/session/memory-repo.js +5 -0
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.js +1 -1
  33. package/dist/orchestration/workflow-size-guideline.d.ts +6 -1
  34. package/dist/orchestration/workflow-size-guideline.js +19 -9
  35. package/dist/orchestration/workflow.d.ts +16 -0
  36. package/dist/orchestration/workflow.js +80 -20
  37. package/dist/prompt-assembly/assemble.js +3 -7
  38. package/dist/prompt-assembly/packs/sema-default.js +8 -5
  39. package/dist/prompts/coordinator.d.ts +1 -1
  40. package/dist/prompts/coordinator.js +45 -0
  41. package/dist/prompts/default.d.ts +4 -5
  42. package/dist/prompts/default.js +16 -18
  43. package/dist/prompts/simple-sections.d.ts +3 -1
  44. package/dist/prompts/simple-sections.js +11 -1
  45. package/dist/stores/file/workflow-journal-store.d.ts +7 -1
  46. package/dist/stores/file/workflow-journal-store.js +70 -33
  47. package/dist/tools/fs/bash-readonly-classifier.js +20 -1
  48. package/dist/tools/fs/fs-bash.d.ts +1 -0
  49. package/dist/tools/fs/fs-bash.js +10 -3
  50. package/dist/tools/fs/fs-read.js +10 -10
  51. package/dist/tools/fs/fs-search-tools.js +42 -7
  52. package/dist/tools/fs/fs-write.js +18 -6
  53. package/dist/tools/fs/index.d.ts +1 -0
  54. package/dist/tools/fs/index.js +2 -1
  55. package/dist/tools/fs/safety.d.ts +4 -0
  56. package/dist/tools/fs/safety.js +102 -6
  57. package/dist/tools/fs/search.d.ts +1 -0
  58. package/dist/tools/fs/search.js +23 -3
  59. package/dist/tools/monitor.d.ts +2 -0
  60. package/dist/tools/monitor.js +3 -1
  61. 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: JSON.stringify({ error: "task not found" }),
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?.suggestion !== undefined)
36
+ if (enrich.suggestion !== undefined)
37
37
  content += `. Did you mean: ${enrich.suggestion}?`;
38
- if (enrich?.runningAgents !== undefined && enrich.runningAgents.length > 0)
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.runningBackgroundAgentLabels(access),
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 {
@@ -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>;
@@ -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
  }
@@ -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, AUTONOMY_SELF_AUDIT, ANTI_VERBOSITY, TOOL_PARAM_JSON, 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";
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, AUTONOMY_SELF_AUDIT, ANTI_VERBOSITY, TOOL_PARAM_JSON, 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";
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
- if (value === "small" || value === "medium" || value === "large")
9
- return value;
10
- return "unrestricted";
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 g = normalizeWorkflowSizeGuideline(value);
29
- if (g !== "unrestricted")
30
- return `\n\nThe user has configured a workflow size guideline in /config: ${describeWorkflowSizeGuideline(g)}. ${guidelineNotHardLimit()}`;
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 "The user removed their workflow size guideline in /configworkflow size is unrestricted again.";
36
- return `The user changed their workflow size guideline in /config: ${describeWorkflowSizeGuideline(g)}. ${guidelineNotHardLimit()}`;
45
+ return "Workflow size is now unrestrictedno 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
- if (journalStore.resumeClaim) {
1376
- const verdict = journalStore.resumeClaim({ sourceRunId: opts.resumeFromRunId, newRunId: runId, scope });
1377
- resumeClaim = { sourceRunId: opts.resumeFromRunId, verdict };
1378
- const decision = await verdict;
1379
- if (!decision.granted) {
1380
- const holder = decision.holder !== undefined ? clampRunIdText(decision.holder) : "";
1381
- throw new Error(`startWorkflow: resume from "${clampRunIdText(opts.resumeFromRunId)}" was REFUSED — ` +
1382
- `another run already holds the resume claim on it${holder ? ` (holder: ${holder})` : " (holder unknown to the store)"}. ` +
1383
- "Two concurrent resumes of one source run fork its execution: both replay the same prefix and then " +
1384
- "re-run the whole suffix live, duplicating every side effect. Wait for the holder to reach a terminal " +
1385
- "state (or stop it) and resume again.");
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 && journalStore?.releaseResumeClaim) {
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
- try {
1492
- await journalStore.releaseResumeClaim({ sourceRunId: resumeClaim.sourceRunId, newRunId: runId, scope });
1544
+ if (resumeClaim.fallback) {
1545
+ if (journalStore) {
1546
+ workflowResumeClaimFallback.release(journalStore, { sourceRunId: resumeClaim.sourceRunId, newRunId: runId, scope });
1547
+ }
1493
1548
  }
1494
- catch {
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
  }