@sema-agent/core 5.25.0 → 5.27.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 (62) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/dist/agents/agent-definition.js +5 -0
  3. package/dist/agents/agent-transcript-tool.d.ts +5 -2
  4. package/dist/agents/agent-transcript-tool.js +2 -1
  5. package/dist/agents/send-message-tool.d.ts +4 -1
  6. package/dist/agents/send-message-tool.js +1 -0
  7. package/dist/agents/subagent.d.ts +6 -2
  8. package/dist/agents/subagent.js +5 -0
  9. package/dist/core/checkpoint-store.d.ts +7 -2
  10. package/dist/core/hooks.d.ts +39 -4
  11. package/dist/core/hooks.js +18 -14
  12. package/dist/core/memory-engine/dual-root.js +3 -1
  13. package/dist/core/memory-engine/engine.d.ts +53 -6
  14. package/dist/core/memory-engine/engine.js +43 -11
  15. package/dist/core/memory-engine/file-backend.d.ts +81 -0
  16. package/dist/core/memory-engine/file-backend.js +250 -24
  17. package/dist/core/memory-engine/index.d.ts +1 -1
  18. package/dist/core/memory-engine/index.js +1 -1
  19. package/dist/core/memory-engine/types.d.ts +8 -1
  20. package/dist/core/memory-vector.d.ts +6 -1
  21. package/dist/core/memory-vector.js +14 -4
  22. package/dist/core/memory.js +1 -6
  23. package/dist/core/permission-rule-consent.js +8 -1
  24. package/dist/core/permission-rule-model.d.ts +70 -5
  25. package/dist/core/permission-rule-model.js +58 -0
  26. package/dist/core/runner/compaction-call-options.d.ts +4 -4
  27. package/dist/core/runner/compaction-call-options.js +3 -4
  28. package/dist/core/runner/prepare-memory.d.ts +34 -15
  29. package/dist/core/runner/prepare-memory.js +99 -26
  30. package/dist/core/runner/prepare-task.d.ts +9 -3
  31. package/dist/core/runner/prepare-task.js +60 -15
  32. package/dist/core/runner/runtask.d.ts +8 -1
  33. package/dist/core/store-contracts/tool-result-store-contract.d.ts +6 -0
  34. package/dist/core/store-contracts/tool-result-store-contract.js +24 -0
  35. package/dist/core/task-registry-agent.d.ts +4 -3
  36. package/dist/core/task-registry-agent.js +3 -3
  37. package/dist/core/task-registry-monitor.js +6 -5
  38. package/dist/core/task-registry.d.ts +6 -3
  39. package/dist/core/tool-policy.d.ts +9 -2
  40. package/dist/core/tool-result-budget.d.ts +1 -1
  41. package/dist/core/tool-result-budget.js +3 -3
  42. package/dist/core/tool-result-store.d.ts +164 -9
  43. package/dist/core/tool-result-store.js +82 -23
  44. package/dist/core/types.d.ts +103 -7
  45. package/dist/core/untrusted-text.d.ts +6 -2
  46. package/dist/core/untrusted-text.js +1 -1
  47. package/dist/engine/loop/types.d.ts +10 -3
  48. package/dist/engine/session/import-validate.js +2 -1
  49. package/dist/index.d.ts +3 -3
  50. package/dist/index.js +3 -3
  51. package/dist/orchestration/run-workflow-tool.d.ts +5 -3
  52. package/dist/orchestration/workflow.d.ts +9 -6
  53. package/dist/orchestration/workflow.js +2 -0
  54. package/dist/prompts/default.d.ts +11 -0
  55. package/dist/prompts/default.js +3 -0
  56. package/dist/stores/file/checkpoint-store.d.ts +2 -1
  57. package/dist/stores/file/fs-atomic.d.ts +1 -1
  58. package/dist/stores/file/index.d.ts +1 -1
  59. package/dist/stores/file/tool-result-store.d.ts +45 -9
  60. package/dist/stores/file/tool-result-store.js +76 -9
  61. package/dist/tools/fs/fs-shared.js +5 -4
  62. package/package.json +1 -1
@@ -2,6 +2,34 @@ import { createHash } from "node:crypto";
2
2
  import { Type } from "typebox";
3
3
  import { defineTool, errorResult } from "./tools.js";
4
4
  import { TOOL_SEARCH_NAME } from "./runner/tool-disclosure.js";
5
+ export const TOOL_RESULT_REF_CONFLICT_CODE = "tool_result.ref_conflict";
6
+ export class ToolResultRefConflictError extends Error {
7
+ ref;
8
+ stored;
9
+ incoming;
10
+ code = TOOL_RESULT_REF_CONFLICT_CODE;
11
+ constructor(ref, stored, incoming) {
12
+ super(`tool-result store: ref ${JSON.stringify(ref)} already belongs to a different origin ` +
13
+ `(stored ${describeProvenance(stored)}, offered ${describeProvenance(incoming)}) — refusing to store under it`);
14
+ this.ref = ref;
15
+ this.stored = stored;
16
+ this.incoming = incoming;
17
+ this.name = "ToolResultRefConflictError";
18
+ }
19
+ }
20
+ function describeProvenance(p) {
21
+ return p.taskId === undefined ? `session=${p.sessionId}` : `session=${p.sessionId} task=${p.taskId}`;
22
+ }
23
+ export function assertToolResultProvenanceMatch(ref, stored, incoming) {
24
+ if (stored === undefined || incoming === undefined)
25
+ return;
26
+ if (stored.sessionId === incoming.sessionId && stored.taskId === incoming.taskId)
27
+ return;
28
+ throw new ToolResultRefConflictError(ref, stored, incoming);
29
+ }
30
+ export function normalizeToolResultProvenance(p) {
31
+ return p.taskId === undefined ? { sessionId: p.sessionId } : { sessionId: p.sessionId, taskId: p.taskId };
32
+ }
5
33
  export function assertSafeToolResultRef(ref) {
6
34
  const bad = ref === "" ||
7
35
  ref === "." ||
@@ -14,14 +42,23 @@ export function assertSafeToolResultRef(ref) {
14
42
  }
15
43
  const NATIVE_REF_CHARSET = /^[A-Za-z0-9_.-]+$/;
16
44
  const MAX_REF_SEGMENT_CHARS = 128;
17
- export function buildToolResultRef(sessionId, toolCallId) {
18
- return `tr_${refSegment(sessionId)}_${refSegment(toolCallId)}`;
45
+ const REF_SEGMENT_SEPARATOR = "~";
46
+ const FOLDED_SEGMENT_PREFIX = "fold.";
47
+ export function buildToolResultRef(sessionId, toolCallId, ...extraSegments) {
48
+ return [`tr_${refSegment(sessionId)}`, refSegment(toolCallId), ...extraSegments.map(refSegment)].join(REF_SEGMENT_SEPARATOR);
19
49
  }
20
50
  function refSegment(raw) {
21
- if (raw.length <= MAX_REF_SEGMENT_CHARS && NATIVE_REF_CHARSET.test(raw))
51
+ if (raw.length <= MAX_REF_SEGMENT_CHARS && NATIVE_REF_CHARSET.test(raw) && !raw.startsWith(FOLDED_SEGMENT_PREFIX))
22
52
  return raw;
23
53
  const base = raw.replace(/[^A-Za-z0-9_.-]/g, "-").slice(0, 32);
24
- return `${base || "x"}-${createHash("sha256").update(raw, "utf8").digest("hex")}`;
54
+ return `${FOLDED_SEGMENT_PREFIX}${base || "x"}-${digestOfString(raw)}`;
55
+ }
56
+ export const MAX_MINTED_TOOL_RESULT_REF_CHARS = 3 + 4 * MAX_REF_SEGMENT_CHARS + 3;
57
+ export function toolResultContentSegment(text) {
58
+ return `s${digestOfString(text).slice(0, 32)}`;
59
+ }
60
+ function digestOfString(s) {
61
+ return createHash("sha256").update(Buffer.from(s, "utf16le")).digest("hex");
25
62
  }
26
63
  export class InMemoryToolResultStore {
27
64
  opts;
@@ -35,27 +72,33 @@ export class InMemoryToolResultStore {
35
72
  throw new Error("InMemoryToolResultStore: maxTotalChars must be a number (got NaN) — omit it for an unbounded store");
36
73
  }
37
74
  }
38
- put(ref, content) {
75
+ put(ref, content, provenance) {
39
76
  assertSafeToolResultRef(ref);
40
- if (this.map.has(ref))
77
+ const existing = this.map.get(ref);
78
+ if (existing !== undefined) {
79
+ assertToolResultProvenanceMatch(ref, existing.provenance, provenance);
41
80
  return;
42
- this.map.set(ref, content);
81
+ }
82
+ this.map.set(ref, provenance === undefined ? { content } : { content, provenance: normalizeToolResultProvenance(provenance) });
43
83
  this.totalChars += content.length;
44
84
  const cap = this.opts?.maxTotalChars;
45
85
  if (cap !== undefined && Number.isFinite(cap)) {
46
- for (const [oldRef, oldContent] of this.map) {
86
+ for (const [oldRef, oldEntry] of this.map) {
47
87
  if (this.totalChars <= cap || oldRef === ref)
48
88
  break;
49
89
  this.map.delete(oldRef);
50
- this.totalChars -= oldContent.length;
90
+ this.totalChars -= oldEntry.content.length;
51
91
  }
52
92
  }
53
93
  }
94
+ ownerOf(ref) {
95
+ return this.map.get(ref)?.provenance;
96
+ }
54
97
  isEmpty() {
55
98
  return this.map.size === 0;
56
99
  }
57
100
  get(ref, opts) {
58
- const full = this.map.get(ref);
101
+ const full = this.map.get(ref)?.content;
59
102
  if (full === undefined)
60
103
  return undefined;
61
104
  const offset = Math.min(full.length, Math.max(0, intOr(opts?.offset, 0)));
@@ -77,21 +120,32 @@ export class ScopedToolResultStore {
77
120
  this.volatileBacking = inner instanceof InMemoryToolResultStore;
78
121
  }
79
122
  key(ref) {
80
- const scope = encodeURIComponent(this.scope);
123
+ const scope = encodeScopeSegment(this.scope);
81
124
  return `${scope.length}:${scope}:${ref}`;
82
125
  }
83
- put(ref, content) {
126
+ put(ref, content, provenance) {
84
127
  assertSafeToolResultRef(ref);
85
128
  this.localPuts++;
86
- return this.inner.put(this.key(ref), content);
129
+ return this.inner.put(this.key(ref), content, provenance);
87
130
  }
88
131
  get(ref, opts) {
89
132
  return this.inner.get(this.key(ref), opts);
90
133
  }
134
+ ownerOf(ref) {
135
+ return this.inner.ownerOf?.(this.key(ref));
136
+ }
91
137
  isEmpty() {
92
138
  return this.localPuts === 0;
93
139
  }
94
140
  }
141
+ function encodeScopeSegment(scope) {
142
+ let out = "";
143
+ for (let i = 0; i < scope.length; i++) {
144
+ const ch = scope[i];
145
+ out += /[A-Za-z0-9]/.test(ch) ? ch : `%${ch.charCodeAt(0).toString(16).padStart(4, "0")}`;
146
+ }
147
+ return out;
148
+ }
95
149
  export function isVolatileOffloadStore(store) {
96
150
  if (store instanceof ScopedToolResultStore)
97
151
  return store.volatileBacking;
@@ -99,15 +153,19 @@ export function isVolatileOffloadStore(store) {
99
153
  }
100
154
  export const OFFLOAD_TOOL_NAME = "ReadToolResult";
101
155
  export function createOffloadPersist(store, sessionId) {
156
+ const provenance = toolResultProvenanceOf(sessionId);
102
157
  return (toolCallId, fullText) => {
103
- const ref = buildToolResultRef(sessionId, toolCallId);
104
- void Promise.resolve(store.put(ref, fullText)).catch((err) => {
158
+ const ref = buildToolResultRef(sessionId, toolCallId, toolResultContentSegment(fullText));
159
+ void Promise.resolve(store.put(ref, fullText, provenance)).catch((err) => {
105
160
  const cause = err instanceof Error ? err.message : String(err);
106
- void Promise.resolve(store.put(ref, `[offload LOST at write time: ${cause} — the inline preview is all that survived]`)).catch(() => undefined);
161
+ void Promise.resolve(store.put(ref, `[offload LOST at write time: ${cause} — the inline preview is all that survived]`, provenance)).catch(() => undefined);
107
162
  });
108
163
  return ref;
109
164
  };
110
165
  }
166
+ export function toolResultProvenanceOf(sessionId, taskId) {
167
+ return taskId === undefined ? { sessionId } : { sessionId, taskId };
168
+ }
111
169
  export function offloadPagebackHint(ref, form, reachableTools) {
112
170
  const activate = reachableTools !== undefined && !reachableTools.has(OFFLOAD_TOOL_NAME)
113
171
  ? `${OFFLOAD_TOOL_NAME} is not active yet — call ${TOOL_SEARCH_NAME} with {"query":"select:${OFFLOAD_TOOL_NAME}"} to activate it, then `
@@ -164,9 +222,10 @@ export function buildPreview(full, ref, sizes, reachableTools) {
164
222
  export function withToolResultOffload(tool, store, thresholdChars, sessionId, reachableTools) {
165
223
  if (!(Number.isFinite(thresholdChars) && thresholdChars > 0))
166
224
  return tool;
225
+ const provenance = toolResultProvenanceOf(sessionId);
167
226
  const wrappedExecute = async (toolCallId, params, signal, onUpdate) => {
168
227
  const res = await tool.execute(toolCallId, params, signal, onUpdate);
169
- const offloadedDetails = res.details === undefined ? undefined : await offloadOversizedDetailStrings(res.details, store, thresholdChars, sessionId, toolCallId);
228
+ const offloadedDetails = res.details === undefined ? undefined : await offloadOversizedDetailStrings(res.details, store, thresholdChars, sessionId, toolCallId, provenance);
170
229
  const withDetails = (r) => offloadedDetails === undefined || offloadedDetails.value === res.details ? r : { ...r, details: offloadedDetails.value };
171
230
  if (totalTextChars(res.content) <= thresholdChars)
172
231
  return withDetails(res);
@@ -176,20 +235,20 @@ export function withToolResultOffload(tool, store, thresholdChars, sessionId, re
176
235
  .join("\n");
177
236
  if (full.length <= PREVIEW_HEAD_CHARS + PREVIEW_TAIL_CHARS)
178
237
  return withDetails(res);
179
- const ref = buildToolResultRef(sessionId, toolCallId);
180
- await store.put(ref, full);
238
+ const ref = buildToolResultRef(sessionId, toolCallId, toolResultContentSegment(full));
239
+ await store.put(ref, full, provenance);
181
240
  const images = res.content.filter((b) => b.type !== "text");
182
241
  return withDetails({ ...res, content: [{ type: "text", text: buildPreview(full, ref, undefined, reachableTools?.()) }, ...images] });
183
242
  };
184
243
  return { ...tool, execute: wrappedExecute };
185
244
  }
186
245
  const OFFLOADED_DETAIL_HEAD_CHARS = 2_000;
187
- const OFFLOADED_DETAIL_NOTICE_ALLOWANCE_CHARS = 400;
246
+ const OFFLOADED_DETAIL_NOTICE_ALLOWANCE_CHARS = MAX_MINTED_TOOL_RESULT_REF_CHARS + 200;
188
247
  export const OFFLOADED_DETAIL_NOTICE_PREFIX = "…[offloaded — ";
189
248
  export function isOffloadedDetailReplacement(s) {
190
249
  return s.includes(`\n${OFFLOADED_DETAIL_NOTICE_PREFIX}`);
191
250
  }
192
- async function offloadOversizedDetailStrings(details, store, thresholdChars, sessionId, toolCallId) {
251
+ async function offloadOversizedDetailStrings(details, store, thresholdChars, sessionId, toolCallId, provenance) {
193
252
  const puts = [];
194
253
  const onStack = new Set();
195
254
  const isPlainObject = (v) => {
@@ -199,8 +258,8 @@ async function offloadOversizedDetailStrings(details, store, thresholdChars, ses
199
258
  return p === Object.prototype || p === null;
200
259
  };
201
260
  const replace = (full, path) => {
202
- const detailRef = buildToolResultRef(sessionId, toolCallId + " " + path);
203
- puts.push(Promise.resolve(store.put(detailRef, full)));
261
+ const detailRef = buildToolResultRef(sessionId, toolCallId, path, toolResultContentSegment(full));
262
+ puts.push(Promise.resolve(store.put(detailRef, full, provenance)));
204
263
  return (`${full.slice(0, OFFLOADED_DETAIL_HEAD_CHARS)}\n` +
205
264
  `${OFFLOADED_DETAIL_NOTICE_PREFIX}${full.length} chars total; ref "${detailRef}"; the remainder is retained in this run's tool-result store and reads back through the deployment's tool-results face]`);
206
265
  };
@@ -320,6 +320,12 @@ export interface ToolSpec<TParams extends TSchema = TSchema> {
320
320
  * text under `"static"`, where the placeholder is never swapped. Use for large/MCP tool sets where
321
321
  * inlining hundreds of schemas blows up turn-1 tokens and risks prefix-cache breakage. Default: not
322
322
  * deferred (full schema inlined). See {@link RunnerDeps.deferMode}.
323
+ *
324
+ * SCOPE: this flag reaches the classifier through CALLER specs (`TaskSpec.tools`) only — a
325
+ * built-in/injected tool's spec never enters that list, so setting `defer` on a spec that shadows a
326
+ * built-in name does nothing. To defer an already-mounted tool (built-ins included), list its wire
327
+ * name in {@link TaskSpec.deferTools}. Also inert when the tool ends up excluded (exclusion wins)
328
+ * or pinned ({@link alwaysLoad} / {@link TaskSpec.alwaysLoadTools}).
323
329
  */
324
330
  defer?: boolean;
325
331
  /**
@@ -705,6 +711,17 @@ export interface ToolExecuteContext {
705
711
  * is a DEPLOYMENT property, not a per-task one — a child in the same sandbox needs the same facts
706
712
  * (its env block renders the scratchpad section; its root fence admits the scratchpad dir). */
707
713
  envFacts?: TaskSpec["envFacts"];
714
+ /**
715
+ * The parent task's DECLARED {@link TaskSpec.memoryPersistenceCapable} — present ONLY when the spec
716
+ * set it (an inferred run's ctx gains no key), Runner-filled on the same trusted seat as
717
+ * {@link principal}: never a model/tool argument. A delegation tool (`createSubagentTool`) forwards
718
+ * it into every child spec it builds so the deployment's persistence statement survives delegation:
719
+ * `false` is a floor a chosen agent definition cannot loosen (the read-only-memory disclosure must
720
+ * hold tree-wide when the deployment says nothing durable is reachable), `true` is a default a
721
+ * definition may narrow back to `false`. Absent ⇒ nothing is forwarded and each child's own roster
722
+ * inference decides, exactly as before the seat existed.
723
+ */
724
+ memoryPersistenceCapable?: boolean;
708
725
  /**
709
726
  * [893]④a — the parent task's per-model auth hook ({@link TaskSpec.getApiKeyAndHeaders}), inherited
710
727
  * verbatim down the delegation tree like `principal`/`clientContext` (Runner-filled, read-only, NEVER
@@ -838,8 +855,11 @@ export interface ToolExecuteContext {
838
855
  * `RunInternals.onForwardEvent`) to receive a SUBAGENT's live `task_progress` ticks that otherwise stay in the
839
856
  * child's ISOLATED stream. A delegation tool threads it to the child so nested progress bubbles to one sink. This
840
857
  * is a DISPLAY channel ONLY — the child stream is NEVER merged into the parent's MODEL context, and nothing
841
- * security-relevant consumes a forwarded event. Present ONLY when the deployment opted in; the Runner filters it
842
- * to `task_progress` (a tool cannot push other event types). ⚠️ Forwarded ticks are UNTRUSTED display hints any
858
+ * security-relevant consumes a forwarded event. Present ONLY when the deployment opted in. The tool-ctx wrapper
859
+ * passes `task_progress` unconditionally and when the deployment sets `forwardSubagentEvents: true`the
860
+ * transcript classes too (text_delta/reasoning_delta/tool_start/tool_end); other event types never cross it.
861
+ * The delegation lane's OWN tap is trusted and forwards the child's FULL event stream (bg frames tagged
862
+ * with bgAgentId). ⚠️ Forwarded ticks are UNTRUSTED display hints — any
843
863
  * tool holding this ctx could forge one, so a consumer validates `parentTaskId` against its known runs.
844
864
  */
845
865
  forwardEvent?: (event: TaskEvent) => void;
@@ -1058,6 +1078,19 @@ export interface AgentDefinition {
1058
1078
  maxTurns?: number;
1059
1079
  /** Long-term memory scope for this agent (same shape as {@link TaskSpec.memory}). */
1060
1080
  memory?: TaskSpec["memory"];
1081
+ /**
1082
+ * This agent's own {@link TaskSpec.memoryPersistenceCapable} declaration — the same three-state
1083
+ * statement, made per definition: an agent whose closure tools persist memory declares `true`
1084
+ * (suppressing the read-only-memory disclosure no roster inference can clear), one whose roster
1085
+ * looks write-capable but reaches no durable store declares `false` (forcing the disclosure).
1086
+ * Arbitration with the spawning run's declaration: a parent's explicit `false` is a FLOOR and wins
1087
+ * over a definition `true` (the disclosure is about the deployment's storage, which choosing this
1088
+ * agent does not change); otherwise a declared definition value wins over the parent's; absent
1089
+ * both, the child spec carries no value and the child's own roster inference decides.
1090
+ * On the workflow lane (`agent(…, {agentType})`) the definition value fills an ABSENT governed-spec
1091
+ * value only — a base/spec declaration of either polarity wins (the lane's spec-pinned-fields rule).
1092
+ */
1093
+ memoryPersistenceCapable?: boolean;
1061
1094
  /**
1062
1095
  * Observer agents (CC 2.1.206 parity — 逐字锚 docs/CC206-OBSERVER-ANCHORS-2026-07-11.md, schema
1063
1096
  * 面 C @28385708): "Agent type auto-spawned as a background observer whenever this agent runs.
@@ -1841,7 +1874,9 @@ export interface TaskSpec {
1841
1874
  * write-capable fs tools (CC's same gate); zero cost/behavior change otherwise. `false` opts out.
1842
1875
  */
1843
1876
  lspDiagnostics?: boolean;
1844
- /** In-process hooks (PreToolUse/PostToolUse/UserPromptSubmit) for this task. Overrides `RunnerDeps.hooks`. */
1877
+ /** In-process hooks for this task the full `Hooks` lifecycle seam (tool pre/post/failure/batch
1878
+ * interception, prompt gating, stop pushback, compaction taps, permission-denied observation; each
1879
+ * member's contract is on the interface). Overrides `RunnerDeps.hooks`. */
1845
1880
  hooks?: import("./hooks.js").Hooks;
1846
1881
  /**
1847
1882
  * design/45 **durable suspend-on-approval** (F4). When set AND a `CheckpointStore` is wired
@@ -2016,6 +2051,39 @@ export interface TaskSpec {
2016
2051
  * modify the project. No effect unless an `executionEnv` is injected. Default false.
2017
2052
  */
2018
2053
  handsReadOnly?: boolean;
2054
+ /**
2055
+ * Whether this run can persist a user's "remember X" somewhere durable — the deployment's own
2056
+ * statement, overriding the runner's inference. The runner mounts a read-only-memory disclosure
2057
+ * (the model must decline to "remember" instead of receipting a save that never happens) when the
2058
+ * session provably cannot persist; provability is inferred from KNOWN store paths only (a mounted
2059
+ * file-write tool or a write-capable shell). A caller tool that persists through its own closure
2060
+ * (a custom memory writer) is invisible to that inference and would be contradicted by the
2061
+ * disclosure — set `true` to declare the channel and suppress it. Set `false` to force the
2062
+ * disclosure even when write-capable tools mount (e.g. they cannot reach any durable store);
2063
+ * a forced `false` also replaces the `# Memory` write instruction itself and drops the
2064
+ * preference-writing discipline and index seed that serve it.
2065
+ * Default: inferred. The inference also treats a REMOTE execution env's hand band as unable to
2066
+ * reach the host-side memory root (sandbox filesystem); a deployment whose remote env shares a
2067
+ * mount with the memory root declares `true` — on such a remote, `true` also restores the
2068
+ * `# Memory` write instruction when a Write tool mounts (a declared-capable session must be told
2069
+ * the path, not left silent). If your persistence channel is NOT the file face, do not mount a
2070
+ * tool named `Write` alongside the declaration, or keep the default and teach your own channel.
2071
+ * Delegation: a DECLARED value crosses the delegation boundary (sync / background / fork spawns and
2072
+ * the retained-resume snapshot alike). An explicit `false` is a FLOOR — a chosen
2073
+ * {@link AgentDefinition.memoryPersistenceCapable} `true` cannot loosen it (the disclosure is
2074
+ * about the deployment's storage, which no agent selection changes); an explicit `true` is a
2075
+ * DEFAULT the chosen definition may narrow back to `false`; absent = absent downstream too — each
2076
+ * child runs its own inference over its own roster. A retained child woken by a DIFFERENT run
2077
+ * additionally folds the waker's declared `false` on top of the spawn snapshot (tighten-only,
2078
+ * like the other resume clamps).
2079
+ * KNOWN GAPS (registered, whole-clamp-family shapes — `handsReadOnly`/`interactiveTools` share
2080
+ * them): a TIER-3 durable revival rebuilds the child from the REVIVER's context (the durable row
2081
+ * records lookup keys, never a serialized spec), so the spawn-time declaration does not survive
2082
+ * that lane — the reviver's own declaration governs; and the workflow HOST lane does not forward
2083
+ * the host TaskSpec's declaration into `agent()` children (an {@link AgentDefinition} on the
2084
+ * workflow agent type does carry).
2085
+ */
2086
+ memoryPersistenceCapable?: boolean;
2019
2087
  /**
2020
2088
  * design/119 (CC --add-dir parity): extra directories the FILE tools may access in addition to the
2021
2089
  * containment root — each is canonicalized into the containment allowlist and listed in the
@@ -2090,6 +2158,11 @@ export interface TaskSpec {
2090
2158
  * (writes, egress, pipes, unknown commands) tightens to ask. An egress command (e.g. `curl`) suspends under
2091
2159
  * the IRREVERSIBLE axis (the shell mark is irreversibility, not egress) — the kind is non-budgetable either
2092
2160
  * way, so the budget resolver still never auto-approves it; the axis label is informational.
2161
+ * NOT unconditional: a POSITIVE per-tool mark on the shell tool keeps its seat — an explicit
2162
+ * `irreversibility:"always"` is never downgraded (the tier combine is tighten-only) and an explicit
2163
+ * `"maybe"` keeps its own probe (a probe-less explicit `"maybe"` stays fail-closed ask; the doctrine's
2164
+ * generic probe installs only on doctrine-owned seats). An explicit `"never"` is NOT a mark — the
2165
+ * doctrine governs that seat as if unmarked.
2093
2166
  * No effect under `handsReadOnly` (that mounts `bash_readonly`, already allowlisted) or with no execution env.
2094
2167
  * **Re-supply on resume:** like `toolPolicy`/`tools`/`durableApproval`, `shellGate` is part of the resume
2095
2168
  * task config — a resume that omits it leaves the resumed run's SUBSEQUENT `bash` calls ungated (the approved
@@ -4661,10 +4734,15 @@ export interface RunnerDeps {
4661
4734
  * task's own `lspManager` overrides this. Unset ⇒ no `lsp` tool. */
4662
4735
  lspManager?: import("./lsp.js").LspServerManager;
4663
4736
  /**
4664
- * Default in-process hooks for all tasks (design/37): PreToolUse (rewrite/restrict args + inject
4665
- * context), PostToolUse (rewrite output + inject context), UserPromptSubmit (block/inject before the
4666
- * objective becomes a message). A task's own `hooks` overrides this. A PreToolUse hook's `allow`
4667
- * never bypasses `toolPolicy` the policy is always the final say.
4737
+ * Default in-process hooks for all tasks (design/37) the FULL lifecycle seam of the `Hooks`
4738
+ * interface, not just the tool-call trio: `preToolUse` (rewrite/restrict args + inject context),
4739
+ * `postToolUse` (rewrite output + inject context), `userPromptSubmit` (block/inject before the
4740
+ * objective becomes a message), plus `stop` (push back when the run would otherwise end and continue
4741
+ * it), `postToolUseFailure` / `postToolBatch` / `permissionDenied` (failure, batch-boundary and
4742
+ * deny observers), `preCompact` / `postCompact` (compaction gate + observer), `stopFailure`
4743
+ * (API-error terminal observer) and the `preToolUseObservational` declaration flag — each member's
4744
+ * contract is documented on the `Hooks` interface itself. A task's own `hooks` overrides this. A
4745
+ * PreToolUse hook's `allow` never bypasses `toolPolicy` — the policy is always the final say.
4668
4746
  */
4669
4747
  hooks?: import("./hooks.js").Hooks;
4670
4748
  /**
@@ -4682,12 +4760,30 @@ export interface RunnerDeps {
4682
4760
  * - `"prompt-constitution"` — a `stableSystem` provider returned an ALREADY-assembled prompt
4683
4761
  * (constitution anchor found); core passed it through un-doubled. Upgrade the provider to return
4684
4762
  * only the role base (or set `replaceAll: true` to own the whole base).
4763
+ * - `"degraded"` — the task kept running with a capability quietly reduced: a question auto-answered
4764
+ * with no human present, a skipped env git snapshot, a lost checkpoint-put confirmation, an
4765
+ * unroutable descendant notification, and similar best-effort arms. The run proceeds; the reduced
4766
+ * arm is what is being disclosed (`classification` names it).
4767
+ * - `"config"` — a deployment-wiring problem detected while preparing or tearing down a task: tool
4768
+ * mount conflicts, refused/ignored knob values, gate-off notices, store/env teardown legs. The
4769
+ * broadest class by call-site count — most misconfigurations announce here rather than failing
4770
+ * the task.
4685
4771
  * - `"memory"` — a post-task memory-consolidation pass failed or skipped a malformed reconcile
4686
4772
  * decision (design/41). Best-effort: the notes the model saved are kept; the task still succeeds.
4687
4773
  * - `"mcp"` — an MCP server failed to connect / list its tools and was skipped (fail-open, design/29);
4688
4774
  * the task runs with the remaining servers' tools. One call per failed server.
4689
4775
  * - `"a2a"` — the A2A sibling of `"mcp"`: a declared peer's agent card could not be fetched/read, or it
4690
4776
  * advertises no transport this client speaks, so the peer was skipped. One call per skipped peer.
4777
+ * - `"interrupt-reconcile"` — repairing an interrupted session's transcript on re-entry (synthesizing
4778
+ * tool results for orphaned calls, flushing held session writes) failed; the run proceeds on the
4779
+ * unrepaired transcript.
4780
+ * - `"suggestions"` — the best-effort follow-up-suggestions pass failed or timed out; the result
4781
+ * simply carries no suggestions.
4782
+ * - `"rewind"` — the per-turn working-tree snapshot backing file rewind failed or was skipped (e.g. a
4783
+ * too-large root inside its cooldown window); turns without a snapshot cannot be rewound to.
4784
+ * - `"hook"` — a deployment hook misbehaved: a callback threw, or returned a verdict it was not
4785
+ * allowed to (e.g. a declared-observational hook). The engine applies the hook's own documented
4786
+ * fallback (swallow, or fail-closed deny, per its contract) and reports the fact here.
4691
4787
  */
4692
4788
  onError?: (err: unknown, context: {
4693
4789
  phase: "compaction" | "prompt-cache" | "prompt-constitution" | "degraded" | "config" | "memory" | "mcp" | "a2a" | "interrupt-reconcile" | "suggestions" | "rewind" | "hook";
@@ -170,8 +170,12 @@ export declare function defuseControlChars(text: string): string;
170
170
  * Make untrusted text safe to interpolate INLINE on a trusted prompt line (not inside a {@link delimitUntrusted}
171
171
  * fence) — e.g. design/80 D-F echoes a chosen option label into the "The user answered:" block. Folds EVERY
172
172
  * line/space separator to a single space so the text cannot forge a new line — `\s` (covers CR/LF/TAB/VT/FF and
173
- * U+2028/U+2029 + the Unicode spaces) PLUS U+0085 NEL, which `\s` does NOT match caps the length, then applies
174
- * the same tag-neutralization (`</system-reminder>`) + fence-sentinel (`<<<`/`>>>`) defusing the fenced body gets.
173
+ * U+2028/U+2029 + the Unicode spaces) PLUS the whole C0 + DEL + C1 block (U+0000–U+001F, U+007F–U+009F), which
174
+ * `\s` does NOT fully match: the C1 half carries the 8-bit CSI/OSC/ST forms (U+009B/U+009D/U+009C) a
175
+ * C1-honoring terminal treats like their ESC-prefixed spellings, so leaving them through would let a
176
+ * 'sanitized' value repaint the trusted line it is interpolated onto (adversarial round finding) — caps the
177
+ * length, then applies the same tag-neutralization (`</system-reminder>`) + fence-sentinel (`<<<`/`>>>`)
178
+ * defusing the fenced body gets.
175
179
  * Defense-in-depth, NOT a guarantee (same posture as the rest of this module).
176
180
  */
177
181
  export declare function inlineUntrusted(text: string, maxLen?: number): string;
@@ -99,7 +99,7 @@ export function defuseControlChars(text) {
99
99
  }
100
100
  const LABEL_MAX = 160;
101
101
  export function inlineUntrusted(text, maxLen = LABEL_MAX) {
102
- const oneLine = text.replace(/[\s\u0000-\u001f\u007f\u0085]+/g, " ").trim();
102
+ const oneLine = text.replace(/[\s\u0000-\u001f\u007f-\u009f]+/g, " ").trim();
103
103
  const cps = [...oneLine];
104
104
  const capped = cps.length > maxLen ? cps.slice(0, maxLen).join("") + "…" : oneLine;
105
105
  const out = defuseFenceMarkers(sanitizeUntrustedText(capped));
@@ -23,7 +23,11 @@ export type ToolExecutionMode = "sequential" | "parallel";
23
23
  * Controls how many queued user messages are injected when the agent loop reaches a queue drain point.
24
24
  *
25
25
  * - "all": drain and inject every queued message at that point.
26
- * - "one-at-a-time": drain and inject only the oldest queued message, leaving the rest queued for later drain points.
26
+ * - "one-at-a-time": drain and inject only the oldest queued message, leaving the rest queued for later
27
+ * drain points. Exception: when the oldest frame is an engine note (a task-notification sidecar
28
+ * payload), every consecutive engine-note frame behind it drains with it as ONE batch, stopping at
29
+ * the first non-note frame (e.g. a real user steer) — N buffered completion notices are one
30
+ * boundary's worth of frames, not N sequential turns.
27
31
  */
28
32
  export type QueueMode = "all" | "one-at-a-time";
29
33
  /** A single tool call content block emitted by an assistant message. */
@@ -394,8 +398,11 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
394
398
  }
395
399
  /**
396
400
  * Thinking/reasoning level for models that support it.
397
- * Note: "xhigh" is only supported by selected model families. Use model thinking-level metadata
398
- * from openclaw/plugin-sdk/llm to detect support for a concrete model.
401
+ * Note: "xhigh" is only supported by selected model families. Per-model/per-endpoint support is
402
+ * declared in this package's model metadata (`src/engine/llm/types.ts`): the compat blocks'
403
+ * accepted-tier sets (`reasoningEffortLevels` on the OpenAI-family compats, `effortLevels` on the
404
+ * Anthropic Messages compat — a requested tier above the declared set clamps down rather than
405
+ * erroring), and `Model.thinkingLevelMap`, where `null` marks a level as unsupported.
399
406
  */
400
407
  export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
401
408
  export interface BashExecutionMessage {
@@ -3,12 +3,13 @@ import { leafIdAfterEntry } from "./storage-base.js";
3
3
  import { parseSessionTimestampMs } from "./timestamps.js";
4
4
  import { flattenableUserText, normalizeEngineSegments } from "../../core/untrusted-text.js";
5
5
  import { normalizePromptEpoch } from "../../prompt-assembly/epoch.js";
6
+ import { MAX_MINTED_TOOL_RESULT_REF_CHARS } from "../../core/tool-result-store.js";
6
7
  import { PERSISTED_OUTPUT_REFS_MAX_ENTRIES, SKILL_RETENTION_PER_SKILL_MAX_CHARS, SKILL_RETENTION_TOTAL_MAX_CHARS, } from "../compaction/utils.js";
7
8
  const PATH_LIST_MAX_CHARS = 4096;
8
9
  const PATH_LIST_MAX_ENTRIES = 1000;
9
10
  const INVOKED_SKILLS_MAX_ENTRIES = 1000;
10
11
  const INVOKED_SKILL_NAME_MAX_CHARS = 1024;
11
- const PERSISTED_REF_MAX_CHARS = 256;
12
+ const PERSISTED_REF_MAX_CHARS = MAX_MINTED_TOOL_RESULT_REF_CHARS;
12
13
  const ACTIVE_TOOL_NAME_MAX_CHARS = 1024;
13
14
  const ACTIVE_TOOLS_MAX_ENTRIES = 1000;
14
15
  export class StreamingImportValidator {
package/dist/index.d.ts CHANGED
@@ -87,7 +87,7 @@ export type { InvariantKind, FunctionContract, Invariant, InvariantViolation, Ch
87
87
  export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
88
88
  export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, type BashReadonlyRootBoundary, type CompoundReadonlyVerdict, } from "./tools/fs/index.js";
89
89
  export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
90
- export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, type ToolResultStore, type ToolResultSlice, } from "./core/tool-result-store.js";
90
+ export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, type ToolResultProvenance, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, type ToolResultStore, type ToolResultSlice, } from "./core/tool-result-store.js";
91
91
  export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, type ActorAssertion, type PendingSteerEntry, type PendingSteerInput, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, type RiskDescriptor, type CheckpointStore, type CheckpointSummary, type Checkpoint, type CheckpointToken, type CheckpointGate, type CheckpointState, type SerializedCheckpointState, type CheckpointFaultMode, type PendingAction, type ResumeOutcome, type ResolvedOutcome, type ReopenReason, type ResolveExpectation, type SafetyAxis, type RealApprovalGateBit, type ResourceLedger, type ResourceLimitReason, type PlatformLimitReason, } from "./core/checkpoint-store.js";
92
92
  export { InMemoryUsageWindowStore, GLOBAL_USAGE_KEY, EMPTY_USAGE_WINDOW_RECORD, chargeUsageRecord, readUsageRecord, usageRetryAfterMs, resolveUsageWindows, type UsageWindow, type UsageWindowStore, type UsageWindowReading, type UsageWindowRecord, type UsageSlot, type UsageBucketRow, } from "./core/usage-window-store.js";
93
93
  export { FileUsageWindowStore } from "./stores/file/usage-window-store.js";
@@ -160,7 +160,7 @@ export { AdoptionError, assertAdoptionBootGate, readRootAdoptionFile, writeRootA
160
160
  export { adoptLocalDataRoot, ackAdoptionConfig, witnessAdoptionConfig, listAdoptionQuarantine, readAdoptionStatus, type AdoptionStatus, type AdoptLocalDataRootOptions, type AdoptLocalDataRootResult, type AdoptionCarriageLeg, type AdoptionCarriageLegContext, type AdoptionConfigWitnessReceipt, } from "./stores/file/adoption/adopt.js";
161
161
  export { formatHookFeedback, runToolGate, createHookEnvCapabilities, createPreToolUseConstraintPolicy, type Hooks, type HookToolContext, type HookEnvCapabilities, type HookToolOutput, type PreToolUseResult, type PostToolUseResult, type UserPromptSubmitResult, type HookToolFailure, type PostToolUseFailureResult, type PostToolBatchCall, type PostToolBatchResult, type PreCompactContext, type PreCompactResult, type PostCompactContext, type StopFailureContext, type PermissionDeniedPayload, type PermissionDeniedSource, } from "./core/hooks.js";
162
162
  export { InMemoryMemoryStore, composeMemoryBlock, composeLayeredMemoryBlock, normalizeMemorySpec, type NormalizedMemorySpec, type MemorySpecInput, supportsConsolidation, supportsPeriodicConsolidation, firstSentence, expandLexicalTerms, lexicalSearchMatch, type Embedder, classifyPromotable, enforcePromotableWriteGate, guardedMemoryStore, MemoryGateError, detectSecret, enforceSecretWriteGate, enforceStructuredNoteSecretGate, type UtilityGate, type MemoryStore, type MemoryVectorMode, type ScoredMemory, type MemoryNoteHeader, type MemoryNoteRecord, type MemoryNoteType, type StructuredNoteInput, } from "./core/memory.js";
163
- export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, type MemoryBackendContractHooks, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, readV2HeaderHints, type V2HeaderHints, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, type ControlPlaneRebuildReceipt, type StrictControlPlaneLedger, type ChallengeAssignment, type ChallengeEvent, type ChallengedHistoryRow, type LineagePendingTxn, type LineagePromotion, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, type MemorySearchDetails, type MemorySearchHit, type MemoryGetDetails, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, type MemoryAnnouncement, type ScanFinding, type MemoryEngineOptions, type MemoryInjection, type MemoryBackend, type MemoryEntry, type MemoryEntryFrontmatter, type MemoryEntryHeader, type ScoredMemoryEntry, type NotePatch, type PatchReport, type MaterializedFile, type MemorySessionHandle, type HarvestReport, type HarvestRejection, type HarvestRejectionCode, type ParsedEntryFile, type ScannedEntryFile, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, type ParsedScopeKey, migrateScope, type MigrateScopeReport, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, type RemoteMemoryFile, type RemoteMemoryBaseline, type RemoteMaterialization, type RemoteHarvestResult, type InboundEntryFinding, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, type MemorySyncCursor, type MemorySyncConflict, type MemorySyncPlan, type RecallSource, syncMemoryScope, type MemorySyncTransport, type MemorySyncRequestBody, type MemorySyncResponseBody, type MemorySyncClientConflict, type SyncMemoryScopeOptions, type MemorySyncClientResult, } from "./core/memory-engine/index.js";
163
+ export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, type MemoryBackendContractHooks, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, readV2HeaderHints, type V2HeaderHints, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, type ControlPlaneRebuildReceipt, type StrictControlPlaneLedger, type ChallengeAssignment, type ChallengeEvent, type ChallengedHistoryRow, type LineagePendingTxn, type LineagePromotion, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, type MemorySearchDetails, type MemorySearchHit, type MemoryGetDetails, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, type MemoryAnnouncement, type ScanFinding, type MemoryEngineOptions, type MemoryInjection, type MemoryBackend, type MemoryEntry, type MemoryEntryFrontmatter, type MemoryEntryHeader, type ScoredMemoryEntry, type NotePatch, type PatchReport, type MaterializedFile, type MemorySessionHandle, type HarvestReport, type HarvestRejection, type HarvestRejectionCode, type ParsedEntryFile, type ScannedEntryFile, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, type ParsedScopeKey, migrateScope, type MigrateScopeReport, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, type RemoteMemoryFile, type RemoteMemoryBaseline, type RemoteMaterialization, type RemoteHarvestResult, type InboundEntryFinding, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, type MemorySyncCursor, type MemorySyncConflict, type MemorySyncPlan, type RecallSource, syncMemoryScope, type MemorySyncTransport, type MemorySyncRequestBody, type MemorySyncResponseBody, type MemorySyncClientConflict, type SyncMemoryScopeOptions, type MemorySyncClientResult, } from "./core/memory-engine/index.js";
164
164
  export { SHARED_MEMORY_READ_CAP_BYTES, SHARED_MEMORY_LIST_PAGE_SIZE, SharedMemoryStoreError, type SharedMemoryStoreProvider, type SharedMemoryStoreReader, type SharedMemoryPagedList, type SharedMemoryStoreInfo, type SharedMemoryDocumentEntry, type SharedMemorySnapshot, type SharedMemoryRequestContext, type MemoryListDetails, type MemoryReadDetails, } from "./core/shared-memory/types.js";
165
165
  export { sharedMemoryStoreContract, type SharedMemoryFixture, type SharedMemoryStoreContractHooks, } from "./core/shared-memory/contract.js";
166
166
  export { termSet, jaccardDistance, cosineDistance } from "./core/memory-vector.js";
@@ -168,7 +168,7 @@ export { encodeSurfacedKey, buildManifestText, validateSelectedIds, composeSelec
168
168
  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";
169
169
  export { consolidateScope, advanceCursorAfterInline, type ConsolidateScopeDeps, type ConsolidateScopeOptions, } from "./core/consolidate-scope.js";
170
170
  export { DEFAULT_COMPACTION_INSTRUCTIONS } from "./core/auto-compaction.js";
171
- 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";
171
+ export { DEFAULT_SYSTEM_PROMPT, CODE_AGENT_PROMPT, SUBAGENT_PROMPT, CODE_SYSTEM_PROMPT, MEMORY_GUIDANCE, MEMORY_SAFETY, MEMORY_HYGIENE, NO_PERSISTENT_MEMORY_NOTICE, 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";
172
172
  export { SUPERVISOR_PROMPT, ORCHESTRATION_GUIDANCE, ORCHESTRATION_AWARENESS, GOAL_COMPLETION_GUIDANCE } from "./prompts/supervisor.js";
173
173
  export { compose, validatePack } from "./prompt-assembly/composer.js";
174
174
  export { SEMA_DEFAULT_PACK } from "./prompt-assembly/packs/sema-default.js";
package/dist/index.js CHANGED
@@ -68,7 +68,7 @@ export { deriveInvariants, checkInvariants } from "./core/property-harness.js";
68
68
  export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
69
69
  export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, } from "./tools/fs/index.js";
70
70
  export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
71
- export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, } from "./core/tool-result-store.js";
71
+ export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, } from "./core/tool-result-store.js";
72
72
  export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, } from "./core/checkpoint-store.js";
73
73
  export { InMemoryUsageWindowStore, GLOBAL_USAGE_KEY, EMPTY_USAGE_WINDOW_RECORD, chargeUsageRecord, readUsageRecord, usageRetryAfterMs, resolveUsageWindows, } from "./core/usage-window-store.js";
74
74
  export { FileUsageWindowStore } from "./stores/file/usage-window-store.js";
@@ -123,7 +123,7 @@ export { AdoptionError, assertAdoptionBootGate, readRootAdoptionFile, writeRootA
123
123
  export { adoptLocalDataRoot, ackAdoptionConfig, witnessAdoptionConfig, listAdoptionQuarantine, readAdoptionStatus, } from "./stores/file/adoption/adopt.js";
124
124
  export { formatHookFeedback, runToolGate, createHookEnvCapabilities, createPreToolUseConstraintPolicy, } from "./core/hooks.js";
125
125
  export { InMemoryMemoryStore, composeMemoryBlock, composeLayeredMemoryBlock, normalizeMemorySpec, supportsConsolidation, supportsPeriodicConsolidation, firstSentence, expandLexicalTerms, lexicalSearchMatch, classifyPromotable, enforcePromotableWriteGate, guardedMemoryStore, MemoryGateError, detectSecret, enforceSecretWriteGate, enforceStructuredNoteSecretGate, } from "./core/memory.js";
126
- export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, readV2HeaderHints, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, migrateScope, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, syncMemoryScope, } from "./core/memory-engine/index.js";
126
+ export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, readV2HeaderHints, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, migrateScope, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, syncMemoryScope, } from "./core/memory-engine/index.js";
127
127
  export { SHARED_MEMORY_READ_CAP_BYTES, SHARED_MEMORY_LIST_PAGE_SIZE, SharedMemoryStoreError, } from "./core/shared-memory/types.js";
128
128
  export { sharedMemoryStoreContract, } from "./core/shared-memory/contract.js";
129
129
  export { termSet, jaccardDistance, cosineDistance } from "./core/memory-vector.js";
@@ -131,7 +131,7 @@ export { encodeSurfacedKey, buildManifestText, validateSelectedIds, composeSelec
131
131
  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";
132
132
  export { consolidateScope, advanceCursorAfterInline, } from "./core/consolidate-scope.js";
133
133
  export { DEFAULT_COMPACTION_INSTRUCTIONS } from "./core/auto-compaction.js";
134
- 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";
134
+ export { DEFAULT_SYSTEM_PROMPT, CODE_AGENT_PROMPT, SUBAGENT_PROMPT, CODE_SYSTEM_PROMPT, MEMORY_GUIDANCE, MEMORY_SAFETY, MEMORY_HYGIENE, NO_PERSISTENT_MEMORY_NOTICE, 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";
135
135
  export { SUPERVISOR_PROMPT, ORCHESTRATION_GUIDANCE, ORCHESTRATION_AWARENESS, GOAL_COMPLETION_GUIDANCE } from "./prompts/supervisor.js";
136
136
  export { compose, validatePack } from "./prompt-assembly/composer.js";
137
137
  export { SEMA_DEFAULT_PACK } from "./prompt-assembly/packs/sema-default.js";
@@ -229,9 +229,11 @@ export interface RunWorkflowToolDeps {
229
229
  * has inherited this hook since [893]④a; the workflow lane never did — the governance whitelist
230
230
  * rightly blocks SCRIPTS from setting it, but host inheritance is a different lane. */
231
231
  parentGetApiKeyAndHeaders?: import("../core/types.js").TaskSpec["getApiKeyAndHeaders"];
232
- /** The HOST run's display sink (its `RunInternals.onForwardEvent`, already filtered to `task_progress`
233
- * by the runner's ctx wrapper)threaded via `startWorkflow` into every spawned agent's trusted
234
- * internals so a workflow child's progress ticks bubble to the deployment's one sink, the same
232
+ /** The HOST run's display sink (its `RunInternals.onForwardEvent` behind the runner's ctx wrapper:
233
+ * `task_progress` always, plus the children's content events — `text_delta`/`reasoning_delta`/
234
+ * `tool_start`/`tool_end`, UNTRUSTED-RAW: the consumer must redact when the HOST spec set
235
+ * `forwardSubagentEvents: true`) — threaded via `startWorkflow` into every spawned agent's trusted
236
+ * internals so a workflow child's events bubble to the deployment's one sink, the same
235
237
  * channel a `createSubagentTool` delegation threads. Display-only; absent ⇒ ticks stay in each
236
238
  * child's own stream. (A dep, not read off the execute ctx: the mounted tool's `AgentTool.execute`
237
239
  * wrapper builds a minimal `{toolCallId, signal}` ctx — the rich-ctx injection only wraps
@@ -31,8 +31,9 @@ export declare const WORKFLOW_SUBAGENT_APPEND_SCHEMA = "---\n\nNOTE: You are run
31
31
  * F4 agentType (CC 198 锚 pretty.js:446608-446627): resolve `opts.agentType` against the registry
32
32
  * (deployment SHADOW over built-ins) and fold the definition into the child spec — persona as
33
33
  * `systemPrompt` (so {@link withWorkflowChildPersona} composes the return-contract NOTE via the
34
- * custom-persona APPEND arm = CC `O0m` semantics), model/thinking/maxTurns/skills/memory when the spec
35
- * didn't pin them, and allow/denyTools as a ToolPolicy (combined deny-wins with any spec policy).
34
+ * custom-persona APPEND arm = CC `O0m` semantics), model/thinking/maxTurns/skills/memory/
35
+ * memoryPersistenceCapable when the spec didn't pin them, and allow/denyTools as a ToolPolicy
36
+ * (combined deny-wins with any spec policy).
36
37
  * The definition is DEPLOYMENT-TRUSTED (registry-declared, not script-authored), so its model bypasses
37
38
  * the script-facing modelName allowlist by design — same trust tier as the Agent tool's registry.
38
39
  */
@@ -382,10 +383,12 @@ export interface RunWorkflowOptions {
382
383
  * from ctx): every spawned agent composes the same closure (codex F2a). */
383
384
  parentCenterArtifactDigest?: string;
384
385
  parentCenterSourceRevision?: string;
385
- /** The launching run's display sink (`RunInternals.onForwardEvent`, already filtered by the runner to
386
- * `task_progress`) threaded into every spawned agent's trusted internals so the children's progress
387
- * ticks bubble out of their isolated streams to the deployment's one sink (fleet footer/monitor rows).
388
- * Display-only; absent ticks stay in each child's own stream. */
386
+ /** The launching run's display sink (`RunInternals.onForwardEvent` behind the runner's ctx wrapper:
387
+ * `task_progress` always, PLUS the children's content events `text_delta`/`reasoning_delta`/
388
+ * `tool_start`/`tool_end`, UNTRUSTED-RAW: the consumer must redact when the HOST spec set
389
+ * `forwardSubagentEvents: true`) threaded into every spawned agent's trusted internals so the
390
+ * children's events bubble out of their isolated streams to the deployment's one sink (fleet
391
+ * footer/monitor rows). Display-only; absent ⇒ ticks stay in each child's own stream. */
389
392
  onForwardEvent?: (event: TaskEvent) => void;
390
393
  /** Parent effective-policy inheritance (tighten-only): the HOST task's evaluated gate chain
391
394
  * (`ToolExecuteContext.inheritedGateForChildren()` — its session rules + toolPolicy/frozen onAsk +
@@ -105,6 +105,8 @@ export function applyWorkflowAgentType(spec, agentType, registry) {
105
105
  out.skills = def.skills;
106
106
  if (out.memory === undefined && def.memory !== undefined)
107
107
  out.memory = def.memory;
108
+ if (out.memoryPersistenceCapable === undefined && def.memoryPersistenceCapable !== undefined)
109
+ out.memoryPersistenceCapable = def.memoryPersistenceCapable;
108
110
  if (def.allowTools !== undefined || def.denyTools !== undefined) {
109
111
  const perType = createAllowDenyPolicy({ ...(def.allowTools ? { allow: def.allowTools } : {}), ...(def.denyTools ? { deny: def.denyTools } : {}) });
110
112
  out.toolPolicy = out.toolPolicy ? combinePolicies(out.toolPolicy, perType) : perType;