@sema-agent/core 5.57.0 → 5.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +96 -0
- package/dist/agents/cascade.d.ts +1 -1
- package/dist/agents/cumulative-stats.d.ts +1 -1
- package/dist/agents/observer.d.ts +2 -2
- package/dist/agents/peer-admission.d.ts +1 -1
- package/dist/agents/retain-ledger.d.ts +2 -2
- package/dist/agents/roster-store.d.ts +8 -8
- package/dist/agents/send-message-tool.d.ts +2 -2
- package/dist/agents/subagent-steps.d.ts +1 -1
- package/dist/agents/subagent.d.ts +13 -13
- package/dist/agents/team.d.ts +5 -5
- package/dist/agents/tool-filter.d.ts +2 -2
- package/dist/agents/verify.d.ts +1 -1
- package/dist/bench/metrics.d.ts +35 -35
- package/dist/brain/degrading.d.ts +1 -1
- package/dist/brain/errors.d.ts +3 -3
- package/dist/brain/reasoning.d.ts +2 -2
- package/dist/brain/repetition.d.ts +1 -1
- package/dist/brain/route-adjudicator.d.ts +8 -1
- package/dist/brain/route-adjudicator.js +8 -1
- package/dist/brain/status-sink.d.ts +2 -2
- package/dist/brain/stream-shared.d.ts +1 -1
- package/dist/config/catalog.d.ts +5 -5
- package/dist/core/arg-summary.d.ts +4 -4
- package/dist/core/ask-class.d.ts +2 -2
- package/dist/core/ask-question.d.ts +1 -1
- package/dist/core/auto-compaction.d.ts +15 -15
- package/dist/core/auto-mode.d.ts +5 -5
- package/dist/core/background-agent-store.d.ts +20 -20
- package/dist/core/background-shell.d.ts +4 -4
- package/dist/core/checkpoint-store.d.ts +35 -27
- package/dist/core/context-edit.d.ts +1 -1
- package/dist/core/context-guard.d.ts +1 -1
- package/dist/core/exec-output-tail.d.ts +6 -6
- package/dist/core/file-snapshot-store.d.ts +8 -8
- package/dist/core/git-worktree-env.d.ts +3 -3
- package/dist/core/governance-codes.d.ts +37 -10
- package/dist/core/governance-codes.js +54 -1
- package/dist/core/hooks.d.ts +73 -33
- package/dist/core/hooks.js +87 -25
- package/dist/core/image-downsample.d.ts +1 -1
- package/dist/core/locked-config.d.ts +1 -1
- package/dist/core/lsp.d.ts +1 -1
- package/dist/core/mailbox-store.d.ts +1 -1
- package/dist/core/mcp.d.ts +3 -3
- package/dist/core/memory-engine/consolidation-driver.d.ts +211 -0
- package/dist/core/memory-engine/consolidation-driver.js +427 -0
- package/dist/core/memory-engine/consolidation.d.ts +119 -3
- package/dist/core/memory-engine/consolidation.js +22 -1
- package/dist/core/memory-engine/data-plane.d.ts +1 -1
- package/dist/core/memory-engine/distiller.d.ts +550 -0
- package/dist/core/memory-engine/distiller.js +598 -0
- package/dist/core/memory-engine/dual-root.d.ts +1 -1
- package/dist/core/memory-engine/engine.d.ts +143 -10
- package/dist/core/memory-engine/engine.js +149 -23
- package/dist/core/memory-engine/file-backend.d.ts +14 -2
- package/dist/core/memory-engine/file-backend.js +3 -0
- package/dist/core/memory-engine/index.d.ts +6 -3
- package/dist/core/memory-engine/index.js +4 -2
- package/dist/core/memory-engine/layout.js +20 -6
- package/dist/core/memory-engine/origin-clearance.d.ts +1 -1
- package/dist/core/memory-engine/scope-contract.d.ts +4 -4
- package/dist/core/memory-engine/sync-client.d.ts +16 -16
- package/dist/core/memory-engine/sync.d.ts +4 -4
- package/dist/core/memory-engine/types.d.ts +17 -0
- package/dist/core/memory-recall.d.ts +1 -1
- package/dist/core/memory.d.ts +2 -2
- package/dist/core/permission-rule-consent.d.ts +185 -36
- package/dist/core/permission-rule-consent.js +219 -44
- package/dist/core/permission-rule-model.d.ts +235 -31
- package/dist/core/permission-rule-model.js +105 -35
- package/dist/core/permission-rules.d.ts +9 -9
- package/dist/core/remote-env.d.ts +8 -8
- package/dist/core/roles.d.ts +3 -3
- package/dist/core/roles.js +1 -0
- package/dist/core/runner/assemble-result.d.ts +2 -2
- package/dist/core/runner/compaction-call-options.d.ts +3 -3
- package/dist/core/runner/memory-consolidation-driver.d.ts +49 -0
- package/dist/core/runner/memory-consolidation-driver.js +60 -0
- package/dist/core/runner/memory-consolidation.d.ts +1 -1
- package/dist/core/runner/prepare-config-doors.d.ts +3 -3
- package/dist/core/runner/prepare-task.d.ts +21 -21
- package/dist/core/runner/prepare-task.js +24 -17
- package/dist/core/runner/prepare-workspace-restore.d.ts +2 -2
- package/dist/core/runner/runtask.d.ts +11 -11
- package/dist/core/runner/runtask.js +4 -4
- package/dist/core/runner/session-rule-policy.d.ts +1 -1
- package/dist/core/runner/teardown-bounded.d.ts +1 -1
- package/dist/core/runner/tool-disclosure.d.ts +2 -2
- package/dist/core/runner/turn-attachments.d.ts +11 -11
- package/dist/core/scheduler.d.ts +5 -5
- package/dist/core/secret-env.d.ts +1 -1
- package/dist/core/sensitive-path-policy.d.ts +1 -1
- package/dist/core/session-policy-store.d.ts +2 -2
- package/dist/core/session-reconcile.d.ts +2 -2
- package/dist/core/session-store.d.ts +3 -3
- package/dist/core/session.d.ts +1 -1
- package/dist/core/shutdown-debug.d.ts +2 -2
- package/dist/core/side-query.d.ts +2 -2
- package/dist/core/spec-contract.d.ts +1 -1
- package/dist/core/store-contracts/contract-harness.d.ts +2 -2
- package/dist/core/store-contracts/contract-kit-version.d.ts +2 -2
- package/dist/core/store-contracts/mailbox-store-contract.d.ts +1 -1
- package/dist/core/store-contracts/mailbox-store-contract.js +1 -1
- package/dist/core/task-notification.d.ts +5 -5
- package/dist/core/task-registry-agent.d.ts +12 -12
- package/dist/core/task-registry-monitor.d.ts +1 -1
- package/dist/core/task-registry-shared.d.ts +41 -41
- package/dist/core/task-registry.d.ts +12 -12
- package/dist/core/tool-detach.d.ts +2 -2
- package/dist/core/tool-errors.d.ts +3 -3
- package/dist/core/tool-policy.d.ts +113 -28
- package/dist/core/tool-policy.js +80 -1
- package/dist/core/tool-result-budget.d.ts +1 -1
- package/dist/core/tool-result-store.d.ts +2 -2
- package/dist/core/tools.d.ts +1 -1
- package/dist/core/trace.d.ts +26 -23
- package/dist/core/types.d.ts +164 -86
- package/dist/core/types.js +4 -3
- package/dist/core/untrusted-egress.d.ts +1 -1
- package/dist/core/untrusted-text.d.ts +7 -7
- package/dist/core/wiring-manifest.d.ts +5 -5
- package/dist/core/workflow-journal-store.d.ts +14 -14
- package/dist/core/workflow-run-store-contract.d.ts +1 -1
- package/dist/core/workflow-run-store-contract.js +1 -1
- package/dist/core/workflow-run-store.d.ts +4 -4
- package/dist/engine/compaction/compaction.d.ts +3 -3
- package/dist/engine/compaction/utils.d.ts +2 -2
- package/dist/engine/execution-env/kill-tree.d.ts +1 -1
- package/dist/engine/execution-env/node-execution-env.d.ts +8 -8
- package/dist/engine/harness/agent-harness.d.ts +6 -6
- package/dist/engine/harness/messages.d.ts +1 -1
- package/dist/engine/harness/types.d.ts +10 -10
- package/dist/engine/llm/types.d.ts +14 -14
- package/dist/engine/loop/agent-loop.d.ts +3 -3
- package/dist/engine/loop/types.d.ts +4 -4
- package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
- package/dist/engine/session/import-validate.d.ts +1 -1
- package/dist/engine/session/log-digest.d.ts +1 -1
- package/dist/engine/session/memory-repo.d.ts +2 -2
- package/dist/engine/session/session.d.ts +4 -4
- package/dist/fixtures/index.d.ts +4 -4
- package/dist/index.d.ts +6 -5
- package/dist/index.js +5 -4
- package/dist/orchestration/goal.d.ts +1 -1
- package/dist/orchestration/run-spec.d.ts +1 -1
- package/dist/orchestration/run-workflow-tool.d.ts +12 -12
- package/dist/orchestration/workflow-governance.d.ts +4 -4
- package/dist/orchestration/workflow-observe.d.ts +1 -1
- package/dist/orchestration/workflow-script-runner.d.ts +1 -1
- package/dist/orchestration/workflow-script-store.d.ts +9 -9
- package/dist/orchestration/workflow-size-guideline.d.ts +1 -1
- package/dist/orchestration/workflow-types.d.ts +5 -5
- package/dist/orchestration/workflow.d.ts +10 -10
- package/dist/prompt-assembly/artifact-store.d.ts +1 -1
- package/dist/prompt-assembly/artifact.d.ts +1 -1
- package/dist/prompt-assembly/assemble.d.ts +1 -1
- package/dist/prompt-assembly/composer.d.ts +2 -2
- package/dist/prompt-assembly/epoch.d.ts +2 -2
- package/dist/prompt-assembly/event-registry.d.ts +1 -1
- package/dist/prompt-assembly/explain.d.ts +3 -3
- package/dist/prompt-assembly/tool-catalog.d.ts +1 -1
- package/dist/prompt-assembly/turn-snapshot.d.ts +4 -4
- package/dist/prompt-assembly/types.d.ts +12 -12
- package/dist/prompts/coordinator.d.ts +1 -1
- package/dist/prompts/default.d.ts +8 -8
- package/dist/prompts/simple-sections.d.ts +3 -3
- package/dist/prompts/supervisor.d.ts +2 -2
- package/dist/scenarios/full-body.d.ts +3 -3
- package/dist/scenarios/scenario-registry.d.ts +1 -1
- package/dist/stores/cc/sidecar-transcript.d.ts +3 -3
- package/dist/stores/file/fs-atomic.d.ts +2 -2
- package/dist/stores/file/index.d.ts +1 -1
- package/dist/stores/file/session-store.d.ts +2 -2
- package/dist/stores/file/workflow-journal-store.d.ts +4 -4
- package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
- package/dist/tools/fs/encoding.d.ts +4 -4
- package/dist/tools/fs/fs-bash.d.ts +3 -3
- package/dist/tools/fs/fs-pdf.d.ts +1 -1
- package/dist/tools/fs/fs-shared.d.ts +6 -6
- package/dist/tools/fs/index.d.ts +2 -2
- package/dist/tools/fs/notebook.d.ts +1 -1
- package/dist/tools/fs/pdf.d.ts +1 -1
- package/dist/tools/fs/read-deny.d.ts +1 -1
- package/dist/tools/fs/safety.d.ts +9 -9
- package/dist/tools/fs/search.d.ts +2 -2
- package/dist/tools/monitor.d.ts +3 -3
- package/dist/tools/task-list.d.ts +2 -2
- package/dist/tools/web.d.ts +4 -4
- package/dist/tools/worktree.d.ts +5 -5
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +1826 -3
|
@@ -52,7 +52,7 @@ export interface WorkspaceHandle {
|
|
|
52
52
|
snapshotId?: SnapshotId;
|
|
53
53
|
/** Token for {@link RemoteExecutionEnv.reconnect} when the VM is still running (vs suspended to a snapshot). */
|
|
54
54
|
sessionToken?: SessionToken;
|
|
55
|
-
/** How a checkpointed workspace is restored on resume (1.257.2 hardening
|
|
55
|
+
/** How a checkpointed workspace is restored on resume (1.257.2 hardening):
|
|
56
56
|
* `"park_only"` = durable-park-only suspend degrade (non-suspendable env; workspace persists on the
|
|
57
57
|
* target, resume skips `resumeVM`). Absent = legacy/snapshot handle — resume treats a MISSING
|
|
58
58
|
* `snapshotId` as corruption (fail-closed) unless the resumed env is itself non-suspendable
|
|
@@ -98,7 +98,7 @@ export interface VmLifecycleOptions {
|
|
|
98
98
|
/** Abort a hung pause/restore. */
|
|
99
99
|
abortSignal?: AbortSignal;
|
|
100
100
|
/**
|
|
101
|
-
*
|
|
101
|
+
* (resume workspace-root fidelity) — the CHECKPOINTED {@link WorkspaceHandle} of the workspace
|
|
102
102
|
* being restored, passed by the engine on the `resumeVM` leg. An adapter SHOULD bind the restored
|
|
103
103
|
* workspace root (cwd / mount) to `priorHandle.mountPath` rather than its CURRENT deployment
|
|
104
104
|
* configuration: across a config-knob change window (e.g. a mount-path rotation) the resumed task's
|
|
@@ -289,7 +289,7 @@ export interface RemoteExecutionEnv extends ExecutionEnv {
|
|
|
289
289
|
*
|
|
290
290
|
* ⚠️ LOAD-BEARING INVARIANT (opus review 1.257.2): `suspendable:false` on a remote env also asserts
|
|
291
291
|
* the workspace is EXTERNALLY DURABLE — it persists on the target across the env object's lifetime
|
|
292
|
-
* (true for SSH hosts / ADB devices). The durable-park-only suspend degrade
|
|
292
|
+
* (true for SSH hosts / ADB devices). The durable-park-only suspend degrade rests
|
|
293
293
|
* on this: it skips `suspendVM` and trusts the factory to reconnect to the SAME workspace on resume.
|
|
294
294
|
* An adapter for a non-suspendable EPHEMERAL backend (e.g. a snapshot-less container torn down with
|
|
295
295
|
* the transport) must NOT be modeled as `suspendable:false` remote — it would silently take the
|
|
@@ -359,7 +359,7 @@ export interface RemoteExecutionEnv extends ExecutionEnv {
|
|
|
359
359
|
* expired while suspended). Any failed step → `Err("post_resume_failed")` and the caller MUST destroy the env.
|
|
360
360
|
*
|
|
361
361
|
* Note: a resumed guest's network/long-lived connections (git remote / API / registry) are NOT guaranteed to
|
|
362
|
-
* survive the snapshot (
|
|
362
|
+
* survive the snapshot (decision 2), so this is also where the VM-internal agent rebuilds those.
|
|
363
363
|
*
|
|
364
364
|
* 🔴 Ordering red line (design/48 §5/#6): at-rest encryption of the memory snapshot must be ensured BEFORE
|
|
365
365
|
* secrets are injected — never let plaintext credentials land in an unencrypted snapshot. That encryption is a
|
|
@@ -391,10 +391,10 @@ export interface RemoteExecutionEnv extends ExecutionEnv {
|
|
|
391
391
|
* once on task end, and `prepareTask` calls it on a prepare-time throw; a remote impl may also be reaped,
|
|
392
392
|
* so a defensive double-call must not error or double-bill.
|
|
393
393
|
*
|
|
394
|
-
*
|
|
394
|
+
* contract note: on a NON-isolated env (no `capabilities.isolation` — host lane, SSH host),
|
|
395
395
|
* destroy() is workspace/object-level cleanup and must NOT reap still-running background processes —
|
|
396
396
|
* that is `disposeBackgroundShells`' job, which honours the timeout/session keep-alive except-list
|
|
397
|
-
* (
|
|
397
|
+
* (monitor timeout anchor). An isolated env (container/VM) naturally takes everything down;
|
|
398
398
|
* the Runner's envDying settle accounts for that with an accurate killed receipt beforehand.
|
|
399
399
|
*/
|
|
400
400
|
destroy(): Promise<void>;
|
|
@@ -423,7 +423,7 @@ export interface ExecutionEnvFactoryContext {
|
|
|
423
423
|
*/
|
|
424
424
|
isolation?: "worktree";
|
|
425
425
|
/**
|
|
426
|
-
*
|
|
426
|
+
* Field-reported (sub-agents landing in an EMPTY sandbox): the PARENT task's
|
|
427
427
|
* effective working root, threaded (like `isolation`) via the TRUSTED `RunInternals` channel when this
|
|
428
428
|
* task is a sub-agent (workflow `ctx.agent` / Task delegation). CC parity: a Task sub-agent inherits the
|
|
429
429
|
* main session's cwd. A single-user/TOC factory SHOULD root the child env here (unless `isolation`
|
|
@@ -436,7 +436,7 @@ export interface ExecutionEnvFactoryContext {
|
|
|
436
436
|
/**
|
|
437
437
|
* A **trusted control-plane** factory that mints a per-task {@link ExecutionEnv} (design/48 §5 answers core
|
|
438
438
|
* gap-a / §7 Q7). Lives on `RunnerDeps` (deployment-level) — NOT on `TaskSpec` — so an untrusted caller can
|
|
439
|
-
* never inject its own env and escape the sandbox (design/44 §7 Q4 red line;
|
|
439
|
+
* never inject its own env and escape the sandbox (design/44 §7 Q4 red line; decision 3: the Docker
|
|
440
440
|
* fast-lane must be control-plane-assigned, never task-selectable).
|
|
441
441
|
*
|
|
442
442
|
* The remote model is "one container per task", so the factory is invoked once per task. The Runner owns the
|
package/dist/core/roles.d.ts
CHANGED
|
@@ -24,10 +24,10 @@ export declare const CC_MODEL_TIER_ALIASES: Readonly<Record<string, string>>;
|
|
|
24
24
|
* - a binding may itself be a catalog name or a Model object (resolved through `resolveModel`);
|
|
25
25
|
* an unknown binding name throws at construction (config error surfaces at boot, not first use).
|
|
26
26
|
*/
|
|
27
|
-
/**
|
|
27
|
+
/** the DISPLAY face of a string model ref: a CC tier ALIAS (haiku/sonnet/…) resolves to its
|
|
28
28
|
* sema tier name (lite/flash/…) for labels — the alias verbatim reads as a strong claim about a
|
|
29
29
|
* specific vendor's model while the actual routing may land anywhere ("haiku" showing over a
|
|
30
|
-
* DeepSeek run,
|
|
30
|
+
* DeepSeek run, an operator field report). A non-alias string is the deployment's own model key — its
|
|
31
31
|
* honest name — and passes through unchanged. Display only; routing (`expandTiers`) is untouched. */
|
|
32
32
|
export declare function resolveModelDisplayLabel(ref: string): string;
|
|
33
33
|
export declare function expandTiers(models: Record<string, Model> | undefined, tiers: Record<string, ModelRef> | undefined): Record<string, Model> | undefined;
|
|
@@ -49,7 +49,7 @@ export interface ModelMention {
|
|
|
49
49
|
*/
|
|
50
50
|
export declare function parseModelMention(text: string, allowedNames: Iterable<string>): ModelMention;
|
|
51
51
|
/**
|
|
52
|
-
* Role → 档位默认表(
|
|
52
|
+
* Role → 档位默认表(裁定 2026-07-11:「role 体系用户不感知」)。配了 `RunnerDeps.tiers` 的部署,
|
|
53
53
|
* 各 role 未显式配置时自动落到语义档位——用户/壳只感知档位,roles 降级为内部路由层:
|
|
54
54
|
* - `default` → `pro`(主力档兜底;用户/部署 roles.default / TaskSpec.model 恒可覆盖);
|
|
55
55
|
* - `summarize` → `flash`(压缩本该便宜;flash 未绑沿降档链到 lite);
|
package/dist/core/roles.js
CHANGED
|
@@ -42,7 +42,7 @@ export interface Stats {
|
|
|
42
42
|
toolArg?: string;
|
|
43
43
|
}>;
|
|
44
44
|
};
|
|
45
|
-
/** TB telemetry B2
|
|
45
|
+
/** TB telemetry B2: engine-mechanism engagement counters. Set by the run loop
|
|
46
46
|
* only when ≥1 engaged; flows straight through to `TaskResult.stats.mechanisms` via the
|
|
47
47
|
* public-stats spread. */
|
|
48
48
|
mechanisms?: {
|
|
@@ -102,7 +102,7 @@ export interface ResultFlags {
|
|
|
102
102
|
threw: unknown;
|
|
103
103
|
/** design/99 MF-25: the EFFECTIVE (resolved) model id that served the task — echoed on `TaskResult.model`. */
|
|
104
104
|
model?: string;
|
|
105
|
-
/** RB-368
|
|
105
|
+
/** RB-368: some spend was accounted while the serving model had NO configured price table
|
|
106
106
|
* (no `RunnerDeps.pricing` entry, no `Model.cost`). The numeric total is then a fabricated
|
|
107
107
|
* undercount — `stats.costMicroUsd`/`costBreakdown` are OMITTED from the public result instead of
|
|
108
108
|
* reporting a 0 indistinguishable from "declared free". The internal accumulator (budget gate's
|
|
@@ -22,7 +22,7 @@ import type { Prepared } from "./prepare-task.js";
|
|
|
22
22
|
/**
|
|
23
23
|
* Opt-in working-file attachments (LONGRUN-2 / CC 198 post-compact restore parity).
|
|
24
24
|
*
|
|
25
|
-
*
|
|
25
|
+
* CC parity: post-compact file re-read is DEFAULT ON (CC hard-codes it;
|
|
26
26
|
* LONGRUN-2 measured ≈2.3 extra read round-trips per compaction without it). `false` opts out; an
|
|
27
27
|
* object customizes caps. Selection set = most recently READ (CC readFileState semantics) with a
|
|
28
28
|
* modified-files fallback inside maybeCompact. Returns `undefined` when attachments are off or the
|
|
@@ -52,7 +52,7 @@ export declare function buildWorkingFileAttachments(spec: TaskSpec, prepared: Pr
|
|
|
52
52
|
*/
|
|
53
53
|
export declare function forkContextOption(prepared: Prepared, disable: boolean): Pick<MaybeCompactOptions, "forkContext">;
|
|
54
54
|
/**
|
|
55
|
-
* RB-31
|
|
55
|
+
* RB-31 — compaction-boundary center-candidate adoption. Absent (an empty object, NOT a
|
|
56
56
|
* `centerAdoption: undefined` key) = restate as before; a boundary re-pin hashes the same artifact
|
|
57
57
|
* identity on every lane.
|
|
58
58
|
*/
|
|
@@ -106,7 +106,7 @@ export declare function projectStaleToolResults(context: Context, cfg: ResolvedS
|
|
|
106
106
|
* write-then-discard round-trip per stale result per turn. Caller owns the Set's lifetime. */
|
|
107
107
|
writtenRefs: Set<string>): Promise<Context>;
|
|
108
108
|
/**
|
|
109
|
-
* MF-18 修②
|
|
109
|
+
* MF-18 修② — fidelity disclosure: the summary INPUT was clipped to the compaction model's
|
|
110
110
|
* window. Fires pre-call, so the disclosure survives a failed attempt. All three lanes emit the SAME
|
|
111
111
|
* frame; the end-of-task lane passes `spec.tracer ?? deps.tracer` / `spec.taskId ?? sessionId`
|
|
112
112
|
* explicitly because `RunState.telemetry` (which holds exactly those two resolved values) is not in
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design/376 片② — the RUNNER convenience half of the consolidation driver: resolve the model
|
|
3
|
+
* seat out of a deployment's `RunnerDeps` (explicit chat seat wins; else the `consolidate` role
|
|
4
|
+
* chain) into the options `runMemoryConsolidationDriver` eats.
|
|
5
|
+
*
|
|
6
|
+
* Role resolution lives HERE and not in the engine-layer verb on purpose (design/339 §1.1 + the
|
|
7
|
+
* d2 ruling): the driver function's own seat is an explicit chat function — models, roles and
|
|
8
|
+
* tiers are runner vocabulary. The chain is `consolidate → summarize → coded refusal`, and the
|
|
9
|
+
* refusal is deliberate: one consolidation run reads the whole library (~10⁵ prompt tokens), so
|
|
10
|
+
* an unset role silently escalating to the most expensive `default` model would be the
|
|
11
|
+
* bad-value-silence family. A deployment that already declared a cheap `summarize` tier has made
|
|
12
|
+
* exactly the cost choice this role wants, so that hop stays; pointing `roles.consolidate` at the
|
|
13
|
+
* main model is a legal explicit choice.
|
|
14
|
+
*/
|
|
15
|
+
import type { Model } from "../../internal/llm.js";
|
|
16
|
+
import type { Brain, ModelRoles, ModelRef, TaskSpec } from "../types.js";
|
|
17
|
+
import { type DistillerChatFn } from "../memory-engine/distiller.js";
|
|
18
|
+
import type { RunMemoryConsolidationOptions } from "../memory-engine/consolidation-driver.js";
|
|
19
|
+
import type { MemoryConsolidationOptions } from "../memory-engine/consolidation.js";
|
|
20
|
+
import type { LlmDistillerContract } from "../memory-engine/distiller.js";
|
|
21
|
+
/** The slice of `RunnerDeps` this resolver reads (pass the deps object itself). */
|
|
22
|
+
export interface MemoryConsolidationDriverDeps {
|
|
23
|
+
brain: Brain;
|
|
24
|
+
models?: Record<string, Model>;
|
|
25
|
+
tiers?: Record<string, ModelRef>;
|
|
26
|
+
roles?: ModelRoles;
|
|
27
|
+
memoryConsolidation?: MemoryConsolidationOptions;
|
|
28
|
+
memoryConsolidationDriver?: {
|
|
29
|
+
chat?: {
|
|
30
|
+
baseUrl: string;
|
|
31
|
+
apiKey: string;
|
|
32
|
+
model: string;
|
|
33
|
+
} | DistillerChatFn;
|
|
34
|
+
model?: string;
|
|
35
|
+
contract?: LlmDistillerContract;
|
|
36
|
+
maxCycles?: number;
|
|
37
|
+
mintRepairBudget?: number;
|
|
38
|
+
};
|
|
39
|
+
/** Optional per-model auth hook (the TaskSpec-shaped seat) for the role-resolved path. */
|
|
40
|
+
getApiKeyAndHeaders?: TaskSpec["getApiKeyAndHeaders"];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a deployment's driver options: `{ chat, model }` from the explicit seat or the role
|
|
44
|
+
* chain, plus the deployment knobs (contract / maxCycles / mintRepairBudget) and the protocol
|
|
45
|
+
* parameters (`memoryConsolidation`) threaded through verbatim. The result feeds
|
|
46
|
+
* `runMemoryConsolidationDriver(engine, scope, resolved)` directly; every bad value refuses
|
|
47
|
+
* coded here or in the verb's own screen (#123), never folds to a default.
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveMemoryConsolidationDriver(deps: MemoryConsolidationDriverDeps): RunMemoryConsolidationOptions;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { brainToRuntime } from "../runtime.js";
|
|
2
|
+
import { expandTiers, roleModelIfSet } from "../roles.js";
|
|
3
|
+
import { isAliasModelId, openAiCompatChatSeat } from "../memory-engine/distiller.js";
|
|
4
|
+
function seatRefusal(message) {
|
|
5
|
+
const e = new Error(message);
|
|
6
|
+
e.code = "config.memory_consolidation_driver";
|
|
7
|
+
return e;
|
|
8
|
+
}
|
|
9
|
+
export function resolveMemoryConsolidationDriver(deps) {
|
|
10
|
+
const seat = deps.memoryConsolidationDriver;
|
|
11
|
+
let chat;
|
|
12
|
+
let model;
|
|
13
|
+
if (typeof seat?.chat === "function") {
|
|
14
|
+
if (typeof seat.model !== "string" || seat.model.trim() === "" || isAliasModelId(seat.model)) {
|
|
15
|
+
throw seatRefusal(`memoryConsolidationDriver.chat is a bare function, so memoryConsolidationDriver.model must carry the EXPLICIT model id it serves (got ${JSON.stringify(seat.model)}).`);
|
|
16
|
+
}
|
|
17
|
+
chat = seat.chat;
|
|
18
|
+
model = seat.model;
|
|
19
|
+
}
|
|
20
|
+
else if (seat?.chat !== undefined) {
|
|
21
|
+
chat = openAiCompatChatSeat(seat.chat);
|
|
22
|
+
model = seat.chat.model;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
const models = expandTiers(deps.models, deps.tiers);
|
|
26
|
+
const resolved = roleModelIfSet("consolidate", {}, { models, roles: deps.roles }) ?? roleModelIfSet("summarize", {}, { models, roles: deps.roles });
|
|
27
|
+
if (resolved === undefined) {
|
|
28
|
+
throw seatRefusal('memory consolidation has no model seat: configure RunnerDeps.memoryConsolidationDriver.chat (an endpoint triple or a chat function), or declare roles.consolidate / roles.summarize (a tier-expanded catalog\'s "flash" binding also satisfies the summarize hop). The chain deliberately does NOT fall back to the default main model — a whole-library distillation must not silently ride the most expensive seat.');
|
|
29
|
+
}
|
|
30
|
+
if (isAliasModelId(resolved.id)) {
|
|
31
|
+
throw seatRefusal(`the resolved consolidation model id ${JSON.stringify(resolved.id)} is an alias spelling — an alias can be re-pointed upstream and would move the run with no trace in the archive; bind an explicit id.`);
|
|
32
|
+
}
|
|
33
|
+
model = resolved.id;
|
|
34
|
+
const runtime = brainToRuntime(deps.brain);
|
|
35
|
+
chat = async (req) => {
|
|
36
|
+
const auth = await deps.getApiKeyAndHeaders?.(resolved);
|
|
37
|
+
const message = await runtime.completeSimple(resolved, { systemPrompt: req.system, messages: [{ role: "user", content: req.user, timestamp: Date.now() }] }, { temperature: req.temperature, maxTokens: req.maxTokens, apiKey: auth?.apiKey, headers: auth?.headers });
|
|
38
|
+
if (message.stopReason === "error" || message.stopReason === "aborted") {
|
|
39
|
+
throw new Error(message.errorMessage ?? `model completion ${message.stopReason}`);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
content: message.content
|
|
43
|
+
.filter((c) => c.type === "text")
|
|
44
|
+
.map((c) => c.text)
|
|
45
|
+
.join(""),
|
|
46
|
+
finishReason: message.stopReason,
|
|
47
|
+
usage: { promptTokens: message.usage.input, completionTokens: message.usage.output },
|
|
48
|
+
model: resolved.id,
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
chat,
|
|
54
|
+
model,
|
|
55
|
+
...(seat?.contract !== undefined ? { contract: seat.contract } : {}),
|
|
56
|
+
...(seat?.maxCycles !== undefined ? { maxCycles: seat.maxCycles } : {}),
|
|
57
|
+
...(seat?.mintRepairBudget !== undefined ? { mintRepairBudget: seat.mintRepairBudget } : {}),
|
|
58
|
+
...(deps.memoryConsolidation !== undefined ? { consolidation: deps.memoryConsolidation } : {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -27,7 +27,7 @@ export declare const DEFAULT_CONSOLIDATION_MAX_NOTES = 50;
|
|
|
27
27
|
export declare const DEFAULT_CONSOLIDATION_TIMEOUT_SEC = 30;
|
|
28
28
|
/** Normalize a note body for the EXACT-restatement check in the `<lo` near-dup cheap path. Case- + whitespace-
|
|
29
29
|
* insensitive ONLY (lower-case, collapse whitespace runs, trim); it PRESERVES every symbol/punctuation so it can
|
|
30
|
-
* NOT false-equate semantically-distinct facts. (
|
|
30
|
+
* NOT false-equate semantically-distinct facts. (falsification-style: the earlier `[^\p{L}\p{N}]`-stripping
|
|
31
31
|
* normalization made "C#"≡"C", "delta +5"≡"delta -5", "100%"≡"100" — and a textual-CONTAINMENT test on top of it
|
|
32
32
|
* also mis-judged "rate limit 1000" ⊇ "rate limit 100" (word boundary). A fire-on-every-note, no-LLM path must not
|
|
33
33
|
* make a semantic-subset call, so the cheap path now folds ONLY an exact restatement; every real near-dup defers
|
|
@@ -46,17 +46,17 @@ export declare function limitConfigError(code: string, message: string): Error &
|
|
|
46
46
|
*/
|
|
47
47
|
export declare function resolveTaskLimits(limits: TaskLimits | undefined): TaskLimits | undefined;
|
|
48
48
|
/**
|
|
49
|
-
* R2 双形轴(
|
|
49
|
+
* R2 双形轴(追加令 2026-07-18): CC 2.1.212's fable-variant prompt gate (b9e —
|
|
50
50
|
* `fable_5_mitigations` capability / claude-mythos-5), ORTHOGONAL to the simple/classic profile.
|
|
51
51
|
* sema is BYOM, so the id may carry provider prefixes ("anthropic/claude-fable-5",
|
|
52
52
|
* "openrouter/anthropic/claude-fable-5"): BOUNDARY-AWARE family match on the last path segment
|
|
53
|
-
* (
|
|
53
|
+
* (统一复审 F3 — raw substring classified "vendor/not-claude-fable-5" and "claude-mythos-50"
|
|
54
54
|
* as fable), case-normalized. Recognition set = CC's _Nr (startsWith "claude-fable-") + b9e
|
|
55
55
|
* (mythos-5). R3 system sections fork on the resulting fact.
|
|
56
56
|
*/
|
|
57
57
|
export declare function isFableFamilyModelId(id: string): boolean;
|
|
58
58
|
/**
|
|
59
|
-
* RB-50 (CC 2.1.220 启示①,
|
|
59
|
+
* RB-50 (CC 2.1.220 启示①, 2026-07-25): the SINGLE decision point for the two prompt-shape axes.
|
|
60
60
|
* Both were resolved in separate places with different mechanisms — `promptProfile` off a TaskSpec field,
|
|
61
61
|
* `fableMitigations` off a raw model-id prefix test — so "which shape does this task speak" had no one
|
|
62
62
|
* place to read. CC 2.1.220's counterpart is a model-registry `capabilities` array (one table drives
|
|
@@ -329,13 +329,13 @@ export interface Prepared {
|
|
|
329
329
|
chars: number;
|
|
330
330
|
hash: string;
|
|
331
331
|
}>;
|
|
332
|
-
/** Manifest v2 (
|
|
332
|
+
/** Manifest v2 (stage S1, additive): the compiled-IR section view (slot/carrier/cadence/
|
|
333
333
|
* cacheClass per section). `contentHash` is the
|
|
334
|
-
* digest-tiering face
|
|
334
|
+
* digest-tiering face: present ONLY on operator-declared sections (the typed
|
|
335
335
|
* `stableBlocks` lane, owner "deployment") as an UNSALTED full sha256 of the section text —
|
|
336
336
|
* the center-publish ↔ manifest end-to-end reconciliation anchor. Core-native sections (env
|
|
337
337
|
* facts, memory tail, user role text) stay salted-only: a plain hash of a short guessable
|
|
338
|
-
* block is a dictionary-preimage surface for trace readers (
|
|
338
|
+
* block is a dictionary-preimage surface for trace readers ([E]). */
|
|
339
339
|
sections?: Array<{
|
|
340
340
|
id: string;
|
|
341
341
|
slot: string;
|
|
@@ -346,7 +346,7 @@ export interface Prepared {
|
|
|
346
346
|
hash: string;
|
|
347
347
|
contentHash?: string;
|
|
348
348
|
}>;
|
|
349
|
-
/**
|
|
349
|
+
/** S2 (additive): the mounted tools plane — contract identity, durable-replay aliases,
|
|
350
350
|
* presentation-invariant shape digest and full wire digest per tool (initial mount face). */
|
|
351
351
|
tools?: ToolManifestRow[];
|
|
352
352
|
/** design/148 S3 (additive, §10.2): the nine-element cache identity — digests only, no prompt
|
|
@@ -363,7 +363,7 @@ export interface Prepared {
|
|
|
363
363
|
* seat chose it. Absent when nothing is deferred (there is no disclosure to describe). */
|
|
364
364
|
toolDisclosure?: ToolDisclosureManifest;
|
|
365
365
|
};
|
|
366
|
-
/**
|
|
366
|
+
/** This run's provider-declared prompt sections (epoch declaration axis),
|
|
367
367
|
* threaded to every compaction-boundary epoch selection so boundary re-pins hash the same
|
|
368
368
|
* artifact identity as the prepare-time pin. Empty on declaration-free runs. */
|
|
369
369
|
epochDeclaredSections: import("../../prompt-assembly/epoch.js").EpochDeclaredSections;
|
|
@@ -690,7 +690,7 @@ export interface Prepared {
|
|
|
690
690
|
* RB-197: seeded entries are excluded — their content lives in the system-prompt lane, which
|
|
691
691
|
* compaction never touches, so re-attaching them into the summary would be a duplicate. */
|
|
692
692
|
recentlyReadFiles?: () => string[];
|
|
693
|
-
/** RB-197②(独立复审 +
|
|
693
|
+
/** RB-197②(独立复审 + 命中,已修) — canonicalizes a raw (often relative, model-typed) path the
|
|
694
694
|
* SAME way the hands toolkit does (`resolveKey` against the same containment root `readTaskFile`
|
|
695
695
|
* uses), so `maybeCompact`'s kept-tail/instruction-source exclusions compare paths in the SAME
|
|
696
696
|
* coordinate `recentlyReadFiles()` already uses — without this a plain string match silently never
|
|
@@ -698,7 +698,7 @@ export interface Prepared {
|
|
|
698
698
|
* readTaskFile); absent there is no containment root to resolve against, and every path source is
|
|
699
699
|
* already in the same raw coordinate anyway. */
|
|
700
700
|
normalizeAttachmentPath?: (raw: string) => Promise<string>;
|
|
701
|
-
/** RB-197②(
|
|
701
|
+
/** RB-197②(交叉复审命中,已修) — recognizes the hands Read tool's own dedup-stub markers
|
|
702
702
|
* ({@link isReadDedupStubResult}), so `maybeCompact`'s kept-tail scan can tell a SUBSTANTIVE Read
|
|
703
703
|
* result from a stub hit whose original full transmission may already be summarized away. Present
|
|
704
704
|
* only with hands (same gate as readTaskFile). */
|
|
@@ -707,7 +707,7 @@ export interface Prepared {
|
|
|
707
707
|
* `MaybeCompactOptions.onApplied` by every compaction lane. Drops the non-seeded read-state entries
|
|
708
708
|
* (the summary just replaced the Read results the dedup stubs point at) and re-registers the files
|
|
709
709
|
* that were re-attached WHOLE. Present only with hands (same gate as readTaskFile).
|
|
710
|
-
* RB-197②(
|
|
710
|
+
* RB-197②(命中,已修): `preserveReadState` carries the kept-tail-visible files that were
|
|
711
711
|
* deliberately skipped for re-attachment — their existing entry must survive the clear too (same
|
|
712
712
|
* reasoning as a seeded entry: the model's view of the file did not change). */
|
|
713
713
|
onCompactionApplied?: (attachedComplete: ReadonlyArray<{
|
|
@@ -731,7 +731,7 @@ export interface Prepared {
|
|
|
731
731
|
planModeRef: {
|
|
732
732
|
active: boolean;
|
|
733
733
|
};
|
|
734
|
-
/**
|
|
734
|
+
/** A1 (design/66 anchor revision) — the date-flip detector seam: `legDate` = the date frozen
|
|
735
735
|
* into this leg's system prefix; `today()` = the boundary-time LOCAL date (user-zone-bound closure).
|
|
736
736
|
* The run loop feeds both to the `date_change` attachment producer; the prefix itself never
|
|
737
737
|
* re-renders mid-leg (cache preservation, CC parity). Present only when the prompt carries a date. */
|
|
@@ -953,7 +953,7 @@ export interface PrepareResume {
|
|
|
953
953
|
/** Digest of that question's batch — the id alone can repeat, so the pairing is what keeps a LATER
|
|
954
954
|
* question from inheriting the claim that an operator approved it. */
|
|
955
955
|
redeemedContentAskQuestionsHash?: string;
|
|
956
|
-
/**
|
|
956
|
+
/** true iff this resume will EXECUTE an approved pending tool (`tool_approval`
|
|
957
957
|
* checkpoint × an `allow` winner). The divergent-restore fail-closed guard keys on THIS, not on the
|
|
958
958
|
* batch being non-empty: a deny winner executes nothing, so a workspace-root divergence must not
|
|
959
959
|
* wedge the refusal (it proceeds under the path-state rebase and records the deny). */
|
|
@@ -1089,14 +1089,14 @@ export interface InheritedGate {
|
|
|
1089
1089
|
/**
|
|
1090
1090
|
* The environment this screening face was installed against — the source of the `HookEnvCapabilities`
|
|
1091
1091
|
* handed to it, compared BY REFERENCE, `undefined` when the installation had no env face. Part of the
|
|
1092
|
-
* installation's identity for the same reason the frozen approver is (
|
|
1092
|
+
* installation's identity for the same reason the frozen approver is (HIGH): a face that
|
|
1093
1093
|
* resolves paths through `ctx.env` answers a different question in a different environment, so an
|
|
1094
1094
|
* ancestor running against the deployment's tree and a descendant running in an isolated worktree are
|
|
1095
1095
|
* two screenings, not one. A deployment with a per-task `executionEnvFactory` therefore has its face
|
|
1096
1096
|
* consulted once per environment on the chain rather than once overall — which is the point: the
|
|
1097
1097
|
* ancestor's environment is the only place the ancestor's verdict can be reproduced.
|
|
1098
1098
|
*
|
|
1099
|
-
* ⚠️ RECORDED BOUNDARY (delegation provenance
|
|
1099
|
+
* ⚠️ RECORDED BOUNDARY (delegation provenance): the approver half of the key compares
|
|
1100
1100
|
* through {@link askApproverIdentity}, which by its own contract answers "the same decision-maker",
|
|
1101
1101
|
* collapsing a `withDelegationProvenance` wrapper onto the approver it forwards to. Two installations
|
|
1102
1102
|
* whose approvers differ ONLY in the provenance they attach therefore count as one. Raw reference
|
|
@@ -1270,7 +1270,7 @@ export interface RunInternals {
|
|
|
1270
1270
|
*/
|
|
1271
1271
|
reminderMark?: string;
|
|
1272
1272
|
/**
|
|
1273
|
-
* RB-204 P1
|
|
1273
|
+
* RB-204 P1 — set UNCONDITIONALLY by every core spawn path (`createSubagentTool`'s
|
|
1274
1274
|
* `childInternals` — shared by all four spawn legs sync/steer/background/fork, its persisted
|
|
1275
1275
|
* `internalsSnapshot` for revive, and the workflow orchestrator's `spawnAttribution`), regardless
|
|
1276
1276
|
* of whether a NAMEABLE `parentTaskId`/`parentToolCallId` exists. A directly-started workflow (no
|
|
@@ -1310,7 +1310,7 @@ export interface RunInternals {
|
|
|
1310
1310
|
* Why internal (not a public `TaskSpec.goalMode`): the guidance promises "declaring done STOPS iteration and
|
|
1311
1311
|
* surfaces" — a promise only `runGoal`'s loop makes real. A public field would let a caller inject that
|
|
1312
1312
|
* prompt with no loop behind it (§6.3 honesty violation). `runGoal` injects the `declare_done` tool itself;
|
|
1313
|
-
* this flag only governs the PROMPT (
|
|
1313
|
+
* this flag only governs the PROMPT (injection-ownership split).
|
|
1314
1314
|
*/
|
|
1315
1315
|
goalMode?: boolean;
|
|
1316
1316
|
/**
|
|
@@ -1353,7 +1353,7 @@ export interface RunInternals {
|
|
|
1353
1353
|
*/
|
|
1354
1354
|
agentName?: string;
|
|
1355
1355
|
/**
|
|
1356
|
-
* design/147 S3 (
|
|
1356
|
+
* design/147 S3 (label-vs-identity): the EXPLICIT `Agent({name})` spawn identity — set
|
|
1357
1357
|
* ONLY when the spawn carried a `name` parameter, unlike {@link agentName} (a DISPLAY label:
|
|
1358
1358
|
* description or agent-type, present on nearly every spawn). Teammate semantics (hierarchy clamp,
|
|
1359
1359
|
* teammate addendum, uplink attribution) key on THIS field; keying on the display label made
|
|
@@ -1393,7 +1393,7 @@ export interface RunInternals {
|
|
|
1393
1393
|
* {@link delegationTaskType} above. TRUSTED run-scoped channel (never a TaskSpec field).
|
|
1394
1394
|
*/
|
|
1395
1395
|
cycleSeq?: number;
|
|
1396
|
-
/**
|
|
1396
|
+
/** The ROOT host session of the whole delegation tree (fixed point: the
|
|
1397
1397
|
* spawner passes its own `ctx.rootSessionId ?? ctx.sessionId`, so depth 1 gets the host session
|
|
1398
1398
|
* and every deeper level inherits it verbatim). `parentSessionId` is the IMMEDIATE spawner —
|
|
1399
1399
|
* after a restart those intermediate sessions are dead ends, and a recovery face enumerating
|
|
@@ -1421,7 +1421,7 @@ export interface RunInternals {
|
|
|
1421
1421
|
* delegation tool resolves this axis TO that principal.
|
|
1422
1422
|
*/
|
|
1423
1423
|
registryScope?: string;
|
|
1424
|
-
/** design/148 S1
|
|
1424
|
+
/** design/148 S1 — the spawning run's ADOPTED center artifact, threaded down the
|
|
1425
1425
|
* trusted internals chain so every child in the tree composes the SAME closure (a child must
|
|
1426
1426
|
* never spontaneously adopt the live candidate while its parent runs an older pin — that is the
|
|
1427
1427
|
* exact mixed-version state the epoch exists to prevent). Resolved by-digest through the
|
|
@@ -1482,7 +1482,7 @@ export interface RunInternals {
|
|
|
1482
1482
|
priority?: import("../task-notification.js").SystemInjectionPriority;
|
|
1483
1483
|
}) => void;
|
|
1484
1484
|
/**
|
|
1485
|
-
* design/147 S3a
|
|
1485
|
+
* design/147 S3a — the PARENT run's subagent-retain ledger, threaded to a CHILD so its
|
|
1486
1486
|
* SendMessage can continue a RETAINED SIBLING (the sibling's retain entry lives on the parent's
|
|
1487
1487
|
* ledger — without this, sibling resolution succeeds but delivery always reads not-retained).
|
|
1488
1488
|
* TRUSTED chain; read-only use (the sibling leg resumes through the same fenced resume face).
|
|
@@ -1522,7 +1522,7 @@ export interface RunInternals {
|
|
|
1522
1522
|
*/
|
|
1523
1523
|
parentCwd?: string;
|
|
1524
1524
|
/**
|
|
1525
|
-
* [c209-D]
|
|
1525
|
+
* [c209-D] — the EXPLICIT Agent.cwd request, distinct from the best-effort `parentCwd`
|
|
1526
1526
|
* inheritance hint above: inheritance may be silently ignored by a factory (or absent without one),
|
|
1527
1527
|
* but an explicit cwd the model asked for MUST either take effect or fail loud. prepareTask enforces
|
|
1528
1528
|
* the contract: no `executionEnvFactory` ⇒ throw `config.cwd_unsupported`; after env creation the
|
|
@@ -1558,7 +1558,7 @@ export interface RunInternals {
|
|
|
1558
1558
|
*/
|
|
1559
1559
|
onActivity?: (activity: ToolActivity) => void;
|
|
1560
1560
|
/**
|
|
1561
|
-
* RB-393①
|
|
1561
|
+
* RB-393① — a TRUSTED run-scoped WORKSPACE-observation sink, filled ONLY by a core caller
|
|
1562
1562
|
* (the workflow's `ctx.agent` / `ctx.agentStream`, to record an isolated agent's worktree directory on the
|
|
1563
1563
|
* persisted run record). NEVER a {@link TaskSpec} field — same posture as {@link onActivity}.
|
|
1564
1564
|
*
|
|
@@ -1566,7 +1566,7 @@ export interface RunInternals {
|
|
|
1566
1566
|
* {@link RunnerDeps.executionEnvFactory} and lands only on the per-task env's `cwd`; no runner-outward face
|
|
1567
1567
|
* (TaskEvent union / TaskStream / TaskResult) carries it back. An orchestrator therefore could not record
|
|
1568
1568
|
* WHERE its isolated agent worked — the recovery path after an interrupted run had to enumerate the
|
|
1569
|
-
* worktrees directory and guess, which is exactly the contradiction
|
|
1569
|
+
* worktrees directory and guess, which is exactly the contradiction recorded against the
|
|
1570
1570
|
* "isolate-only, userland decides verify/merge" intent.
|
|
1571
1571
|
*
|
|
1572
1572
|
* Called at most ONCE per prepared run with the task's FINAL working root (see {@link ResolvedWorkspace}),
|
|
@@ -22,7 +22,7 @@ import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send
|
|
|
22
22
|
import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
23
23
|
import { askApproverIdentity, checkToolPolicyProjection, combinePolicies, constraintChainDigest, constraintChainEntryOf, isApprovalSettledBy, isAskDenyResolution, screenApproverAttribution, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
|
|
24
24
|
const PERSISTED_RULE_TOOL = "Bash";
|
|
25
|
-
import { findAdmittingRule, suggestRulesForCommand } from "../permission-rule-model.js";
|
|
25
|
+
import { findAdmittingRule, segmentCoverageOf, suggestRulesForCommand } from "../permission-rule-model.js";
|
|
26
26
|
import { ActiveSkillScope, createActiveSkillScopePolicy } from "./active-skill-scope.js";
|
|
27
27
|
import { CHANGED_FILES_MTIME_EPS_MS, fenceMcpServerInstructions, renderAgentListingDelta } from "./turn-attachments.js";
|
|
28
28
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
@@ -437,7 +437,9 @@ function orgRevisionEvidenceOf(resolution, onDefect) {
|
|
|
437
437
|
return {};
|
|
438
438
|
}
|
|
439
439
|
function persistedRuleHitOf(admitting) {
|
|
440
|
-
return admitting === undefined
|
|
440
|
+
return admitting === undefined
|
|
441
|
+
? undefined
|
|
442
|
+
: { rules: admitting.map((r) => ({ rule: r.rule, dots: r.adds.map((a) => ({ actor: a.dot.actor, counter: a.dot.counter })) })) };
|
|
441
443
|
}
|
|
442
444
|
function cwdConflictsRestoreError(requestedCwd) {
|
|
443
445
|
const e = new Error(`RunInternals.requestedCwd ("${requestedCwd}") cannot be combined with a checkpoint workspace restore — ` +
|
|
@@ -480,7 +482,7 @@ async function derivedRouteFallsBack(args) {
|
|
|
480
482
|
const verdict = await adjudicateDerivedRoute({ brain: args.brain, model: args.derived, getApiKeyAndHeaders: args.getApiKeyAndHeaders });
|
|
481
483
|
if (verdict === undefined || verdict.ok)
|
|
482
484
|
return false;
|
|
483
|
-
deliverEngineNotice(args.onNotice, fallbackToPrimaryNotice({ seat: args.seat, from: args.derived.id, to: args.primary.id, verdict }));
|
|
485
|
+
deliverEngineNotice(args.onNotice, fallbackToPrimaryNotice({ seat: args.seat, from: args.derived.id, to: args.primary.id, verdict, ...(args.sessionId !== undefined ? { sessionId: args.sessionId } : {}) }));
|
|
484
486
|
return true;
|
|
485
487
|
}
|
|
486
488
|
catch {
|
|
@@ -507,7 +509,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
507
509
|
let effectiveCompModel = compModel;
|
|
508
510
|
if (spec.compactionModel === undefined &&
|
|
509
511
|
compModel !== undefined &&
|
|
510
|
-
(await derivedRouteFallsBack({ seat: "compaction-summary", derived: compModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice }))) {
|
|
512
|
+
(await derivedRouteFallsBack({ seat: "compaction-summary", derived: compModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice, sessionId }))) {
|
|
511
513
|
effectiveCompModel = undefined;
|
|
512
514
|
}
|
|
513
515
|
warnCompactionWindowHazard(deps.tracer, spec, model, effectiveCompModel, hostTaskId);
|
|
@@ -1094,7 +1096,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1094
1096
|
catch {
|
|
1095
1097
|
classifierModel = model;
|
|
1096
1098
|
}
|
|
1097
|
-
if (await derivedRouteFallsBack({ seat: "auto-mode-classifier", derived: classifierModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice })) {
|
|
1099
|
+
if (await derivedRouteFallsBack({ seat: "auto-mode-classifier", derived: classifierModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice, sessionId })) {
|
|
1098
1100
|
classifierModel = model;
|
|
1099
1101
|
}
|
|
1100
1102
|
const classifierSystemPrompt = buildAutoModePrompt(am);
|
|
@@ -3019,7 +3021,11 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3019
3021
|
}));
|
|
3020
3022
|
return { unreadable: true };
|
|
3021
3023
|
}
|
|
3022
|
-
|
|
3024
|
+
const admitting = findAdmittingRule(listed.rules, { tool: req.toolName, command, cwd: root });
|
|
3025
|
+
if (admitting !== undefined)
|
|
3026
|
+
return persistedRuleHitOf(admitting);
|
|
3027
|
+
const coverage = segmentCoverageOf(command, { persisted: listed.rules }, { tool: req.toolName, cwd: root });
|
|
3028
|
+
return coverage !== undefined ? { segmentCoverage: coverage } : undefined;
|
|
3023
3029
|
},
|
|
3024
3030
|
};
|
|
3025
3031
|
})();
|
|
@@ -3049,7 +3055,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3049
3055
|
},
|
|
3050
3056
|
};
|
|
3051
3057
|
})();
|
|
3052
|
-
const
|
|
3058
|
+
const ruleOffersOf = (toolName, args, ask) => {
|
|
3053
3059
|
if (permissionRuleLane === undefined || toolName !== PERSISTED_RULE_TOOL)
|
|
3054
3060
|
return {};
|
|
3055
3061
|
if ((spec.principal === undefined || spec.principal === "") && deps.localOwnerRules !== true)
|
|
@@ -3071,8 +3077,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3071
3077
|
const command = args?.command;
|
|
3072
3078
|
if (typeof command !== "string")
|
|
3073
3079
|
return {};
|
|
3074
|
-
const
|
|
3075
|
-
return
|
|
3080
|
+
const offers = suggestRulesForCommand(command, ask?.segmentCoverage !== undefined ? { coverage: ask.segmentCoverage } : undefined);
|
|
3081
|
+
return offers.length > 0 ? { ruleOffers: offers } : {};
|
|
3076
3082
|
};
|
|
3077
3083
|
const frozenClassifierExcluded = (d) => d.decisionReason === "hook" || d.matchedAskRule !== undefined;
|
|
3078
3084
|
const recheckApprovedEdit = async (pol, onAskOf, creq, edit, csignal, ancestorDecider) => {
|
|
@@ -3121,7 +3127,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3121
3127
|
toolName: creq.toolName,
|
|
3122
3128
|
toolCallId: creq.toolCallId,
|
|
3123
3129
|
args: editArgs,
|
|
3124
|
-
...
|
|
3130
|
+
...ruleOffersOf(creq.toolName, editArgs, { ...(re ?? {}), ancestorResolved: true }),
|
|
3125
3131
|
message: re.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
3126
3132
|
...askSourceIdentity(),
|
|
3127
3133
|
...riskAxesOf(creq.toolName),
|
|
@@ -3207,7 +3213,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3207
3213
|
toolName: creq.toolName,
|
|
3208
3214
|
toolCallId: creq.toolCallId,
|
|
3209
3215
|
args: presentedArgs,
|
|
3210
|
-
...
|
|
3216
|
+
...ruleOffersOf(creq.toolName, presentedArgs, { ...(first.action === "ask" ? first : {}), ancestorResolved: true }),
|
|
3211
3217
|
message: first.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
3212
3218
|
...askSourceIdentity(),
|
|
3213
3219
|
...riskAxesOf(creq.toolName),
|
|
@@ -3295,7 +3301,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3295
3301
|
toolName: creq.toolName,
|
|
3296
3302
|
toolCallId: creq.toolCallId,
|
|
3297
3303
|
args: presentedArgs,
|
|
3298
|
-
...
|
|
3304
|
+
...ruleOffersOf(creq.toolName, presentedArgs, { ...(decision.action === "ask" ? decision : {}), ancestorResolved: true }),
|
|
3299
3305
|
message: decision.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
3300
3306
|
...askSourceIdentity(),
|
|
3301
3307
|
...riskAxesOf(creq.toolName),
|
|
@@ -3587,7 +3593,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3587
3593
|
const preview = approvalPreviewOf(req.toolName, req.args);
|
|
3588
3594
|
return preview !== undefined ? { preview } : {};
|
|
3589
3595
|
})(),
|
|
3590
|
-
...
|
|
3596
|
+
...ruleOffersOf(req.toolName, req.args, decision.action === "ask" ? decision : undefined),
|
|
3591
3597
|
message: decision.message ?? `approval required for "${req.toolName}"`,
|
|
3592
3598
|
...askSourceIdentity(),
|
|
3593
3599
|
...riskAxesOf(req.toolName),
|
|
@@ -4064,7 +4070,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4064
4070
|
}
|
|
4065
4071
|
};
|
|
4066
4072
|
const suspendAsk = parkLaneArmed && checkpointStore !== undefined
|
|
4067
|
-
? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason, probeReason, probeCause) => {
|
|
4073
|
+
? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason, probeReason, probeCause, segmentCoverage) => {
|
|
4068
4074
|
const syncFirstEligible = req.toolName === ASK_USER_QUESTION_TOOL_NAME ? contentAskRoutable(req.toolCallId) : isLiveApproverSeat(onAsk);
|
|
4069
4075
|
if (syncFirstEligible &&
|
|
4070
4076
|
runtimeCaps?.forceDurableGate !== true &&
|
|
@@ -4225,11 +4231,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4225
4231
|
const preview = approvalPreviewOf(req.toolName, parkedArgs);
|
|
4226
4232
|
return preview !== undefined ? { preview } : {};
|
|
4227
4233
|
})(),
|
|
4228
|
-
...
|
|
4234
|
+
...ruleOffersOf(req.toolName, parkedArgs, {
|
|
4229
4235
|
...(realApproval !== undefined ? { requiresRealApproval: true } : {}),
|
|
4230
4236
|
...(shadowedRule !== undefined ? { persistedRuleShadowed: shadowedRule } : {}),
|
|
4231
4237
|
...(askDecisionReason !== undefined ? { decisionReason: askDecisionReason } : {}),
|
|
4232
4238
|
...(inheritedUnavailableAsks.has(req.toolCallId) ? { inheritedUnresolved: true } : {}),
|
|
4239
|
+
...(segmentCoverage !== undefined ? { segmentCoverage } : {}),
|
|
4233
4240
|
}),
|
|
4234
4241
|
boundInputHash: boundInputHashOf(parkedArgs),
|
|
4235
4242
|
batchToolCallIds,
|
|
@@ -4355,11 +4362,11 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4355
4362
|
admits: permissionRuleLane.admits,
|
|
4356
4363
|
onResolved: (info) => emitTrace(deps.tracer, () => ({
|
|
4357
4364
|
kind: "permission.persisted_rule_allowed",
|
|
4358
|
-
version:
|
|
4365
|
+
version: 2,
|
|
4359
4366
|
taskId: spec.taskId ?? sessionId,
|
|
4360
4367
|
toolName: info.toolName,
|
|
4361
4368
|
toolCallId: info.toolCallId,
|
|
4362
|
-
|
|
4369
|
+
rules: [...info.rules],
|
|
4363
4370
|
ts: Date.now(),
|
|
4364
4371
|
})),
|
|
4365
4372
|
},
|