@sema-agent/core 7.5.2 → 7.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/dist/agents/agent-transcript-tool.d.ts +2 -2
- package/dist/agents/cascade.d.ts +4 -5
- package/dist/agents/cascade.js +12 -10
- package/dist/agents/repair-loop.d.ts +7 -5
- package/dist/agents/repair-loop.js +13 -15
- package/dist/agents/retain-ledger.d.ts +2 -3
- package/dist/agents/send-message-tool.d.ts +2 -2
- package/dist/agents/session-util.d.ts +2 -2
- package/dist/agents/subagent.d.ts +27 -46
- package/dist/agents/subagent.js +119 -105
- package/dist/agents/suspend-guard.d.ts +31 -19
- package/dist/agents/suspend-guard.js +14 -8
- package/dist/agents/teacher.d.ts +2 -2
- package/dist/agents/teacher.js +9 -9
- package/dist/agents/team.d.ts +6 -5
- package/dist/agents/team.js +10 -8
- package/dist/agents/verify.d.ts +8 -9
- package/dist/agents/verify.js +17 -17
- package/dist/core/a2a.js +2 -1
- package/dist/core/agent-definition.d.ts +172 -0
- package/dist/core/agent-definition.js +1 -0
- package/dist/core/ask-origin.d.ts +60 -7
- package/dist/core/ask-origin.js +26 -1
- package/dist/core/checkpoint-store.d.ts +78 -76
- package/dist/core/checkpoint-store.js +17 -1
- package/dist/core/delegation-frames.d.ts +298 -0
- package/dist/core/delegation-frames.js +21 -0
- package/dist/core/engine-notice.d.ts +555 -0
- package/dist/core/engine-notice.js +55 -0
- package/dist/core/gate-fold.d.ts +12 -0
- package/dist/core/gate-fold.js +158 -0
- package/dist/core/gate-lanes.d.ts +93 -0
- package/dist/core/gate-lanes.js +626 -0
- package/dist/core/gate-outcome.d.ts +189 -0
- package/dist/core/gate-outcome.js +70 -0
- package/dist/core/hands-band.d.ts +134 -0
- package/dist/core/hands-band.js +1 -0
- package/dist/core/hooks.d.ts +22 -177
- package/dist/core/hooks.js +53 -851
- package/dist/core/mcp-failure.d.ts +142 -0
- package/dist/core/mcp-failure.js +145 -0
- package/dist/core/mcp-server-spec.d.ts +217 -0
- package/dist/core/mcp-server-spec.js +1 -0
- package/dist/core/mcp.d.ts +21 -77
- package/dist/core/mcp.js +76 -150
- package/dist/core/model-seat.d.ts +99 -0
- package/dist/core/model-seat.js +1 -0
- package/dist/core/pause-registry.d.ts +131 -0
- package/dist/core/pause-registry.js +27 -0
- package/dist/core/reminder-mint.d.ts +10 -0
- package/dist/core/reminder-mint.js +3 -0
- package/dist/core/runner/assemble-result.d.ts +32 -41
- package/dist/core/runner/assemble-result.js +55 -74
- package/dist/core/runner/contracts.d.ts +427 -69
- package/dist/core/runner/denial-limit-arms.d.ts +1 -1
- package/dist/core/runner/denial-limit-arms.js +3 -3
- package/dist/core/runner/gate-exit.d.ts +242 -0
- package/dist/core/runner/gate-exit.js +124 -0
- package/dist/core/runner/park-commit.d.ts +17 -23
- package/dist/core/runner/park-commit.js +14 -15
- package/dist/core/runner/prepare-ask-lane.d.ts +0 -3
- package/dist/core/runner/prepare-ask-lane.js +3 -5
- package/dist/core/runner/prepare-boundary-parks.d.ts +3 -6
- package/dist/core/runner/prepare-boundary-parks.js +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +2 -7
- package/dist/core/runner/prepare-caps-and-workflow.js +1 -1
- package/dist/core/runner/prepare-delegation-surface.d.ts +2 -7
- package/dist/core/runner/prepare-gate-stations.d.ts +4 -7
- package/dist/core/runner/prepare-gate-stations.js +29 -54
- package/dist/core/runner/prepare-inherited-gate.js +1 -1
- package/dist/core/runner/prepare-memory.d.ts +44 -26
- package/dist/core/runner/prepare-park-ask.d.ts +2 -4
- package/dist/core/runner/prepare-park-ask.js +5 -5
- package/dist/core/runner/prepare-task.d.ts +2 -2
- package/dist/core/runner/prepare-task.js +8 -9
- package/dist/core/runner/prepare-wiring-manifest.d.ts +7 -15
- package/dist/core/runner/prepare-wiring-manifest.js +9 -10
- package/dist/core/runner/runtask.d.ts +20 -102
- package/dist/core/runner/runtask.js +119 -121
- package/dist/core/runner/terminal-projection.d.ts +22 -0
- package/dist/core/runner/terminal-projection.js +28 -0
- package/dist/core/runner-deps.d.ts +1416 -0
- package/dist/core/runner-deps.js +1 -0
- package/dist/core/runtime-caps.d.ts +164 -0
- package/dist/core/runtime-caps.js +1 -0
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +4 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +8 -2
- package/dist/core/task-event.d.ts +910 -0
- package/dist/core/task-event.js +1 -0
- package/dist/core/task-limits.d.ts +110 -0
- package/dist/core/task-limits.js +1 -0
- package/dist/core/task-result.d.ts +809 -0
- package/dist/core/task-result.js +1 -0
- package/dist/core/task-spec.d.ts +1370 -0
- package/dist/core/task-spec.js +1 -0
- package/dist/core/task-stream.d.ts +382 -0
- package/dist/core/task-stream.js +1 -0
- package/dist/core/terminal-cause.d.ts +137 -0
- package/dist/core/terminal-cause.js +9 -0
- package/dist/core/tool-policy.d.ts +43 -139
- package/dist/core/tool-policy.js +79 -112
- package/dist/core/tool-spec.d.ts +1174 -0
- package/dist/core/tool-spec.js +1 -0
- package/dist/core/types.d.ts +27 -7789
- package/dist/core/types.js +2 -76
- package/dist/core/warm-resume.d.ts +2 -2
- package/dist/core/wiring-manifest.d.ts +6 -3
- package/dist/core/workflow-journal-store.js +3 -4
- package/dist/engine/harness/agent-harness.d.ts +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.js +9 -6
- package/dist/orchestration/builtin-workflows.d.ts +2 -2
- package/dist/orchestration/builtin-workflows.js +1 -1
- package/dist/orchestration/goal.d.ts +2 -2
- package/dist/orchestration/goal.js +8 -7
- package/dist/orchestration/run-spec.d.ts +2 -2
- package/dist/orchestration/run-spec.js +5 -3
- package/dist/orchestration/run-workflow-tool.d.ts +4 -4
- package/dist/orchestration/run-workflow-tool.js +4 -4
- package/dist/orchestration/workflow-governance.d.ts +4 -4
- package/dist/orchestration/workflow-governance.js +4 -2
- package/dist/orchestration/workflow-primitives.d.ts +1 -1
- package/dist/orchestration/workflow-primitives.js +1 -1
- package/dist/orchestration/workflow.d.ts +15 -4
- package/dist/orchestration/workflow.js +64 -39
- package/dist/prompts/supervisor.d.ts +1 -1
- package/dist/prompts/supervisor.js +3 -3
- package/dist/scenarios/scenario-registry.d.ts +3 -3
- package/dist/scenarios/scenario-registry.js +1 -1
- package/dist/scenarios/teacher-quickstart.d.ts +2 -2
- package/dist/server/http.d.ts +2 -2
- package/dist/stores/file/fs-atomic.d.ts +88 -12
- package/dist/stores/file/fs-atomic.js +184 -55
- package/dist/stores/file/index.d.ts +1 -0
- package/dist/stores/file/index.js +1 -0
- package/package.json +3 -1
- package/test/export-surface.snapshot.json +82 -22
|
@@ -0,0 +1,1416 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HOST wiring seat: `RunnerDeps` — every store, sink, hook and policy provider a deployment hands
|
|
3
|
+
* the engine — plus the three structured shapes only its own callbacks produce (`ProjectMemoryLoad`,
|
|
4
|
+
* `ResumePreflightInfo`, `ResumePreflightVerdict`). Layer 0 vocabulary; `types.ts` re-exports all four,
|
|
5
|
+
* so no consumer's import changes.
|
|
6
|
+
*/
|
|
7
|
+
import type { Model } from "../internal/llm.js";
|
|
8
|
+
import type { AgentDefinition } from "./agent-definition.js";
|
|
9
|
+
import type { BackgroundChildEvent, DelegationLifecycleEvent } from "./delegation-frames.js";
|
|
10
|
+
import type { EngineNotice } from "./engine-notice.js";
|
|
11
|
+
import type { HandsBandOptions } from "./hands-band.js";
|
|
12
|
+
import type { OnElicit } from "./mcp-server-spec.js";
|
|
13
|
+
import type { Brain, ModelRef, ModelRoles } from "./model-seat.js";
|
|
14
|
+
import type { RuntimeCaps, WorkflowGovernanceBaseline } from "./runtime-caps.js";
|
|
15
|
+
/**
|
|
16
|
+
* parity-204 — structured `RunnerDeps.loadProjectMemory` return (backward-compatible: a bare
|
|
17
|
+
* `string | null` keeps its exact historical meaning). Adds the CC 2.1.204 `seededFromContext`
|
|
18
|
+
* seam (cc204-bundle @17917159: CC preloads readFileState for CLAUDE.md/nested-memory files it
|
|
19
|
+
* placed in context, and a DEFAULT whole-file Read of an unchanged seeded file answers with the
|
|
20
|
+
* already-in-context reminder instead of re-transmitting the body — token economy, GA in 204).
|
|
21
|
+
*/
|
|
22
|
+
export interface ProjectMemoryLoad {
|
|
23
|
+
/** The project-context text to inject — same semantics as returning a bare string (null/blank ⇒ no injection). */
|
|
24
|
+
content: string | null;
|
|
25
|
+
/**
|
|
26
|
+
* Files whose FULL, disk-verbatim text the deployment has placed in the model's context (e.g. the
|
|
27
|
+
* CLAUDE.md that `content` embeds). The Runner pre-seeds the hands' read state (through the same
|
|
28
|
+
* resolveKey containment as a real Read — an out-of-root path is silently skipped), so a default
|
|
29
|
+
* whole-file Read of an UNCHANGED seeded file returns the CC-verbatim reminder
|
|
30
|
+
* (`This file is already in your context (see "Contents of <path>" above) and has not changed on
|
|
31
|
+
* disk…`) instead of the body. `content` here MUST be the file's exact disk text at load time —
|
|
32
|
+
* the unchanged check hashes it; never declare a truncated/annotated variant (CC's `isPartialView`
|
|
33
|
+
* exemption). A file that later changes on disk always reads normally (hash mismatch bypasses the
|
|
34
|
+
* dedup and disarms it). Requires hands (an executionEnv); ignored on a hands-less task.
|
|
35
|
+
*/
|
|
36
|
+
seededFiles?: ReadonlyArray<{
|
|
37
|
+
path: string;
|
|
38
|
+
content: string;
|
|
39
|
+
}>;
|
|
40
|
+
/**
|
|
41
|
+
* design-A C-3 (AGENTS.md×ingest, [A1]) — the deployment DECLARES which instruction files this
|
|
42
|
+
* snapshot was built from and their fingerprints (e.g. `AGENTS.md` / `CLAUDE.md`, hash over the
|
|
43
|
+
* raw pre-cap file bytes). Core does NOT interpret the hash algorithm or re-derive it — the pair
|
|
44
|
+
* is an OPAQUE comparison baseline (`path` + verbatim `contentHash` equality only), kept so the
|
|
45
|
+
* mid-session change probe can answer "did the file behind my snapshot move?" without core
|
|
46
|
+
* ever learning a file-name policy (types.ts seam rule: core never hardcodes instruction file
|
|
47
|
+
* names). [A2]: this list now FEEDS the C-4 `instructions_change` lane — when the deployment also
|
|
48
|
+
* wires {@link RunnerDeps.probeInstructionSources}, the run loop probes these paths at eligible
|
|
49
|
+
* turn boundaries and a moved hash is announced via a fixed tail attachment. Optional and inert
|
|
50
|
+
* when absent (today's loaders keep their exact behavior; no sources ⇒ the lane never runs).
|
|
51
|
+
*/
|
|
52
|
+
instructionSources?: ReadonlyArray<{
|
|
53
|
+
path: string;
|
|
54
|
+
/** (additive widening): `null` = the source is DECLARED but currently
|
|
55
|
+
* ABSENT from disk — the change probe then watches for its APPEARANCE (renders "absent → hash").
|
|
56
|
+
* Runtime already handled null (collectInstructionsChange's from===null arm); this widening
|
|
57
|
+
* only aligns the declared type with the shipped behavior. */
|
|
58
|
+
contentHash: string | null;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* design/380 O2 — the facts handed to a deployment's {@link RunnerDeps.resumePreflight}: the row's
|
|
63
|
+
* own recorded identity + placement record, read off the persisted checkpoint (never off the resume
|
|
64
|
+
* caller's bag), so the hook judges the SAME row the CAS is about to consume.
|
|
65
|
+
*/
|
|
66
|
+
export interface ResumePreflightInfo {
|
|
67
|
+
token: import("./checkpoint-store.js").CheckpointToken;
|
|
68
|
+
sessionId: string;
|
|
69
|
+
/** design/380 O1③ — the row's recorded placement root, when stamped. */
|
|
70
|
+
placementRootSessionId?: string;
|
|
71
|
+
/** The row's recorded identity ({@link import("./checkpoint-store.js").Checkpoint.principal}), when stamped —
|
|
72
|
+
* the resume entry's identity-continuity rung already refused a contradicting supplied principal
|
|
73
|
+
* before this hook runs, so a present value IS the resumed leg's identity. */
|
|
74
|
+
principal?: string;
|
|
75
|
+
/** provider / deviceId / mountPath — the placement facts. */
|
|
76
|
+
workspaceHandle?: import("./remote-env.js").WorkspaceHandle;
|
|
77
|
+
gateKind: import("./checkpoint-store.js").CheckpointGate["kind"];
|
|
78
|
+
}
|
|
79
|
+
/** design/380 O2 — a {@link RunnerDeps.resumePreflight} answer. */
|
|
80
|
+
export type ResumePreflightVerdict = {
|
|
81
|
+
ok: true;
|
|
82
|
+
}
|
|
83
|
+
/** Transient obstacle (device offline, dependency briefly down): the checkpoint stays `pending`,
|
|
84
|
+
* the SAME token is redeemable later; retryAfterMs threads the wait hint (the #449 G1 carrier). */
|
|
85
|
+
| {
|
|
86
|
+
ok: false;
|
|
87
|
+
disposition?: "retry_later";
|
|
88
|
+
message: string;
|
|
89
|
+
retryAfterMs?: number;
|
|
90
|
+
}
|
|
91
|
+
/** PERMANENT obstacle (binding revoked, placement identity gone): the row is settled TERMINALLY —
|
|
92
|
+
* core CASes it out of `pending` before answering, so a dead binding cannot
|
|
93
|
+
* be redialed forever (prose-terminal + mechanically-retriable = unbounded redeem). The `expire`
|
|
94
|
+
* CAS is a pure STATUS flip — the row records no reason (the store seam has no seat for one);
|
|
95
|
+
* the REASON travels on the typed `resume.preflight_rejected` refusal (the hook's `message`
|
|
96
|
+
* inlined), the `onError` disclosure sink, and the deployment hook's own audit plane — it
|
|
97
|
+
* authored the verdict and owns the durable record of why. The exact
|
|
98
|
+
* terminal CAS form is the single-shot `expire` CAS (reaper parity — exactly one settler wins; a
|
|
99
|
+
* LOST race is re-read and reported as concurrent movement, never claimed as this refusal's
|
|
100
|
+
* settle); the CONTRACT is: terminal verdict ⇒ atomic single-shot settle, never a silent
|
|
101
|
+
* pending-forever. PHYSICAL reclamation of a suspended workspace stays with the DEPLOYMENT that
|
|
102
|
+
* ruled the binding dead — core holds no env pre-restore and a factory cannot reconnect to a
|
|
103
|
+
* revoked target; the session pin follows the store contract's documented reap posture. Trust
|
|
104
|
+
* grant is acceptable: the hook lives on RunnerDeps, the same deployment plane that owns the
|
|
105
|
+
* checkpoint store itself. */
|
|
106
|
+
| {
|
|
107
|
+
ok: false;
|
|
108
|
+
disposition: "terminal";
|
|
109
|
+
message: string;
|
|
110
|
+
};
|
|
111
|
+
/** Runtime dependencies shared across tasks. */
|
|
112
|
+
export interface RunnerDeps {
|
|
113
|
+
brain: Brain;
|
|
114
|
+
/** Catalog used to resolve string ModelRefs to Model objects. */
|
|
115
|
+
models?: Record<string, Model>;
|
|
116
|
+
/**
|
|
117
|
+
* design/338 (mid-turn MCP revocation) — the HOST's revocation ledger, probed synchronously at
|
|
118
|
+
* every MCP dispatch (tool call + the three resource tools) BEFORE the transport. The engine
|
|
119
|
+
* never caches the answer: the ledger's one authority lives on the host (a cached copy would be
|
|
120
|
+
* a split-state second authority). A revoked server's calls settle as the coded refusal
|
|
121
|
+
* `mcp.server_revoked` with known-not-executed wording; in-flight calls a revocation raced are
|
|
122
|
+
* deliberately not chased (the threat shape is "new calls after removal"). Absent seat = the
|
|
123
|
+
* pre-338 semantics. A THROWING probe fails open (revocation is a tightening face — a broken
|
|
124
|
+
* probe must not brick every MCP call) with a once-per-MATERIALIZATION `mcp.revocation_probe_failed`
|
|
125
|
+
* notice (a resume re-materializes and may announce again — the standing condition is re-news at
|
|
126
|
+
* each fresh mount, never per-call). The notice is a deployment wiring fact: `detail: { message }`
|
|
127
|
+
* only, no session attribution, `"operator"` audience by its explicit {@link NOTICE_AUDIENCE}
|
|
128
|
+
* row (#433 made the registry total over the catalog — no engine-minted code is
|
|
129
|
+
* audience-defaulted any more) — a
|
|
130
|
+
* wire projector forwards it operator-tier and needs no per-session de-duplication of its own.
|
|
131
|
+
*/
|
|
132
|
+
mcpRevocations?: {
|
|
133
|
+
isRevoked(serverName: string): boolean;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* design/147 S1c (ruled 2026-07-18) — the DURABLE name→agent roster behind explicit-name
|
|
137
|
+
* addressing, a storage-tier seam like the checkpoint store: core bundles `MemoryRosterStore`
|
|
138
|
+
* (default when omitted... none — omitted means NO durable roster; the in-memory task registry
|
|
139
|
+
* still resolves live/recent names exactly as before) and `FileRosterStore`; a server deployment
|
|
140
|
+
* plugs pg/tidb behind the same interface. Named background-agent spawns are recorded here
|
|
141
|
+
* (advisory — a throwing store never faults a spawn), and SendMessage consults it AFTER the live
|
|
142
|
+
* registry misses, re-applying the caller's access axes (owner/scope) before disclosing anything.
|
|
143
|
+
*/
|
|
144
|
+
rosterStore?: import("../agents/roster-store.js").RosterStore;
|
|
145
|
+
/**
|
|
146
|
+
* design/148 S1 — the center prompt-artifact source (protocol §9.2). `current()` supplies the
|
|
147
|
+
* validated candidate NEW sessions adopt at session_start; `get(digest)` resolves a PINNED
|
|
148
|
+
* center artifact on resume (a miss is fail-loud `prompt.snapshot_unavailable` — core never
|
|
149
|
+
* recomposes a hybrid from same-named newer content). Core bundles `CenterPromptSource`
|
|
150
|
+
* (+ memory/file artifact & source-state stores); a server deployment feeds it verified
|
|
151
|
+
* effective-snapshot envelopes via `applySnapshot`. Omitted = bundled-only (today's shape).
|
|
152
|
+
*/
|
|
153
|
+
promptSource?: import("../prompt-assembly/artifact-store.js").PromptArtifactSource;
|
|
154
|
+
/**
|
|
155
|
+
* 模型档位绑定(2026-07-11):sema 档位词(`max`/`pro`/`flash`/`lite`,`ultra` 预留)→ 具体模型
|
|
156
|
+
* (catalog 名或 Model)。Runner 构造时经 `expandTiers` 增广进 catalog,使档位词与 CC alias
|
|
157
|
+
* (`fable`/`mythos`/`opus`/`sonnet`/`haiku`,内置固定映射)在全部模型寻址面直接可用(TaskSpec.model /
|
|
158
|
+
* roles / Agent 工具 model / workflow agent() / `@model` mention)。无绑定档沿 DEFAULT_TIER_ORDER 降档;
|
|
159
|
+
* 与部署 catalog 同名键时 catalog WINS(SHADOW)。center 经配置域下发/覆写此表。
|
|
160
|
+
*/
|
|
161
|
+
tiers?: Record<string, ModelRef>;
|
|
162
|
+
/**
|
|
163
|
+
* F4 agentType 注册表(2026-07-11):部署声明的 {@link AgentDefinition} 集,workflow `agent(…,{agentType})`
|
|
164
|
+
* 经此解析(内置 Explore/Plan 之上 SHADOW)。design/141 件A:Agent 工具 lane(`createSubagentTool`)未显式
|
|
165
|
+
* 传 `agents` 时经 `Runner.agentCatalog` 自动取此表——同一 Runner 两 lane 单源;显式传参仍 WINS。
|
|
166
|
+
*/
|
|
167
|
+
agents?: AgentDefinition[];
|
|
168
|
+
/** `false` 从 workflow agentType 注册表移除内置 Explore/Plan(镜像 createSubagentTool 同名开关)。 */
|
|
169
|
+
builtinAgents?: boolean;
|
|
170
|
+
/**
|
|
171
|
+
* design/141 件2 — hands 工具带(Read/Write/Edit/Bash…)的部署方窄配置面。刻意不导出
|
|
172
|
+
* `createHandsToolkit` 整厂(部署自装配塞 `spec.tools` 会被 Runner 的自动装配同名覆盖=反模式;
|
|
173
|
+
* CC 亦无 options-bag 开放面,走单例+hook)。只开放安全子集,Runner 装配时合成:
|
|
174
|
+
* - `beforeWrite` 与引擎的 MemoryEngine 写扫描门 **COMPOSE 而非替换**(部署 hook 先判,拒即拒;
|
|
175
|
+
* 放行后引擎门仍全量生效——写保护不可能被部署配置规避)。
|
|
176
|
+
* Runner 私有编排字段(taskRegistry/detachHub/execClamp/cwdRef/…)不在此面,永不开放。
|
|
177
|
+
*/
|
|
178
|
+
hands?: HandsBandOptions;
|
|
179
|
+
/**
|
|
180
|
+
* design/199 件B — DEPLOYMENT-layer ADDITIONS to the built-in sensitive-path READ deny set
|
|
181
|
+
* ({@link import("../tools/fs/read-deny.js").READ_FACE_DEFAULT_DENY_ENTRIES}); see
|
|
182
|
+
* {@link TaskSpec.readDenyPatterns} for the judged surfaces. Add-only on THIS seat: unions with
|
|
183
|
+
* the active built-ins and any task-layer additions, and `[]` ≡ absent — narrowing the built-in
|
|
184
|
+
* set is the sibling knobs' job ({@link readDenyBuiltinTiers} / {@link readDenyBuiltinExclude},
|
|
185
|
+
* #245 revision of D-4), not a replacement escape hatch here. Bad entry shapes refuse loudly at
|
|
186
|
+
* prepare (#123).
|
|
187
|
+
*/
|
|
188
|
+
readDenyPatterns?: readonly import("../tools/fs/read-deny.js").ReadDenyEntry[];
|
|
189
|
+
/**
|
|
190
|
+
* #245 (revises the D-4 zero-shrink ruling) — the deployment's built-in deny-table TIER selection:
|
|
191
|
+
* EXACTLY the listed tiers of
|
|
192
|
+
* {@link import("../tools/fs/read-deny.js").READ_FACE_BUILTIN_DENY_TABLE} are active (`[]` = none —
|
|
193
|
+
* explicit and legal); absent = the default selection
|
|
194
|
+
* ({@link import("../tools/fs/read-deny.js").READ_DENY_DEFAULT_TIERS}: every tier except
|
|
195
|
+
* `shell-history`, which is unrestricted by default). Composes with
|
|
196
|
+
* {@link readDenyBuiltinExclude} (tiers first, then row removal); never touches the additions
|
|
197
|
+
* seats or the write faces. Unknown tier names refuse loudly at prepare (#123). DEPLOYMENT seat
|
|
198
|
+
* ONLY: deliberately not a TaskSpec key and not in the governed workflow whitelist — a task or
|
|
199
|
+
* governed script can only ADD deny entries, never widen the built-in face below its deployment.
|
|
200
|
+
* Not frozen into checkpoints: the checkpoint face section persists ADDITIONS only, so a resumed
|
|
201
|
+
* row's built-in face follows the CURRENT deployment configuration (the operator's live
|
|
202
|
+
* authority), while frozen additions stay stricter-wins.
|
|
203
|
+
*/
|
|
204
|
+
readDenyBuiltinTiers?: readonly string[];
|
|
205
|
+
/**
|
|
206
|
+
* #245 — per-row removal from the built-in deny table by STABLE NAME (= the row's canonical
|
|
207
|
+
* pattern text, e.g. `".ssh"`, `".config/gcloud"`; the admin delete channel), applied after
|
|
208
|
+
* {@link readDenyBuiltinTiers} selection. Naming a row of an inactive tier is a satisfied intent
|
|
209
|
+
* (no-op); a name matching NO table row refuses loudly at prepare (#123 — never silently
|
|
210
|
+
* ignored). Same deployment-only seat, clamp argument and checkpoint posture as the tiers key.
|
|
211
|
+
*/
|
|
212
|
+
readDenyBuiltinExclude?: readonly string[];
|
|
213
|
+
/**
|
|
214
|
+
* backlog #286 (#279 — CC 2.1.233 `DANGEROUS_FILES`/`DANGEROUS_DIRECTORIES`/
|
|
215
|
+
* `DANGEROUS_DIRECTORY_PATHS` parity): the WRITE-protection table. DEFAULT-ON: a path-confinable
|
|
216
|
+
* write (Write/Edit/NotebookEdit) whose target lands on a table row has a surviving `allow`
|
|
217
|
+
* demoted to `ask` at the tool gate (`decisionReason: "safety"`; a deny/ask verdict is untouched;
|
|
218
|
+
* approval flows the ordinary ask-resolution chain — classifier, blanket `onAsk`, durable park —
|
|
219
|
+
* with no `requiresRealApproval` mandate). Absent =
|
|
220
|
+
* {@link import("./write-protect.js").WRITE_PROTECTED_DEFAULT_TABLE} (the CC triple verbatim +
|
|
221
|
+
* the two argued sema rows). This key IS the whole-table escape hatch, deployment seat ONLY (no
|
|
222
|
+
* TaskSpec twin, no governed-workflow channel): `[]` = no table (explicit and legal); a non-empty
|
|
223
|
+
* list REPLACES the built-in table whole (compose additions as
|
|
224
|
+
* `[...WRITE_PROTECTED_DEFAULT_TABLE, …]`; drop rows by filtering the exported table — the
|
|
225
|
+
* visible/deletable admin face). Bad values refuse loudly at prepare (#123): garbage shapes,
|
|
226
|
+
* glob metacharacters (the table speaks LITERAL names — glob semantics live in
|
|
227
|
+
* `createSensitivePathPolicy`), unknown kinds, impossible kind/name combinations. Matching is
|
|
228
|
+
* lexical over the spelled target with one case fold (ı/ſ included) — a symlink alias evades it
|
|
229
|
+
* by construction; the canonicalizing opt-in deny policy remains the hard layer. Not frozen into
|
|
230
|
+
* checkpoints: a resumed task follows the CURRENT deployment table (an approved parked call
|
|
231
|
+
* bypasses the gate as always — the human already adjudicated it).
|
|
232
|
+
*/
|
|
233
|
+
writeProtectedPaths?: readonly import("./write-protect.js").WriteProtectedEntry[];
|
|
234
|
+
/**
|
|
235
|
+
* design/324 (#324 ruling ① containment) — the EVIDENCE STANDARD the delegation arm of the
|
|
236
|
+
* content-origin wrap applies when deciding whether a delegation call marks THIS session's memory
|
|
237
|
+
* polluted (design/178 §3 / design/180 half A):
|
|
238
|
+
* - `"static-face"` (absent ≡ this; the default) — today's behavior, byte-identical: a delegation
|
|
239
|
+
* whose delivered attestation is missing/unknown and whose static tool face can reach external
|
|
240
|
+
* content marks the session (the capability over-approximation: possibility counts as exposure).
|
|
241
|
+
* - `"attested-only"` — exactly that ONE static-face mark is waived, and each prepared leg
|
|
242
|
+
* announces the first waiver (`"memory.delegation_static_mark_waived"`). Everything else is
|
|
243
|
+
* unchanged: a delivered `"external"` attestation still marks, the chain's `incomplete`
|
|
244
|
+
* recording still happens, a non-delegation polluting-class tool still marks, and a delegation
|
|
245
|
+
* tool that is ITSELF classified polluting still marks pre-call.
|
|
246
|
+
* ACCEPTED COST (the deployment's to own, stated as mechanism, not as absence of risk): under
|
|
247
|
+
* `"attested-only"` a BACKGROUND child's real external contact does not mark this session — its
|
|
248
|
+
* content re-enters through the TaskOutput result, the task-notification injection, or the
|
|
249
|
+
* AgentTranscript step summaries, none of which carries an attestation — and a foreground child
|
|
250
|
+
* that ended abnormally (crash/salvage) is likewise not marked on its face alone (the chain still
|
|
251
|
+
* records `incomplete`). Already-marked sessions are never retroactively cleaned; the key only
|
|
252
|
+
* governs NEW marks.
|
|
253
|
+
* DEPLOYMENT seat ONLY (same posture as {@link readDenyBuiltinTiers}): deliberately no TaskSpec
|
|
254
|
+
* twin and not in the governed workflow whitelist — a task author or governed script gets no
|
|
255
|
+
* channel to loosen the evidence standard below its deployment. Not frozen into checkpoints: a
|
|
256
|
+
* resumed leg follows the CURRENT deployment configuration. Each run reads the deps of the Runner
|
|
257
|
+
* that PREPARES it — a multi-runner assembly should configure every runner with the same value
|
|
258
|
+
* (drift is the deployment's own configuration hazard; recorder/attestation semantics are
|
|
259
|
+
* value-independent, so the chain's evidence quality never varies with this key). Any other value
|
|
260
|
+
* refuses loudly at prepare (`config.memory_delegation_evidence`, #123 — exact spellings only,
|
|
261
|
+
* never truthiness).
|
|
262
|
+
*/
|
|
263
|
+
memoryDelegationEvidence?: "static-face" | "attested-only";
|
|
264
|
+
/**
|
|
265
|
+
* design/336 §13-3 — the memory-provenance master switch (F-020 深修):
|
|
266
|
+
* - `"carry"` (absent ≡ this; the default) — an externally exposed session's ORDINARY memory
|
|
267
|
+
* writes are committed WITH an engine-minted `origin` marker that travels with the entry
|
|
268
|
+
* (backend/sync/bundle); instruction-form files (type: feedback / pinned/triggers/applies-when
|
|
269
|
+
* hints) from exposed sessions are withheld and quarantined; the derived index's session prose
|
|
270
|
+
* still rolls back; the content scan gates run unchanged (a marker is not an exemption).
|
|
271
|
+
* - `"off"` — the pre-336 behavior: no origin marker is minted and an exposed session's harvest
|
|
272
|
+
* admits nothing (full containment, quarantined for host review). Standing committed markers
|
|
273
|
+
* still carry forward on edits — "off" stops minting, it never strips facts already recorded.
|
|
274
|
+
* ORTHOGONAL to {@link memoryDelegationEvidence} on purpose: that key decides WHEN a delegation
|
|
275
|
+
* marks the session exposed (the evidence standard); this key decides WHAT the mark does to the
|
|
276
|
+
* session's writes (tag-admit vs containment). All four combinations are legal and pinned.
|
|
277
|
+
* DEPLOYMENT seat ONLY (same posture as {@link memoryDelegationEvidence}): no TaskSpec twin and
|
|
278
|
+
* not in the governed workflow whitelist — a task author or governed script gets no channel to
|
|
279
|
+
* change the provenance posture below its deployment; an org administers it through this seat.
|
|
280
|
+
* Not frozen into checkpoints: a resumed leg follows the CURRENT deployment configuration. Any
|
|
281
|
+
* other value refuses loudly at prepare (`config.memory_provenance`, #123 — exact spellings,
|
|
282
|
+
* never truthiness).
|
|
283
|
+
*/
|
|
284
|
+
memoryProvenance?: "off" | "carry";
|
|
285
|
+
/**
|
|
286
|
+
* design/383 §3.1 — the deployment's memory-capture POSTURE (the frame the per-principal
|
|
287
|
+
* {@link RuntimeCaps.allowMemoryOptOut} verdict is read inside). Closed spellings, screened at
|
|
288
|
+
* the config doors (`config.memory_capture_policy` — a garbage value refuses the prepare, never
|
|
289
|
+
* folds to any posture):
|
|
290
|
+
* - absent / `"open"` — an opt-out declaration takes effect at face value; only a resolver's
|
|
291
|
+
* EXPLICIT `false` denies it (`memory.capture_optout_denied`, terminal). Resolver fault
|
|
292
|
+
* (throw / non-boolean / absent-on-fault) ⇒ the opt-out STILL TAKES EFFECT, with a named
|
|
293
|
+
* `onError({phase:"config"})` disclosure — the polarity-reversal arm: on this posture the
|
|
294
|
+
* irreversible failure is capturing a session whose user said no, so the fail-safe faces the
|
|
295
|
+
* other way from the caps family's deny-on-outage degrade. Under the ABSENT posture the first
|
|
296
|
+
* opt-out actually honored additionally announces one operator line (once per process): the
|
|
297
|
+
* switch is in use and no policy was ever configured.
|
|
298
|
+
* - `"governed"` — the per-principal verdict is MANDATORY (the mixed-fleet compliance
|
|
299
|
+
* deployment: some principals are retention-required). A verdict of `true` allows, `false`
|
|
300
|
+
* denies; resolver fault / absent verdict ⇒ the task is REFUSED (fail-closed): this posture
|
|
301
|
+
* is the deployment explicitly declaring the compliance side's irreversibility (a
|
|
302
|
+
* retention-required user acquiring an opt-out during an outage window) the heavier one.
|
|
303
|
+
* - `"capture-required"` — every opt-out declaration is refused (deployment-wide forced
|
|
304
|
+
* retention, one line, resolver-independent).
|
|
305
|
+
* The two fault polarities are BOTH deliberate and BOTH pinned; the cost table lives beside the
|
|
306
|
+
* adjudicator. Authority vs record separation (§3.3): this posture governs whether a NEW
|
|
307
|
+
* declaration (or a resumed run) may proceed — it never retroactively re-enables capture for a
|
|
308
|
+
* session whose one-way opt-out record already stands (record wins for capture; under
|
|
309
|
+
* `"governed"`/`"capture-required"` a standing record whose entitlement has been revoked refuses
|
|
310
|
+
* to CONTINUE instead — zero capture and zero out-of-policy running, availability paid).
|
|
311
|
+
*/
|
|
312
|
+
memoryCapturePolicy?: "open" | "governed" | "capture-required";
|
|
313
|
+
/**
|
|
314
|
+
* design/383 §2.1b — an optional host-supplied carrier for the session capture opt-out record
|
|
315
|
+
* (per memory plane, keyed by that plane's control dir). Absent = the control-plane file trio
|
|
316
|
+
* (single-process/single-host deployments: byte-identical behavior). A deployment whose control
|
|
317
|
+
* plane does not survive into resume replicas (the stateless multi-replica server form) supplies
|
|
318
|
+
* a durable store here; see {@link import("./memory-engine/layout.js").SessionCaptureRecordStore}
|
|
319
|
+
* for the per-leg fail-closed contract. Also the §2.1b capability signal: on a REMOTE-execution
|
|
320
|
+
* deployment with no store supplied here, a `capture:"off"` declaration is refused
|
|
321
|
+
* (`config.memory_capture_unsupported`) rather than accepted onto a carrier that the next resume
|
|
322
|
+
* cannot see — a false privacy promise is worse than a loud one.
|
|
323
|
+
*/
|
|
324
|
+
memoryCaptureRecordStore?: (plane: {
|
|
325
|
+
controlDir: string;
|
|
326
|
+
}) => import("./memory-engine/layout.js").SessionCaptureRecordStore;
|
|
327
|
+
/**
|
|
328
|
+
* design/339 §6.1 — the v3 memory-consolidation switch, threaded verbatim to the engine seat
|
|
329
|
+
* ({@link import("../core/memory-engine/engine.js").MemoryEngineOptions.consolidation} — same
|
|
330
|
+
* transport as {@link memoryProvenance}). ABSENT = OFF (the shipped default): no gate state is
|
|
331
|
+
* ever written, no recommendation is ever minted, and the consolidation verbs on an engine the
|
|
332
|
+
* host constructs over the same store refuse coded. PRESENT = enabled: the engine counts
|
|
333
|
+
* distinct terminal-harvest sessions per scope and surfaces the advisory
|
|
334
|
+
* `memory.consolidation_recommended` notice when the configured thresholds cross — EXECUTION
|
|
335
|
+
* stays host-owned (the four verbs are engine host API; nothing in a task can trigger a run).
|
|
336
|
+
* `provenance: "off"` beside this refuses loudly at prepare
|
|
337
|
+
* (`config.memory_consolidation_provenance_off` — the fold law must be able to mint);
|
|
338
|
+
* `multiNode: true` without a lease refuses (`config.memory_consolidation_lease_required`);
|
|
339
|
+
* every other bad value refuses under `config.memory_consolidation` (#123). DEPLOYMENT seat
|
|
340
|
+
* ONLY (no TaskSpec twin, not in the governed workflow whitelist), same law as
|
|
341
|
+
* {@link memoryProvenance}.
|
|
342
|
+
*/
|
|
343
|
+
memoryConsolidation?: import("../core/memory-engine/consolidation.js").MemoryConsolidationOptions;
|
|
344
|
+
/**
|
|
345
|
+
* design/376 片② — the memory-consolidation DRIVER seat (the LLM half), ORTHOGONAL to
|
|
346
|
+
* {@link memoryConsolidation} on purpose: that key is the write PROTOCOL's parameters (fuse,
|
|
347
|
+
* caps, cadence — engine-side), this key is who thinks (model seat + mint knobs — driver-side).
|
|
348
|
+
* The ENGINE itself keeps zero model seats (design/339 §1.1: engine adjudicates, driver
|
|
349
|
+
* proposes; an engine-held model would put distillation authority on the wrong side of the
|
|
350
|
+
* trust boundary), so the seat lives here and is consumed by the host driver verb
|
|
351
|
+
* (`runMemoryConsolidationDriver` + `resolveMemoryConsolidationDriver(deps)`).
|
|
352
|
+
*
|
|
353
|
+
* `chat` — the explicit model seat: an endpoint triple (screened loudly: explicit model id, no
|
|
354
|
+
* alias spellings, key present) or any JSON-only completion function. ABSENT ⇒ the driver
|
|
355
|
+
* resolves the `consolidate` role (`consolidate → summarize → coded refusal`; deliberately
|
|
356
|
+
* never `default` — see the {@link ModelRole} doc). Every other knob mirrors the driver verb's
|
|
357
|
+
* options and is screened there (#123: bad values refuse coded, never fold to defaults).
|
|
358
|
+
* DEPLOYMENT seat ONLY, same law as {@link memoryConsolidation}: no TaskSpec twin — a task
|
|
359
|
+
* author gets no channel to point the library's consolidation at a model of its choosing.
|
|
360
|
+
* Absent alongside absent {@link memoryConsolidation} ⇒ nothing runs and nothing changes
|
|
361
|
+
* (the v3 default posture).
|
|
362
|
+
*
|
|
363
|
+
* ⚠️ EGRESS — read this before configuring the seat (design/383 §1.5). Turning consolidation on
|
|
364
|
+
* SENDS MEMORY CONTENT TO A MODEL PROVIDER. Specifically:
|
|
365
|
+
* - WHAT: that cycle's ELIGIBLE CANDIDATES — not the whole library (entries already superseded,
|
|
366
|
+
* of an ineligible type, or held out by a governance gate are not in the set) — each rendered
|
|
367
|
+
* as its `name`, `description` and `body`. The bodies are the user's own notes, verbatim.
|
|
368
|
+
* - WHERE: the provider this seat resolves to. When `chat` is absent that is whatever the
|
|
369
|
+
* `consolidate → summarize` role chain lands on, which MAY BE THE SAME PROVIDER AS THE MAIN
|
|
370
|
+
* MODEL and may equally be a different vendor — the resolution is the deployment's, and the
|
|
371
|
+
* driver announces the model id and endpoint it actually used at the moment of the first call.
|
|
372
|
+
* - MARKED CONTENT: entries carrying an external-origin (or repo-ingest `trust`) marker are
|
|
373
|
+
* withheld from the CLEAN products' prompts and minted through a separate marked-only call.
|
|
374
|
+
* "Withheld from the clean arm" is NOT "not sent" — such rows still go to the provider in
|
|
375
|
+
* their own call, except a lone marked row, which is sent nowhere. The run receipt reports the
|
|
376
|
+
* two counts separately (`withheldFromCleanArm` / `withheldNeverSent`) and so should any UI.
|
|
377
|
+
* - IRREVERSIBILITY: bytes that have already crossed to a provider cannot be recalled by any
|
|
378
|
+
* later configuration change. Disabling the seat stops FUTURE egress and nothing else.
|
|
379
|
+
*/
|
|
380
|
+
memoryConsolidationDriver?: {
|
|
381
|
+
chat?: {
|
|
382
|
+
baseUrl: string;
|
|
383
|
+
apiKey: string;
|
|
384
|
+
model: string;
|
|
385
|
+
} | import("../core/memory-engine/distiller.js").DistillerChatFn;
|
|
386
|
+
/** The audit model id — REQUIRED when `chat` is a bare function (a function carries no id and
|
|
387
|
+
* the run/archive must not record a guess); derived from the endpoint triple or the resolved
|
|
388
|
+
* role otherwise. */
|
|
389
|
+
model?: string;
|
|
390
|
+
contract?: import("../core/memory-engine/distiller.js").LlmDistillerContract;
|
|
391
|
+
maxCycles?: number;
|
|
392
|
+
mintRepairBudget?: number;
|
|
393
|
+
};
|
|
394
|
+
/**
|
|
395
|
+
* design/199 件A — the DEPLOYMENT's read-face declaration
|
|
396
|
+
* ({@link import("../tools/fs/read-face.js").ReadFace}; see {@link TaskSpec.readFace} for the
|
|
397
|
+
* task layer and the resolution order). An interactive single-user product declares
|
|
398
|
+
* `readFace: "open"` here in one line; the engine default stays "roots" (D-1b). Under org
|
|
399
|
+
* governance this seat may still open (it is the deployment's own declaration); the task layer
|
|
400
|
+
* may not. Beside a read-only (verifier) mount this seat's "open" silently CLAMPS to roots (a
|
|
401
|
+
* deployment default cannot override a load-bearing containment wall) — TaskSpec.readFace's own
|
|
402
|
+
* "open" there is the genuine per-task contradiction, and that one still refuses loudly (#123).
|
|
403
|
+
*/
|
|
404
|
+
readFace?: import("../tools/fs/read-face.js").ReadFace;
|
|
405
|
+
/**
|
|
406
|
+
* design/96 §20 (P1a) — DEPLOYMENT-DECLARED training knowledge cutoff per model id (e.g.
|
|
407
|
+
* `{ "qwen-3.5-35b": "2025-01", "deepseek-pro": "2024-07" }`). When the resolved model id is present, the
|
|
408
|
+
* `# Environment` block shows `Knowledge cutoff: <value>` so the agent reasons about what it may not know.
|
|
409
|
+
* Deployment-owned ON PURPOSE: core hardcodes no model→date map (a stale cutoff = a §25 honesty breach, and
|
|
410
|
+
* the model catalog is a deployment/profile concern). Omitted ⇒ no cutoff line (honest absence). Optional.
|
|
411
|
+
*/
|
|
412
|
+
modelKnowledgeCutoffs?: Record<string, string>;
|
|
413
|
+
/**
|
|
414
|
+
* Default model roles: declare your models once and let every subsystem (main task, compaction,
|
|
415
|
+
* subagent, team) resolve its role instead of repeating a model name. `TaskSpec.roles` overrides
|
|
416
|
+
* per task/scenario. e.g. `{ default: "strong", summarize: "cheap", subagent: "cheap" }`.
|
|
417
|
+
*/
|
|
418
|
+
roles?: ModelRoles;
|
|
419
|
+
/**
|
|
420
|
+
* Session lifecycle/persistence backend. Defaults to `TtlSessionStore` (in-memory, 7-day idle TTL
|
|
421
|
+
* — lost on restart). For **durable** sessions (a "session center" that survives restarts and is
|
|
422
|
+
* shared across stateless replicas), supply a `TtlSessionStore` over a durable `SessionRepo`
|
|
423
|
+
* (e.g. TiDB-backed), or a fully custom `SessionStore`. See `design/10-会话持久化与会话中心.md`.
|
|
424
|
+
*/
|
|
425
|
+
sessionStore?: import("./session.js").SessionStore;
|
|
426
|
+
/**
|
|
427
|
+
* Durable-checkpoint backend for suspend/resume (design/45). A task with `durableApproval` set
|
|
428
|
+
* persists its awaiting-human checkpoints here and ends `status:"suspended"`; `runner.resume()` reads
|
|
429
|
+
* them back — cross-replica when this is durable (service's approval/run-store). A task's own
|
|
430
|
+
* `checkpointStore` overrides this. Omitted → no durable suspension (policy `ask` stays synchronous).
|
|
431
|
+
*/
|
|
432
|
+
checkpointStore?: import("./checkpoint-store.js").CheckpointStore;
|
|
433
|
+
/**
|
|
434
|
+
* design/380 O2 — deployment-supplied resume preflight, called INSIDE the pre-CAS ladder (after the
|
|
435
|
+
* row-integrity rungs, immediately before the CAS) with a bounded deadline
|
|
436
|
+
* ({@link resumePreflightTimeoutMs}). Refusal / throw / timeout ⇒ typed `resume.preflight_rejected`
|
|
437
|
+
* (CheckpointError closed-set addition); the DEFAULT/absent disposition is `retry_later` (the safe
|
|
438
|
+
* arm — fail-closed hooks that just throw can never accidentally terminalize a row) — the
|
|
439
|
+
* entrance-screen posture of the wake-message hook (design/373 D2), generalized from "screen the
|
|
440
|
+
* wake message" to "screen the resume". A deployment binding runs to targets (a device lane) checks
|
|
441
|
+
* its placement/binding tables here: a transient obstacle answers `retry_later` (+`retryAfterMs`,
|
|
442
|
+
* the #449 G1 wait-hint carrier — the row stays `pending`, the SAME token redeems later); a
|
|
443
|
+
* PERMANENT one answers `terminal` (the row is settled by the single-shot `expire` CAS — never a
|
|
444
|
+
* silent pending-forever; bounded waiting for a target to come back also belongs HERE, inside the
|
|
445
|
+
* deadline, never in the env factory). Absent ⇒ no preflight, byte-identical resume behavior.
|
|
446
|
+
*/
|
|
447
|
+
resumePreflight?: (info: ResumePreflightInfo, signal: AbortSignal) => Promise<ResumePreflightVerdict>;
|
|
448
|
+
/**
|
|
449
|
+
* design/380 O2 — the {@link resumePreflight} deadline in ms. Default 10s (its own bound,
|
|
450
|
+
* deliberately NOT the hooks-record default: a resume preflight sits on every redeem attempt of a
|
|
451
|
+
* parked row and must answer promptly or get out of the way). Bad values (non-finite, ≤ 0,
|
|
452
|
+
* non-number) are DISCLOSED loudly through `onError` and fall back to the default (the bad-value
|
|
453
|
+
* loudness default: announce, never silently absorb).
|
|
454
|
+
*/
|
|
455
|
+
resumePreflightTimeoutMs?: number;
|
|
456
|
+
/**
|
|
457
|
+
* design/381 — the per-edited-file rewind history backend (CC FileHistory topology; replaces the
|
|
458
|
+
* E19 whole-tree `fileSnapshotStore`). WIRING IS THE OPT-IN (DV-9, the reference's own SDK
|
|
459
|
+
* posture): with a store present, every Edit/Write/NotebookEdit first-touch is backed up before
|
|
460
|
+
* the byte changes, every turn mints a history boundary keyed by its own initial session entry
|
|
461
|
+
* ("file state when this turn began"), and a {@link TaskSpec.resumeAt}+{@link TaskSpec.restoreFiles}
|
|
462
|
+
* (or {@link TaskSpec.rewindFilesTo}) run converges the tracked set back to a boundary. Omitted ⇒
|
|
463
|
+
* rewind is OFF: zero track calls, zero boundary work, zero disclosure noise; a restore request
|
|
464
|
+
* then fails loud (`rewind.store_unconfigured`). The reference impl is
|
|
465
|
+
* {@link InMemoryFileHistoryStore}; a deployment supplies a durable backend behind the same seam
|
|
466
|
+
* (backends MUST reuse the exported env-half helpers — see the contract JSDoc). Works against ANY
|
|
467
|
+
* ExecutionEnv (local or remote): bytes are read/written through the env's FileSystem face and
|
|
468
|
+
* stored host-side, so an env rebuild does not lose history.
|
|
469
|
+
*/
|
|
470
|
+
fileHistoryStore?: import("./file-history-store.js").FileHistoryStore;
|
|
471
|
+
/**
|
|
472
|
+
* design/381 DV-14 (终审落锤) — what an Edit/Write/NotebookEdit does when its FIRST-TOUCH history
|
|
473
|
+
* record cannot be persisted (`FileHistoryStore.trackEdit` answered `{ok:false}` = NO durable
|
|
474
|
+
* first-touch state exists). Default **"refuse"**: the edit is refused with a typed tool error
|
|
475
|
+
* naming the history-store failure — proceeding would leave the path indistinguishable from
|
|
476
|
+
* never-touched, and the store's post-recovery retry would then mint already-modified bytes as
|
|
477
|
+
* pristine v1 (the fake-pristine hole this default welds shut).
|
|
478
|
+
*
|
|
479
|
+
* `"proceed-unprotected"` is the availability concession arm: the edit proceeds and the failure is
|
|
480
|
+
* disclosed via `onError` (phase "rewind"). ITS RESIDUAL, stated so the choice is informed: during
|
|
481
|
+
* a store outage NO durable trace of the touch can be written, so "the pristine promise is void"
|
|
482
|
+
* lives only in this process's memory — after a process restart the risk is exactly the default
|
|
483
|
+
* arm's nightmare again (the store recovers, the next edit mints already-modified bytes as v1).
|
|
484
|
+
* A deployment choosing this arm owns that window and is expected to record the disclosed events
|
|
485
|
+
* in its own audit surface. Values outside the two-member set refuse prepare loudly.
|
|
486
|
+
*/
|
|
487
|
+
onTrackFailure?: "refuse" | "proceed-unprotected";
|
|
488
|
+
/**
|
|
489
|
+
* design/381 §5.3① / §10 S-1 — the ADDITIVE discriminant for the rewind refusal that this engine
|
|
490
|
+
* cannot decide on its own. When a restore finds no boundary for the requested entry, there are
|
|
491
|
+
* two very different worlds: the boundary NEVER existed (those turns ran with no history store, or
|
|
492
|
+
* it was reaped), or it exists in the RETIRED whole-tree snapshot epoch, which the per-edited-file
|
|
493
|
+
* seat deliberately does not restore. The engine holds no whole-tree state, so absent this probe
|
|
494
|
+
* the refusal honestly names BOTH possibilities rather than picking one — the default, and the
|
|
495
|
+
* only honest wording for a deployment that never had a whole-tree epoch at all.
|
|
496
|
+
*
|
|
497
|
+
* A deployment that DOES hold the retired epoch (its assembly layer can ask the old table whether
|
|
498
|
+
* that entry has a legacy row) supplies the bit here, and the refusal splits into the two precise
|
|
499
|
+
* messages. `true` = a whole-tree-era boundary exists for that entry; `false` = it definitively
|
|
500
|
+
* does not; `undefined` = the deployment cannot tell, which reads exactly like an absent probe.
|
|
501
|
+
*
|
|
502
|
+
* Consulted ONLY on the refusal path (never on a successful restore), so a slow probe costs
|
|
503
|
+
* nothing in the ordinary case. It is a DIAGNOSTIC input, never a control input: a probe that
|
|
504
|
+
* throws, hangs past the caller's own patience, or answers a non-boolean cannot change the outcome
|
|
505
|
+
* — the refusal still happens, the wording falls back to the honest both-possibilities form, and
|
|
506
|
+
* the bad value is disclosed through `onError` (phase "rewind") rather than silently absorbed.
|
|
507
|
+
*/
|
|
508
|
+
legacyRewindBoundaryProbe?: (query: {
|
|
509
|
+
sessionId: string;
|
|
510
|
+
entryId: string;
|
|
511
|
+
}) => boolean | undefined | Promise<boolean | undefined>;
|
|
512
|
+
/**
|
|
513
|
+
* design/99 §E6 — DURABLE per-session permission rules (a deny-narrowing folded into the task-time tool
|
|
514
|
+
* policy). Omitted ⇒ the feature is OFF (no rules read, zero behavior change). A deployment supplies a
|
|
515
|
+
* cross-replica backend whose CAS-rev + tighten-only semantics match the core
|
|
516
|
+
* {@link import("./session-policy-store.js").InMemorySessionPolicyStore}. Rules are read at prepare time for a
|
|
517
|
+
* task carrying its own `sessionId` (a delegated subagent inherits none) and composed BEFORE the caller
|
|
518
|
+
* policy, so they can only SUBTRACT capability. core does NOT verify owner/operator identity — the store
|
|
519
|
+
* honors the `operator` flag the (service) caller freezes from a verified principal.
|
|
520
|
+
*/
|
|
521
|
+
sessionPolicyStore?: import("./session-policy-store.js").SessionPolicyStore;
|
|
522
|
+
/**
|
|
523
|
+
* design/389 — the ONE permission-rule seam: the unified store, partitioned by source
|
|
524
|
+
* (`org | user | project | session`), built by `createPermissionRuleStoreProvider({ durable?, org?, session? })`.
|
|
525
|
+
*
|
|
526
|
+
* It is the one seam in this file that LOOSENS, and it is shaped so it can only do so within limits the
|
|
527
|
+
* engine holds. The provider hands out a store whose ONE read query answers every partition with each
|
|
528
|
+
* row's source and effective status; the write face is an exported BACKEND CONTRACT for the durable
|
|
529
|
+
* partition (an out-of-repo store twin builds against the same definitions instead of mirroring them),
|
|
530
|
+
* but the ENGINE reaches it only on the consent lanes — redemption of a confirmed human decision, the
|
|
531
|
+
* tighten-delete, and the sync join — a wiring invariant pinned by a registered-caller scan
|
|
532
|
+
* (test/permission-rule-writer-callers); no exported convenience mints a rule around consent. Personal
|
|
533
|
+
* rules are consumed post-fold, in the gate's ask branch, and never resolve an ask carrying
|
|
534
|
+
* `requiresRealApproval` or one a PreToolUse hook raised.
|
|
535
|
+
*
|
|
536
|
+
* The ORG partition (present iff the provider was composed with an `org` config — the governance
|
|
537
|
+
* declaration, design/182 §7.4) is the outermost TIGHTENING authority: every tool call is adjudicated
|
|
538
|
+
* against the org's published deny/ask snapshot BEFORE the ask-resolution chain (org deny > org ask >
|
|
539
|
+
* personal allow rule > bare ask), and while the org partition cannot adjudicate the WHOLE decision
|
|
540
|
+
* boundary fails closed — every terminal allow becomes a real-approval ask and both ask→allow seams are
|
|
541
|
+
* disarmed. The SESSION partition (present iff composed with one) is where `{kind:"session"}` grants
|
|
542
|
+
* land and are read from, spliced in front of the durable rows on every adjudication of that session.
|
|
543
|
+
*
|
|
544
|
+
* Omitted ⇒ no rule lane, no org layer, no session source: no rules are read, no field is added to any
|
|
545
|
+
* ask, and the decision path is byte-identical to a build without it. An unauthenticated task (no
|
|
546
|
+
* `principal`) resolves to zero personal rules rather than to a shared bucket (the org partition still
|
|
547
|
+
* adjudicates), and a store whose durable partition cannot be read yields zero rules plus a disclosure
|
|
548
|
+
* — a loosening face fails toward asking.
|
|
549
|
+
*/
|
|
550
|
+
permissionRuleStore?: import("./permission-rule-provider.js").PermissionRuleStoreProvider;
|
|
551
|
+
/**
|
|
552
|
+
* design/182 §4.5 (F-011) — declare that this deployment keeps its permission rules in the
|
|
553
|
+
* IDENTITY-LESS local bucket: a task with no `principal` resolves rules through the provider's
|
|
554
|
+
* `forLocalOwner()` face instead of resolving to zero rules.
|
|
555
|
+
*
|
|
556
|
+
* A DECLARATION, never an inference: the local bucket is "this machine's owner", a fact only the
|
|
557
|
+
* deployment knows. Omitted/`false` ⇒ an unauthenticated task reads no rules. Declared `true` while the
|
|
558
|
+
* wired provider has no local-owner face (or while no provider is wired at all) is a configuration
|
|
559
|
+
* contradiction and is refused loudly at prepare, rather than silently degrading to "the rules this
|
|
560
|
+
* person approved stopped applying".
|
|
561
|
+
*
|
|
562
|
+
* A local-owner bucket cannot cloud-sync (syncing is an authenticated act); it is adopted into a
|
|
563
|
+
* principal bucket by `adoptFilePermissionRuleStore`, after which the local-owner face resolves the
|
|
564
|
+
* adopted principal's bucket forever.
|
|
565
|
+
*/
|
|
566
|
+
localOwnerRules?: boolean;
|
|
567
|
+
/**
|
|
568
|
+
* design/182 §9 — DECLARE that this deployment drives cloud sync (`syncPermissionRules`) for the
|
|
569
|
+
* wired rule store. Purely a disclosure input: it is reported as `permissionRules.syncWired` on the
|
|
570
|
+
* wiring manifest and changes no decision. It exists because the sync loop is HOST-driven (core
|
|
571
|
+
* bundles no fetch and owns no timer), so there is nothing for the engine to infer — and "are this
|
|
572
|
+
* machine's standing approvals shared with other devices and a server?" is exactly the trust-domain
|
|
573
|
+
* fact an operator must be able to read off the manifest instead of guessing.
|
|
574
|
+
*/
|
|
575
|
+
permissionRuleSyncWired?: boolean;
|
|
576
|
+
/**
|
|
577
|
+
* design/99 §K — resolve the **per-principal runtime ENTITLEMENTS** the engine
|
|
578
|
+
* ENFORCES server-side, keyed by `spec.principal`. A deployment (the SERVICE) implements it over center's
|
|
579
|
+
* `GET /api/config/effective?principal=` `runtimeCaps`. Resolved once at prepare time.
|
|
580
|
+
*
|
|
581
|
+
* 🔐 This is a TRUSTED deployment seam, NOT a caller-facing `TaskSpec` field: entitlements are purely
|
|
582
|
+
* server-governed — an untrusted caller must never be able to self-grant a capability. `undefined` (no
|
|
583
|
+
* resolver, or an unset cap) = NO per-principal restriction (TIGHTEN-ONLY: a cap can only ever DENY or
|
|
584
|
+
* FORCE; absence falls back to the deployment-level default). Symmetric with {@link sessionPolicyStore}.
|
|
585
|
+
*
|
|
586
|
+
* core enforces the six caps it CAN enforce in the engine (polarity per member on {@link RuntimeCaps}):
|
|
587
|
+
* `allowWorkflows` (the third stage of the workflows gate — task opt-in ∧ deployment capability ∧ this),
|
|
588
|
+
* `allowFork` (the Agent-fork route), `allowObservers` (explicit opt-in for observer auto-spawn),
|
|
589
|
+
* `autoMode` (the per-principal deny bit of the user-enabled auto mode), `allowMemoryOptOut` (may this
|
|
590
|
+
* principal declare a capture opt-out), and `forceDurableGate` (forces a run onto the durable-approval
|
|
591
|
+
* path so a policy `ask` suspends to the wire — where a checkpoint store is wired; a store-less leg
|
|
592
|
+
* announces `config.durable_gate_unavailable`). `allowUltracode` is a shell/UX concern (reasoning-tier,
|
|
593
|
+
* not a core primitive); `allowBypassPermissions` is enforced at the service settings-resolution layer
|
|
594
|
+
* (the engine sees only a resolved policy, never a "this allow came from bypass" signal).
|
|
595
|
+
*/
|
|
596
|
+
runtimeCapsResolver?: (principal: string | undefined) => RuntimeCaps | undefined | Promise<RuntimeCaps | undefined>;
|
|
597
|
+
/**
|
|
598
|
+
* Administrator lock layer (ruled 2026-08-05) — the deployment's config-time declaration of which
|
|
599
|
+
* lockable keys are locked (closed registry: `mcp` / `toolPolicy` / `compliancePosture` /
|
|
600
|
+
* `retentionPolicy`; see {@link import("./locked-config.js").LOCKED_KEY_REGISTRY}). A task spec
|
|
601
|
+
* carrying a value for a locked key is refused wholesale at the prepare door
|
|
602
|
+
* (`config.locked_key`); an unknown key in this declaration is itself refused
|
|
603
|
+
* (`config.locked_registry` — a security control that doesn't parse must not half-arm).
|
|
604
|
+
*
|
|
605
|
+
* 🔐 TRUSTED deployment seam, never a `TaskSpec` field. Locks are minted only by the deployment's
|
|
606
|
+
* own configuration plane (env / central governance domain) under that plane's existing
|
|
607
|
+
* authentication — "who can unlock" ≡ "who can change the deployment config"; there is no
|
|
608
|
+
* request-time unlock.
|
|
609
|
+
*/
|
|
610
|
+
lockedConfig?: import("./locked-config.js").LockedConfig;
|
|
611
|
+
/**
|
|
612
|
+
* Compliance capability veto (ruled 2026-08-05) — resolve the running principal's compliance
|
|
613
|
+
* POSTURE (closed profile + closed additional denies; effective denies =
|
|
614
|
+
* `BUILTIN_COMPLIANCE_DENIES[profile] ∪ additionalDenies`, supply can only tighten). Mirrors
|
|
615
|
+
* {@link runtimeCapsResolver}: a 🔐 TRUSTED deployment seam keyed by `spec.principal`, resolved
|
|
616
|
+
* ONCE per prepare; the verdict is frozen for the run and follows the delegation tree through the
|
|
617
|
+
* inherited principal. Fail-closed: a resolver that throws (or returns an unparseable posture)
|
|
618
|
+
* denies EVERY managed capability for the run and surfaces via `onError` (phase `"config"`); a
|
|
619
|
+
* denied capability that the spec explicitly requests refuses the prepare loudly
|
|
620
|
+
* (`config.compliance_denied`, or `config.compliance_required` when the refusal came from the
|
|
621
|
+
* resolver fault rather than an adjudicated posture). `undefined` (no resolver / no posture for
|
|
622
|
+
* the principal) = no compliance restriction — existing deployments are untouched.
|
|
623
|
+
*/
|
|
624
|
+
compliancePostureResolver?: (principal: string | undefined) => import("./compliance.js").CompliancePosture | undefined | Promise<import("./compliance.js").CompliancePosture | undefined>;
|
|
625
|
+
/**
|
|
626
|
+
* Org-memory tenant admission (ruled 2026-08-05) — the fail-closed adjudicator for REQUEST-origin
|
|
627
|
+
* `org:` memory scopes (`TaskSpec.memory` / a subagent's `AgentDefinition.memory`, under the v2
|
|
628
|
+
* scope contract). Runs at prepare, after `normalizeMemorySpec` and before any memory-backend I/O.
|
|
629
|
+
* Verdicts are two-state ({@link import("./memory-admission.js").MemoryAdmissionVerdict}): any
|
|
630
|
+
* request-origin org scope not admitted refuses the WHOLE prepare (`memory.admission_denied`) —
|
|
631
|
+
* silent narrowing would turn mount visibility into an org-membership probe. Absent resolver +
|
|
632
|
+
* request-origin org scope ⇒ `memory.admission_required` (fail-closed, transient — the control
|
|
633
|
+
* plane may be mid-rollout); resolver throw ⇒ same code. Deployment-origin scopes
|
|
634
|
+
* ({@link deploymentMemoryScopes}) pass WITHOUT this resolver; when present it may still narrow
|
|
635
|
+
* them (disclosed via `onError`, phase `"memory"`).
|
|
636
|
+
*
|
|
637
|
+
* 🔐 TRUSTED deployment seam, never a `TaskSpec` field (an untrusted caller must never adjudicate
|
|
638
|
+
* its own tenant admission). The admitted org set is frozen into the run's child chain — a
|
|
639
|
+
* delegated child's request plane can only narrow it, never exceed it.
|
|
640
|
+
*/
|
|
641
|
+
memoryScopeAdmission?: import("./memory-admission.js").MemoryScopeAdmission;
|
|
642
|
+
/**
|
|
643
|
+
* The deployment's SELF-CERTIFIED org memory scopes (ruled 2026-08-05): org keys the operator
|
|
644
|
+
* pinned in the deployment's own configuration (env / registry defaultScopes). A scope listed here
|
|
645
|
+
* is `origin: "deployment"` at the admission door — the operator writing the config IS the
|
|
646
|
+
* authorization: no principal required, no {@link memoryScopeAdmission} resolver required (the
|
|
647
|
+
* single-user zero-migration path). Everything not listed here that requests an org plane is
|
|
648
|
+
* `origin: "request"` and must pass fail-closed admission.
|
|
649
|
+
*/
|
|
650
|
+
deploymentMemoryScopes?: readonly string[];
|
|
651
|
+
/**
|
|
652
|
+
* Managed retention policy (ruled 2026-08-05, retention.ts). Core consumes it ONLY for the
|
|
653
|
+
* startup/prepare capability validation: when the locked-config plane LOCKS the `retentionPolicy`
|
|
654
|
+
* key AND this policy is configured, every wired store (session/checkpoint/tool-result) must
|
|
655
|
+
* declare `retention: "managed"` — otherwise the deployment refuses to start
|
|
656
|
+
* (`config.retention_capability`; the prepare preflight is the per-leg backstop). The engine
|
|
657
|
+
* itself never deletes data on the task path — retention EXECUTION (scheduling, retries, audit)
|
|
658
|
+
* is the deployment scheduler's job against the `ManagedRetentionCapability` contract.
|
|
659
|
+
*/
|
|
660
|
+
retentionPolicy?: import("./retention.js").RetentionPolicy;
|
|
661
|
+
/**
|
|
662
|
+
* design/143 批2b — the auto-mode classifier's DEPLOYMENT assembly face. This is the
|
|
663
|
+
* CAPABILITY arm of the three-arm arming: a run arms auto mode only when the task carries the
|
|
664
|
+
* auto-mode INTENT ({@link TaskSpec.autoModeRequested}, or the same bit inherited on the chain by an
|
|
665
|
+
* engine-spawned child) AND this face is present AND the per-principal DENY bit is not set
|
|
666
|
+
* ({@link RuntimeCaps.autoMode}` !== false` — absent is not a denial).
|
|
667
|
+
*
|
|
668
|
+
* 🔐 Trust gate (the CC 2.1.207 three-source invariant): classifier RULES enter EXCLUSIVELY
|
|
669
|
+
* here — a deployment-constructed object, never a `TaskSpec` field, never a repo-file plane. The
|
|
670
|
+
* prompt is assembled from the SHA-locked CC 2.1.207 assets (`buildAutoModePrompt`) with these
|
|
671
|
+
* rules merged CC-exactly (user rules replace a section; the `"$defaults"` sentinel splices the
|
|
672
|
+
* stock rules back in). The classify leg runs on the `classifier` role's model (fallback
|
|
673
|
+
* `summarize → default`; tier default `flash`) via the brain's simple-completion pipe — same
|
|
674
|
+
* lane as compaction/consolidation, no tools, no session writes.
|
|
675
|
+
*/
|
|
676
|
+
autoMode?: {
|
|
677
|
+
/** Rule overrides for the four permissions sections (omitted section = CC defaults verbatim). */
|
|
678
|
+
rules?: import("./auto-mode-prompt.js").AutoModeRules;
|
|
679
|
+
/** Settings-plane deny rules rendered into the `<settings_deny_rules>` slot (circumvention catch). */
|
|
680
|
+
settingsDenyRules?: readonly string[];
|
|
681
|
+
/** Extra `## Session Context` facts (e.g. the user-identity line). */
|
|
682
|
+
sessionContext?: readonly string[];
|
|
683
|
+
/** Transcript-window bounds for the classify prompt (defaults: 40 entries / 2000 chars each). */
|
|
684
|
+
window?: import("./auto-mode-prompt.js").AutoModeWindowOptions;
|
|
685
|
+
/** Classify round-trip cap, ms (default 15_000). */
|
|
686
|
+
timeoutMs?: number;
|
|
687
|
+
/** Consecutive-failure threshold opening the one-way session breaker (default 3). */
|
|
688
|
+
failureThreshold?: number;
|
|
689
|
+
/** Fired ONCE when the session breaker opens (the "退回非 auto" alarm — surface it to the operator). */
|
|
690
|
+
onBreakerOpen?: (info: {
|
|
691
|
+
consecutiveFailures: number;
|
|
692
|
+
lastCause: string;
|
|
693
|
+
}) => void;
|
|
694
|
+
/**
|
|
695
|
+
* The classifier DENIAL LIMIT (CC 2.1.250 `FO`/`AKe`): a run whose classifier keeps blocking falls
|
|
696
|
+
* back to a PERSON instead of being denied without end. Per run: a `block` first increments the
|
|
697
|
+
* consecutive and total counts and then judges `consecutive >= maxConsecutive || total >= maxTotal`
|
|
698
|
+
* — the block that reaches a bound is itself the one that becomes an `ask` (the 3rd consecutive
|
|
699
|
+
* block asks). That ask carries `requiresRealApproval: true` (no automatic lane may clear it — not
|
|
700
|
+
* a sandbox admission, not an inherited resolver, not a blanket `onAsk:"allow"`) plus the additive
|
|
701
|
+
* `denialLimitFallback` member with the counts and its own auto-deny window; an ask handed to a LIVE
|
|
702
|
+
* function approver and left unanswered auto-denies after `autoDenyAfterMs` (default 120s; `0` = no
|
|
703
|
+
* window) — the window is armed at that hand-out only, so a durably PARKED fallback carries `0`
|
|
704
|
+
* (nothing counts down on that lane). A classifier allow, or a
|
|
705
|
+
* person's allow of the fallback ask, zeroes the consecutive count; reaching the total bound
|
|
706
|
+
* zeroes everything. With no approver wired at all (headless), the fallback has nowhere to go and
|
|
707
|
+
* the run STOPS with `TaskResult.errorCode = "classifier.denial_limit"` (a notice of the same
|
|
708
|
+
* code is minted).
|
|
709
|
+
*
|
|
710
|
+
* Every member optional (defaults 3 / 20 / 120_000). A present member with a bad value is refused
|
|
711
|
+
* loudly at prepare — never clamped, never silently read as the default.
|
|
712
|
+
* See {@link import("./auto-mode.js").AutoModeDenialLimitOptions}.
|
|
713
|
+
*/
|
|
714
|
+
denialLimit?: import("./auto-mode.js").AutoModeDenialLimitOptions;
|
|
715
|
+
/**
|
|
716
|
+
* #503 — OPT IN to recording this arming's serializable criteria (an
|
|
717
|
+
* {@link import("./auto-mode-arming.js").AutoModeArmingRecipe}) on the constraint-chain entries a
|
|
718
|
+
* durable park freezes, so a CROSS-PROCESS redemption can rebuild an equivalent classifier over its
|
|
719
|
+
* own model leg ({@link import("./auto-mode-rebuild.js").rebuildAutoModeDecider}) instead of
|
|
720
|
+
* answering `unavailable` forever and parking the same approval for a human on every redemption.
|
|
721
|
+
*
|
|
722
|
+
* DEFAULT OFF, and the default is the safety story: a row minted without it is byte-identical to
|
|
723
|
+
* the pre-#503 shape, so a mixed-version fleet (a worker that predates this field resuming a row a
|
|
724
|
+
* newer worker minted) keeps the digest it can compute. Turn it on fleet-wide, not per-worker.
|
|
725
|
+
*
|
|
726
|
+
* What gets written down: the rule overrides, the settings-deny rules, the session-context facts,
|
|
727
|
+
* the window bounds and the two knobs — i.e. deployment-authored configuration, on a durable row.
|
|
728
|
+
* `sessionContext` is the one field that routinely carries per-session facts (a user-identity
|
|
729
|
+
* line); a deployment that treats those as sensitive at rest should keep them out of the arming
|
|
730
|
+
* face rather than out of this flag.
|
|
731
|
+
*
|
|
732
|
+
* Recording is CONDITIONAL on the session breaker still being closed at park time: a session that
|
|
733
|
+
* already fell back to non-auto hands nothing forward (the redemption must never run wider than the
|
|
734
|
+
* ancestor it inherits). A `true` here with a non-serializable face announces once and records
|
|
735
|
+
* nothing (`phase:"config"`).
|
|
736
|
+
*/
|
|
737
|
+
persistArming?: boolean;
|
|
738
|
+
/**
|
|
739
|
+
* #503 — the deployment's auto-mode SETTINGS GENERATION (a config revision, a settings hash, a
|
|
740
|
+
* publish id — the string is opaque to the engine). Recorded in the arming recipe and compared at
|
|
741
|
+
* rebuild time against the REDEEMING deployment's own: it is a declaration axis that can only make
|
|
742
|
+
* the rebuild stricter (see {@link import("./auto-mode-arming.js").foldAutoModeArming}). Absent =
|
|
743
|
+
* undeclared, which the fold treats as "cannot assume unchanged".
|
|
744
|
+
*/
|
|
745
|
+
settingsEpoch?: string;
|
|
746
|
+
};
|
|
747
|
+
/**
|
|
748
|
+
* design/129-B — PROCESS-level observer for background delegation children (Agent/Fork
|
|
749
|
+
* `run_in_background`): `spawn` (at registry registration) / `tick` (every forwarded `task_progress`)
|
|
750
|
+
* / `terminal` (after the registry settle), for the child's WHOLE lifetime. Motivation: every per-leg
|
|
751
|
+
* sink (`RunInternals.onForwardEvent` etc.) carries the SPAWNING turn's lifecycle — correct for sync
|
|
752
|
+
* children, structurally wrong for a session-scoped background child that outlives the turn (the
|
|
753
|
+
* deployment rightly freezes per-leg publishers at leg settle, which silences the child). This seam is
|
|
754
|
+
* keyed to nothing but the process: wire it once at Runner construction and publish fleet rows /
|
|
755
|
+
* completion pushes from it. Observability only — a throwing observer is swallowed, never faults a run.
|
|
756
|
+
*/
|
|
757
|
+
onBackgroundChildEvent?: (event: BackgroundChildEvent) => void;
|
|
758
|
+
/**
|
|
759
|
+
* #281 件B — PROCESS-level observer for EVERY delegated child leg's lifecycle (`spawn`/`terminal`),
|
|
760
|
+
* all delegation lanes at once: synchronous and steer-handle delegations (whose spawn/settle
|
|
761
|
+
* previously reached only the trusted `RunInternals.onSubagentSpawn` third parameter — and whose
|
|
762
|
+
* `settled` is a void promise), background/fork/revive children (whose {@link BackgroundChildEvent}
|
|
763
|
+
* family this seat complements, not replaces — BCE stays the fleet-row lane with registry `a*`
|
|
764
|
+
* handles and ticks; this seat is the leg-identity lane), and workflow-spawned agents. Frames are
|
|
765
|
+
* minted at the child leg's own runner chokepoint and carry the #281 件A identity envelope plus a
|
|
766
|
+
* terminal status summary — see {@link DelegationLifecycleEvent} for the emission points, the
|
|
767
|
+
* per-leg semantics and the recorded honest absences. Wire it once at Runner construction; a
|
|
768
|
+
* deployment needs NO RunInternals access to observe delegation any more. Observation only — a
|
|
769
|
+
* throwing or rejecting observer is contained ({@link deliverDelegationLifecycle}) and never
|
|
770
|
+
* faults the child run; a PRESENT non-function value here is announced once per process and the
|
|
771
|
+
* frames are simply not delivered (#123 — a bad seat must be loud, and this stream's loud exit is
|
|
772
|
+
* the seat defect, not a per-frame console flood).
|
|
773
|
+
*/
|
|
774
|
+
onDelegationLifecycle?: (event: DelegationLifecycleEvent) => void;
|
|
775
|
+
/**
|
|
776
|
+
* design/98 (S8) — the HARD sandbox seam for LLM-AUTHORED workflow scripts (`TaskSpec.selfOrchestration`).
|
|
777
|
+
* A deployment supplies an isolated-vm / separate-process runner whose `safeForUntrustedScripts === true`;
|
|
778
|
+
* core does NOT ship one (the Node `vm` dev runner is NOT a security boundary). With this unset (or
|
|
779
|
+
* `safeForUntrustedScripts:false`), `selfOrchestration` is FAIL-CLOSED: the `run_workflow` tool is not
|
|
780
|
+
* mounted and the orchestration prompt is not injected. See {@link WorkflowGovernanceBaseline}.
|
|
781
|
+
*/
|
|
782
|
+
workflowScriptRunner?: import("../orchestration/workflow-script-runner.js").WorkflowScriptRunner;
|
|
783
|
+
/**
|
|
784
|
+
* design/98 §2.5 (S8b) — the deployment-trusted governance every workflow-spawned sub-agent inherits
|
|
785
|
+
* (tighten-only). REQUIRED (alongside a hard `workflowScriptRunner`) for `selfOrchestration` to activate:
|
|
786
|
+
* unset ⇒ FAIL-CLOSED (the `run_workflow` tool is not mounted — a script-spawned agent must never run
|
|
787
|
+
* without a governed baseline). See {@link WorkflowGovernanceBaseline}.
|
|
788
|
+
*/
|
|
789
|
+
workflowGovernanceBaseline?: WorkflowGovernanceBaseline;
|
|
790
|
+
/** design/98 §D.6 (S8c) — deployment hard CEILINGS for an LLM-authored workflow (script length / agent
|
|
791
|
+
* count / timeouts / per-child cost+token caps / budget / result+log size). Defaults bound a runaway. */
|
|
792
|
+
workflowLimits?: import("../orchestration/run-workflow-tool.js").WorkflowLimits;
|
|
793
|
+
/** design/98 §D.5 (S8c) — re-invoke seam: called once when an LLM-authored workflow completes, with a
|
|
794
|
+
* redacted, bounded summary. Unset ⇒ the run still completes + is queryable; the originator just isn't
|
|
795
|
+
* pushed a notification. */
|
|
796
|
+
workflowCompletionNotifier?: import("../orchestration/run-workflow-tool.js").WorkflowCompletionNotifier;
|
|
797
|
+
/** design/97 S1b / design/98 §D.5 — persist LLM-authored workflow runs here for cross-replica `/workflows`
|
|
798
|
+
* history (opt-in; in-process runs are still subscribable without it). */
|
|
799
|
+
workflowRunStore?: import("./workflow-run-store.js").WorkflowRunStore;
|
|
800
|
+
/** design/151 S1a — the durable background-agent execution ledger (the `workflowRunStore` twin for
|
|
801
|
+
* `a*` rows): registrations write restart-surviving rows, and the auto-mounted TaskOutput/TaskStop
|
|
802
|
+
* fall back to it on a live-registry miss (terminal snapshots / honest cross-instance reads, behind
|
|
803
|
+
* the fail-closed §3.1c predicate). Opt-in; absent ⇒ the in-memory-only pre-151 lifecycle. A
|
|
804
|
+
* deployment that composes its own Agent tool MUST pass the SAME instance as
|
|
805
|
+
* `SubagentToolOptions.background.agentStore` (spawn-side writer half) — the engine cannot
|
|
806
|
+
* verify the pairing (the Agent tool is deployment-composed), and a half-wired deployment is a
|
|
807
|
+
* SILENT degrade: reader-only ⇒ no rows ever exist; writer-only ⇒ the auto-mounted fallbacks
|
|
808
|
+
* here never see them (RB-37). Third consumer: delegated
|
|
809
|
+
* CHILDREN's auto-mounted TaskOutput/TaskStop/AgentTranscript read THIS deps too (prepare-task
|
|
810
|
+
* wires them from the child's runner) — a sub-runner with different deps splits the read face. */
|
|
811
|
+
backgroundAgentStore?: import("./background-agent-store.js").BackgroundAgentStore;
|
|
812
|
+
/** design/151 §7 S3c — the durable SendMessage mailbox (delivery-ladder third rung): a message to a
|
|
813
|
+
* SETTLED named teammate is parked here ONLY inside the tier-3 claim-winner window and delivered
|
|
814
|
+
* when the revival spawns (claim/lease + ack — never a destructive drain; a crashed revival's
|
|
815
|
+
* lease expires and the messages re-serve with their original seq). Tier-3 activates only when
|
|
816
|
+
* BOTH this and `backgroundAgentStore` are wired AND a delegation tool (createSubagentTool) is
|
|
817
|
+
* mounted on the task — anything less keeps SendMessage's honest refusal texts, byte for byte.
|
|
818
|
+
* Same single-instance pairing discipline as `backgroundAgentStore` (RB-37): a deployment that
|
|
819
|
+
* composes its own tools must thread the SAME instance everywhere. */
|
|
820
|
+
mailboxStore?: import("./mailbox-store.js").MailboxStore;
|
|
821
|
+
/**
|
|
822
|
+
* Subagent transcript persistence — the delegation ENTRY caps (CC parity values: 20 concurrent /
|
|
823
|
+
* 200 cumulative per session tree; defaults exported as `DELEGATION_MAX_CONCURRENT_DEFAULT` /
|
|
824
|
+
* `DELEGATION_MAX_PER_SESSION_DEFAULT`). Key = `(scope, rootSessionId)`, full depth (grandchildren
|
|
825
|
+
* share the tree's pool). `maxConcurrent` bounds the tree's RUNNING children of every local lane:
|
|
826
|
+
* running/pending a* handles in this process's registry PLUS the synchronous and forked children in
|
|
827
|
+
* flight in this process (parked does not burn a slot — a suspension is not concurrency; a revival
|
|
828
|
+
* claim counts like a spawn); `maxCumulativePerSession` bounds the RETAINED-WINDOW cumulative count
|
|
829
|
+
* (registry-retained + store-retained rows, plus those same in-flight local children — a reaped row
|
|
830
|
+
* returns its quota, and a synchronous child retains nothing so it leaves the window when it
|
|
831
|
+
* settles; deliberately NOT CC's lifetime-monotonic session counter, which would require a
|
|
832
|
+
* persistent counting surface this economic bound does not justify — registered divergence). One
|
|
833
|
+
* consequence, stated rather than left to be discovered: a tree that only ever spawns synchronously
|
|
834
|
+
* is bounded by the concurrency cap alone. Refusals are coded (`delegation.concurrency_cap` /
|
|
835
|
+
* `delegation.session_cap`) with the current value and this knob's name in the text.
|
|
836
|
+
*
|
|
837
|
+
* BAD VALUES REFUSE LOUDLY at prepare (`config.delegation_entry_caps`, the #123 posture): a
|
|
838
|
+
* non-positive/non-integer/NaN member, or a resolved pair where `maxConcurrent` exceeds
|
|
839
|
+
* `maxCumulativePerSession` (you cannot run more at once than you may ever create) — never a
|
|
840
|
+
* silent fold to the defaults. Per-replica bound (multi-replica deployments are each honestly
|
|
841
|
+
* bounded; row-level CAS owns correctness, this cap owns economics).
|
|
842
|
+
*/
|
|
843
|
+
delegationEntryCaps?: {
|
|
844
|
+
maxConcurrent?: number;
|
|
845
|
+
maxCumulativePerSession?: number;
|
|
846
|
+
};
|
|
847
|
+
/** design/176 — deployment tuning for the always-on peer-message admission gate (SendMessage entry
|
|
848
|
+
* judgment: rate/dedup/hop-chain/queue bounds). Per-field range-validated against the upstream
|
|
849
|
+
* table with out-of-range values falling back to that field's default; there is NO off switch —
|
|
850
|
+
* absent means the default table, not "guard off". Threaded into the auto-mounted SendMessage as
|
|
851
|
+
* `SendMessageToolOptions.admission`; read per call (a value change governs the next message). */
|
|
852
|
+
peerAdmission?: Partial<import("../agents/peer-admission.js").PeerAdmissionConfig>;
|
|
853
|
+
/**
|
|
854
|
+
* design/385 §2.1 / §5.1 — the peer-session DIRECTORY: the discovery truth of the cross-session
|
|
855
|
+
* lane (other sessions of this engine for the same user). A HOST implements the contract (cli: the
|
|
856
|
+
* pid-keyed registration files; server: its session table) and writes the rows; the engine only
|
|
857
|
+
* reads them. Seat semantics = the mailbox seat's: NO seat, NO lane — every face stays byte-identical
|
|
858
|
+
* to a pre-385 build. With the seat wired the engine (a) mounts `ListAgents` (alias `ListPeers`),
|
|
859
|
+
* (b) opens SendMessage's `session.<id>` address arm and its last `name [ref]` rung, (c) drains this
|
|
860
|
+
* session's own `session.<sessionId>` box at every turn boundary (the mailbox contract's third
|
|
861
|
+
* consumption chain), and (d) splices the cross-session rule into the auto-mode classifier slot.
|
|
862
|
+
* HARD PRECONDITION (design/385 §1.2⑥): `mailboxStore` must be wired AND declare
|
|
863
|
+
* `crossProcessSafe: true`; otherwise the lane is REFUSED at prepare with a named
|
|
864
|
+
* `config.peer_lane_unmounted` notice (never mounted on luck), and the seat is inert for that run.
|
|
865
|
+
*/
|
|
866
|
+
peerDirectory?: import("../agents/peer-directory.js").PeerDirectory;
|
|
867
|
+
/**
|
|
868
|
+
* design/385 §4.4 — this deployment's `crossSessionInbound` setting LAYERS for the drain-point
|
|
869
|
+
* judgment (managed / user / repo, resolved by `resolveCrossSessionInboundSetting` — explicit value
|
|
870
|
+
* wins, unset ⇒ mode parity, an unrecognized value forces `hold` loudly). Read at every drain, so a
|
|
871
|
+
* host that re-reads its settings files hands the current layers through a getter. Absent = unset
|
|
872
|
+
* everywhere (mode parity, the CC default). Inert without {@link peerDirectory}.
|
|
873
|
+
*/
|
|
874
|
+
crossSessionInbound?: import("../agents/cross-session-judge.js").CrossSessionInboundSettingLayers | (() => import("../agents/cross-session-judge.js").CrossSessionInboundSettingLayers);
|
|
875
|
+
/**
|
|
876
|
+
* design/385 §4.4 (slice 4) — this deployment's `dialogExpiry` (CC settings key, verbatim vocabulary
|
|
877
|
+
* `"60s" | "5m" | "10m" | "never"`, default `"5m"`): how long a HELD cross-session message whose cause
|
|
878
|
+
* a human review could resolve (mode-mismatch / no-mode-asserted / invalid attestation) waits in this
|
|
879
|
+
* session's held queue before it resolves to its safe no-action default — EXPIRED, dropped WITH a
|
|
880
|
+
* receipt to the sender, never silently. `"never"` disables the deadline. Read at every drain round
|
|
881
|
+
* (getter form for a host that re-reads its settings). A value outside the vocabulary is announced
|
|
882
|
+
* ONCE per leg through `onError` (`classification: "peer-dialog-expiry"`) and the default applies —
|
|
883
|
+
* a garbage setting never silently reads as a policy. Absent = the default. Inert without
|
|
884
|
+
* {@link peerDirectory}.
|
|
885
|
+
*/
|
|
886
|
+
crossSessionDialogExpiry?: import("../agents/peer-notices.js").CrossSessionDialogExpiry | (() => import("../agents/peer-notices.js").CrossSessionDialogExpiry | undefined);
|
|
887
|
+
/**
|
|
888
|
+
* design/164 件五 — DEPLOYMENT-level usage governance: allowances that span TASKS, evaluated per
|
|
889
|
+
* principal (or once for the whole deployment when a task declares none). A different axis from
|
|
890
|
+
* `TaskSpec.limits`, which is the allowance ONE task asked for — an operator granting "N tokens per 5
|
|
891
|
+
* hours" cannot express it as a task limit, because nothing stops the next task from asking again.
|
|
892
|
+
*
|
|
893
|
+
* A window carries a TOKEN ceiling (`UsageWindow.maxTokens`), a MONEY ceiling (`UsageWindow.maxCostUsd`,
|
|
894
|
+
* absolute USD — the `TaskLimits.maxCostUsd` quantity one governance level up), or both: each is
|
|
895
|
+
* optional on its own and at least one is required, so a deployment governed by spend alone declares
|
|
896
|
+
* only `maxCostUsd` (a window with neither ceiling is refused, `config.usage_window_invalid`). The two
|
|
897
|
+
* are independent and every declared one binds. A $ ceiling requires a PRICED run: a task whose model
|
|
898
|
+
* has neither a {@link pricing}
|
|
899
|
+
* entry nor a `Model.cost` declaration is refused at the door (`config.usage_window_unpriced`) rather
|
|
900
|
+
* than charged the fabricated 0 an unpriced run would otherwise file into an operator's ceiling.
|
|
901
|
+
*
|
|
902
|
+
* Unset (the default) ⇒ NO governance: no ledger is read or written and no task can be refused for
|
|
903
|
+
* usage. When set, every window is evaluated at two moments:
|
|
904
|
+
* - **entry** (before the first model call of a fresh task): an exhausted window REFUSES the task with
|
|
905
|
+
* `usage.window_exhausted`, since there is no checkpoint seat yet to suspend into;
|
|
906
|
+
* - **each clean turn boundary**: an exhausted window suspends the run durably
|
|
907
|
+
* (`ResourceLimitReason:"usage_window"`, with a `resumeAfterMs` hint on the gate) when the
|
|
908
|
+
* infrastructure supports it, and otherwise stops it loudly with the same code.
|
|
909
|
+
*
|
|
910
|
+
* Resume is CALLER-DRIVEN, as everywhere else in this engine: core mints the checkpoint and the hint,
|
|
911
|
+
* and a host schedules the resume (its own scheduler, delayed by `resumeAfterMs`).
|
|
912
|
+
*
|
|
913
|
+
* Requires {@link usageWindowStore} — windows without a ledger are reported through `onError` and NOT
|
|
914
|
+
* enforced (a ledger-less window cannot be evaluated, and failing every task closed on a config typo
|
|
915
|
+
* would take the deployment down instead of telling the operator).
|
|
916
|
+
*/
|
|
917
|
+
usageWindows?: readonly import("./usage-window-store.js").UsageWindow[];
|
|
918
|
+
/** design/164 件五 — the cross-task usage ledger {@link usageWindows} is evaluated against. Core bundles
|
|
919
|
+
* `InMemoryUsageWindowStore` (process-local) and `FileUsageWindowStore` (restart-surviving); a fleet
|
|
920
|
+
* deployment plugs a database behind the same two-method seam. Ignored when `usageWindows` is unset. */
|
|
921
|
+
usageWindowStore?: import("./usage-window-store.js").UsageWindowStore;
|
|
922
|
+
/** design/97 CORE-9 (Part A) — the LOAD-BEARING resume journal for LLM-authored (`run_workflow` tool) workflows
|
|
923
|
+
* (the twin of `workflowRunStore`; service ships a durable Postgres/TiDB one). Wired into the auto-mounted
|
|
924
|
+
* run_workflow tool so a tool-launched workflow can durably RESUME across a replica crash. Opt-in. */
|
|
925
|
+
workflowJournalStore?: import("./workflow-journal-store.js").WorkflowJournalStore;
|
|
926
|
+
/** B5/F2 + design/140 §6 1a — the script-persistence / named-registry seam, threaded into the auto-mounted
|
|
927
|
+
* run_workflow tool: every invocation's resolved script is persisted (iterate via `{scriptPath}`), `{name}`
|
|
928
|
+
* resolves a saved workflow, and a registration's `defaultArgs` merge under the call-time args. Opt-in
|
|
929
|
+
* (absent ⇒ inline `script` + built-in names only). */
|
|
930
|
+
workflowScriptStore?: import("../orchestration/workflow-script-store.js").WorkflowScriptStore;
|
|
931
|
+
/** design/140 §6 1c — `false` removes the BUILT-IN named workflows (`discussion`, …) from the
|
|
932
|
+
* auto-mounted run_workflow tool (the `builtinAgents:false` analog; default ON). A deployment
|
|
933
|
+
* `workflowScriptStore` registration of the same name shadows a built-in regardless. */
|
|
934
|
+
builtinWorkflows?: boolean;
|
|
935
|
+
/**
|
|
936
|
+
* design/97 CORE-9 (Part B) — a TRUSTED deployment sink for the steerable handle of every agent an LLM-authored
|
|
937
|
+
* (`run_workflow` tool) workflow spawns. When set, the tool's `agent()` primitive runs each agent STEERABLE and
|
|
938
|
+
* emits its {@link import("../orchestration/workflow.js").WorkflowAgentHandle} here (the script never receives
|
|
939
|
+
* the handle — no host-context leak into the runner); the deployment registers it by `runId`+`label` to route a
|
|
940
|
+
* human/cross-replica steer to `handle.steer(...)`. Unset ⇒ agents run non-steerable (no overhead). Opt-in.
|
|
941
|
+
*
|
|
942
|
+
* **Interaction with {@link workflowJournalStore}**: a steerable agent runs via
|
|
943
|
+
* `agentStream`, whose results ARE RECORDED in the journal since 1.355 (complete read-face: the journal
|
|
944
|
+
* API / diagnostics coordinate show every agent) but are NEVER REPLAYED — `resumeFromRunId` on a
|
|
945
|
+
* steerable workflow still re-runs agents LIVE (steer input is not deterministic; a cache would replay
|
|
946
|
+
* a result the operator steered). Pre-1.355 the stream lane recorded NOTHING — with both deps set the
|
|
947
|
+
* journal read empty on every workflow (the zero-write case), silently.
|
|
948
|
+
*/
|
|
949
|
+
onWorkflowAgentSpawn?: (handle: import("../orchestration/workflow.js").WorkflowAgentHandle) => void;
|
|
950
|
+
/** design/72 §2.2 (B): deployment default for the suspend-loop cap (see `TaskSpec.maxSuspends`).
|
|
951
|
+
* Default 5 (loose — human-approval flows re-suspend rarely). A task's own `maxSuspends` overrides. */
|
|
952
|
+
maxSuspends?: number;
|
|
953
|
+
/** RB-458 (ruled 2026-08-03): deployment default for the outer per-brain-call guardrail — see
|
|
954
|
+
* `TaskSpec.limits.brainCallGuardrailMs` for the semantics (default one hour; `false`/`0` = off;
|
|
955
|
+
* an invalid value is a fail-loud config error). A task's own `limits.brainCallGuardrailMs`
|
|
956
|
+
* overrides it. Set it here to cover EVERY run of this Runner, delegated children included — a
|
|
957
|
+
* sub-agent's spec carries only its own `maxTurns`/`maxWalltimeMs`, so the deployment-level knob is
|
|
958
|
+
* the one that reaches a background child. */
|
|
959
|
+
brainCallGuardrailMs?: import("../brain/timeout.js").BrainCallGuardrailKnob;
|
|
960
|
+
/**
|
|
961
|
+
* design/91: injectable wall-clock source for **human-review timing** (and `Checkpoint.suspendedAt`).
|
|
962
|
+
* Defaults to `Date.now`. The Runner reads EVERY human-time timestamp through this — the synchronous
|
|
963
|
+
* `resolveAsk` wait (`t1 − t0`), the durable suspend's `suspendedAt`, and the resume's derived human
|
|
964
|
+
* latency (`now() − suspendedAt`) — so a test can inject a controllable clock (`let t = 0; now = () => t`)
|
|
965
|
+
* and assert `stats.humanReview` deterministically (design/87 §4.3 flaky-guard: no bare `Date.now()` in a
|
|
966
|
+
* hard-gate test). **Scope = human-time only**: this is NOT a general clock for token/cost/turn accounting
|
|
967
|
+
* (those keep `Date.now`); `humanReview` is a budget-EXCLUDED side observable, never folded into cost. */
|
|
968
|
+
now?: () => number;
|
|
969
|
+
/**
|
|
970
|
+
* design/138 S1 — the NEW-ARCHITECTURE memory seam (MemoryEngine over a {@link import("./memory-engine/types.js").MemoryBackend}).
|
|
971
|
+
* When set AND `spec.memory.enabled`, the task runs the INJECTION-FIRST file-based memory lifecycle
|
|
972
|
+
* (materialize → session file ops → harvest with the full gate set), the CC-verbatim `# Memory`
|
|
973
|
+
* instruction + fenced derived index replace the legacy `<user_memory>` block, and NO remember/recall
|
|
974
|
+
* tools are mounted (memory = the model's ordinary file skills). **S4: unset ⇒
|
|
975
|
+
* the task runs MEMORY-LESS** — the legacy `memoryStore` runtime path was retired (one deprecation
|
|
976
|
+
* warning via `onError` phase `"config"`); this field is the ONLY live memory seam.
|
|
977
|
+
*/
|
|
978
|
+
memoryBackend?: import("./memory-engine/types.js").MemoryBackend;
|
|
979
|
+
/**
|
|
980
|
+
* design/177 — the SHARED memory-store supply seam: connected team/deployment memory libraries the
|
|
981
|
+
* model may browse and read through the `memory_list` / `memory_read` tool pair. Presence of this dep
|
|
982
|
+
* IS the deployment's intent, so it is the single mount switch — there is no TaskSpec companion knob.
|
|
983
|
+
* Per-task removal goes through the existing `TaskSpec.excludeTools` valve (a true unmount).
|
|
984
|
+
*
|
|
985
|
+
* ORTHOGONAL to {@link memoryBackend}, deliberately and completely. That one is the LOCAL memory chain
|
|
986
|
+
* (fenced index injection, file projection, harvest, entry transactions with a write path); this one is
|
|
987
|
+
* a document-only READ face reached exclusively through tool calls. Shared content never enters the
|
|
988
|
+
* injected memory block or index, never lands on a model-writable directory, and is never harvested;
|
|
989
|
+
* the two seams are not bridged in either direction. Write governance for a shared store stays entirely
|
|
990
|
+
* with the host — core's model face has no write channel at all and only relays each store's
|
|
991
|
+
* `writable` bit.
|
|
992
|
+
*
|
|
993
|
+
* Unset ⇒ neither tool is mounted (not even a deferred placeholder).
|
|
994
|
+
*/
|
|
995
|
+
sharedMemoryStores?: import("./shared-memory/types.js").SharedMemoryStoreProvider;
|
|
996
|
+
/**
|
|
997
|
+
* design/138 §2.7 — the memory-engine directory CONFIG ROOT used when {@link memoryBackend} does not
|
|
998
|
+
* pin its own directory (a {@link import("./memory-engine/file-backend.js").FileMemoryEngineBackend}
|
|
999
|
+
* exposes `directoryRoot` and wins). The per-repo memory dir derives as `<root>/<repoKey>/memory`
|
|
1000
|
+
* (repoKey = CC-style slug of the task root). Default: `AGENT_DATA_DIR` env or `~/.ai-agent`.
|
|
1001
|
+
* Location is PURE CONFIG (§2.7 r2.1): a deployment may equally construct the FileBackend over an
|
|
1002
|
+
* IN-REPO git-tracked directory for a project scope.
|
|
1003
|
+
*/
|
|
1004
|
+
memoryEngineDir?: string;
|
|
1005
|
+
/**
|
|
1006
|
+
* design/138 S2-B (O-F7/C-F3) — the Runner-owned harvest-report sink. Both harvest boundaries
|
|
1007
|
+
* (the checkpoint mint inside commitSuspendSaga, `phase: "checkpoint"`, and the task terminal in
|
|
1008
|
+
* runtask, `phase: "terminal"`) deliver their {@link import("./memory-engine/types.js").HarvestReport}
|
|
1009
|
+
* here instead of dropping it — trace/accounting/alerting is the deployment's call. Swallow-guarded:
|
|
1010
|
+
* a throwing consumer never fails the boundary. (The model-facing announcement lane is separate:
|
|
1011
|
+
* the engine enqueues gate events at its harvest tail; the NEXT session's first injection drains
|
|
1012
|
+
* them — 时机①.) Optional; unset ⇒ no behavior change beyond the report no longer being discarded.
|
|
1013
|
+
*/
|
|
1014
|
+
onMemoryHarvestReport?: (report: import("./memory-engine/types.js").HarvestReport, info: {
|
|
1015
|
+
sessionId: string;
|
|
1016
|
+
phase: "checkpoint" | "terminal";
|
|
1017
|
+
}) => void;
|
|
1018
|
+
/**
|
|
1019
|
+
* design/113 C4 — proactive project startup. The Runner calls this once during `prepareTask` (every prepare,
|
|
1020
|
+
* incl. durable resume — design/113 §4 H8) and injects the returned text as a READ-ONLY, FENCED, virtual
|
|
1021
|
+
* `"project"` memory layer at the TAIL (so the agent "sees" what project it is in from turn 1, like CC's
|
|
1022
|
+
* CLAUDE.md/git injection — the cure for "say hi → only greets"). The deployment/profile owns the CONTENT
|
|
1023
|
+
* (read CLAUDE.md / `.claude/` + fold in `git log -5`/status + a byte budget); core owns the seam + fence +
|
|
1024
|
+
* cache placement and NEVER hardcodes a filename (same trust posture as {@link executionEnvFactory}/
|
|
1025
|
+
* {@link promptProvider}/{@link modelKnowledgeCutoffs}). A RunnerDeps FUNCTION, not a `TaskSpec` field: it is
|
|
1026
|
+
* unserializable (durable resume) and reading files for an UNTRUSTED caller would be a confused-deputy hole
|
|
1027
|
+
* (design/44 §7 Q4). DECOUPLED from `memoryStore` — injects even with NO long-term memory configured (H5).
|
|
1028
|
+
*
|
|
1029
|
+
* `undefined` (default) ⇒ the whole path is skipped = byte-identical to today (full parity, H3). Returning
|
|
1030
|
+
* `null`/blank ⇒ no injection (also byte-identical). The content is fenced/sanitized like every other memory
|
|
1031
|
+
* layer (repo-controlled CLAUDE.md is UNTRUSTED data, not an instruction — H2). The ctx lets a profile tailor
|
|
1032
|
+
* or suppress: `handsEnabled:false` = a hands-less chat with no real project (return null); `isSubagent` =
|
|
1033
|
+
* a delegated child (a profile may trim CLAUDE.md for a cheap "grep this" subagent — H7).
|
|
1034
|
+
*/
|
|
1035
|
+
loadProjectMemory?: (ctx: {
|
|
1036
|
+
/** The task's working directory (= the execution env's cwd; a worktree task = its worktree root). */
|
|
1037
|
+
cwd: string;
|
|
1038
|
+
/** False ⇒ a hands-less chat (no real filesystem/project) — a profile typically returns null. */
|
|
1039
|
+
handsEnabled: boolean;
|
|
1040
|
+
/** True ⇒ a delegated subagent run (a profile may trim/suppress project content to control cost). */
|
|
1041
|
+
isSubagent: boolean;
|
|
1042
|
+
/** The subagent's type name, when `isSubagent` — lets a profile tailor content per agent type. */
|
|
1043
|
+
agentName?: string;
|
|
1044
|
+
/**
|
|
1045
|
+
* design-A C-1 ([A2]) — the acquired session's id (uuid domain; equals the run's `sessionId`).
|
|
1046
|
+
* A deployment keys its SESSION-LEVEL snapshot cache on this (design-A §4.1 freeze unit: read
|
|
1047
|
+
* once per session, reuse across the session's prepares) — core itself caches nothing across
|
|
1048
|
+
* prepares. Always filled by the Runner; optional so hand-built ctx objects stay valid.
|
|
1049
|
+
*/
|
|
1050
|
+
sessionId?: string;
|
|
1051
|
+
/**
|
|
1052
|
+
* design-A C-1/C-2 ([A2]) — where this prepare sits in the snapshot lifecycle; the deployment's
|
|
1053
|
+
* cache-invalidation signal (core only derives and forwards it, and ignores the answer):
|
|
1054
|
+
* - `"fresh"` — no prior context: a brand-new session (no `spec.sessionId`, or an empty one).
|
|
1055
|
+
* - `"resume"` — an existing session's context continues (a durable-resume leg, a same-session
|
|
1056
|
+
* follow-up run, or a `resumeAt` rewind): a cached snapshot is still the one
|
|
1057
|
+
* the model has been seeing.
|
|
1058
|
+
* - `"post-compact"`— a compaction landed since the model's last full turn on this branch (branch
|
|
1059
|
+
* tail shows a `compaction` entry with no *user* message after it): the context
|
|
1060
|
+
* was rebuilt — the design-A §4.1 re-read boundary; a deployment should drop
|
|
1061
|
+
* its snapshot cache and read fresh. HONEST APPROXIMATION (recorded, see
|
|
1062
|
+
* docs/REVIEW-BACKLOG.md): a compaction followed by a mid-run user-role
|
|
1063
|
+
* injection (steer) before the next prepare reads as `"resume"` — the freshness
|
|
1064
|
+
* gap is covered by the C-4 `instructions_change` lane, not by this signal.
|
|
1065
|
+
* - `"post-clear"` — RESERVED: core has no session-clear primitive today (a CC `/clear` maps to a
|
|
1066
|
+
* NEW session ⇒ `"fresh"`; rewinding past the root is rejected), so core never
|
|
1067
|
+
* emits this value — it exists so the seam's enum is stable when a clear verb lands.
|
|
1068
|
+
*/
|
|
1069
|
+
phase?: "fresh" | "resume" | "post-compact" | "post-clear";
|
|
1070
|
+
}) => Promise<string | null | ProjectMemoryLoad> | string | null | ProjectMemoryLoad;
|
|
1071
|
+
/**
|
|
1072
|
+
* design-A C-4 ([A2]) — the mid-session instruction-file change PROBE (the server half of the
|
|
1073
|
+
* `instructions_change` attachment lane). At eligible turn boundaries (continuation-gated like
|
|
1074
|
+
* `date_change`) the run loop calls this with the snapshot's declared
|
|
1075
|
+
* {@link ProjectMemoryLoad.instructionSources} and compares the answer against the lane's
|
|
1076
|
+
* per-path last-announced hash; a moved hash appends a fixed ≤512-byte tail notice (the model is
|
|
1077
|
+
* told the file changed and can Read the truth — the snapshot itself stays frozen, §4.1).
|
|
1078
|
+
* Contract:
|
|
1079
|
+
* - return `null` ⇒ this boundary is SKIPPED silently (nothing announced, nothing committed; the
|
|
1080
|
+
* next boundary retries) — the fail-soft path for a remote cwd / EACCES / transient IO.
|
|
1081
|
+
* - a per-entry `contentHash: null` ⇒ the file is GONE from disk (announced once as removed).
|
|
1082
|
+
* - paths not in `sources` are ignored; the probe should never throw (core guards anyway and
|
|
1083
|
+
* treats a throw exactly like `null`).
|
|
1084
|
+
* - core never reads files here and never interprets the hash (opaque equality only — the same
|
|
1085
|
+
* posture as `instructionSources`); the lstat/read/hash economics live in the deployment.
|
|
1086
|
+
* Unset ⇒ the lane does not exist: zero calls, zero state, byte-identical output.
|
|
1087
|
+
*/
|
|
1088
|
+
probeInstructionSources?: (sources: ReadonlyArray<{
|
|
1089
|
+
path: string;
|
|
1090
|
+
contentHash: string | null;
|
|
1091
|
+
}>) => Promise<ReadonlyArray<{
|
|
1092
|
+
path: string;
|
|
1093
|
+
contentHash: string | null;
|
|
1094
|
+
}> | null>;
|
|
1095
|
+
/** Override system-prompt assembly. Defaults to `defaultPromptProvider`. */
|
|
1096
|
+
promptProvider?: import("../prompts/default.js").PromptProvider;
|
|
1097
|
+
/**
|
|
1098
|
+
* design/99 §E23 — the LIVE-ONLY inbound-elicitation seam (an MCP server asking the END USER for input
|
|
1099
|
+
* mid-tool-call). Wired ONLY for servers that opted in via {@link McpServerSpec.elicitation}; absent ⇒ core
|
|
1100
|
+
* advertises no elicitation capability (fail-closed). The deployment answers synchronously (e.g. prompts the
|
|
1101
|
+
* terminal user); core replies to the server on the open connection.
|
|
1102
|
+
*/
|
|
1103
|
+
onElicit?: OnElicit;
|
|
1104
|
+
/**
|
|
1105
|
+
* design/116 CONFIRM-1 (加 seam 保体验): resize an over-limit MCP image so the model still SEES a
|
|
1106
|
+
* degraded image instead of a spill-to-disk note. Absent ⇒ core auto-detects `sharp` (dynamic import;
|
|
1107
|
+
* turnkey deployments with sharp installed get the CC experience with zero config) and otherwise spills.
|
|
1108
|
+
*/
|
|
1109
|
+
mcpImageResizer?: import("./mcp.js").McpImageResizer;
|
|
1110
|
+
/** Default tool-call gate for all tasks (a task's own `toolPolicy` overrides this). */
|
|
1111
|
+
toolPolicy?: import("./tool-policy.js").ToolPolicy;
|
|
1112
|
+
/**
|
|
1113
|
+
* design/277 — the deployment seat of the tool-registration MODEL GATE (default ON; the gate
|
|
1114
|
+
* itself only ever touches entries tagged via {@link ToolSpec.modelGate}, so an untagged roster
|
|
1115
|
+
* is byte-identical under any value here):
|
|
1116
|
+
* · `false` — deployment kill switch: the gate never trims anything.
|
|
1117
|
+
* · `{ classes }` — per-class rule rows merged into the built-in table
|
|
1118
|
+
* ({@link import("./tool-model-gate.js").TOOL_MODEL_GATE_CLASSES}) PER AXIS: a present
|
|
1119
|
+
* `floors`/`modelIds` axis REPLACES that axis, an absent axis INHERITS the built-in one — so
|
|
1120
|
+
* a row adding only `modelIds` (the BYOM channel for gating a deployment's own strong model)
|
|
1121
|
+
* keeps the built-in claude floors armed. `floors: []` / `modelIds: []` are the EXPLICIT
|
|
1122
|
+
* per-axis clears; a class whose merged rule has both axes empty gates nothing (legal
|
|
1123
|
+
* per-class off). New class names extend the open vocabulary for deployment-authored tags.
|
|
1124
|
+
* The legality set is CLOSED (#123): any other shape — array/null/true seat, unknown keys at
|
|
1125
|
+
* either level, malformed floors/modelIds rows, families that can never match the canonical id
|
|
1126
|
+
* grammar, duplicate families/ids, `/`-prefixed ids — refuses the prepare loudly
|
|
1127
|
+
* (`config.tool_model_gate_invalid`), never folds to a guess. Process-level counterpart: env
|
|
1128
|
+
* `SEMA_TOOL_MODEL_GATE=off`. Removals are announced per (model, class) through `onNotice`
|
|
1129
|
+
* (`config.tool_model_gate_removed`) — a silent default-face trim is forbidden (#237).
|
|
1130
|
+
*/
|
|
1131
|
+
toolModelGate?: false | {
|
|
1132
|
+
classes?: Record<string, import("./tool-model-gate.js").ToolModelGateRule>;
|
|
1133
|
+
};
|
|
1134
|
+
/** Deployment default for {@link TaskSpec.basePolicyForResumeEdit} (#93 / F-012 L3): the resume-edit
|
|
1135
|
+
* re-adjudication override. Resolution: `spec.basePolicyForResumeEdit ?? THIS ?? (spec.toolPolicy ??
|
|
1136
|
+
* deps.toolPolicy)` — absence falls back to the caller policy, never to a silent skip. */
|
|
1137
|
+
basePolicyForResumeEdit?: import("./tool-policy.js").ToolPolicy;
|
|
1138
|
+
/**
|
|
1139
|
+
* How `ask` decisions resolve when a policy/hook requests human confirmation (design/37). Default
|
|
1140
|
+
* (omitted) = `"deny"`: **headless auto-deny** — no approver, so `ask` resolves deterministically to
|
|
1141
|
+
* deny with a model-readable reason (the safe default for stateless automation). `"allow"` to
|
|
1142
|
+
* auto-approve, or a fail-closed approver function. A task's own `onAsk` overrides this.
|
|
1143
|
+
*/
|
|
1144
|
+
onAsk?: import("./tool-policy.js").OnAsk;
|
|
1145
|
+
/** Content-ask seam (design/64 §5): when set, every task mounts an AskUserQuestion tool that routes a
|
|
1146
|
+
* model-issued question to this callback (a real human/UI). Distinct from `onAsk` (permission). A task's
|
|
1147
|
+
* own `onQuestion` overrides this. Unset ⇒ the tool mounts only if another delivery face exists
|
|
1148
|
+
* (durable park consumer / explicit `TaskSpec.interactiveTools` — see that knob's criterion table). */
|
|
1149
|
+
onQuestion?: import("./ask-question.js").OnQuestion;
|
|
1150
|
+
/**
|
|
1151
|
+
* design/173 §3 — the deployment-level default interaction posture (see
|
|
1152
|
+
* {@link TaskSpec.interactionPosture} for the full contract; the spec seat overrides per run, so
|
|
1153
|
+
* one Runner can serve an interactive session AND cron/batch legs without the deps-level
|
|
1154
|
+
* declaration refusing the batch legs). Absent = no posture check anywhere.
|
|
1155
|
+
*/
|
|
1156
|
+
interactionPosture?: "interactive" | "headless";
|
|
1157
|
+
/** LSP code-intelligence seam (design/64 §13.1): when set, every task mounts an `lsp` tool routed to this
|
|
1158
|
+
* manager (a deployment-provided language-server bridge, e.g. an E2B LSP sidecar). The manager returns
|
|
1159
|
+
* `undefined` for a file whose language has no server, and the tool degrades to "fall back to grep". A
|
|
1160
|
+
* task's own `lspManager` overrides this. Unset ⇒ no `lsp` tool. */
|
|
1161
|
+
lspManager?: import("./lsp.js").LspServerManager;
|
|
1162
|
+
/**
|
|
1163
|
+
* Default in-process hooks for all tasks (design/37) — the FULL lifecycle seam of the `Hooks`
|
|
1164
|
+
* interface, not just the tool-call trio: `preToolUse` (rewrite/restrict args + inject context),
|
|
1165
|
+
* `postToolUse` (rewrite output + inject context), `userPromptSubmit` (design/373 §4.3: screens
|
|
1166
|
+
* EVERY user-lane entrance — the objective, a live `TaskStream.steer`, a wake resume's message,
|
|
1167
|
+
* and a parked steer's redelivery, discriminated by `ctx.source`; a hook that blocks
|
|
1168
|
+
* unconditionally refuses steers/wakes too — see the ⚠️ WIDENED INVOCATION SET banner on
|
|
1169
|
+
* {@link import("./hooks.js").Hooks.userPromptSubmit}), plus `stop` (push back when the run would otherwise end and continue
|
|
1170
|
+
* it), `postToolUseFailure` / `postToolBatch` / `permissionDenied` (failure, batch-boundary and
|
|
1171
|
+
* deny observers), `preCompact` / `postCompact` (compaction gate + observer), `stopFailure`
|
|
1172
|
+
* (API-error terminal observer) and the `preToolUseObservational` declaration flag — each member's
|
|
1173
|
+
* contract is documented on the `Hooks` interface itself. A task's own `hooks` overrides this. A
|
|
1174
|
+
* PreToolUse hook's `allow` never bypasses `toolPolicy` — the policy is always the final say.
|
|
1175
|
+
*/
|
|
1176
|
+
hooks?: import("./hooks.js").Hooks;
|
|
1177
|
+
/**
|
|
1178
|
+
* Policy for remote image URLs (`TaskSpec.images[].url`). Return false to reject. When set it
|
|
1179
|
+
* **replaces** the built-in SSRF guard (https-only + private/reserved-host block) — e.g. supply a
|
|
1180
|
+
* CDN allowlist, or a stricter resolve-and-pin check. Omit to use the safe default.
|
|
1181
|
+
*/
|
|
1182
|
+
allowImageUrl?: (url: string) => boolean;
|
|
1183
|
+
/**
|
|
1184
|
+
* Optional sink for best-effort background errors/warnings that are otherwise swallowed:
|
|
1185
|
+
* - `"compaction"` — a compaction failure (F8); a persistently failing one (flaky gateway, empty
|
|
1186
|
+
* summaries) is invisible without this and lets a session grow until it tops the window.
|
|
1187
|
+
* - `"prompt-cache"` — a prefix-cache hazard on the wire: a detected cache break (design/31), or a
|
|
1188
|
+
* mis-declared/mismatched `promptCacheFamily`. The prefix cache will miss. Log/alert on both.
|
|
1189
|
+
* - `"prompt-constitution"` — a `stableSystem` provider returned an ALREADY-assembled prompt
|
|
1190
|
+
* (constitution anchor found); core passed it through un-doubled. Upgrade the provider to return
|
|
1191
|
+
* only the role base (or set `replaceAll: true` to own the whole base).
|
|
1192
|
+
* - `"degraded"` — the task kept running with a capability quietly reduced: a question auto-answered
|
|
1193
|
+
* with no human present, a skipped env git snapshot, a lost checkpoint-put confirmation, an
|
|
1194
|
+
* unroutable descendant notification, and similar best-effort arms. The run proceeds; the reduced
|
|
1195
|
+
* arm is what is being disclosed (`classification` names it).
|
|
1196
|
+
* - `"config"` — a deployment-wiring problem detected while preparing or tearing down a task: tool
|
|
1197
|
+
* mount conflicts, refused/ignored knob values, gate-off notices, store/env teardown legs. The
|
|
1198
|
+
* broadest class by call-site count — most misconfigurations announce here rather than failing
|
|
1199
|
+
* the task.
|
|
1200
|
+
* - `"memory"` — a post-task memory-consolidation pass failed or skipped a malformed reconcile
|
|
1201
|
+
* decision (design/41). Best-effort: the notes the model saved are kept; the task still succeeds.
|
|
1202
|
+
* - `"mcp"` — an MCP server failed to connect / list its tools and was skipped (fail-open, design/29);
|
|
1203
|
+
* the task runs with the remaining servers' tools. One call per failed server.
|
|
1204
|
+
* - `"a2a"` — the A2A sibling of `"mcp"`: a declared peer's agent card could not be fetched/read, or it
|
|
1205
|
+
* advertises no transport this client speaks, so the peer was skipped. One call per skipped peer.
|
|
1206
|
+
* - `"interrupt-reconcile"` — repairing an interrupted session's transcript on re-entry (synthesizing
|
|
1207
|
+
* tool results for orphaned calls, flushing held session writes) failed; the run proceeds on the
|
|
1208
|
+
* unrepaired transcript.
|
|
1209
|
+
* - `"suggestions"` — the best-effort follow-up-suggestions pass failed or timed out; the result
|
|
1210
|
+
* simply carries no suggestions.
|
|
1211
|
+
* - `"rewind"` — the file-history machinery backing rewind reported a best-effort failure: a
|
|
1212
|
+
* turn-start boundary capture failed or timed out (that turn cannot be rewound to; the turn
|
|
1213
|
+
* itself proceeds), or a first-touch failure was tolerated under
|
|
1214
|
+
* `onTrackFailure:"proceed-unprotected"`. (design/381: the whole-tree size gate and its
|
|
1215
|
+
* `classification:"too_large"` payload are retired — per-edited-file history has no size axis.)
|
|
1216
|
+
* - `"hook"` — a deployment hook misbehaved: a callback threw, or returned a verdict it was not
|
|
1217
|
+
* allowed to (e.g. a declared-observational hook). The engine applies the hook's own documented
|
|
1218
|
+
* fallback (swallow, or fail-closed deny, per its contract) and reports the fact here.
|
|
1219
|
+
*/
|
|
1220
|
+
onError?: (err: unknown, context: {
|
|
1221
|
+
phase: "compaction" | "prompt-cache" | "prompt-constitution" | "degraded" | "config" | "memory" | "mcp" | "a2a" | "interrupt-reconcile" | "suggestions" | "rewind" | "hook";
|
|
1222
|
+
sessionId: string;
|
|
1223
|
+
/**
|
|
1224
|
+
* Machine-readable sub-classification when the phase has one — currently the design/31
|
|
1225
|
+
* cache-break detector's root cause (`"model-switch" | "tool-schema" | "tool-set" |
|
|
1226
|
+
* "system-prefix" | "server-or-ttl"`). Lets the host pick its own log level: `"server-or-ttl"`
|
|
1227
|
+
* is usually benign in agentic tasks (slow tools ⇒ 5min+ request gaps expire provider caches —
|
|
1228
|
+
* service[64] flag②) and warrants info/warn, while the prefix-bug causes warrant attention.
|
|
1229
|
+
*/
|
|
1230
|
+
classification?: string;
|
|
1231
|
+
}) => void;
|
|
1232
|
+
/**
|
|
1233
|
+
* Structured sink for operator-facing NOTICES ({@link EngineNotice}) — announcements that are not
|
|
1234
|
+
* errors and do not affect the run, which the engine otherwise prints via `console.warn` (e.g. a
|
|
1235
|
+
* configured timeout/env value discarded in favor of another). When wired, a notice goes HERE
|
|
1236
|
+
* INSTEAD of `console.warn` (structured replaces the console line — a host forwarding notices to
|
|
1237
|
+
* its own surface must not show every fact twice); when absent, the historic `console.warn` line
|
|
1238
|
+
* is printed verbatim, so an unwired build keeps its exact loudness. Swallow-guarded at every
|
|
1239
|
+
* emission point (`onError`/`tracer` posture): neither a throwing sink nor an async sink's
|
|
1240
|
+
* rejected promise ever affects the run. A PRESENT NON-function value (an untyped host wiring
|
|
1241
|
+
* null/junk) is a bad deployment value, not a wired sink: every notice then falls back to the
|
|
1242
|
+
* `console.warn` line and the seat defect itself is announced once per process (#170 — a bad seat
|
|
1243
|
+
* must not silence both channels). Per-process announcement de-duplication is unchanged and
|
|
1244
|
+
* sits BEFORE the sink branch — a deduplicated repeat reaches neither channel.
|
|
1245
|
+
*/
|
|
1246
|
+
onNotice?: (notice: EngineNotice) => void;
|
|
1247
|
+
/** Best-effort fire-and-forget trace sink (task/turn/brain/tool); `TaskSpec.tracer` overrides per task. */
|
|
1248
|
+
tracer?: import("./trace.js").TracerHook;
|
|
1249
|
+
/**
|
|
1250
|
+
* design/73 §1 v1 — fire-and-forget sink for {@link import("./task-outcome.js").TaskOutcome} facts
|
|
1251
|
+
* (the feedback ledger's core seam; tracer/onError posture — swallow-guarded, a throwing sink never
|
|
1252
|
+
* affects the run). 🔴 Red lines: core emits ONLY the `mechanical` tier (objective-oracle results —
|
|
1253
|
+
* llm_assisted/self-report never enter v1); every outcome carries `oracleHadRedRun`; ONE emission
|
|
1254
|
+
* point — `runGoal`'s terminal state (goal `doneCheck` = the natural mechanical oracle) plus explicit
|
|
1255
|
+
* `emitTaskOutcome` from harnesses that own a real oracle. Plain `runTask` never auto-emits (no
|
|
1256
|
+
* objective oracle → no fact). core emits facts only; aggregation/policy live in the service ledger.
|
|
1257
|
+
*/
|
|
1258
|
+
onTaskOutcome?: (outcome: import("./task-outcome.js").TaskOutcome) => void;
|
|
1259
|
+
/** Per-model pricing (per-1M absolute USD) keyed by model id; falls back to each `Model.cost`. */
|
|
1260
|
+
pricing?: Record<string, import("./pricing.js").ModelPricing>;
|
|
1261
|
+
/**
|
|
1262
|
+
* The execution environment the agent's "hand" acts on (design/44) — file system + shell. Default:
|
|
1263
|
+
* `StubExecutionEnv` (no real I/O, current behavior). Inject `NodeExecutionEnv` for local fs/shell, or
|
|
1264
|
+
* a sandboxed/remote impl for multi-tenant isolation. **Injecting a non-stub env is the opt-in** that
|
|
1265
|
+
* activates the file tool band. ⚠️ A bare `NodeExecutionEnv` is unsandboxed — a multi-tenant deployment
|
|
1266
|
+
* MUST inject a chroot/container/per-principal-scoped impl; do not expose raw `NodeExecutionEnv`.
|
|
1267
|
+
*/
|
|
1268
|
+
executionEnv?: import("../internal/harness.js").ExecutionEnv;
|
|
1269
|
+
/**
|
|
1270
|
+
* design/48 remote seam — a **trusted control-plane** factory that mints a FRESH execution env per
|
|
1271
|
+
* task (e.g. one isolated container per task in the remote model). Called once at task start; the env
|
|
1272
|
+
* it returns is owned by that task and released on task end. **Wins over** the shared static
|
|
1273
|
+
* `executionEnv`. Injecting it also activates the hand tool band (design/44), exactly like
|
|
1274
|
+
* `executionEnv`. This is the ONLY per-task env injection point — `TaskSpec` deliberately has no
|
|
1275
|
+
* `executionEnv`, so an untrusted caller can't swap the sandbox (design/44 §7 Q4); the trusted
|
|
1276
|
+
* deployment wires the factory.
|
|
1277
|
+
*/
|
|
1278
|
+
executionEnvFactory?: import("./remote-env.js").ExecutionEnvFactory;
|
|
1279
|
+
/**
|
|
1280
|
+
* design/77 §1 Gate-1 (oracleIsolation) — a **DISTINCT** trusted control-plane factory that mints the
|
|
1281
|
+
* ISOLATED grader env an out-of-process oracle grades in (the 4th auto-promote gate). It is a SIBLING of
|
|
1282
|
+
* {@link executionEnvFactory}, never the same env: the worker runs in `executionEnvFactory`'s env, the
|
|
1283
|
+
* grader in `graderEnvFactory`'s — so a worker can't corrupt the env that judges it. It inherits the exact
|
|
1284
|
+
* same trust contract: `TaskSpec` deliberately has NO env field (design/44 §7 Q4), so an untrusted caller
|
|
1285
|
+
* can never inject/swap a grader env and grade itself; only the trusted deployment wires this factory. The
|
|
1286
|
+
* RESERVED SEAM, stated plainly (ruled 2026-08-04, field-liveness review): the engine has NO call
|
|
1287
|
+
* site for this factory today — nothing mints, wraps, or asserts through it yet. The intended wirer
|
|
1288
|
+
* is the hardened-grader path a deployment builds on its remote-env side (the k8s pod-spec-patch
|
|
1289
|
+
* seam is the documented server-side half); when that live runner lands, the env it mints is to be
|
|
1290
|
+
* wrapped in a {@link import("./oracle-isolation.js").GraderEnv} and asserted via
|
|
1291
|
+
* {@link import("./oracle-isolation.js").assertOracleIsolation} before any positive oracle verdict.
|
|
1292
|
+
* **Auto-promote stays mandate-OFF until the four gates are implemented + verified end-to-end** (design/77
|
|
1293
|
+
* §7); this seam only makes auto-accept implementable — it does not turn it on.
|
|
1294
|
+
*/
|
|
1295
|
+
graderEnvFactory?: import("./remote-env.js").ExecutionEnvFactory;
|
|
1296
|
+
/**
|
|
1297
|
+
* Containment root for the file tools (design/44 §4 invariant 7): every path must resolve within it,
|
|
1298
|
+
* else the tool rejects (`PathNotInRoot`). Default `process.cwd()`. A defense-in-depth guard rail at the
|
|
1299
|
+
* tool layer — NOT a sandbox (it does not constrain `bash`; that needs OS-level containment) and not a
|
|
1300
|
+
* substitute for an isolated `executionEnv`.
|
|
1301
|
+
*/
|
|
1302
|
+
rootPath?: string;
|
|
1303
|
+
/**
|
|
1304
|
+
* Backing store for offloaded large tool results (design/30). Default: a task-scoped
|
|
1305
|
+
* `InMemoryToolResultStore` (no cross-wake durability). Inject a durable store (keyed by ref) so an
|
|
1306
|
+
* offloaded result survives wake/resume.
|
|
1307
|
+
*/
|
|
1308
|
+
toolResultStore?: import("./tool-result-store.js").ToolResultStore;
|
|
1309
|
+
/**
|
|
1310
|
+
* Offload a tool result to the store + leave a preview when its text exceeds this many chars. Default
|
|
1311
|
+
* ~20000; set `0` or `Infinity` to disable offloading. Per-tool override via `ToolSpec.offloadThresholdChars`.
|
|
1312
|
+
*/
|
|
1313
|
+
toolResultThresholdChars?: number;
|
|
1314
|
+
/**
|
|
1315
|
+
* design/374 — microCompact machine-alignment knobs. Since the slice-3 default flip BOTH knobs
|
|
1316
|
+
* default ON (`machine: "cc"`, `clearOnRejection: true`); the pre-374 behavior is the explicit
|
|
1317
|
+
* opt-out `{ machine: "legacy", clearOnRejection: false }`.
|
|
1318
|
+
*
|
|
1319
|
+
* - `machine`: which stale-tool-result clearing machine the request pipeline runs —
|
|
1320
|
+
* `"cc"` (default: the CC 2.1.223 rejection-leg form — keep 5, ≥20k minimum-savings gate, one
|
|
1321
|
+
* deep clear beyond the keep window, CC marker bytes), `"legacy"` (the historical keep-3 /
|
|
1322
|
+
* clear-to-budget machine, kept as the compatibility opt-out; it also keeps the pre-374
|
|
1323
|
+
* backstop order — guard trim as the ordinary second line), or `"off"` (no proactive frontier
|
|
1324
|
+
* clearing at all — the D-2 off switch; the unified machine then gets its ONE shot at the
|
|
1325
|
+
* blocking point instead, the slice-3 arm A, trace trigger `"blocking"`). The pre-flip P-form
|
|
1326
|
+
* warning on {@link import("./context-edit.js").ContextEditMachine} is resolved: the slice-3
|
|
1327
|
+
* fallback re-ordering shipped with this default (blocking-point re-run → in-turn forced
|
|
1328
|
+
* compaction behind the adopt seam → trim demoted to the disaster-only last resort), so an
|
|
1329
|
+
* under-20k refusal no longer falls straight into a message-dropping trim.
|
|
1330
|
+
* - `clearOnRejection` (MC-R, slice 2): on a provider input-too-long rejection, run ONE cheap
|
|
1331
|
+
* deterministic clear over the rejected projection (same cc machine, savings ≥20k or nothing)
|
|
1332
|
+
* and retry inside the turn BEFORE the forced-compaction recovery. Default true (flipped with
|
|
1333
|
+
* the machine default in slice 3; X2's dark-landing clause is spent). Independent of
|
|
1334
|
+
* `machine` — an enabled MC-R always clears in the cc form (the rejection arm has no
|
|
1335
|
+
* budget coordinate for the legacy incremental form to stop at). BUDGET ACCOUNTING (design/374
|
|
1336
|
+
* §3.2.1, stated here because it is otherwise invisible to a deployment): a successful MC-R
|
|
1337
|
+
* clear-and-retry SPENDS one attempt of the shared prompt-too-long recovery budget (default 2
|
|
1338
|
+
* attempts per chain), so a chain that clears and is rejected AGAIN has one forced-compaction
|
|
1339
|
+
* attempt left where the knob-off chain nominally had two — the trade costs no effective
|
|
1340
|
+
* compaction pass, because the second forced-compaction call of the off chain is structurally
|
|
1341
|
+
* a no-op whenever the first one landed (the branch leaf is already a compaction entry).
|
|
1342
|
+
*
|
|
1343
|
+
* A declaration outside the closed vocabulary (a `machine` string not in the union, a
|
|
1344
|
+
* non-boolean `clearOnRejection` — JSON/env-derived config the type cannot guard) refuses the
|
|
1345
|
+
* whole prepare loudly (`code: "config.microcompact_invalid"`, no silent re-default): folding it
|
|
1346
|
+
* would silently run the DEFAULT machine while the deployment believes its declaration took
|
|
1347
|
+
* effect.
|
|
1348
|
+
*/
|
|
1349
|
+
microCompact?: {
|
|
1350
|
+
machine?: "off" | "legacy" | "cc";
|
|
1351
|
+
clearOnRejection?: boolean;
|
|
1352
|
+
};
|
|
1353
|
+
/**
|
|
1354
|
+
* Two-phase prefix-cache-break detection (design/31): per turn, fingerprint the prefix and, on a
|
|
1355
|
+
* confirmed `cacheRead` drop, emit a root-cause finding via `onError(phase:"prompt-cache")`. Cheap
|
|
1356
|
+
* (hashes only) and only fires on a detected break. Default **on**; set `false` to disable.
|
|
1357
|
+
*/
|
|
1358
|
+
cacheBreakDetection?: boolean;
|
|
1359
|
+
/**
|
|
1360
|
+
* Aggregate inline-MEDIA (image) byte budget for the request (batch-C/§17.2 sibling of the text budget;
|
|
1361
|
+
* CC 2.1.196 `tengu_media_byte_cap` parity). Image reads (`Read` → ImageContent) are per-image-capped but
|
|
1362
|
+
* accumulate across turns; this bounds the SUM in the request by stripping the OLDEST images (replaced with a
|
|
1363
|
+
* marker) — request-only, non-destructive (the durable session keeps the image; the model can re-Read it).
|
|
1364
|
+
* Default {@link AGGREGATE_MEDIA_BUDGET_BYTES}; set `Infinity`/`0` to disable.
|
|
1365
|
+
*/
|
|
1366
|
+
mediaByteCapBytes?: number;
|
|
1367
|
+
/** Optional telemetry fired when {@link mediaByteCapBytes} strips media in a request (CC's
|
|
1368
|
+
* `tengu_media_byte_cap_stripped` fields). Pure observability — no effect on the request. */
|
|
1369
|
+
onMediaStripped?: (info: {
|
|
1370
|
+
totalBytes: number;
|
|
1371
|
+
limitBytes: number;
|
|
1372
|
+
removedCount: number;
|
|
1373
|
+
removedBytes: number;
|
|
1374
|
+
}) => void;
|
|
1375
|
+
/**
|
|
1376
|
+
* Deferred-tool disclosure policy (design/36). Default (omitted): deterministic per-tool, so the same
|
|
1377
|
+
* `TaskSpec` always yields the same `tools[]`. The default deferral sources are `ToolSpec.defer === true`,
|
|
1378
|
+
* every REMOTE-PROTOCOL tool name (MCP **and** A2A peer tools), {@link TaskSpec.deferTools}, and built-in
|
|
1379
|
+
* tools that declare their own `defer` (today the shared-memory pair) — minus anything pinned inline by
|
|
1380
|
+
* `ToolSpec.alwaysLoad` / {@link TaskSpec.alwaysLoadTools}. `classifyDeferred`'s own contract is the
|
|
1381
|
+
* single authority if this list ever drifts again. `"auto"`: additionally auto-defer tools when the total
|
|
1382
|
+
* inlined schema size would exceed a fraction of the model's context window. Auto is an explicit opt-in
|
|
1383
|
+
* (council minor #6) because an implicit threshold makes `tools[]` depend on the model/context, which
|
|
1384
|
+
* callers find hard to predict; explicit `defer` flags always win regardless of this setting.
|
|
1385
|
+
*/
|
|
1386
|
+
deferMode?: "auto";
|
|
1387
|
+
/**
|
|
1388
|
+
* design/84 Seam C — compact-boundary COST optimization (data-gated, default OFF). A FUNCTION seam (a
|
|
1389
|
+
* trusted control-plane injection), so it lives on `RunnerDeps` — NOT `TaskSpec`: `TaskSpec` is the
|
|
1390
|
+
* serializable, untrusted-caller, durable-resume-round-trippable per-task config (a function can be
|
|
1391
|
+
* neither serialized nor durably checkpointed, and the sandbox/env-class injections deliberately sit on
|
|
1392
|
+
* `RunnerDeps` for the same trust reason). Same placement as `promptProvider` / `executionEnvFactory`.
|
|
1393
|
+
*
|
|
1394
|
+
* When `summaryProvider` is set, BOTH compaction call sites (the within-task turn boundary AND `finish()`
|
|
1395
|
+
* at task end) consult it: a NON-EMPTY string is reused verbatim (the real `generateSummary` LLM call is
|
|
1396
|
+
* skipped); `null`/`undefined`/blank falls back to the existing LLM path (byte-identical to today).
|
|
1397
|
+
* `onCompaction` is a pure post-compaction observer. `maxConsecutiveProviderReuse` (default 3, CC parity)
|
|
1398
|
+
* forces a real summary after N consecutive reuses to bound drift; the Runner OWNS the consecutive-reuse
|
|
1399
|
+
* counter across both call sites for the task (reset on a real summary, incremented on a reused one), so
|
|
1400
|
+
* the caller does not have to thread it back. **Absent (default) ⇒ no behavior change whatsoever.**
|
|
1401
|
+
*
|
|
1402
|
+
* SCOPE (design/141 examples 批实测): the seam covers the whole-turn compaction sites only. A
|
|
1403
|
+
* SPLIT-TURN cut point (mid-turn prefix summarization — the common shape inside a long single-user-
|
|
1404
|
+
* message tool loop) bypasses the provider by design (A2 algorithm sovereignty): the turn-prefix
|
|
1405
|
+
* summary is engine-owned. Deployments needing a clean provider-visible boundary should shape work
|
|
1406
|
+
* as separate tasks sharing `sessionId`.
|
|
1407
|
+
*/
|
|
1408
|
+
summaryProvider?: import("./auto-compaction.js").MaybeCompactOptions["summaryProvider"];
|
|
1409
|
+
/** design/84 Seam C — pure post-compaction observer (fires only on a real compaction, never a no-op).
|
|
1410
|
+
* Consumed ONLY when `summaryProvider` is also set (the Seam C options early-return otherwise) — a
|
|
1411
|
+
* pure-observer deployment passes a provider that always returns `null` (keeps the LLM path intact). */
|
|
1412
|
+
onCompaction?: import("./auto-compaction.js").MaybeCompactOptions["onCompaction"];
|
|
1413
|
+
/** design/84 Seam C — force a real `generateSummary` after this many consecutive provider reuses (default
|
|
1414
|
+
* 3, CC parity). The Runner tracks the consecutive-reuse counter per task across both compaction sites. */
|
|
1415
|
+
maxConsecutiveProviderReuse?: number;
|
|
1416
|
+
}
|