@sema-agent/core 2.13.0 → 4.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 (184) hide show
  1. package/CHANGELOG.md +2345 -0
  2. package/dist/agents/cascade.d.ts +6 -1
  3. package/dist/agents/cascade.js +76 -32
  4. package/dist/agents/cumulative-stats.d.ts +29 -0
  5. package/dist/agents/cumulative-stats.js +29 -0
  6. package/dist/agents/observer.d.ts +4 -0
  7. package/dist/agents/observer.js +11 -10
  8. package/dist/agents/repair-loop.d.ts +2 -0
  9. package/dist/agents/repair-loop.js +110 -3
  10. package/dist/agents/retain-ledger.d.ts +20 -2
  11. package/dist/agents/retain-ledger.js +38 -8
  12. package/dist/agents/roster-store.d.ts +4 -0
  13. package/dist/agents/roster-store.js +20 -1
  14. package/dist/agents/subagent.js +66 -64
  15. package/dist/agents/teacher.d.ts +1 -9
  16. package/dist/agents/teacher.js +23 -24
  17. package/dist/agents/team.js +7 -4
  18. package/dist/agents/verify.d.ts +15 -2
  19. package/dist/agents/verify.js +39 -3
  20. package/dist/bin/sema-tb.js +2 -2
  21. package/dist/brain/anthropic.js +24 -21
  22. package/dist/brain/circuit-breaker.d.ts +1 -0
  23. package/dist/brain/circuit-breaker.js +26 -9
  24. package/dist/brain/degrading.js +13 -1
  25. package/dist/brain/failover.js +2 -0
  26. package/dist/brain/openai.js +24 -21
  27. package/dist/brain/repetition.js +10 -1
  28. package/dist/brain/routing.js +5 -1
  29. package/dist/brain/status-sink.js +5 -1
  30. package/dist/brain/stream-engine.js +5 -2
  31. package/dist/brain/stream-shared.d.ts +1 -0
  32. package/dist/brain/stream-shared.js +14 -0
  33. package/dist/brain/terminal-cause.d.ts +4 -0
  34. package/dist/brain/terminal-cause.js +12 -0
  35. package/dist/brain/tool-call-id.d.ts +1 -0
  36. package/dist/brain/tool-call-id.js +3 -0
  37. package/dist/brain/tool-call-repair.js +34 -15
  38. package/dist/core/auto-compaction.js +33 -32
  39. package/dist/core/background-agent-store.d.ts +22 -22
  40. package/dist/core/background-agent-store.js +19 -23
  41. package/dist/core/checkpoint-store.d.ts +7 -13
  42. package/dist/core/checkpoint-store.js +39 -15
  43. package/dist/core/consolidate-scope.js +4 -3
  44. package/dist/core/context-edit.js +3 -0
  45. package/dist/core/context-guard.js +3 -0
  46. package/dist/core/file-snapshot-store.d.ts +8 -0
  47. package/dist/core/file-snapshot-store.js +4 -3
  48. package/dist/core/git-worktree-env.d.ts +6 -1
  49. package/dist/core/git-worktree-env.js +12 -1
  50. package/dist/core/hooks.d.ts +4 -1
  51. package/dist/core/hooks.js +35 -7
  52. package/dist/core/lsp-diagnostics.js +13 -7
  53. package/dist/core/lsp-protocol.d.ts +1 -1
  54. package/dist/core/lsp-protocol.js +20 -8
  55. package/dist/core/lsp-session.d.ts +12 -2
  56. package/dist/core/lsp-session.js +114 -49
  57. package/dist/core/lsp.d.ts +13 -1
  58. package/dist/core/lsp.js +65 -9
  59. package/dist/core/mailbox-store.d.ts +7 -10
  60. package/dist/core/mcp.d.ts +7 -10
  61. package/dist/core/mcp.js +7 -3
  62. package/dist/core/memory-engine/data-plane.js +4 -0
  63. package/dist/core/memory-engine/engine.d.ts +2 -2
  64. package/dist/core/memory-engine/engine.js +96 -31
  65. package/dist/core/memory-engine/file-backend.d.ts +2 -2
  66. package/dist/core/memory-engine/file-backend.js +38 -39
  67. package/dist/core/memory-engine/layout.d.ts +8 -0
  68. package/dist/core/memory-engine/layout.js +71 -4
  69. package/dist/core/memory-engine/scope-contract.js +9 -3
  70. package/dist/core/memory-engine/types.d.ts +1 -1
  71. package/dist/core/memory.js +3 -0
  72. package/dist/core/permission-rules.d.ts +2 -1
  73. package/dist/core/permission-rules.js +24 -3
  74. package/dist/core/runner/active-skill-scope.d.ts +0 -1
  75. package/dist/core/runner/active-skill-scope.js +1 -15
  76. package/dist/core/runner/assemble-result.d.ts +3 -8
  77. package/dist/core/runner/assemble-result.js +3 -5
  78. package/dist/core/runner/compaction-call-options.d.ts +7 -0
  79. package/dist/core/runner/compaction-call-options.js +30 -0
  80. package/dist/core/runner/prepare-task.d.ts +6 -23
  81. package/dist/core/runner/prepare-task.js +31 -53
  82. package/dist/core/runner/runtask.d.ts +1 -0
  83. package/dist/core/runner/runtask.js +86 -178
  84. package/dist/core/runner/session-rule-policy.js +4 -1
  85. package/dist/core/runner/teardown-bounded.d.ts +7 -0
  86. package/dist/core/runner/teardown-bounded.js +36 -0
  87. package/dist/core/runner/tool-disclosure.d.ts +2 -5
  88. package/dist/core/runner/turn-attachments.d.ts +5 -15
  89. package/dist/core/runner/turn-attachments.js +1 -1
  90. package/dist/core/runner/usage-accounting.d.ts +6 -0
  91. package/dist/core/runner/usage-accounting.js +9 -8
  92. package/dist/core/safe-notify.d.ts +17 -0
  93. package/dist/core/safe-notify.js +59 -0
  94. package/dist/core/secret-env.js +5 -1
  95. package/dist/core/sensitive-path-policy.js +11 -4
  96. package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
  97. package/dist/core/store-contracts/session-repo-contract.js +40 -0
  98. package/dist/core/stub-env.d.ts +2 -6
  99. package/dist/core/surrogate-safe-slice.d.ts +4 -0
  100. package/dist/core/surrogate-safe-slice.js +18 -0
  101. package/dist/core/task-registry-monitor.js +15 -14
  102. package/dist/core/task-registry-shared.d.ts +22 -0
  103. package/dist/core/task-registry-shared.js +16 -0
  104. package/dist/core/task-registry.d.ts +1 -0
  105. package/dist/core/task-registry.js +40 -57
  106. package/dist/core/tool-errors.js +5 -4
  107. package/dist/core/tool-name-aliases.js +2 -1
  108. package/dist/core/tool-policy.js +72 -19
  109. package/dist/core/tool-result-store.d.ts +1 -0
  110. package/dist/core/tool-result-store.js +17 -1
  111. package/dist/core/trace.d.ts +3 -9
  112. package/dist/core/types.d.ts +16 -12
  113. package/dist/core/untrusted-egress.js +7 -5
  114. package/dist/core/workflow-journal-store.d.ts +9 -20
  115. package/dist/core/workflow-run-store.d.ts +11 -10
  116. package/dist/core/workflow-run-store.js +22 -16
  117. package/dist/engine/compaction/compaction.d.ts +1 -0
  118. package/dist/engine/compaction/compaction.js +14 -5
  119. package/dist/engine/compaction/utils.js +4 -2
  120. package/dist/engine/execution-env/node-execution-env.d.ts +2 -24
  121. package/dist/engine/harness/agent-harness.d.ts +1 -0
  122. package/dist/engine/harness/agent-harness.js +13 -33
  123. package/dist/engine/harness/types.d.ts +29 -67
  124. package/dist/engine/llm/types.d.ts +1 -1
  125. package/dist/engine/loop/agent-loop.js +11 -4
  126. package/dist/engine/loop/types.d.ts +11 -9
  127. package/dist/engine/lsp/frame-decoder.js +6 -3
  128. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -0
  129. package/dist/engine/lsp/node-lsp-manager.js +103 -26
  130. package/dist/engine/lsp/stdio-lsp-transport.js +16 -7
  131. package/dist/engine/session/memory-repo.d.ts +2 -6
  132. package/dist/engine/session/session.d.ts +5 -30
  133. package/dist/index.d.ts +3 -1
  134. package/dist/index.js +2 -1
  135. package/dist/internal/harness-types.d.ts +3 -1
  136. package/dist/internal/harness.d.ts +1 -1
  137. package/dist/internal/harness.js +1 -1
  138. package/dist/orchestration/run-spec.js +7 -4
  139. package/dist/orchestration/run-workflow-tool.js +3 -35
  140. package/dist/orchestration/workflow-governance.js +43 -9
  141. package/dist/orchestration/workflow-sandbox-conformance.js +85 -51
  142. package/dist/orchestration/workflow-script-store.js +34 -4
  143. package/dist/orchestration/workflow.d.ts +3 -11
  144. package/dist/orchestration/workflow.js +164 -204
  145. package/dist/prompt-assembly/artifact-store.d.ts +2 -0
  146. package/dist/prompt-assembly/artifact-store.js +39 -24
  147. package/dist/prompt-assembly/assemble.js +38 -7
  148. package/dist/prompt-assembly/epoch.js +19 -2
  149. package/dist/prompt-assembly/event-registry.js +3 -3
  150. package/dist/prompt-assembly/explain.js +1 -1
  151. package/dist/prompt-assembly/tool-catalog.d.ts +10 -9
  152. package/dist/prompt-assembly/tool-catalog.js +16 -1
  153. package/dist/prompt-assembly/turn-snapshot.d.ts +2 -5
  154. package/dist/prompt-assembly/turn-snapshot.js +4 -1
  155. package/dist/stores/file/background-agent-store.d.ts +3 -12
  156. package/dist/stores/file/background-agent-store.js +3 -41
  157. package/dist/stores/file/checkpoint-store.js +6 -6
  158. package/dist/stores/file/fs-atomic.d.ts +3 -0
  159. package/dist/stores/file/fs-atomic.js +53 -8
  160. package/dist/stores/file/index.d.ts +1 -0
  161. package/dist/stores/file/index.js +7 -0
  162. package/dist/stores/file/mailbox-store.d.ts +2 -6
  163. package/dist/stores/file/session-policy-store.js +15 -1
  164. package/dist/stores/file/session-store.d.ts +4 -6
  165. package/dist/stores/file/session-store.js +28 -1
  166. package/dist/stores/file/workflow-journal-store.d.ts +3 -11
  167. package/dist/stores/file/workflow-run-store.d.ts +3 -7
  168. package/dist/stores/file/workflow-run-store.js +4 -17
  169. package/dist/tools/fs/bash-readonly-classifier.d.ts +2 -0
  170. package/dist/tools/fs/bash-readonly-classifier.js +73 -21
  171. package/dist/tools/fs/fs-bash.d.ts +1 -1
  172. package/dist/tools/fs/fs-bash.js +407 -91
  173. package/dist/tools/fs/fs-pdf.d.ts +2 -12
  174. package/dist/tools/fs/fs-shared.js +2 -2
  175. package/dist/tools/fs/notebook.d.ts +2 -5
  176. package/dist/tools/fs/safety.d.ts +3 -0
  177. package/dist/tools/fs/safety.js +43 -14
  178. package/dist/tools/fs/search.js +5 -5
  179. package/dist/tools/loop-tick.js +1 -1
  180. package/dist/tools/monitor.d.ts +1 -0
  181. package/dist/tools/monitor.js +57 -9
  182. package/dist/tools/worktree.d.ts +4 -7
  183. package/dist/tools/worktree.js +235 -143
  184. package/package.json +9 -2
@@ -1,6 +1,9 @@
1
1
  import { mkdirSync, promises as fs } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { verifyPromptArtifact, } from "./artifact.js";
4
+ function normalizedEnvelope(parsed, artifact) {
5
+ return { ...parsed, artifact };
6
+ }
4
7
  export class MemoryPromptArtifactStore {
5
8
  byDigest = new Map();
6
9
  async get(artifactDigest, opts) {
@@ -17,7 +20,7 @@ export class MemoryPromptArtifactStore {
17
20
  const v = verifyPromptArtifact(parsed, opts);
18
21
  if (!v.ok || v.artifact.artifactDigest !== artifactDigest)
19
22
  return undefined;
20
- return parsed;
23
+ return normalizedEnvelope(parsed, v.artifact);
21
24
  }
22
25
  async put(envelope) {
23
26
  this.byDigest.set(envelope.artifact.artifactDigest, JSON.stringify(envelope));
@@ -54,7 +57,7 @@ export class FilePromptArtifactStore {
54
57
  const v = verifyPromptArtifact(parsed, opts);
55
58
  if (!v.ok || v.artifact.artifactDigest !== artifactDigest)
56
59
  return undefined;
57
- return parsed;
60
+ return normalizedEnvelope(parsed, v.artifact);
58
61
  }
59
62
  async put(envelope) {
60
63
  const file = this.fileFor(envelope.artifact.artifactDigest);
@@ -113,6 +116,12 @@ export class CenterPromptSource {
113
116
  engineVersion;
114
117
  candidate;
115
118
  state = "unavailable";
119
+ tail = Promise.resolve();
120
+ exclusive(fn) {
121
+ const run = this.tail.then(fn, fn);
122
+ this.tail = run.then(() => undefined, () => undefined);
123
+ return run;
124
+ }
116
125
  constructor(opts) {
117
126
  this.store = opts.store;
118
127
  this.stateStore = opts.stateStore ?? new MemoryPromptSourceStateStore();
@@ -128,20 +137,22 @@ export class CenterPromptSource {
128
137
  return env?.artifact;
129
138
  }
130
139
  async restore() {
131
- const state = await this.stateStore.load();
132
- if (state?.state === "disabled") {
133
- this.candidate = undefined;
134
- this.state = "disabled";
135
- return;
136
- }
137
- if (state?.state !== "active") {
138
- this.candidate = undefined;
139
- this.state = "unavailable";
140
- return;
141
- }
142
- const env = await this.store.get(state.artifactDigest, { engineVersion: this.engineVersion });
143
- this.candidate = env !== undefined ? { artifact: env.artifact, sourceRevision: state.sourceRevision } : undefined;
144
- this.state = env !== undefined ? "active" : "unavailable";
140
+ return this.exclusive(async () => {
141
+ const state = await this.stateStore.load();
142
+ if (state?.state === "disabled") {
143
+ this.candidate = undefined;
144
+ this.state = "disabled";
145
+ return;
146
+ }
147
+ if (state?.state !== "active") {
148
+ this.candidate = undefined;
149
+ this.state = "unavailable";
150
+ return;
151
+ }
152
+ const env = await this.store.get(state.artifactDigest, { engineVersion: this.engineVersion });
153
+ this.candidate = env !== undefined ? { artifact: env.artifact, sourceRevision: state.sourceRevision } : undefined;
154
+ this.state = env !== undefined ? "active" : "unavailable";
155
+ });
145
156
  }
146
157
  sourceState() {
147
158
  return this.state;
@@ -151,15 +162,19 @@ export class CenterPromptSource {
151
162
  if (!v.ok)
152
163
  return v;
153
164
  const envelope = raw;
154
- await this.store.put(envelope);
155
- await this.stateStore.save({ state: "active", artifactDigest: v.artifact.artifactDigest, sourceRevision: envelope.sourceRevision });
156
- this.candidate = { artifact: v.artifact, sourceRevision: envelope.sourceRevision };
157
- this.state = "active";
158
- return { ok: true, artifactDigest: v.artifact.artifactDigest };
165
+ return this.exclusive(async () => {
166
+ await this.store.put(envelope);
167
+ await this.stateStore.save({ state: "active", artifactDigest: v.artifact.artifactDigest, sourceRevision: envelope.sourceRevision });
168
+ this.candidate = { artifact: v.artifact, sourceRevision: envelope.sourceRevision };
169
+ this.state = "active";
170
+ return { ok: true, artifactDigest: v.artifact.artifactDigest };
171
+ });
159
172
  }
160
173
  async applyDisabled(sourceRevision) {
161
- await this.stateStore.save({ state: "disabled", sourceRevision });
162
- this.candidate = undefined;
163
- this.state = "disabled";
174
+ return this.exclusive(async () => {
175
+ await this.stateStore.save({ state: "disabled", sourceRevision });
176
+ this.candidate = undefined;
177
+ this.state = "disabled";
178
+ });
164
179
  }
165
180
  }
@@ -40,6 +40,7 @@ function mountDeclarations(basePack, decls, onWarn) {
40
40
  const counters = {};
41
41
  const extra = [];
42
42
  let replacesRoleBase = false;
43
+ let replacedRoleBaseBy;
43
44
  for (const d of decls) {
44
45
  if (!d.id || d.id.startsWith("core/") || d.id.length > 128 || !/^[a-z0-9][a-z0-9/._-]*$/.test(d.id)) {
45
46
  throw new Error(`prompt-provider stableBlocks: invalid section id ${JSON.stringify(d.id).slice(0, 140)} (must match ^[a-z0-9][a-z0-9/._-]*$, ≤128 chars, non-core/)`);
@@ -47,8 +48,10 @@ function mountDeclarations(basePack, decls, onWarn) {
47
48
  const base = SLOT_BASE[d.slot];
48
49
  if (base === undefined)
49
50
  throw new Error(`prompt-provider stableBlocks: unknown slot "${String(d.slot)}" ("${d.id}")`);
50
- if (d.slot === "identity")
51
+ if (d.slot === "identity") {
51
52
  replacesRoleBase = true;
53
+ replacedRoleBaseBy ??= d.id;
54
+ }
52
55
  const n = (counters[d.slot] = (counters[d.slot] ?? 0) + 1);
53
56
  const declaredContentHash = verifyDeclaredContentHash(d, onWarn);
54
57
  extra.push({
@@ -68,10 +71,21 @@ function mountDeclarations(basePack, decls, onWarn) {
68
71
  });
69
72
  }
70
73
  return {
71
- ...basePack,
72
- sections: [...basePack.sections.filter((s) => !(replacesRoleBase && s.id === "core/role.base")), ...extra],
74
+ pack: {
75
+ ...basePack,
76
+ sections: [...basePack.sections.filter((s) => !(replacesRoleBase && s.id === "core/role.base")), ...extra],
77
+ },
78
+ ...(replacedRoleBaseBy !== undefined && basePack.sections.some((s) => s.id === "core/role.base")
79
+ ? { replacedRoleBaseBy }
80
+ : {}),
73
81
  };
74
82
  }
83
+ function warnRoleBaseEviction(replacedRoleBaseBy, origin, onWarn) {
84
+ if (replacedRoleBaseBy === undefined || origin === "pack-default")
85
+ return;
86
+ const what = origin === "task-system-prompt" ? "the caller's per-task systemPrompt" : "the PromptProvider's own role text";
87
+ onWarn?.(`prompt-constitution: identity declaration "${replacedRoleBaseBy}" supplants the role base, so ${what} does NOT compose into this prompt (identity sections replace the role layer by design). Fold that text into the declaration, or use a scenario/behavior slot to keep the role base.`, "prompt-constitution");
88
+ }
75
89
  export function assemblePrompt(inputs) {
76
90
  const { provider, facts } = inputs;
77
91
  const onWarn = inputs.onWarn
@@ -93,6 +107,7 @@ export function assemblePrompt(inputs) {
93
107
  };
94
108
  let pack = SEMA_DEFAULT_PACK;
95
109
  let roleBase = inputs.userSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
110
+ let roleBaseOrigin = inputs.userSystemPrompt !== undefined ? "task-system-prompt" : "pack-default";
96
111
  let constitution = "core";
97
112
  let mountedCenter = [];
98
113
  if (!inputs.isDefaultProvider && provider.stableBlocks) {
@@ -108,6 +123,10 @@ export function assemblePrompt(inputs) {
108
123
  if (inputs.centerDeclarations && inputs.centerDeclarations.length > 0) {
109
124
  onWarn?.("prompt-constitution: a center prompt artifact is adopted but the provider's assembled-identity pass-through owns the prompt — the center sections cannot mount on this migration path.", "prompt-constitution");
110
125
  }
126
+ const droppedDecls = decls.filter((d) => d !== assembledIdentity);
127
+ if (droppedDecls.length > 0) {
128
+ onWarn?.(`prompt-provider stableBlocks: the assembled-identity pass-through owns the prompt — ${droppedDecls.length} other declaration(s) do NOT compose this run: ${droppedDecls.map((d) => `"${d.id}" (${d.slot})`).join(", ")}. Declare ONLY the role layer in the identity block to let them mount.`, "prompt-constitution");
129
+ }
111
130
  const renderInputsPT = (date) => ({
112
131
  roleBase: assembledIdentity.text,
113
132
  facts,
@@ -153,29 +172,41 @@ export function assemblePrompt(inputs) {
153
172
  return true;
154
173
  });
155
174
  mountedCenter = centerDecls;
156
- pack = mountDeclarations(SEMA_DEFAULT_PACK, [...centerDecls, ...decls], onWarn);
175
+ const mountedSB = mountDeclarations(SEMA_DEFAULT_PACK, [...centerDecls, ...decls], onWarn);
176
+ pack = mountedSB.pack;
177
+ warnRoleBaseEviction(mountedSB.replacedRoleBaseBy, roleBaseOrigin, onWarn);
157
178
  }
158
179
  else if (!inputs.isDefaultProvider && provider.stableSystem) {
159
180
  const roleOut = provider.stableSystem(stableCtx);
160
181
  if (provider.replaceAll) {
161
182
  constitution = "replaced";
162
183
  roleBase = roleOut;
184
+ roleBaseOrigin = "provider";
163
185
  pack = subsetPack(SEMA_DEFAULT_PACK, { exclude: REPLACE_ALL_EXCLUDED, roleBaseLegacyId: "provider.replace_all" });
164
186
  }
165
187
  else if (roleOut.includes(CYBER_RISK) && roleOut.includes(URL_SAFETY) && roleOut.includes(HARNESS_SECTION_ANCHOR)) {
166
188
  constitution = "provider-assembled";
167
189
  roleBase = roleOut;
190
+ roleBaseOrigin = "provider";
168
191
  pack = subsetPack(SEMA_DEFAULT_PACK, { keep: OPAQUE_KEPT, roleBaseLegacyId: "provider.assembled" });
169
192
  onWarn?.("prompt-constitution: this PromptProvider returns an already-assembled prompt (constitution anchor found). Core now appends the constitution structurally — return ONLY the role base from stableSystem (or set replaceAll:true to own the whole base). Passed through un-doubled.", "prompt-constitution");
170
193
  }
171
194
  else {
172
195
  roleBase = roleOut;
196
+ roleBaseOrigin = "provider";
173
197
  }
174
198
  }
175
199
  if (inputs.centerDeclarations && inputs.centerDeclarations.length > 0 && !(!inputs.isDefaultProvider && provider.stableBlocks)) {
176
- const composedCenter = inputs.centerDeclarations;
177
- pack = mountDeclarations(pack, composedCenter, onWarn);
178
- mountedCenter = composedCenter;
200
+ if (constitution === "provider-assembled") {
201
+ onWarn?.("prompt-constitution: a center prompt artifact is adopted but this PromptProvider returned an already-assembled prompt — the pass-through owns the prompt and the center sections cannot mount on this migration path (an identity section would evict the provider text that carries the constitution). Return ONLY the role base from stableSystem to let center sections compose.", "prompt-constitution");
202
+ }
203
+ else {
204
+ const composedCenter = inputs.centerDeclarations;
205
+ const mountedC = mountDeclarations(pack, composedCenter, onWarn);
206
+ pack = mountedC.pack;
207
+ warnRoleBaseEviction(mountedC.replacedRoleBaseBy, roleBaseOrigin, onWarn);
208
+ mountedCenter = composedCenter;
209
+ }
179
210
  }
180
211
  const renderInputs = (date) => ({
181
212
  roleBase,
@@ -20,12 +20,29 @@ const PROBE_FACTS_OFF = {
20
20
  teammateEnabled: false,
21
21
  orchestrationDeferred: false,
22
22
  isSubagent: false,
23
+ promptProfile: "simple",
24
+ fableMitigations: false,
25
+ };
26
+ const PROBE_FACTS_ON = {
27
+ policyEnabled: true,
28
+ hooksEnabled: true,
29
+ isolationEnabled: true,
30
+ withinTaskCompactionEnabled: true,
31
+ supervisorEnabled: true,
32
+ orchestrationEnabled: true,
33
+ awarenessEnabled: true,
34
+ goalEnabled: true,
35
+ worktreeIsolated: true,
36
+ teammateEnabled: true,
37
+ orchestrationDeferred: true,
38
+ isSubagent: true,
39
+ promptProfile: "classic",
40
+ fableMitigations: true,
23
41
  };
24
- const PROBE_FACTS_ON = Object.fromEntries(Object.keys(PROBE_FACTS_OFF).map((k) => [k, true]));
25
42
  const PROBE_VECTORS = [
26
43
  PROBE_FACTS_OFF,
27
44
  PROBE_FACTS_ON,
28
- ...Object.keys(PROBE_FACTS_OFF).map((k) => ({ ...PROBE_FACTS_OFF, [k]: true })),
45
+ ...Object.keys(PROBE_FACTS_OFF).map((k) => ({ ...PROBE_FACTS_OFF, [k]: PROBE_FACTS_ON[k] })),
29
46
  ];
30
47
  const PROBE_INPUTS_BASE = {
31
48
  roleBase: "\u0000probe-role\u0000",
@@ -4,7 +4,7 @@ export const EVENT_PROMPT_REGISTRY = new Map([
4
4
  { kind: "changed_files", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderChangedFiles" },
5
5
  { kind: "plan_mode", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#PLAN_MODE_FULL_BODY" },
6
6
  { kind: "date_change", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "always", rendererRef: "turn-attachments.ts#renderDateChange" },
7
- { kind: "instructions_change", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", maxBytes: 512, defaultPolicy: "always", rendererRef: "turn-attachments.ts#renderInstructionsChange" },
7
+ { kind: "instructions_change", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", maxBytes: 512, defaultPolicy: "always", rendererRef: "turn-attachments.ts#collectInstructionsChange" },
8
8
  { kind: "budget_usd", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderBudgetUsd" },
9
9
  { kind: "background_tasks", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderBackgroundTasks" },
10
10
  { kind: "tools_delta", carrier: "message.user-prefix", trust: "external", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderToolsDelta" },
@@ -12,8 +12,8 @@ export const EVENT_PROMPT_REGISTRY = new Map([
12
12
  { kind: "skills_listing", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "on", rendererRef: "turn-attachments.ts#renderSkillsListingDelta" },
13
13
  { kind: "mcp_instructions", carrier: "message.user-prefix", trust: "external", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderMcpInstructionsDelta" },
14
14
  { kind: "mcp_dropped_tools", carrier: "message.user-prefix", trust: "external", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderMcpDroppedTools" },
15
- { kind: "deadline_converge", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "runtask.ts#deadline-t1" },
16
- { kind: "deadline_deliver_now", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "runtask.ts#deadline-t2" },
15
+ { kind: "deadline_converge", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "runtask.ts#nudgeSchedule" },
16
+ { kind: "deadline_deliver_now", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "runtask.ts#nudgeSchedule" },
17
17
  { kind: "deadline_finalize", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "runtask.ts#buildFinalizeText" },
18
18
  { kind: "final_verification", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "runtask.ts#final-verification" },
19
19
  ].map((e) => [e.kind, e]));
@@ -30,7 +30,7 @@ export function describeDefaultPack() {
30
30
  return {
31
31
  packId: SEMA_DEFAULT_PACK.packId,
32
32
  roleBaseText: DEFAULT_SYSTEM_PROMPT,
33
- sections: SEMA_DEFAULT_PACK.sections.map((s) => ({
33
+ sections: [...SEMA_DEFAULT_PACK.sections].sort((a, b) => a.rank - b.rank).map((s) => ({
34
34
  id: s.id,
35
35
  slot: s.slot,
36
36
  rank: s.rank,
@@ -11,6 +11,15 @@ export interface ToolContractDescriptor {
11
11
  wireSchemaDigest: string;
12
12
  cardId: string;
13
13
  }
14
+ export interface ToolManifestRow {
15
+ wireName: string;
16
+ aliases: string[];
17
+ contractId: string;
18
+ implementationRevision: string;
19
+ cardId: string;
20
+ shapeDigest: string;
21
+ wireSchemaDigest: string;
22
+ }
14
23
  export declare function computeShapeDigest(schema: TSchema | undefined): string;
15
24
  export declare function computeWireSchemaDigest(schema: TSchema | undefined, description: string): string;
16
25
  export interface AttachContractInput {
@@ -22,15 +31,7 @@ export interface AttachContractInput {
22
31
  export declare function attachToolContract(tool: AgentTool, input: AttachContractInput): void;
23
32
  export declare function getToolContract(tool: AgentTool, fallbackOrigin?: ToolOrigin): ToolContractDescriptor;
24
33
  export declare function hasToolContract(tool: AgentTool): boolean;
25
- export declare function projectToolManifest(tools: AgentTool[], originOf?: (tool: AgentTool) => ToolOrigin): Array<{
26
- wireName: string;
27
- aliases: string[];
28
- contractId: string;
29
- implementationRevision: string;
30
- cardId: string;
31
- shapeDigest: string;
32
- wireSchemaDigest: string;
33
- }>;
34
+ export declare function projectToolManifest(tools: AgentTool[], originOf?: (tool: AgentTool) => ToolOrigin): ToolManifestRow[];
34
35
  export declare function auditToolCollisions(tools: AgentTool[]): {
35
36
  yields: Array<{
36
37
  name: string;
@@ -1,4 +1,12 @@
1
1
  import { createHash } from "node:crypto";
2
+ const PRESENTATION_KEYWORDS = new Set(["description", "title", "examples", "$id"]);
3
+ const SCHEMA_NAME_MAPS = new Set([
4
+ "properties",
5
+ "patternProperties",
6
+ "definitions",
7
+ "$defs",
8
+ "dependentSchemas",
9
+ ]);
2
10
  function stripPresentation(node) {
3
11
  if (Array.isArray(node))
4
12
  return node.map(stripPresentation);
@@ -6,8 +14,15 @@ function stripPresentation(node) {
6
14
  return node;
7
15
  const out = {};
8
16
  for (const [k, v] of Object.entries(node)) {
9
- if (k === "description" || k === "title" || k === "examples" || k === "$id")
17
+ if (PRESENTATION_KEYWORDS.has(k))
10
18
  continue;
19
+ if (SCHEMA_NAME_MAPS.has(k) && v !== null && typeof v === "object" && !Array.isArray(v)) {
20
+ const named = {};
21
+ for (const [name, sub] of Object.entries(v))
22
+ named[name] = stripPresentation(sub);
23
+ out[k] = named;
24
+ continue;
25
+ }
11
26
  out[k] = k === "required" && Array.isArray(v) ? [...v].sort() : stripPresentation(v);
12
27
  }
13
28
  return out;
@@ -1,3 +1,4 @@
1
+ import type { ToolFingerprintInput } from "../core/cache-break-detector.js";
1
2
  export interface RawPrefixInputs {
2
3
  provider: string;
3
4
  modelId: string;
@@ -9,11 +10,7 @@ export interface RawPrefixInputs {
9
10
  cacheControlBoundary: boolean;
10
11
  }>;
11
12
  stableSystemText: string;
12
- toolWire: ReadonlyArray<{
13
- name: string;
14
- description: string;
15
- parameters: unknown;
16
- }>;
13
+ toolWire: ReadonlyArray<ToolFingerprintInput>;
17
14
  thinkingLevel: string;
18
15
  maxTokens: number | undefined;
19
16
  hasOutputSchema: boolean;
@@ -61,7 +61,10 @@ export function buildTurnPromptSnapshot(raw, api, promptHasher) {
61
61
  loweringVersion: `${lowering.version}/${lowering.form}/${api}`,
62
62
  };
63
63
  const bump = () => {
64
- snapshot.cacheIdentity = identityOf(elements);
64
+ const next = identityOf(elements);
65
+ if (next === snapshot.cacheIdentity)
66
+ return;
67
+ snapshot.cacheIdentity = next;
65
68
  snapshot.onChange?.(snapshot);
66
69
  };
67
70
  const snapshot = {
@@ -1,4 +1,4 @@
1
- import { type BackgroundAgentRecord, type BackgroundAgentRowSummary, type BackgroundAgentStore } from "../../core/background-agent-store.js";
1
+ import { type BackgroundAgentReapOptions, type BackgroundAgentRecord, type BackgroundAgentRowSummary, type BackgroundAgentStore, type BackgroundAgentUpdateIfExpect } from "../../core/background-agent-store.js";
2
2
  export interface FileBackgroundAgentStoreOptions {
3
3
  fsync?: boolean;
4
4
  compactEvery?: number;
@@ -18,12 +18,7 @@ export declare class FileBackgroundAgentStore implements BackgroundAgentStore {
18
18
  update(handle: string, scope: string, record: BackgroundAgentRecord, expect: {
19
19
  rev: number;
20
20
  }): Promise<boolean>;
21
- updateIf(handle: string, scope: string, record: BackgroundAgentRecord, expect: {
22
- rev: number;
23
- status?: BackgroundAgentRecord["status"];
24
- parkedCheckpointToken?: string | null;
25
- parkClaimId?: string | null;
26
- }): Promise<boolean>;
21
+ updateIf(handle: string, scope: string, record: BackgroundAgentRecord, expect: BackgroundAgentUpdateIfExpect): Promise<boolean>;
27
22
  listBySession(scope: string, sessionId: string, opts?: {
28
23
  status?: BackgroundAgentRecord["status"];
29
24
  limit?: number;
@@ -36,11 +31,7 @@ export declare class FileBackgroundAgentStore implements BackgroundAgentStore {
36
31
  delete(handle: string, scope: string, expect?: {
37
32
  rev: number;
38
33
  }): Promise<boolean>;
39
- reap(scope: string, now: number, opts?: {
40
- maxAgeMs?: number;
41
- keep?: number;
42
- staleRunningMaxAgeMs?: number;
43
- }): Promise<number>;
34
+ reap(scope: string, now: number, opts?: BackgroundAgentReapOptions): Promise<number>;
44
35
  compactNow(): void;
45
36
  get size(): number;
46
37
  close(): void;
@@ -1,5 +1,5 @@
1
1
  import { join } from "node:path";
2
- import { BackgroundAgentStoreError, STALE_RUNNING_REAP_ATTRIBUTION, } from "../../core/background-agent-store.js";
2
+ import { BackgroundAgentStoreError, STALE_RUNNING_REAP_ATTRIBUTION, queryBackgroundAgents, } from "../../core/background-agent-store.js";
3
3
  import { SharedLedgerTable } from "./shared-ledger.js";
4
4
  const agentLedgers = new SharedLedgerTable({
5
5
  keyOf: (r) => FileBackgroundAgentStore.key(r.handle, r.scope),
@@ -92,31 +92,10 @@ export class FileBackgroundAgentStore {
92
92
  });
93
93
  }
94
94
  async listBySession(scope, sessionId, opts) {
95
- const out = [];
96
- for (const r of this.rows.values()) {
97
- if (r.scope !== scope)
98
- continue;
99
- const anchor = r.sessionScoped ? r.owner : r.parentSessionId;
100
- if (anchor !== sessionId && r.rootSessionId !== sessionId)
101
- continue;
102
- if (opts?.status !== undefined && r.status !== opts.status)
103
- continue;
104
- out.push(toSummary(r));
105
- }
106
- out.sort((a, b) => b.spawnedAt - a.spawnedAt);
107
- return opts?.limit !== undefined ? out.slice(0, Math.max(0, opts.limit)) : out;
95
+ return queryBackgroundAgents(this.rows.values(), { scope, sessionId, status: opts?.status, limit: opts?.limit });
108
96
  }
109
97
  async listByScope(scope, opts) {
110
- const out = [];
111
- for (const r of this.rows.values()) {
112
- if (r.scope !== scope)
113
- continue;
114
- if (opts?.status !== undefined && r.status !== opts.status)
115
- continue;
116
- out.push(toSummary(r));
117
- }
118
- out.sort((a, b) => b.spawnedAt - a.spawnedAt);
119
- return opts?.limit !== undefined ? out.slice(0, Math.max(0, opts.limit)) : out;
98
+ return queryBackgroundAgents(this.rows.values(), { scope, status: opts?.status, limit: opts?.limit });
120
99
  }
121
100
  async listScopes() {
122
101
  return [...new Set([...this.rows.values()].map((r) => r.scope))].sort();
@@ -205,20 +184,3 @@ export class FileBackgroundAgentStore {
205
184
  this.ledger.release();
206
185
  }
207
186
  }
208
- function toSummary(r) {
209
- return {
210
- handle: r.handle,
211
- owner: r.owner,
212
- sessionScoped: r.sessionScoped,
213
- ...(r.sessionId !== undefined ? { sessionId: r.sessionId } : {}),
214
- ...(r.parentSessionId !== undefined ? { parentSessionId: r.parentSessionId } : {}),
215
- ...(r.rootSessionId !== undefined ? { rootSessionId: r.rootSessionId } : {}),
216
- ...(r.name !== undefined ? { name: r.name } : {}),
217
- ...(r.agentType !== undefined ? { agentType: r.agentType } : {}),
218
- status: r.status,
219
- spawnedAt: r.spawnedAt,
220
- updatedAt: r.updatedAt,
221
- ...(r.settledAt !== undefined ? { settledAt: r.settledAt } : {}),
222
- ...(r.usage !== undefined ? { usage: structuredClone(r.usage) } : {}),
223
- };
224
- }
@@ -1,5 +1,5 @@
1
1
  import { join } from "node:path";
2
- import { CheckpointError, summarizeCheckpoint, validatePendingSteer, winnerFromOutcome, } from "../../core/checkpoint-store.js";
2
+ import { CheckpointError, checkpointOccMatches, checkpointRowMatches, summarizeCheckpoint, validatePendingSteer, winnerFromOutcome, } from "../../core/checkpoint-store.js";
3
3
  import { SharedLedgerTable } from "./shared-ledger.js";
4
4
  function applyCheckpointEvent(cps, ev) {
5
5
  if (ev.t === "put") {
@@ -80,10 +80,10 @@ export class FileCheckpointStore {
80
80
  throw new Error("injected fault: resolve before commit");
81
81
  }
82
82
  const cp = this.cps.get(token);
83
- if (!cp || cp.scope !== scope || cp.status !== "pending") {
83
+ if (!checkpointRowMatches(cp, scope, "pending")) {
84
84
  return false;
85
85
  }
86
- if (expect !== undefined && (cp.rev ?? 0) !== expect.rev) {
86
+ if (!checkpointOccMatches(cp, expect)) {
87
87
  return false;
88
88
  }
89
89
  const winner = winnerFromOutcome(outcome);
@@ -99,7 +99,7 @@ export class FileCheckpointStore {
99
99
  async reopen(token, scope, reason) {
100
100
  return this.withLock(token, () => {
101
101
  const cp = this.cps.get(token);
102
- if (!cp || cp.scope !== scope || cp.status !== "resolved") {
102
+ if (!checkpointRowMatches(cp, scope, "resolved")) {
103
103
  return false;
104
104
  }
105
105
  const rev = (cp.rev ?? 0) + 1;
@@ -111,7 +111,7 @@ export class FileCheckpointStore {
111
111
  const clean = validatePendingSteer(steer);
112
112
  return this.withLock(token, () => {
113
113
  const cp = this.cps.get(token);
114
- if (!cp || cp.scope !== scope || cp.status !== "pending") {
114
+ if (!checkpointRowMatches(cp, scope, "pending")) {
115
115
  return false;
116
116
  }
117
117
  this.commit({ t: "steer", token, steer: clean }, this.fsyncEnabled);
@@ -121,7 +121,7 @@ export class FileCheckpointStore {
121
121
  async expire(token, scope) {
122
122
  return this.withLock(token, () => {
123
123
  const cp = this.cps.get(token);
124
- if (!cp || cp.scope !== scope || cp.status !== "pending") {
124
+ if (!checkpointRowMatches(cp, scope, "pending")) {
125
125
  return false;
126
126
  }
127
127
  this.commit({ t: "expire", token }, true);
@@ -10,7 +10,10 @@ export declare class AppendLog {
10
10
  private fd;
11
11
  private disposed;
12
12
  private readonly path;
13
+ private boundary;
13
14
  constructor(path: string);
15
+ private sizeOrUnknown;
16
+ private repairTornTail;
14
17
  append(record: unknown, fsync: boolean): void;
15
18
  close(): void;
16
19
  closeForSwap(): void;
@@ -1,4 +1,4 @@
1
- import { closeSync, constants as FS, existsSync, linkSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, truncateSync, unlinkSync, writeFileSync, writeSync, fsyncSync, } from "node:fs";
1
+ import { closeSync, constants as FS, existsSync, fstatSync, linkSync, mkdirSync, openSync, readFileSync, readSync, realpathSync, renameSync, truncateSync, unlinkSync, writeFileSync, writeSync, fsyncSync, } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { basename, dirname, join, resolve as resolvePath } from "node:path";
4
4
  import { createHash, randomBytes } from "node:crypto";
@@ -148,30 +148,75 @@ export class AppendLog {
148
148
  fd;
149
149
  disposed = false;
150
150
  path;
151
+ boundary = -1;
151
152
  constructor(path) {
152
153
  this.path = path;
153
154
  ensureDir(dirnameOf(path));
154
155
  truncateTornTail(path);
155
- this.fd = openSync(path, "a", 0o600);
156
+ this.fd = openSync(path, "a+", 0o600);
157
+ this.boundary = this.sizeOrUnknown();
158
+ }
159
+ sizeOrUnknown() {
160
+ try {
161
+ return fstatSync(this.fd).size;
162
+ }
163
+ catch {
164
+ return -1;
165
+ }
166
+ }
167
+ repairTornTail() {
168
+ const size = this.sizeOrUnknown();
169
+ if (size < 0) {
170
+ this.boundary = -1;
171
+ return;
172
+ }
173
+ if (size === this.boundary)
174
+ return;
175
+ if (size === 0) {
176
+ this.boundary = 0;
177
+ return;
178
+ }
179
+ const tail = Buffer.allocUnsafe(1);
180
+ try {
181
+ if (readSync(this.fd, tail, 0, 1, size - 1) === 1 && tail[0] === 0x0a) {
182
+ this.boundary = size;
183
+ return;
184
+ }
185
+ }
186
+ catch {
187
+ return;
188
+ }
189
+ truncateTornTail(this.path);
190
+ this.boundary = this.sizeOrUnknown();
156
191
  }
157
192
  append(record, fsync) {
158
193
  if (this.fd < 0 && !this.disposed) {
159
194
  truncateTornTail(this.path);
160
- this.fd = openSync(this.path, "a", 0o600);
195
+ this.fd = openSync(this.path, "a+", 0o600);
196
+ this.boundary = this.sizeOrUnknown();
161
197
  }
162
198
  if (this.fd < 0) {
163
199
  const e = new Error("append: this log was closed (its session/box was deleted or replaced) — refusing to write through a released descriptor");
164
200
  e.code = "log_closed";
165
201
  throw e;
166
202
  }
203
+ this.repairTornTail();
167
204
  const buf = Buffer.from(`${JSON.stringify(record)}\n`, "utf8");
168
205
  let written = 0;
169
- while (written < buf.length) {
170
- const n = writeSync(this.fd, buf, written, buf.length - written);
171
- if (n <= 0)
172
- throw new Error(`append: write made no progress (${written}/${buf.length} bytes) — refusing to leave a torn record`);
173
- written += n;
206
+ try {
207
+ while (written < buf.length) {
208
+ const n = writeSync(this.fd, buf, written, buf.length - written);
209
+ if (n <= 0)
210
+ throw new Error(`append: write made no progress (${written}/${buf.length} bytes) — refusing to leave a torn record`);
211
+ written += n;
212
+ }
213
+ }
214
+ catch (err) {
215
+ this.boundary = -1;
216
+ throw err;
174
217
  }
218
+ if (this.boundary >= 0)
219
+ this.boundary += buf.length;
175
220
  if (fsync)
176
221
  fsyncSync(this.fd);
177
222
  }
@@ -38,6 +38,7 @@ export declare class FileStorageBackend {
38
38
  private readonly fileCheckpoints;
39
39
  private readonly fileMemory;
40
40
  private readonly ttl;
41
+ private readonly fileSessions;
41
42
  private readonly fileWorkflowJournal;
42
43
  constructor(opts?: FileStorageBackendOptions);
43
44
  dispose(): Promise<void>;