@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,32 @@
1
+ import { Type } from "typebox";
2
+ export declare const TASK_OUTPUT_TOOL_NAME = "TaskOutput";
3
+ export declare const TASK_STOP_TOOL_NAME = "TaskStop";
4
+ export declare const TASK_OUTPUT_ALIASES: readonly ["BashOutput", "AgentOutputTool", "BashOutputTool", "AgentOutput", "WorkflowStatus"];
5
+ export declare const TASK_STOP_ALIASES: readonly ["KillShell", "KillBash"];
6
+ export declare const TASK_OUTPUT_CONTRACT: {
7
+ readonly contractId: "core.task_output@1";
8
+ readonly implementationRevision: "1";
9
+ };
10
+ export declare const TASK_STOP_CONTRACT: {
11
+ readonly contractId: "core.task_stop@1";
12
+ readonly implementationRevision: "1";
13
+ };
14
+ export declare const TASK_OUTPUT_MISSING_ID_MESSAGE = "Error (TaskOutput): Missing required parameter: task_id";
15
+ export declare const TASK_STOP_MISSING_ID_MESSAGE = "Missing required parameter: task_id";
16
+ export declare const TASK_STOP_PARAMS: Type.TObject<{
17
+ task_id: Type.TOptional<Type.TString>;
18
+ shell_id: Type.TOptional<Type.TString>;
19
+ }>;
20
+ export declare function resolveTaskIdArg(...values: unknown[]): string | undefined;
21
+ export interface TaskToolCapabilities {
22
+ lanes: boolean;
23
+ outputFile: boolean;
24
+ notification: boolean;
25
+ blockWait: boolean;
26
+ nameAddressing: boolean;
27
+ }
28
+ export declare const REGISTRY_TASK_TOOL_CAPS: TaskToolCapabilities;
29
+ export declare const ENV_DIRECT_TASK_TOOL_CAPS: TaskToolCapabilities;
30
+ export declare function composeTaskOutputDescription(caps: TaskToolCapabilities): string;
31
+ export declare function composeTaskStopDescription(caps: TaskToolCapabilities): string;
32
+ export declare function composeTaskOutputParams(caps: Pick<TaskToolCapabilities, "blockWait" | "lanes">): ReturnType<typeof Type.Object>;
@@ -0,0 +1,83 @@
1
+ import { Type } from "typebox";
2
+ export const TASK_OUTPUT_TOOL_NAME = "TaskOutput";
3
+ export const TASK_STOP_TOOL_NAME = "TaskStop";
4
+ export const TASK_OUTPUT_ALIASES = ["BashOutput", "AgentOutputTool", "BashOutputTool", "AgentOutput", "WorkflowStatus"];
5
+ export const TASK_STOP_ALIASES = ["KillShell", "KillBash"];
6
+ export const TASK_OUTPUT_CONTRACT = { contractId: "core.task_output@1", implementationRevision: "1" };
7
+ export const TASK_STOP_CONTRACT = { contractId: "core.task_stop@1", implementationRevision: "1" };
8
+ export const TASK_OUTPUT_MISSING_ID_MESSAGE = "Error (TaskOutput): Missing required parameter: task_id";
9
+ export const TASK_STOP_MISSING_ID_MESSAGE = "Missing required parameter: task_id";
10
+ export const TASK_STOP_PARAMS = Type.Object({
11
+ task_id: Type.Optional(Type.String({ description: "The ID of the background task to stop" })),
12
+ shell_id: Type.Optional(Type.String({ description: "Deprecated: use task_id instead" })),
13
+ });
14
+ export function resolveTaskIdArg(...values) {
15
+ for (const value of values) {
16
+ if (typeof value === "string" && value.trim() !== "")
17
+ return value.trim();
18
+ }
19
+ return undefined;
20
+ }
21
+ export const REGISTRY_TASK_TOOL_CAPS = {
22
+ lanes: true,
23
+ outputFile: true,
24
+ notification: true,
25
+ blockWait: true,
26
+ nameAddressing: true,
27
+ };
28
+ export const ENV_DIRECT_TASK_TOOL_CAPS = {
29
+ lanes: false,
30
+ outputFile: false,
31
+ notification: false,
32
+ blockWait: false,
33
+ nameAddressing: false,
34
+ };
35
+ export function composeTaskOutputDescription(caps) {
36
+ const parts = [];
37
+ if (caps.outputFile) {
38
+ parts.push("[Deprecated] — for background Bash tasks, prefer Read on the output file path; for agent tasks, use the Agent tool result directly. " +
39
+ "DEPRECATED: Background tasks return their output file path in the tool result" +
40
+ (caps.notification ? ", and you receive a <task-notification> with the same path when the task completes. " : ". "));
41
+ }
42
+ parts.push("Retrieve output or status for a background task by task_id. ");
43
+ parts.push(caps.lanes
44
+ ? "Supports background Bash tasks and workflow tasks returned by RunWorkflow. Returns only the caller's own task scope; unknown and out-of-scope ids are " +
45
+ "reported the same way. "
46
+ : "Reads a background shell started with Bash(run_in_background): returns the run status (running / exited(code) / killed / failed) plus NEW stdout/stderr since your last call. ");
47
+ if (!caps.notification) {
48
+ parts.push('When using a background command as a wait condition, call this repeatedly until status is no longer "running" — completion ' +
49
+ "is signalled by status/exitCode, not by any automatic notification; after you see a terminal status, one more " +
50
+ "poll captures any final output. ");
51
+ }
52
+ if (!caps.lanes)
53
+ parts.push("Optional `filter` is a regex keeping only matching lines. ");
54
+ parts.push("Legacy bash_id and runId arguments are accepted for compatibility.");
55
+ return parts.join("");
56
+ }
57
+ export function composeTaskStopDescription(caps) {
58
+ return [
59
+ "- Stops a running background task by its ID",
60
+ "- Takes a task_id parameter identifying the task to stop",
61
+ ...(caps.nameAddressing ? ["- To stop a background agent spawned with a name, pass that name as task_id"] : []),
62
+ "- Returns a success or failure status",
63
+ "- Use this tool when you need to terminate a long-running task",
64
+ ].join("\n");
65
+ }
66
+ export function composeTaskOutputParams(caps) {
67
+ return Type.Object({
68
+ task_id: Type.Optional(Type.String({
69
+ description: caps.lanes
70
+ ? "The task_id returned by Bash(run_in_background) or RunWorkflow."
71
+ : "The task_id returned by Bash(run_in_background).",
72
+ })),
73
+ ...(caps.blockWait
74
+ ? {
75
+ block: Type.Optional(Type.Boolean({
76
+ description: "Whether to wait for completion (default true — the call waits until the task finishes or `timeout`, saving re-poll turns). Pass false for an instantaneous snapshot.",
77
+ })),
78
+ timeout: Type.Optional(Type.Number({ description: "Optional timeout in milliseconds when block is true (default 30000, max 600000)." })),
79
+ }
80
+ : {}),
81
+ filter: Type.Optional(Type.String({ description: "Optional regex filter for background Bash output (sema extension)." })),
82
+ });
83
+ }
@@ -29,10 +29,9 @@ export type PermissionResult = {
29
29
  reason?: string;
30
30
  decisionReason?: DecisionReason;
31
31
  };
32
- export type ToolDecision = PermissionResult;
33
32
  export declare function decisionText(d: PermissionResult): string | undefined;
34
33
  export interface ToolPolicy {
35
- check(req: ToolCallRequest, signal?: AbortSignal): ToolDecision | Promise<ToolDecision>;
34
+ check(req: ToolCallRequest, signal?: AbortSignal): PermissionResult | Promise<PermissionResult>;
36
35
  }
37
36
  export interface ToolPolicyNameSets {
38
37
  readonly allow?: readonly string[];
@@ -1,4 +1,4 @@
1
- import type { AgentMessage } from "../internal/harness.js";
1
+ import type { AgentMessage } from "../internal/harness-types.js";
2
2
  import { type ToolResultStore } from "./tool-result-store.js";
3
3
  export declare const AGGREGATE_TOOL_RESULT_BUDGET_CHARS = 200000;
4
4
  export interface AggregateBudgetOptions {
@@ -1,4 +1,4 @@
1
- import type { AgentTool } from "../internal/harness.js";
1
+ import type { AgentTool } from "../internal/harness-types.js";
2
2
  export interface ToolResultStore {
3
3
  put(ref: string, content: string): Promise<void> | void;
4
4
  get(ref: string, opts?: {
@@ -54,4 +54,4 @@ export declare function buildPreview(full: string, ref: string, sizes?: {
54
54
  tail: number;
55
55
  }): string;
56
56
  export declare function withToolResultOffload(tool: AgentTool, store: ToolResultStore, thresholdChars: number, sessionId: string): AgentTool;
57
- export declare function makeReadToolResultTool(store: ToolResultStore): AgentTool;
57
+ export declare function createReadToolResultTool(store: ToolResultStore): AgentTool;
@@ -128,7 +128,7 @@ export function withToolResultOffload(tool, store, thresholdChars, sessionId) {
128
128
  };
129
129
  return { ...tool, execute: wrappedExecute };
130
130
  }
131
- export function makeReadToolResultTool(store) {
131
+ export function createReadToolResultTool(store) {
132
132
  return defineTool({
133
133
  name: OFFLOAD_TOOL_NAME,
134
134
  contract: { contractId: "core.read_tool_result@1", implementationRevision: "1" },
@@ -1,10 +1,9 @@
1
1
  import type { TSchema } from "typebox";
2
- import type { AgentTool, Skill } from "../internal/harness.js";
3
- import type { SkillSpec, ToolSpec } from "./types.js";
2
+ import type { AgentTool } from "../internal/harness.js";
3
+ import type { ToolSpec } from "./types.js";
4
4
  export declare function errorResult(text: string, details?: unknown): {
5
5
  content: string;
6
6
  isError: true;
7
7
  details?: unknown;
8
8
  };
9
9
  export declare function defineTool<TParams extends TSchema = TSchema>(spec: ToolSpec<TParams>): AgentTool<TParams>;
10
- export declare function toSkill(spec: SkillSpec): Skill;
@@ -73,11 +73,3 @@ export function defineTool(spec) {
73
73
  }
74
74
  return tool;
75
75
  }
76
- export function toSkill(spec) {
77
- return {
78
- name: spec.name,
79
- description: spec.description,
80
- content: spec.content,
81
- filePath: `memory://skills/${spec.name}`,
82
- };
83
- }
@@ -1,5 +1,5 @@
1
1
  import type { TaskStatus, ToolEffect } from "./types.js";
2
- import type { ThinkingLevel } from "../internal/harness.js";
2
+ import type { ThinkingLevel } from "../internal/harness-types.js";
3
3
  export interface TraceMechanismsSummary {
4
4
  finalizeInjected?: true;
5
5
  nudgesSent?: number;
@@ -26,7 +26,7 @@ export type TraceEvent = {
26
26
  kind: "prompt.assembled";
27
27
  version: 1;
28
28
  taskId: string;
29
- constitution: "core" | "replaced" | "provider-assembled" | "legacy";
29
+ constitution: "core" | "replaced" | "provider-assembled";
30
30
  blocks: Array<{
31
31
  id: string;
32
32
  chars: number;
@@ -87,7 +87,6 @@ export interface ToolExecuteContext {
87
87
  tokens: number;
88
88
  turns: number;
89
89
  tasks: number;
90
- costUsd?: number;
91
90
  costMicroUsd?: number;
92
91
  }) => void;
93
92
  model?: Model;
@@ -247,7 +246,6 @@ export interface TaskSpec {
247
246
  appendSystemPrompt?: string;
248
247
  promptProvider?: import("../prompts/default.js").PromptProvider;
249
248
  memory?: import("./memory.js").MemorySpecInput;
250
- memoryConsolidation?: MemoryConsolidationConfig;
251
249
  suggestNextPrompts?: boolean | {
252
250
  count?: number;
253
251
  role?: ModelRole;
@@ -398,12 +396,10 @@ export interface TaskResult {
398
396
  outputTokens?: number;
399
397
  cacheWriteTokensLong?: number;
400
398
  costMicroUsd?: number;
401
- costUsd?: number;
402
399
  nested?: {
403
400
  tokens: number;
404
401
  turns: number;
405
402
  tasks: number;
406
- costUsd?: number;
407
403
  costMicroUsd?: number;
408
404
  };
409
405
  memory?: {
@@ -596,7 +592,6 @@ export type TaskEvent = ({
596
592
  export type CompactOutcome = "compacted" | "failed" | "mooted" | "noop" | "blocked" | "disabled";
597
593
  export interface TaskStream extends AsyncIterable<TaskEvent> {
598
594
  result(): Promise<TaskResult>;
599
- consolidation(): Promise<void>;
600
595
  suggestions(): Promise<string[]>;
601
596
  steer(text: string, options?: {
602
597
  trusted?: boolean;
@@ -612,17 +607,6 @@ export interface TaskStream extends AsyncIterable<TaskEvent> {
612
607
  interrupt(): Promise<void>;
613
608
  destroy(): Promise<void>;
614
609
  }
615
- export interface MemoryConsolidationConfig {
616
- enabled: boolean;
617
- role?: ModelRole;
618
- band?: {
619
- lo: number;
620
- hi: number;
621
- };
622
- searchLimit?: number;
623
- maxNotesPerTask?: number;
624
- timeoutSec?: number;
625
- }
626
610
  export interface WorkflowGovernanceBaseline {
627
611
  base: Partial<TaskSpec>;
628
612
  worktreeBase?: Partial<TaskSpec>;
@@ -729,7 +713,6 @@ export interface RunnerDeps {
729
713
  onWorkflowAgentSpawn?: (handle: import("../orchestration/workflow.js").WorkflowAgentHandle) => void;
730
714
  maxSuspends?: number;
731
715
  now?: () => number;
732
- memoryStore?: import("./memory.js").MemoryStore;
733
716
  memoryBackend?: import("./memory-engine/types.js").MemoryBackend;
734
717
  memoryEngineDir?: string;
735
718
  onMemoryHarvestReport?: (report: import("./memory-engine/types.js").HarvestReport, info: {
@@ -751,17 +734,6 @@ export interface RunnerDeps {
751
734
  path: string;
752
735
  contentHash: string | null;
753
736
  }> | null>;
754
- utilityGate?: import("./memory.js").UtilityGate;
755
- memoryConsolidation?: MemoryConsolidationConfig;
756
- memoryRecall?: {
757
- enabled?: boolean;
758
- thresholdBytes?: number;
759
- select?: import("./memory-recall.js").MemorySelector;
760
- selectorModel?: ModelRef;
761
- maxSelected?: number;
762
- maxLinked?: number;
763
- timeoutMs?: number;
764
- };
765
737
  promptProvider?: import("../prompts/default.js").PromptProvider;
766
738
  onElicit?: OnElicit;
767
739
  mcpImageResizer?: import("./mcp.js").McpImageResizer;
@@ -797,6 +769,5 @@ export interface RunnerDeps {
797
769
  summaryProvider?: import("./auto-compaction.js").MaybeCompactOptions["summaryProvider"];
798
770
  onCompaction?: import("./auto-compaction.js").MaybeCompactOptions["onCompaction"];
799
771
  maxConsecutiveProviderReuse?: number;
800
- dynamicRecall?: import("./dynamic-recall.js").DynamicRecallConfig;
801
772
  }
802
773
  export type { AgentTool, Model, StreamFn, ThinkingLevel };
@@ -1,5 +1,5 @@
1
1
  import { type WorkflowRunStore } from "./workflow-run-store.js";
2
2
  import type { WorkflowRun, WorkflowRunStatus } from "../orchestration/workflow.js";
3
- export declare function makeWorkflowRun(over?: Partial<WorkflowRun>): WorkflowRun;
3
+ export declare function createWorkflowRun(over?: Partial<WorkflowRun>): WorkflowRun;
4
4
  export declare function workflowRunStoreContract(make: () => WorkflowRunStore, runAssertion?: (name: string, fn: () => Promise<void>) => void): Promise<void>;
5
5
  export type { WorkflowRunStatus };
@@ -1,6 +1,6 @@
1
1
  import { strict as assert } from "node:assert";
2
2
  import { summarizeWorkflowRun } from "./workflow-run-store.js";
3
- export function makeWorkflowRun(over = {}) {
3
+ export function createWorkflowRun(over = {}) {
4
4
  const createdAt = over.createdAt ?? 1_000;
5
5
  return {
6
6
  id: over.id ?? "run-1",
@@ -27,13 +27,13 @@ export async function workflowRunStoreContract(make, runAssertion = defaultSeque
27
27
  };
28
28
  run("put create-once → already_exists", async () => {
29
29
  const store = make();
30
- const r = makeWorkflowRun();
30
+ const r = createWorkflowRun();
31
31
  await store.put(r.id, r);
32
32
  await assert.rejects(store.put(r.id, r), (e) => e.code === "workflow_run.already_exists");
33
33
  });
34
34
  run("get returns a deep copy (mutating the result must not corrupt the store)", async () => {
35
35
  const store = make();
36
- const r = makeWorkflowRun();
36
+ const r = createWorkflowRun();
37
37
  await store.put(r.id, r);
38
38
  const got = await store.get(r.id);
39
39
  assert.ok(got);
@@ -44,7 +44,7 @@ export async function workflowRunStoreContract(make, runAssertion = defaultSeque
44
44
  });
45
45
  run("update CAS: bumps rev, scope-mismatch loses, stale-rev loses, current-rev wins", async () => {
46
46
  const store = make();
47
- const r = makeWorkflowRun({ status: "running" });
47
+ const r = createWorkflowRun({ status: "running" });
48
48
  await store.put(r.id, r);
49
49
  assert.equal(await store.update(r.id, r.scope, { ...r, status: "running", stats: { ...r.stats, tokens: 200 } }), true);
50
50
  assert.equal((await store.get(r.id)).rev, 1);
@@ -54,11 +54,11 @@ export async function workflowRunStoreContract(make, runAssertion = defaultSeque
54
54
  assert.equal(await store.update(r.id, r.scope, { ...r, status: "completed" }, { rev: 1 }), true);
55
55
  assert.equal((await store.get(r.id)).rev, 2);
56
56
  assert.equal((await store.get(r.id)).status, "completed");
57
- assert.equal(await store.update("ghost", r.scope, makeWorkflowRun({ id: "ghost" })), false);
57
+ assert.equal(await store.update("ghost", r.scope, createWorkflowRun({ id: "ghost" })), false);
58
58
  });
59
59
  run("the key id is authoritative — a payload with a mismatched id never poisons the projection (codex BUG4)", async () => {
60
60
  const store = make();
61
- const r = makeWorkflowRun({ id: "real", status: "running" });
61
+ const r = createWorkflowRun({ id: "real", status: "running" });
62
62
  await store.put("real", { ...r, id: "spoofed" });
63
63
  assert.equal((await store.get("real")).id, "real");
64
64
  assert.equal(await store.get("spoofed"), null);
@@ -70,10 +70,10 @@ export async function workflowRunStoreContract(make, runAssertion = defaultSeque
70
70
  run("listByScope: history included (completed + failed + running), createdAt DESC, status filter + limit", async () => {
71
71
  const store = make();
72
72
  assert.deepEqual(await store.listByScope("tenant-a"), []);
73
- const running = makeWorkflowRun({ id: "r-run", status: "running", createdAt: 3_000, endedAt: undefined });
74
- const done = makeWorkflowRun({ id: "r-done", status: "completed", createdAt: 2_000 });
75
- const failed = makeWorkflowRun({ id: "r-fail", status: "failed", createdAt: 1_000 });
76
- const other = makeWorkflowRun({ id: "r-other", scope: "tenant-b", createdAt: 9_000 });
73
+ const running = createWorkflowRun({ id: "r-run", status: "running", createdAt: 3_000, endedAt: undefined });
74
+ const done = createWorkflowRun({ id: "r-done", status: "completed", createdAt: 2_000 });
75
+ const failed = createWorkflowRun({ id: "r-fail", status: "failed", createdAt: 1_000 });
76
+ const other = createWorkflowRun({ id: "r-other", scope: "tenant-b", createdAt: 9_000 });
77
77
  for (const r of [running, done, failed, other])
78
78
  await store.put(r.id, r);
79
79
  const all = await store.listByScope("tenant-a");
@@ -88,7 +88,7 @@ export async function workflowRunStoreContract(make, runAssertion = defaultSeque
88
88
  assert.equal(doneSummary.currentPhase, "build");
89
89
  assert.equal(doneSummary.name, undefined);
90
90
  assert.equal(doneSummary.description, undefined);
91
- const named = makeWorkflowRun({
91
+ const named = createWorkflowRun({
92
92
  id: "r-named", name: "find-flaky", description: "find flaky tests", createdAt: 4_000,
93
93
  phases: [
94
94
  { title: "scan", status: "completed", startedAt: 4_000, endedAt: 4_001 },
@@ -103,10 +103,10 @@ export async function workflowRunStoreContract(make, runAssertion = defaultSeque
103
103
  });
104
104
  run("reap: only terminal runs, NEVER running; maxAgeMs + keep retention; no policy → no-op", async () => {
105
105
  const store = make();
106
- const running = makeWorkflowRun({ id: "k-run", status: "running", createdAt: 100, endedAt: undefined });
107
- const oldDone = makeWorkflowRun({ id: "k-old", status: "completed", createdAt: 100, endedAt: 100 });
108
- const newDone = makeWorkflowRun({ id: "k-new", status: "completed", createdAt: 9_000, endedAt: 9_000 });
109
- const oldFail = makeWorkflowRun({ id: "k-fail", status: "failed", createdAt: 200, endedAt: 200 });
106
+ const running = createWorkflowRun({ id: "k-run", status: "running", createdAt: 100, endedAt: undefined });
107
+ const oldDone = createWorkflowRun({ id: "k-old", status: "completed", createdAt: 100, endedAt: 100 });
108
+ const newDone = createWorkflowRun({ id: "k-new", status: "completed", createdAt: 9_000, endedAt: 9_000 });
109
+ const oldFail = createWorkflowRun({ id: "k-fail", status: "failed", createdAt: 200, endedAt: 200 });
110
110
  for (const r of [running, oldDone, newDone, oldFail])
111
111
  await store.put(r.id, r);
112
112
  assert.equal(await store.reap("tenant-a", 1_000_000), 0);
@@ -120,10 +120,10 @@ export async function workflowRunStoreContract(make, runAssertion = defaultSeque
120
120
  });
121
121
  run("reap keep-newest-N: deletes all but the newest N terminal runs; running excluded from the count", async () => {
122
122
  const store = make();
123
- const running = makeWorkflowRun({ id: "p-run", status: "running", createdAt: 50, endedAt: undefined });
124
- const t1 = makeWorkflowRun({ id: "p-1", status: "completed", createdAt: 1_000 });
125
- const t2 = makeWorkflowRun({ id: "p-2", status: "completed", createdAt: 2_000 });
126
- const t3 = makeWorkflowRun({ id: "p-3", status: "failed", createdAt: 3_000 });
123
+ const running = createWorkflowRun({ id: "p-run", status: "running", createdAt: 50, endedAt: undefined });
124
+ const t1 = createWorkflowRun({ id: "p-1", status: "completed", createdAt: 1_000 });
125
+ const t2 = createWorkflowRun({ id: "p-2", status: "completed", createdAt: 2_000 });
126
+ const t3 = createWorkflowRun({ id: "p-3", status: "failed", createdAt: 3_000 });
127
127
  for (const r of [running, t1, t2, t3])
128
128
  await store.put(r.id, r);
129
129
  assert.equal(await store.reap("tenant-a", 99_999, { keep: 1 }), 2);
@@ -9,6 +9,19 @@ export declare function getShellConfig(customShellPath?: string): Promise<Result
9
9
  shell: string;
10
10
  args: string[];
11
11
  }, ExecutionError>>;
12
+ export declare function decideSpoolReclaim(args: {
13
+ size: number;
14
+ cursor: number;
15
+ writerLive: boolean;
16
+ rotateBytes?: number;
17
+ hardCapBytes?: number;
18
+ }): {
19
+ reclaim: false;
20
+ } | {
21
+ reclaim: true;
22
+ live: boolean;
23
+ skippedBytes: number;
24
+ };
12
25
  export declare function openSpoolPair(base: string): {
13
26
  outPath: string;
14
27
  errPath: string;
@@ -8,7 +8,7 @@ import { createInterface } from "node:readline";
8
8
  import { ExecutionError, err, FileError, ok, toError, } from "../harness/types.js";
9
9
  import { killProcessTree, shutdownDebug } from "./kill-tree.js";
10
10
  import { scrubSecretEnv } from "../../core/secret-env.js";
11
- import { RollingTailBuffer, markTruncated, newStreamCursorState, sliceStreamIncrement, } from "../../core/exec-output-tail.js";
11
+ import { RollingTailBuffer, markLiveSpoolRotations, markTruncated, newStreamCursorState, sliceStreamIncrement, } from "../../core/exec-output-tail.js";
12
12
  import { BackgroundShellError } from "../../core/background-shell.js";
13
13
  import { SchedulerError } from "../../core/scheduler.js";
14
14
  const MAX_TIMER_TIMEOUT_MS = 2_147_000_000;
@@ -213,6 +213,23 @@ const EXEC_SPOOL_PUMP_MS = 200;
213
213
  const EXEC_SPOOL_ROTATE_BYTES = 8 * 1024 * 1024;
214
214
  const EXEC_SPOOL_FINAL_TAIL_BYTES = 8 * 1024 * 1024;
215
215
  const EXEC_SPOOL_HARD_CAP_BYTES = 16 * 1024 * 1024;
216
+ export function decideSpoolReclaim(args) {
217
+ const rotate = args.rotateBytes ?? EXEC_SPOOL_ROTATE_BYTES;
218
+ const hardCap = args.hardCapBytes ?? EXEC_SPOOL_HARD_CAP_BYTES;
219
+ if (args.cursor < rotate)
220
+ return { reclaim: false };
221
+ if (args.size === args.cursor) {
222
+ if (!args.writerLive)
223
+ return { reclaim: true, live: false, skippedBytes: 0 };
224
+ if (args.size >= hardCap)
225
+ return { reclaim: true, live: true, skippedBytes: 0 };
226
+ return { reclaim: false };
227
+ }
228
+ if (args.size > args.cursor && args.size >= hardCap) {
229
+ return { reclaim: true, live: args.writerLive, skippedBytes: args.size - args.cursor };
230
+ }
231
+ return { reclaim: false };
232
+ }
216
233
  const SIGNUM = { SIGHUP: 1, SIGINT: 2, SIGQUIT: 3, SIGABRT: 6, SIGKILL: 9, SIGSEGV: 11, SIGPIPE: 13, SIGTERM: 15 };
217
234
  export function openSpoolPair(base) {
218
235
  const outPath = `${base}.out.log`;
@@ -313,6 +330,9 @@ export class NodeExecutionEnv {
313
330
  const adoptOnTimeoutRef = {};
314
331
  const pumpSpoolRef = {};
315
332
  const drainSpoolRef = {};
333
+ const liveRotations = { out: 0, err: 0 };
334
+ const liveRotationsTotal = () => liveRotations.out + liveRotations.err;
335
+ const writerLive = () => !settled && child !== undefined && child.exitCode === null && child.signalCode === null;
316
336
  const armForceSettle = (result) => {
317
337
  if (forceSettleRef.current || settled)
318
338
  return;
@@ -331,7 +351,7 @@ export class NodeExecutionEnv {
331
351
  drainSpoolRef.current?.();
332
352
  const so = stdoutTail.result();
333
353
  const se = stderrTail.result();
334
- return { stdout: markTruncated(so.text, so.droppedBytes), stderr: markTruncated(se.text, se.droppedBytes) };
354
+ return { stdout: markTruncated(so.text, so.droppedBytes), stderr: markLiveSpoolRotations(markTruncated(se.text, se.droppedBytes), liveRotationsTotal()) };
335
355
  };
336
356
  const onAbort = () => {
337
357
  abortKillInFlight = true;
@@ -520,16 +540,15 @@ export class NodeExecutionEnv {
520
540
  if (spoolCursor[lane.cursor] < EXEC_SPOOL_ROTATE_BYTES)
521
541
  return;
522
542
  try {
523
- const size = statSync(lane.path).size;
524
- if (size === spoolCursor[lane.cursor]) {
525
- truncateSync(lane.path, 0);
526
- spoolCursor[lane.cursor] = 0;
527
- }
528
- else if (size >= EXEC_SPOOL_HARD_CAP_BYTES) {
529
- lane.tail.recordSkippedBytes(size - spoolCursor[lane.cursor]);
530
- truncateSync(lane.path, 0);
531
- spoolCursor[lane.cursor] = 0;
532
- }
543
+ const d = decideSpoolReclaim({ size: statSync(lane.path).size, cursor: spoolCursor[lane.cursor], writerLive: writerLive() });
544
+ if (!d.reclaim)
545
+ return;
546
+ if (d.skippedBytes > 0)
547
+ lane.tail.recordSkippedBytes(d.skippedBytes);
548
+ if (d.live)
549
+ liveRotations[lane.cursor]++;
550
+ truncateSync(lane.path, 0);
551
+ spoolCursor[lane.cursor] = 0;
533
552
  }
534
553
  catch {
535
554
  }
@@ -564,7 +583,9 @@ export class NodeExecutionEnv {
564
583
  if (settled || !child)
565
584
  return false;
566
585
  pumpSpoolRef.current?.();
567
- const shellId = this.adoptRunningChild(child, stdoutTail.result(), stderrTail.result(), execSpoolRef.current !== undefined ? { spool: execSpoolRef.current, cursors: { out: spoolCursor.out, err: spoolCursor.err } } : undefined);
586
+ const shellId = this.adoptRunningChild(child, stdoutTail.result(), stderrTail.result(), execSpoolRef.current !== undefined
587
+ ? { spool: execSpoolRef.current, cursors: { out: spoolCursor.out, err: spoolCursor.err }, liveRotations: liveRotationsTotal() }
588
+ : undefined);
568
589
  if (shellId === undefined)
569
590
  return false;
570
591
  if (spoolPumpRef.current)
@@ -657,12 +678,12 @@ export class NodeExecutionEnv {
657
678
  const signum = { SIGHUP: 1, SIGINT: 2, SIGQUIT: 3, SIGABRT: 6, SIGKILL: 9, SIGSEGV: 11, SIGPIPE: 13, SIGTERM: 15 };
658
679
  settle(ok({
659
680
  stdout: markTruncated(so.text, so.droppedBytes),
660
- stderr: `${markTruncated(se.text, se.droppedBytes)}${se.text.endsWith("\n") || se.text === "" ? "" : "\n"}[process terminated by ${signal}]`,
681
+ stderr: markLiveSpoolRotations(`${markTruncated(se.text, se.droppedBytes)}${se.text.endsWith("\n") || se.text === "" ? "" : "\n"}[process terminated by ${signal}]`, liveRotationsTotal()),
661
682
  exitCode: 128 + (signum[signal] ?? 9),
662
683
  }));
663
684
  return;
664
685
  }
665
- settle(ok({ stdout: markTruncated(so.text, so.droppedBytes), stderr: markTruncated(se.text, se.droppedBytes), exitCode: code ?? 0 }));
686
+ settle(ok({ stdout: markTruncated(so.text, so.droppedBytes), stderr: markLiveSpoolRotations(markTruncated(se.text, se.droppedBytes), liveRotationsTotal()), exitCode: code ?? 0 }));
666
687
  };
667
688
  child.on("close", (code, signal) => settleFromTermination(code, signal));
668
689
  child.on("exit", (code, signal) => {
@@ -1030,6 +1051,7 @@ export class NodeExecutionEnv {
1030
1051
  ...(handover !== undefined
1031
1052
  ? { spool: { outPath: handover.spool.outPath, errPath: handover.spool.errPath, fileCursor: { ...handover.cursors }, ephemeral: true } }
1032
1053
  : {}),
1054
+ ...(handover !== undefined && (handover.liveRotations ?? 0) > 0 ? { liveRotations: handover.liveRotations } : {}),
1033
1055
  };
1034
1056
  const seedOut = Buffer.from(stdoutSoFar.text, "utf8");
1035
1057
  const seedErr = Buffer.from(stderrSoFar.text, "utf8");
@@ -1219,9 +1241,14 @@ export class NodeExecutionEnv {
1219
1241
  const out = sliceStreamIncrement(entry.stdout, terminal);
1220
1242
  const er = sliceStreamIncrement(entry.stderr, terminal);
1221
1243
  const droppedBeforeCursor = out.droppedBeforeCursor + er.droppedBeforeCursor;
1244
+ let stderrInc = er.inc;
1245
+ if (terminal && (entry.liveRotations ?? 0) > 0 && entry.liveRotationsDisclosed !== true) {
1246
+ entry.liveRotationsDisclosed = true;
1247
+ stderrInc = markLiveSpoolRotations(er.inc, entry.liveRotations ?? 0);
1248
+ }
1222
1249
  return ok({
1223
1250
  stdout: out.inc,
1224
- stderr: er.inc,
1251
+ stderr: stderrInc,
1225
1252
  status: entry.status,
1226
1253
  ...(entry.exitCode !== undefined ? { exitCode: entry.exitCode } : {}),
1227
1254
  ...(droppedBeforeCursor > 0 ? { truncated: true, bytesDroppedBeforeCursor: droppedBeforeCursor } : {}),
@@ -1283,15 +1310,18 @@ export class NodeExecutionEnv {
1283
1310
  }
1284
1311
  }
1285
1312
  if (spool.ephemeral && spool.fileCursor[lane] >= EXEC_SPOOL_ROTATE_BYTES) {
1286
- const size2 = statSync(path).size;
1287
- if (size2 === spool.fileCursor[lane]) {
1288
- truncateSync(path, 0);
1289
- spool.fileCursor[lane] = 0;
1290
- }
1291
- else if (size2 >= EXEC_SPOOL_HARD_CAP_BYTES) {
1292
- const skipped = size2 - spool.fileCursor[lane];
1293
- stream.tail.recordSkippedBytes(skipped);
1294
- stream.totalBytes += skipped;
1313
+ const d = decideSpoolReclaim({
1314
+ size: statSync(path).size,
1315
+ cursor: spool.fileCursor[lane],
1316
+ writerLive: entry.status === "running" || (entry.child.exitCode === null && entry.child.signalCode === null),
1317
+ });
1318
+ if (d.reclaim) {
1319
+ if (d.skippedBytes > 0) {
1320
+ stream.tail.recordSkippedBytes(d.skippedBytes);
1321
+ stream.totalBytes += d.skippedBytes;
1322
+ }
1323
+ if (d.live)
1324
+ entry.liveRotations = (entry.liveRotations ?? 0) + 1;
1295
1325
  truncateSync(path, 0);
1296
1326
  spool.fileCursor[lane] = 0;
1297
1327
  }
@@ -1,2 +1,2 @@
1
1
  export declare function encodeFrame(json: string): Buffer;
2
- export declare function makeFrameDecoder(maxBytes?: number): (chunk: Buffer) => string[];
2
+ export declare function createFrameDecoder(maxBytes?: number): (chunk: Buffer) => string[];
@@ -2,7 +2,7 @@ export function encodeFrame(json) {
2
2
  const payload = Buffer.from(json, "utf8");
3
3
  return Buffer.concat([Buffer.from(`Content-Length: ${payload.length}\r\n\r\n`, "ascii"), payload]);
4
4
  }
5
- export function makeFrameDecoder(maxBytes = 50 * 1024 * 1024) {
5
+ export function createFrameDecoder(maxBytes = 50 * 1024 * 1024) {
6
6
  const HEADER_SLACK = 128;
7
7
  let buf = Buffer.alloc(0);
8
8
  return function decode(chunk) {
@@ -1,7 +1,7 @@
1
1
  import { type SessionWarmup } from "../../core/lsp-session.js";
2
2
  import { type LspChildProcess } from "./stdio-lsp-transport.js";
3
3
  import { type LspServerManager, type LspSession, type LspReadText } from "../../core/lsp.js";
4
- import type { ExecutionEnv } from "../../internal/harness.js";
4
+ import type { ExecutionEnv } from "../../internal/harness-types.js";
5
5
  export declare const DEFAULT_LSP_SERVERS: Readonly<Record<string, string>>;
6
6
  export type LspSpawn = (command: string, args: string[], cwd: string, signal?: AbortSignal) => Promise<LspChildProcess | undefined>;
7
7
  export interface NodeLspManagerOptions {
@@ -1,4 +1,4 @@
1
- import { encodeFrame, makeFrameDecoder } from "./frame-decoder.js";
1
+ import { encodeFrame, createFrameDecoder } from "./frame-decoder.js";
2
2
  import { killProcessTree } from "../execution-env/kill-tree.js";
3
3
  import { brandLspFailure } from "../../core/lsp.js";
4
4
  export class StdioLspTransport {
@@ -7,7 +7,7 @@ export class StdioLspTransport {
7
7
  nextId = 0;
8
8
  pending = new Map();
9
9
  isClosed = false;
10
- decode = makeFrameDecoder();
10
+ decode = createFrameDecoder();
11
11
  notificationHandlers = new Set();
12
12
  constructor(child, requestTimeoutMs = 30_000) {
13
13
  this.child = child;