@sema-agent/core 7.52.0 → 7.53.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 +194 -0
- package/README.md +3 -1
- package/dist/agents/cross-session-envelope.d.ts +8 -0
- package/dist/agents/cross-session-envelope.js +6 -0
- package/dist/agents/cross-session-judge.d.ts +21 -1
- package/dist/agents/cross-session-judge.js +8 -0
- package/dist/agents/cross-session-ref.d.ts +5 -0
- package/dist/agents/cross-session-ref.js +1 -0
- package/dist/agents/org-delivery-gate.d.ts +122 -0
- package/dist/agents/org-delivery-gate.js +69 -0
- package/dist/agents/peer-held-queue.d.ts +9 -4
- package/dist/agents/peer-held-queue.js +6 -5
- package/dist/agents/peer-session-drain.d.ts +35 -5
- package/dist/agents/peer-session-drain.js +271 -180
- package/dist/agents/read-inbox-tool.d.ts +7 -0
- package/dist/agents/read-inbox-tool.js +5 -1
- package/dist/agents/subagent.d.ts +3 -0
- package/dist/agents/subagent.js +99 -72
- package/dist/agents/tool-filter.d.ts +18 -0
- package/dist/agents/tool-filter.js +6 -0
- package/dist/brain/anthropic.d.ts +6 -1
- package/dist/brain/anthropic.js +4 -4
- package/dist/brain/model-presets.js +2 -1
- package/dist/brain/route-adjudicator.d.ts +6 -0
- package/dist/brain/routing.d.ts +24 -1
- package/dist/brain/routing.js +54 -19
- package/dist/brain/timeout.js +5 -4
- package/dist/core/a2a.d.ts +5 -2
- package/dist/core/a2a.js +30 -13
- package/dist/core/allow-entry.d.ts +2 -1
- package/dist/core/allow-entry.js +2 -2
- package/dist/core/auto-compaction.d.ts +57 -0
- package/dist/core/auto-compaction.js +28 -2
- package/dist/core/checkpoint-store.d.ts +29 -20
- package/dist/core/checkpoint-store.js +3 -3
- package/dist/core/compliance.js +3 -1
- package/dist/core/effective-path-target.d.ts +25 -6
- package/dist/core/effective-path-target.js +24 -2
- package/dist/core/engine-notice.d.ts +13 -2
- package/dist/core/gate-fold.js +3 -2
- package/dist/core/gate-lanes.js +11 -13
- package/dist/core/gate-outcome.d.ts +11 -6
- package/dist/core/governance-codes.d.ts +3 -1
- package/dist/core/governance-codes.js +3 -0
- package/dist/core/hands-band-screen.d.ts +7 -0
- package/dist/core/hands-band-screen.js +32 -0
- package/dist/core/hooks.d.ts +7 -5
- package/dist/core/hooks.js +5 -3
- package/dist/core/locked-config.d.ts +84 -32
- package/dist/core/locked-config.js +128 -42
- package/dist/core/mcp-probe.js +1 -1
- package/dist/core/mcp-server-spec.d.ts +88 -10
- package/dist/core/mcp-server-spec.js +183 -33
- package/dist/core/mcp.d.ts +21 -14
- package/dist/core/mcp.js +55 -51
- package/dist/core/memory-engine/engine.d.ts +14 -19
- package/dist/core/memory-engine/engine.js +7 -5
- package/dist/core/mode-policy.d.ts +3 -18
- package/dist/core/mode-policy.js +2 -16
- package/dist/core/mods/kernel/dispatch.js +13 -15
- package/dist/core/mods/kernel/events.d.ts +9 -6
- package/dist/core/mods/kernel/events.js +2 -2
- package/dist/core/mods/kernel/receive.d.ts +39 -10
- package/dist/core/mods/kernel/receive.js +119 -54
- package/dist/core/mods/kernel/site-rules.d.ts +80 -15
- package/dist/core/mods/kernel/site-rules.js +232 -40
- package/dist/core/mods/stations.d.ts +0 -6
- package/dist/core/mods/stations.js +0 -7
- package/dist/core/mounted-mcp.d.ts +57 -0
- package/dist/core/mounted-mcp.js +71 -2
- package/dist/core/own-property-receipt.d.ts +13 -0
- package/dist/core/own-property-receipt.js +3 -1
- package/dist/core/parent-spec-seat.d.ts +2 -2
- package/dist/core/permission-rule-model.d.ts +58 -9
- package/dist/core/permission-rule-model.js +81 -7
- package/dist/core/permission-rule-org.d.ts +163 -33
- package/dist/core/permission-rule-org.js +205 -71
- package/dist/core/permission-rules.d.ts +7 -1
- package/dist/core/permission-rules.js +37 -8
- package/dist/core/retention.js +7 -2
- package/dist/core/retired-keys.d.ts +8 -2
- package/dist/core/retired-keys.js +12 -1
- package/dist/core/roles.js +60 -10
- package/dist/core/rule-lane-reads.js +1 -1
- package/dist/core/runner/contracts.d.ts +1 -0
- package/dist/core/runner/gate-exit.js +4 -3
- package/dist/core/runner/mcp-redial.d.ts +52 -10
- package/dist/core/runner/mcp-redial.js +38 -9
- package/dist/core/runner/permission-rule-lanes.d.ts +5 -2
- package/dist/core/runner/permission-rule-lanes.js +22 -10
- package/dist/core/runner/person-path-rows.d.ts +3 -1
- package/dist/core/runner/person-path-rows.js +2 -2
- package/dist/core/runner/prepare-config-doors.d.ts +48 -2
- package/dist/core/runner/prepare-config-doors.js +161 -145
- package/dist/core/runner/prepare-defer-classify.js +4 -7
- package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
- package/dist/core/runner/prepare-delegation-surface.js +7 -5
- package/dist/core/runner/prepare-hands-readface.d.ts +1 -1
- package/dist/core/runner/prepare-hands-readface.js +2 -1
- package/dist/core/runner/prepare-inherited-gate.js +2 -2
- package/dist/core/runner/prepare-listings.d.ts +1 -1
- package/dist/core/runner/prepare-listings.js +4 -2
- package/dist/core/runner/prepare-memory-engine-session.d.ts +0 -6
- package/dist/core/runner/prepare-memory-engine-session.js +19 -6
- package/dist/core/runner/prepare-memory.d.ts +3 -2
- package/dist/core/runner/prepare-memory.js +6 -2
- package/dist/core/runner/prepare-offload-wrappers.d.ts +4 -4
- package/dist/core/runner/prepare-offload-wrappers.js +2 -1
- package/dist/core/runner/prepare-policy-chain.d.ts +1 -1
- package/dist/core/runner/prepare-policy-chain.js +12 -12
- package/dist/core/runner/prepare-project-context.d.ts +1 -1
- package/dist/core/runner/prepare-project-context.js +6 -4
- package/dist/core/runner/prepare-prompt-assembly.d.ts +1 -1
- package/dist/core/runner/prepare-prompt-assembly.js +20 -16
- package/dist/core/runner/prepare-prompt-inputs.d.ts +12 -6
- package/dist/core/runner/prepare-prompt-inputs.js +6 -3
- package/dist/core/runner/prepare-protocol-tools.d.ts +7 -5
- package/dist/core/runner/prepare-protocol-tools.js +19 -12
- package/dist/core/runner/prepare-question-face.d.ts +4 -2
- package/dist/core/runner/prepare-question-face.js +4 -3
- package/dist/core/runner/prepare-task.js +10 -10
- package/dist/core/runner/prepare-wiring-manifest.d.ts +8 -3
- package/dist/core/runner/prepare-wiring-manifest.js +8 -5
- package/dist/core/runner/resume-admission.d.ts +4 -1
- package/dist/core/runner/resume-admission.js +7 -5
- package/dist/core/runner/resume-internals-and-config.d.ts +3 -3
- package/dist/core/runner/resume-internals-and-config.js +3 -8
- package/dist/core/runner/retired-runner-deps.d.ts +5 -6
- package/dist/core/runner/retired-runner-deps.js +1 -4
- package/dist/core/runner/run-leg.js +42 -12
- package/dist/core/runner/run-recovery-lanes.js +5 -3
- package/dist/core/runner/run-terminal-adoption.js +6 -5
- package/dist/core/runner/run-turn-boundary.js +9 -3
- package/dist/core/runner/runtask.js +1 -1
- package/dist/core/runner/skills-seat.d.ts +15 -0
- package/dist/core/runner/skills-seat.js +57 -0
- package/dist/core/runner/sub-agent-deny-lists.d.ts +13 -11
- package/dist/core/runner/sub-agent-deny-lists.js +7 -5
- package/dist/core/runner/synthetic-tools.d.ts +5 -2
- package/dist/core/runner/synthetic-tools.js +2 -12
- package/dist/core/runner-deps.d.ts +29 -14
- package/dist/core/session-policy-store.d.ts +19 -0
- package/dist/core/session-policy-store.js +22 -2
- package/dist/core/store-contracts/mailbox-store-contract.js +13 -0
- package/dist/core/task-event.d.ts +38 -2
- package/dist/core/task-notification.js +2 -2
- package/dist/core/task-spec.d.ts +1 -1
- package/dist/core/tool-policy.d.ts +3 -0
- package/dist/core/tool-policy.js +10 -4
- package/dist/core/types.d.ts +2 -2
- package/dist/core/types.js +1 -1
- package/dist/core/untrusted-text.d.ts +16 -0
- package/dist/core/untrusted-text.js +7 -2
- package/dist/core/user-attachment.d.ts +13 -2
- package/dist/core/user-attachment.js +47 -41
- package/dist/core/wiring-manifest.d.ts +4 -2
- package/dist/engine/harness/agent-harness.js +2 -1
- package/dist/engine/harness/types.d.ts +3 -0
- package/dist/engine/loop/agent-loop.d.ts +12 -1
- package/dist/engine/loop/agent-loop.js +17 -5
- package/dist/engine/loop/types.d.ts +7 -1
- package/dist/index.d.ts +15 -9
- package/dist/index.js +12 -6
- package/dist/internal/harness.d.ts +1 -0
- package/dist/internal/harness.js +1 -0
- package/dist/orchestration/goal.d.ts +24 -1
- package/dist/orchestration/goal.js +3 -2
- package/dist/prompt-assembly/artifact-store.d.ts +44 -9
- package/dist/prompt-assembly/artifact-store.js +38 -12
- package/dist/prompt-assembly/artifact.d.ts +12 -11
- package/dist/prompt-assembly/artifact.js +52 -21
- package/dist/prompt-assembly/assemble.d.ts +2 -0
- package/dist/prompt-assembly/assemble.js +2 -1
- package/dist/prompt-assembly/catalog/index.js +2 -1
- package/dist/prompt-assembly/catalog/memory.d.ts +13 -0
- package/dist/prompt-assembly/catalog/memory.js +6 -0
- package/dist/prompt-assembly/catalog/schema.d.ts +16 -2
- package/dist/prompt-assembly/composer.d.ts +2 -0
- package/dist/prompt-assembly/composer.js +1 -1
- package/dist/prompt-assembly/overlay.d.ts +21 -11
- package/dist/prompt-assembly/overlay.js +55 -17
- package/dist/prompt-assembly/select.d.ts +21 -6
- package/dist/prompt-assembly/select.js +39 -2
- package/dist/prompts/default.d.ts +0 -11
- package/dist/prompts/default.js +0 -3
- package/dist/stores/file/permission-rule-store.d.ts +1 -1
- package/dist/stores/file/permission-rule-store.js +2 -1
- package/dist/tools/artifact/artifact-tool.js +4 -3
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +65 -5
- package/wire-changes/core@7.53.0.json +1946 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { catalogText } from "../prompt-assembly/catalog/index.js";
|
|
2
|
+
import { ANTHROPIC_DEFAULT_BASE_URL } from "./anthropic.js";
|
|
2
3
|
export const FABLE_5_COMPAT = {
|
|
3
4
|
thinkingMode: "adaptive",
|
|
4
5
|
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
@@ -15,7 +16,7 @@ export function fable5Model(overrides = {}) {
|
|
|
15
16
|
name: "Claude Fable 5",
|
|
16
17
|
api: "anthropic-messages",
|
|
17
18
|
provider: "anthropic",
|
|
18
|
-
baseUrl:
|
|
19
|
+
baseUrl: ANTHROPIC_DEFAULT_BASE_URL,
|
|
19
20
|
reasoning: true,
|
|
20
21
|
defaultThinking: "high",
|
|
21
22
|
input: ["text", "image"],
|
|
@@ -218,6 +218,12 @@ export declare function routeRefusalError(verdict: Extract<RouteAdjudication, {
|
|
|
218
218
|
* nothing. A credential is bound to the origin it was first SENT to, never to one it was only judged for. The request's
|
|
219
219
|
* halves (the entry's id and root, the brain's root and lane default) are read ONCE, here: both judgments and the binding
|
|
220
220
|
* are of the request this build is sending, whatever the caller's objects say by the time it is committed.
|
|
221
|
+
*
|
|
222
|
+
* Exported from the package root for a HOST that sends a request itself (its own `fetch`, outside a first-party brain): it
|
|
223
|
+
* passes this same gate, so the binding does not depend on who sends first. Resolve the credential with
|
|
224
|
+
* {@link resolveRouteCredential} (a credential built by hand carries no identity: it is judged and binds nothing), call this
|
|
225
|
+
* before building, and run the admission it returns immediately before the request goes out; a refusal is the very error a
|
|
226
|
+
* first-party brain throws for the same request. {@link adjudicateModelRoute} stays the read: it judges and binds nothing.
|
|
221
227
|
*/
|
|
222
228
|
export declare function admitRouteRequest(model: Pick<Model, "id" | "baseUrl">, credential: RouteCredential, config: RoutePairingConfig): () => void;
|
|
223
229
|
/**
|
package/dist/brain/routing.d.ts
CHANGED
|
@@ -5,9 +5,31 @@ export interface RoutingBrainOptions {
|
|
|
5
5
|
/** Brain used when no route matches. If omitted, an unmatched model yields an error. */
|
|
6
6
|
fallback?: Brain;
|
|
7
7
|
}
|
|
8
|
+
/** The two `Model` fields a route can be named by — a catalog entry that carries only a provider word is as good a subject as a whole `Model`. */
|
|
9
|
+
type RouteWords = {
|
|
10
|
+
readonly provider?: string | undefined;
|
|
11
|
+
readonly api?: string | undefined;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* THE judge of which line serves a model: the model's route word (`provider`, or `api` when `by` says so) IF the route table
|
|
15
|
+
* has an OWN route under that word, else `undefined` — the fallback (the deployment's gateway) serves it. A word the model
|
|
16
|
+
* carries without a route, and a model with no string word at all, both answer `undefined`: a route that is not mounted is
|
|
17
|
+
* not a line, whatever the model says.
|
|
18
|
+
*
|
|
19
|
+
* `routes` is the route TABLE itself — the very object a host hands {@link createRoutingBrain}, which asks this function for
|
|
20
|
+
* every request — so the judge and the router answer from one reading of one table, whatever its shape. The question is
|
|
21
|
+
* `Object.hasOwn(routes, word)`: it evaluates no entry and lists no keys, so a non-enumerable own route is a route, a lazily
|
|
22
|
+
* keyed table is asked about the one name it is asked about, and a name the table only inherits (`constructor`, `__proto__`)
|
|
23
|
+
* is not a route. The judge never reads a route's value: a route mounted under the word IS the line, and when its value is not
|
|
24
|
+
* a brain the router fails that request on that line (an error terminal naming the route) rather than moving it to the
|
|
25
|
+
* fallback — so the line this answers is the line the request was sent to, either way. A table that cannot answer the question
|
|
26
|
+
* (it is not an object, or its trap throws) throws here, and the router fails the request with an error terminal.
|
|
27
|
+
*/
|
|
28
|
+
export declare function brainLaneOf(model: RouteWords, routes: Readonly<Record<string, unknown>>, by?: NonNullable<RoutingBrainOptions["by"]>): string | undefined;
|
|
8
29
|
/**
|
|
9
30
|
* Route to different brains by a Model field (default `model.provider`). Lets one Runner serve
|
|
10
|
-
* multiple providers/gateways; compose with `createFailoverBrain` for per-route failover.
|
|
31
|
+
* multiple providers/gateways; compose with `createFailoverBrain` for per-route failover. Which route serves a
|
|
32
|
+
* model is {@link brainLaneOf}'s answer.
|
|
11
33
|
*
|
|
12
34
|
* @example
|
|
13
35
|
* createRoutingBrain({
|
|
@@ -16,3 +38,4 @@ export interface RoutingBrainOptions {
|
|
|
16
38
|
* }, { fallback: createOpenAIBrain({ baseUrl: VLLM }) });
|
|
17
39
|
*/
|
|
18
40
|
export declare function createRoutingBrain(routes: Record<string, Brain>, opts?: RoutingBrainOptions): Brain;
|
|
41
|
+
export {};
|
package/dist/brain/routing.js
CHANGED
|
@@ -1,42 +1,77 @@
|
|
|
1
1
|
import { createAssistantMessageEventStream, } from "../internal/llm.js";
|
|
2
2
|
import { ZERO_USAGE } from "./errors.js";
|
|
3
|
-
function errorMessage(
|
|
3
|
+
function errorMessage(who, text) {
|
|
4
4
|
return {
|
|
5
5
|
role: "assistant",
|
|
6
6
|
content: [{ type: "text", text: "" }],
|
|
7
|
-
api:
|
|
8
|
-
provider:
|
|
9
|
-
model:
|
|
7
|
+
api: who.api,
|
|
8
|
+
provider: who.provider,
|
|
9
|
+
model: who.id,
|
|
10
10
|
stopReason: "error",
|
|
11
11
|
errorMessage: text,
|
|
12
12
|
usage: ZERO_USAGE,
|
|
13
13
|
timestamp: Date.now(),
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
|
+
const BLANK_IDENTITY = { api: "", provider: "", id: "" };
|
|
17
|
+
function spelled(word) {
|
|
18
|
+
return (typeof word === "object" && word !== null) || typeof word === "function" ? `[${typeof word}]` : String(word);
|
|
19
|
+
}
|
|
20
|
+
function routeKeyOf(model, by) {
|
|
21
|
+
return by === "api" ? model.api : model.provider;
|
|
22
|
+
}
|
|
23
|
+
function isBrain(value) {
|
|
24
|
+
return value !== null && typeof value === "object" && typeof value.stream === "function";
|
|
25
|
+
}
|
|
26
|
+
export function brainLaneOf(model, routes, by = "provider") {
|
|
27
|
+
const word = routeKeyOf(model, by);
|
|
28
|
+
return typeof word === "string" && Object.hasOwn(routes, word) ? word : undefined;
|
|
29
|
+
}
|
|
16
30
|
export function createRoutingBrain(routes, opts = {}) {
|
|
17
31
|
const by = opts.by ?? "provider";
|
|
18
32
|
const resolveTarget = (model) => {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return matched ?? opts.fallback;
|
|
33
|
+
const lane = brainLaneOf(model, routes, by);
|
|
34
|
+
if (lane === undefined)
|
|
35
|
+
return { target: opts.fallback };
|
|
36
|
+
const routed = routes[lane];
|
|
37
|
+
return isBrain(routed) ? { target: routed } : { broken: `route ${by}="${lane}" is mounted but its value is not a brain` };
|
|
25
38
|
};
|
|
26
39
|
const stream = (model, context, options) => {
|
|
27
|
-
const
|
|
28
|
-
const target = resolveTarget(model);
|
|
29
|
-
if (!target) {
|
|
40
|
+
const reject = (text, named) => {
|
|
30
41
|
const out = createAssistantMessageEventStream();
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
reason: "error",
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
try {
|
|
43
|
+
const wordOf = (field) => (field === by && named !== undefined ? named.value : model[field]);
|
|
44
|
+
out.push({ type: "error", reason: "error", error: errorMessage({ api: wordOf("api"), provider: wordOf("provider"), id: wordOf("id") }, text) });
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
out.push({ type: "error", reason: "error", error: errorMessage(BLANK_IDENTITY, `${text} (the model's own words could not be read either)`) });
|
|
48
|
+
}
|
|
36
49
|
return out;
|
|
50
|
+
};
|
|
51
|
+
let word;
|
|
52
|
+
try {
|
|
53
|
+
word = { value: routeKeyOf(model, by) };
|
|
37
54
|
}
|
|
55
|
+
catch {
|
|
56
|
+
return reject(`createRoutingBrain: the model's ${by} could not be read`);
|
|
57
|
+
}
|
|
58
|
+
const named = word.value;
|
|
59
|
+
const asked = { api: named, provider: named };
|
|
60
|
+
let resolved;
|
|
61
|
+
try {
|
|
62
|
+
resolved = resolveTarget(asked);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return reject(`createRoutingBrain: the route table threw while ${by}="${spelled(named)}" was looked up`, word);
|
|
66
|
+
}
|
|
67
|
+
const target = "target" in resolved ? resolved.target : undefined;
|
|
68
|
+
if (!target)
|
|
69
|
+
return reject("broken" in resolved ? `createRoutingBrain: ${resolved.broken}` : `createRoutingBrain: no brain for ${by}="${spelled(named)}"`, word);
|
|
38
70
|
return target.stream(model, context, options);
|
|
39
71
|
};
|
|
40
|
-
const adjudicateRoute = (model, perModelAuth) =>
|
|
72
|
+
const adjudicateRoute = (model, perModelAuth) => {
|
|
73
|
+
const resolved = resolveTarget(model);
|
|
74
|
+
return "target" in resolved ? resolved.target?.adjudicateRoute?.(model, perModelAuth) : undefined;
|
|
75
|
+
};
|
|
41
76
|
return { stream, adjudicateRoute };
|
|
42
77
|
}
|
package/dist/brain/timeout.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
import { describeRejectedValue } from "../core/tool-search-setting.js";
|
|
1
2
|
export const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
2
3
|
export function resolveStallTimeoutMs(value, knob) {
|
|
3
4
|
if (value === undefined)
|
|
4
5
|
return undefined;
|
|
5
6
|
if (!Number.isFinite(value) || value < 0) {
|
|
6
|
-
const e = new Error(`${knob} must be a non-negative finite number of milliseconds (got ${
|
|
7
|
+
const e = new Error(`${knob} must be a non-negative finite number of milliseconds (got ${describeRejectedValue(value)})`);
|
|
7
8
|
e.code = "config.stall_timeout_invalid";
|
|
8
9
|
throw e;
|
|
9
10
|
}
|
|
10
11
|
if (value > MAX_TIMER_DELAY_MS) {
|
|
11
|
-
const e = new Error(`${knob} must not exceed ${MAX_TIMER_DELAY_MS}ms (~24.8 days) — a host timer truncates a larger delay and fires immediately, so this watchdog would abort every call instead of tolerating a long one (got ${
|
|
12
|
+
const e = new Error(`${knob} must not exceed ${MAX_TIMER_DELAY_MS}ms (~24.8 days) — a host timer truncates a larger delay and fires immediately, so this watchdog would abort every call instead of tolerating a long one (got ${describeRejectedValue(value)}). Pass 0 to disable it.`);
|
|
12
13
|
e.code = "config.stall_timeout_invalid";
|
|
13
14
|
throw e;
|
|
14
15
|
}
|
|
@@ -112,12 +113,12 @@ export function resolveBrainCallGuardrailMs(knob) {
|
|
|
112
113
|
if (knob === false || knob === 0)
|
|
113
114
|
return undefined;
|
|
114
115
|
if (typeof knob !== "number" || !Number.isFinite(knob) || knob < 0) {
|
|
115
|
-
const e = new Error(`brainCallGuardrailMs must be a positive number of milliseconds, 0, or false (got ${
|
|
116
|
+
const e = new Error(`brainCallGuardrailMs must be a positive number of milliseconds, 0, or false (got ${describeRejectedValue(knob)})`);
|
|
116
117
|
e.code = "config.brain_call_guardrail_invalid";
|
|
117
118
|
throw e;
|
|
118
119
|
}
|
|
119
120
|
if (knob > MAX_TIMER_DELAY_MS) {
|
|
120
|
-
const e = new Error(`brainCallGuardrailMs must not exceed ${MAX_TIMER_DELAY_MS}ms (~24.8 days) — a host timer truncates a larger delay and fires immediately, so the backstop would abort every model call (got ${
|
|
121
|
+
const e = new Error(`brainCallGuardrailMs must not exceed ${MAX_TIMER_DELAY_MS}ms (~24.8 days) — a host timer truncates a larger delay and fires immediately, so the backstop would abort every model call (got ${describeRejectedValue(knob)}). Pass false or 0 to turn the backstop off.`);
|
|
121
122
|
e.code = "config.brain_call_guardrail_invalid";
|
|
122
123
|
throw e;
|
|
123
124
|
}
|
package/dist/core/a2a.d.ts
CHANGED
|
@@ -69,8 +69,11 @@ export interface MaterializedA2a {
|
|
|
69
69
|
/** One entry per DECLARED peer, in declaration order, ready or failed — the declaration as this materialization read it once. */
|
|
70
70
|
statuses: A2aPeerStatus[];
|
|
71
71
|
/** Re-read one peer's card (or every declared peer) and re-derive its tools. Unlike the MCP refresh
|
|
72
|
-
* this dials nothing new — every A2A call is a fresh request anyway — so a peer
|
|
73
|
-
* prepare time can genuinely recover here.
|
|
72
|
+
* this dials nothing new — every A2A call is a fresh request anyway — so a peer whose card or network
|
|
73
|
+
* failed at prepare time can genuinely recover here. The DECLARATION is not re-read: a refresh works
|
|
74
|
+
* from the copy this materialization took and judged — the members too (`headers`, `allowSkills`,
|
|
75
|
+
* `toolAxes` are copied, not shared) — so a declaration refused then is refused again and a caller
|
|
76
|
+
* rewriting its object afterwards, by replacing a member or by editing one in place, changes nothing. */
|
|
74
77
|
refresh: (peer?: string) => Promise<A2aRefreshResult[]>;
|
|
75
78
|
/**
|
|
76
79
|
* Task teardown. There is no socket to close (every call is its own request), so what this owns is the
|
package/dist/core/a2a.js
CHANGED
|
@@ -5,6 +5,7 @@ import { assertEndpointBound, redirectPolicy, resolveProtocolHttpHeaders, underD
|
|
|
5
5
|
import { describeHttpTransportFailure } from "./mcp-failure.js";
|
|
6
6
|
import { findNamespacePrefixCollision, mintNamespacePrefix, mintNamespacedToolName } from "./protocol-naming.js";
|
|
7
7
|
import { A2A_NAMESPACE } from "./protocol-table.js";
|
|
8
|
+
import { judgeEntryToolAxes, refusedDeclarer, snapshotDeclaredEntry } from "./locked-config.js";
|
|
8
9
|
import { delimitUntrusted, inlineUntrusted } from "./untrusted-text.js";
|
|
9
10
|
import { thrownText } from "./thrown-value.js";
|
|
10
11
|
const A2A_REQUEST_TIMEOUT_MS = 30_000;
|
|
@@ -540,25 +541,39 @@ function buildSkillTool(spec, t, skill, lifecycle) {
|
|
|
540
541
|
},
|
|
541
542
|
};
|
|
542
543
|
}
|
|
543
|
-
|
|
544
|
-
|
|
544
|
+
function declaredPeerOf(declared, index) {
|
|
545
|
+
let snapshot;
|
|
546
|
+
try {
|
|
547
|
+
snapshot = snapshotDeclaredEntry(declared, "a2a");
|
|
548
|
+
}
|
|
549
|
+
catch (err) {
|
|
550
|
+
return { spec: { name: refusedDeclarer(err) ?? `(peer #${index + 1}, name unreadable)` }, refusal: err };
|
|
551
|
+
}
|
|
552
|
+
const judged = judgeEntryToolAxes(snapshot, "a2a");
|
|
553
|
+
return judged.ok ? { spec: judged.entry } : { spec: snapshot, refusal: judged.error };
|
|
554
|
+
}
|
|
555
|
+
async function materializePeer(peer, principal, lifecycle, signal) {
|
|
556
|
+
if ("refusal" in peer)
|
|
557
|
+
throw peer.refusal;
|
|
558
|
+
const { spec } = peer;
|
|
559
|
+
const declaredAxes = spec.toolAxes;
|
|
545
560
|
const headers = resolveProtocolHttpHeaders(A2A_NAMESPACE.id, spec, principal);
|
|
546
561
|
const discovery = { peer: spec.name, endpoint: spec.url, headers };
|
|
547
562
|
const card = readCard(spec.name, await discoverCardBody(spec, discovery, signal), spec.url);
|
|
548
563
|
assertEndpointBound(A2A_NAMESPACE.id, spec.name, card.endpoint, spec.cardUrl === undefined ? [spec.url] : [spec.url, spec.cardUrl], headers);
|
|
549
564
|
const transport = { peer: spec.name, endpoint: card.endpoint, headers };
|
|
550
565
|
const tools = [];
|
|
551
|
-
const
|
|
566
|
+
const skillAxes = [];
|
|
552
567
|
for (const skill of card.skills) {
|
|
553
568
|
if (spec.allowSkills !== undefined && !spec.allowSkills.includes(skill.id))
|
|
554
569
|
continue;
|
|
555
570
|
const tool = buildSkillTool(spec, transport, skill, lifecycle);
|
|
556
571
|
tools.push(tool);
|
|
557
|
-
|
|
572
|
+
skillAxes.push(a2aAxisFor(tool.name, declaredAxes?.[skill.id]));
|
|
558
573
|
}
|
|
559
574
|
return {
|
|
560
575
|
tools,
|
|
561
|
-
axes,
|
|
576
|
+
axes: skillAxes,
|
|
562
577
|
status: {
|
|
563
578
|
name: spec.name,
|
|
564
579
|
status: "ready",
|
|
@@ -576,8 +591,9 @@ function asPeerWarning(spec, err) {
|
|
|
576
591
|
return warning;
|
|
577
592
|
}
|
|
578
593
|
export async function materializeA2aTools(specs, principal, signal) {
|
|
579
|
-
|
|
580
|
-
|
|
594
|
+
const peers = specs.map((spec, index) => declaredPeerOf(spec, index));
|
|
595
|
+
if (peers.length > 0) {
|
|
596
|
+
const collision = findNamespacePrefixCollision(A2A_NAMESPACE, peers.map((p) => p.spec.name));
|
|
581
597
|
if (collision) {
|
|
582
598
|
const [a, b] = collision.peers;
|
|
583
599
|
const e = new Error(a === b
|
|
@@ -592,9 +608,9 @@ export async function materializeA2aTools(specs, principal, signal) {
|
|
|
592
608
|
const toolAxes = [];
|
|
593
609
|
const warnings = [];
|
|
594
610
|
const statuses = [];
|
|
595
|
-
const settled = await Promise.allSettled(
|
|
596
|
-
for (let i = 0; i <
|
|
597
|
-
const spec =
|
|
611
|
+
const settled = await Promise.allSettled(peers.map((peer) => materializePeer(peer, principal, lifecycle, signal)));
|
|
612
|
+
for (let i = 0; i < peers.length; i++) {
|
|
613
|
+
const spec = peers[i].spec;
|
|
598
614
|
const outcome = settled[i];
|
|
599
615
|
if (outcome.status === "fulfilled") {
|
|
600
616
|
tools.push(...outcome.value.tools);
|
|
@@ -613,12 +629,13 @@ export async function materializeA2aTools(specs, principal, signal) {
|
|
|
613
629
|
statuses,
|
|
614
630
|
refresh: async (peer) => {
|
|
615
631
|
const prefixOf = (name) => mintNamespacePrefix(A2A_NAMESPACE, name);
|
|
616
|
-
const targets = peer !== undefined ?
|
|
632
|
+
const targets = peer !== undefined ? peers.filter((p) => p.spec.name === peer) : peers;
|
|
617
633
|
if (peer !== undefined && targets.length === 0) {
|
|
618
634
|
return [{ peer, prefix: prefixOf(peer), status: "failed", toolCount: 0, added: [], removed: [], error: "no peer of that name is declared for this task" }];
|
|
619
635
|
}
|
|
620
636
|
const results = [];
|
|
621
|
-
for (const
|
|
637
|
+
for (const target of targets) {
|
|
638
|
+
const { spec } = target;
|
|
622
639
|
const prefix = prefixOf(spec.name);
|
|
623
640
|
if (lifecycle.disposed) {
|
|
624
641
|
results.push({ peer: spec.name, prefix, status: "disposed", toolCount: 0, added: [], removed: [] });
|
|
@@ -626,7 +643,7 @@ export async function materializeA2aTools(specs, principal, signal) {
|
|
|
626
643
|
}
|
|
627
644
|
const previous = statuses.find((s) => s.name === spec.name)?.toolNames ?? [];
|
|
628
645
|
try {
|
|
629
|
-
const fresh = await materializePeer(
|
|
646
|
+
const fresh = await materializePeer(target, principal, lifecycle, signal);
|
|
630
647
|
const names = fresh.tools.map((t) => t.name);
|
|
631
648
|
results.push({
|
|
632
649
|
peer: spec.name,
|
|
@@ -163,7 +163,8 @@ export declare function uncoveredCommandSegment(rows: readonly AllowEntryRow[],
|
|
|
163
163
|
* segment covered by a row — or, for a `grant` list, read-only by itself); deny: a row whose program run the call performs (the
|
|
164
164
|
* wide tightening reading; a call that cannot be read against the row is refused too). PATH — the persisted path adjudication
|
|
165
165
|
* (allow: the narrow reading, every view of the target inside what the row names; deny: any view reaching it). NAME / DOMAIN —
|
|
166
|
-
* the one predicate of each (the name the call names, the host it fetches)
|
|
166
|
+
* the one predicate of each (the name the call names, the host it fetches); a keyed tool's call naming no value for its key is
|
|
167
|
+
* refused by a deny list's row of the key and granted by no allow row (the persisted walk's reading, `keyedValueAbsentReason`).
|
|
167
168
|
*/
|
|
168
169
|
export declare function judgeAllowEntries<R extends AllowEntryRow>(rows: readonly R[], req: ToolCallRequest, behavior: "allow" | "deny", ctx: AllowEntryJudgeContext, posture?: AdmissionPosture): readonly R[] | undefined;
|
|
169
170
|
/** The allow rows of a list, as the allow layer reads them in `mode`: the strip applied BEFORE the match (in `auto`). */
|
package/dist/core/allow-entry.js
CHANGED
|
@@ -4,7 +4,7 @@ import { parsePermissionRule, ruleSpellingOf } from "./permission-rule-syntax.js
|
|
|
4
4
|
import { judgeRuleToolName, splitNamespacedName } from "./tool-name-aliases.js";
|
|
5
5
|
import { catalogToolNames, isShellFaceToolName } from "./tool-registry.js";
|
|
6
6
|
import { ALLOW_RULE_MEMBERS } from "./allow-rule-shadow.js";
|
|
7
|
-
import { COMMAND_RULE_TOOL, adjudicatePersistedDomainRules, adjudicatePersistedNameRules, commandCoverageOf, domainRuleSubjectOf, laneToolNamesOf, nameRuleSubjectOf, parseRuleText, programRunReachOf, ruleToolGrammarOf } from "./permission-rule-model.js";
|
|
7
|
+
import { COMMAND_RULE_TOOL, adjudicatePersistedDomainRules, adjudicatePersistedNameRules, commandCoverageOf, domainRuleSubjectOf, laneToolNamesOf, nameRuleSubjectOf, noticeRuleText, parseRuleText, programRunReachOf, ruleToolGrammarOf } from "./permission-rule-model.js";
|
|
8
8
|
import { pathRuleLaneAnswer } from "./runner/permission-rule-lanes.js";
|
|
9
9
|
import { callTargetReadingOf } from "./effective-path-target.js";
|
|
10
10
|
import { splitShellCompoundSegments } from "../tools/fs/bash-readonly-classifier.js";
|
|
@@ -462,6 +462,6 @@ export function allowedToolsNotGrantedNotice(inert, sessionId) {
|
|
|
462
462
|
message: `${inert.length} allowed-tools entr${inert.length === 1 ? "y grants" : "ies grant"} or refuse${inert.length === 1 ? "s" : ""} nothing — ${shown.join("; ")}${rest > 0 ? `; and ${rest} more` : ""}. ` +
|
|
463
463
|
"A list holding `*` voids the list, `mcp__*` names no server, a rule the grammar refuses (`grammar`) or one on a tool with no such rule form (`content_unsupported`: an allow by path is an `Edit(…)` or `Read(…)` rule) grants nothing, and a tool name must be spelled as the tool is mounted; name the tools (or `mcp__<server>`) instead." +
|
|
464
464
|
(refusing ? " Such an entry in a disallowed-tools list keeps its Skill from activating: a refusal that reads as nothing is not dropped." : ""),
|
|
465
|
-
detail: { ...(sessionId !== undefined ? { sessionId } : {}), entries: inert.map((e) => ({ ...e })) },
|
|
465
|
+
detail: { ...(sessionId !== undefined ? { sessionId } : {}), entries: inert.map((e) => ({ ...e, entry: noticeRuleText(e.entry) })) },
|
|
466
466
|
};
|
|
467
467
|
}
|
|
@@ -101,6 +101,18 @@ export interface CompactionForkContext {
|
|
|
101
101
|
* eligibility gate refuses a snapshot from a different model (see the engine-side twin's doc). */
|
|
102
102
|
modelId?: string;
|
|
103
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* What a compaction pass reports at the moment it commits ({@link MaybeCompactOptions.onStarted}) — and the
|
|
106
|
+
* whole content of its `compaction_started` wire frame. `pass` is minted HERE, by the one function every lane
|
|
107
|
+
* runs, so no lane can number its own passes; the frame that ends the pass carries the same value.
|
|
108
|
+
*/
|
|
109
|
+
export interface CompactionPassStart {
|
|
110
|
+
readonly pass: string;
|
|
111
|
+
/** The trigger this pass runs under — the value it hands the `preCompact` hook. */
|
|
112
|
+
readonly trigger: "auto" | "manual" | "forced";
|
|
113
|
+
/** Engine clock, epoch ms, at the commit. */
|
|
114
|
+
readonly startedAt: number;
|
|
115
|
+
}
|
|
104
116
|
export interface MaybeCompactOptions {
|
|
105
117
|
/** Disclosure channel for CONTAINED host-callback failures in this scope (the RB-463
|
|
106
118
|
* isolation primitive's bounded first-failure per site). Absent ⇒ failures are counted only. */
|
|
@@ -482,6 +494,17 @@ export interface MaybeCompactOptions {
|
|
|
482
494
|
* not optional there, so a preCompact `block` is IGNORED on "forced").
|
|
483
495
|
*/
|
|
484
496
|
trigger?: "auto" | "manual" | "forced";
|
|
497
|
+
/**
|
|
498
|
+
* The pass has COMMITTED: the trigger gate passed, a valid cut point exists and no `preCompact` handler
|
|
499
|
+
* blocked it, and nothing has yet consulted the summary provider or called the model. Fired exactly once per
|
|
500
|
+
* pass — a retry inside the summary call is the same pass — and never for a pass that ends before this point
|
|
501
|
+
* (under threshold, held under the floor, disabled, nothing to compact, blocked, or cut by its own signal while
|
|
502
|
+
* the `preCompact` hook ran). A pass that fired it ends
|
|
503
|
+
* in `compacted: true` or a throw, so the caller that put a frame on the wire here owes the matching end.
|
|
504
|
+
* Observe-only: a throw — or the rejection of an `async` callback, which this seat does not await — is
|
|
505
|
+
* contained and never reaches the pass.
|
|
506
|
+
*/
|
|
507
|
+
onStarted?: (start: CompactionPassStart) => void;
|
|
485
508
|
/**
|
|
486
509
|
* design/134 (CC PreCompact parity) — fired AFTER the trigger gate and a CONFIRMED valid cut point
|
|
487
510
|
* (so every invocation corresponds to a compaction that would actually happen), BEFORE the
|
|
@@ -553,6 +576,10 @@ export declare function maybeCompact(opts: MaybeCompactOptions): Promise<{
|
|
|
553
576
|
* nothing to do looked identical to one that never wanted to run; the caller's outcome event
|
|
554
577
|
* (`compaction_outcome{outcome:"noop"}`) reads this flag. */
|
|
555
578
|
noop?: true;
|
|
579
|
+
/** The id of the pass this result concludes — the value {@link MaybeCompactOptions.onStarted} was handed.
|
|
580
|
+
* Set exactly when `compacted` is true: every other return of this function ends before the pass commits,
|
|
581
|
+
* and a pass that commits and does not land THROWS (the caller learned the id from the start callback). */
|
|
582
|
+
pass?: string;
|
|
556
583
|
tokensBefore?: number;
|
|
557
584
|
/** The §25.2 STRUCTURAL pre/post delta in ONE coordinate — the only subtractable quantity here.
|
|
558
585
|
* Absent ⇒ the post measurement failed and no delta was taken. */
|
|
@@ -669,6 +696,8 @@ export declare function maybeCompact(opts: MaybeCompactOptions): Promise<{
|
|
|
669
696
|
/** What a landed compaction hands the wire frame. Structural, so both the full `maybeCompact` result
|
|
670
697
|
* and the settle lane's narrower `EndOfTaskCompaction` satisfy it without either importing the other. */
|
|
671
698
|
export interface CompactedFrameSource {
|
|
699
|
+
/** The pass this landing concludes (see {@link MaybeCompactOptions.onStarted}). */
|
|
700
|
+
pass?: string;
|
|
672
701
|
tokensBefore?: number;
|
|
673
702
|
freedTokens?: number;
|
|
674
703
|
postTriggerTokens?: number;
|
|
@@ -703,6 +732,34 @@ export interface CompactedFrameSource {
|
|
|
703
732
|
export declare function mintCompactedEvent(comp: CompactedFrameSource, trigger: "auto" | "manual" | "forced", identity: TaskEventIdentity): Extract<TaskEvent, {
|
|
704
733
|
type: "compacted";
|
|
705
734
|
}>;
|
|
735
|
+
/**
|
|
736
|
+
* THE mint of the `compaction_started` wire frame — the opening half of the pair whose closing half is the
|
|
737
|
+
* `compacted` frame above (or a `compaction_outcome` that carries the same `pass`). One function, three
|
|
738
|
+
* lanes, as the landing's: the frame is the start report itself plus the identity envelope, so a lane that
|
|
739
|
+
* wants to announce a pass can only announce the one `maybeCompact` committed.
|
|
740
|
+
*/
|
|
741
|
+
export declare function mintCompactionStartedEvent(start: CompactionPassStart, identity: TaskEventIdentity): Extract<TaskEvent, {
|
|
742
|
+
type: "compaction_started";
|
|
743
|
+
}>;
|
|
744
|
+
/**
|
|
745
|
+
* One compaction pass's account on the wire: the `onStarted` seat to hand `maybeCompact`, and the id the frame
|
|
746
|
+
* that ENDS the pass must carry. A lane makes one per pass, hands `onStarted` to the pass, and spreads
|
|
747
|
+
* {@link CompactionPassReport.end} into every `compaction_outcome` the pass can end with after it committed — a
|
|
748
|
+
* pass that threw before it committed leaves it empty, which is exactly "no start was announced, so no `pass`
|
|
749
|
+
* names it". The landing needs no spread: `maybeCompact` returns the id and `mintCompactedEvent` reads it.
|
|
750
|
+
*/
|
|
751
|
+
export interface CompactionPassReport {
|
|
752
|
+
readonly onStarted: (start: CompactionPassStart) => void;
|
|
753
|
+
/** The announced pass's id; `undefined` until a start went on the wire. */
|
|
754
|
+
readonly pass: string | undefined;
|
|
755
|
+
/** `{ pass }` once a start was announced, `{}` before — for the frame that ends the pass. */
|
|
756
|
+
readonly end: {
|
|
757
|
+
pass?: string;
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
export declare function compactionPassReport(sink: {
|
|
761
|
+
push(event: TaskEvent): void;
|
|
762
|
+
}, ident: () => TaskEventIdentity): CompactionPassReport;
|
|
706
763
|
/**
|
|
707
764
|
* How many cross-session message frames the summarized prefix held: the engine-note user messages
|
|
708
765
|
* whose text is the drain's `<cross-session-message …>` envelope, before the kept tail begins. The
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { CROSS_SESSION_MESSAGE_TAG } from "../agents/cross-session-envelope.js";
|
|
2
|
-
import { DEFAULT_CHARS_PER_TOKEN, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, CompactionError, compact, computeFileLists, dryRunSummarizationClamp, estimateContextTokens, estimateTokens, formatFileOperations, formatPersistedOutputRefs, prepareCompaction, shouldCompact, } from "../internal/harness.js";
|
|
2
|
+
import { DEFAULT_CHARS_PER_TOKEN, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, CompactionError, compact, computeFileLists, dryRunSummarizationClamp, estimateContextTokens, estimateTokens, formatFileOperations, formatPersistedOutputRefs, prepareCompaction, shouldCompact, uuidv7, } from "../internal/harness.js";
|
|
3
3
|
import { fileArgPath } from "./tool-registry.js";
|
|
4
4
|
import { contextEditFrontier, resolveTriggerWindow } from "./context-edit.js";
|
|
5
5
|
import { selectCompactionEpoch } from "../prompt-assembly/epoch.js";
|
|
6
6
|
import { brainToRuntime } from "./runtime.js";
|
|
7
7
|
import { defuseFenceMarkers, sanitizeUntrustedText } from "./untrusted-text.js";
|
|
8
|
-
import { createSafeNotifier } from "./safe-notify.js";
|
|
8
|
+
import { createSafeNotifier, observeThenableRejection } from "./safe-notify.js";
|
|
9
9
|
export const DEFAULT_COMPACTION_INSTRUCTIONS = "Preserve verbatim every code, identifier, name, number, file path, error message, credential " +
|
|
10
10
|
"reference, and explicit user instruction or decision mentioned anywhere in the conversation. " +
|
|
11
11
|
"When in doubt, keep the specific detail rather than generalizing it.";
|
|
@@ -152,6 +152,12 @@ export async function maybeCompact(opts) {
|
|
|
152
152
|
? `${specInstructions}\n\n${hookSanitized}`
|
|
153
153
|
: hookSanitized
|
|
154
154
|
: specInstructions;
|
|
155
|
+
const signalAtCommit = opts.signal;
|
|
156
|
+
if (signalAtCommit?.aborted === true) {
|
|
157
|
+
throw Object.assign(new CompactionError("aborted", "compaction cancelled before the pass committed — nothing was summarized or persisted"), opts.manualCancelSignal?.aborted === true ? { [COMPACTION_MANUAL_CANCEL_FLAG]: true } : {});
|
|
158
|
+
}
|
|
159
|
+
const pass = uuidv7();
|
|
160
|
+
notifier.notify(() => observeThenableRejection(opts.onStarted?.({ pass, trigger, startedAt: Date.now() }), notifier, "compaction.onStarted"), "compaction.onStarted");
|
|
155
161
|
const maxReuse = opts.maxConsecutiveProviderReuse ?? 3;
|
|
156
162
|
const forceRealSummary = maxReuse > 0 && (opts.consecutiveProviderReuse ?? 0) >= maxReuse;
|
|
157
163
|
let providerSummary;
|
|
@@ -555,6 +561,7 @@ export async function maybeCompact(opts) {
|
|
|
555
561
|
return {
|
|
556
562
|
contextUsage,
|
|
557
563
|
compacted: true,
|
|
564
|
+
pass,
|
|
558
565
|
tokensBefore: summaryTokensBefore,
|
|
559
566
|
freedTokens,
|
|
560
567
|
triggerTokens: tokens,
|
|
@@ -585,6 +592,7 @@ export function mintCompactedEvent(comp, trigger, identity) {
|
|
|
585
592
|
return {
|
|
586
593
|
type: "compacted",
|
|
587
594
|
trigger,
|
|
595
|
+
...(comp.pass !== undefined ? { pass: comp.pass } : {}),
|
|
588
596
|
tokensBefore: comp.tokensBefore ?? 0,
|
|
589
597
|
...(comp.freedTokens !== undefined ? { freedTokens: comp.freedTokens } : {}),
|
|
590
598
|
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
@@ -601,6 +609,24 @@ export function mintCompactedEvent(comp, trigger, identity) {
|
|
|
601
609
|
...identity,
|
|
602
610
|
};
|
|
603
611
|
}
|
|
612
|
+
export function mintCompactionStartedEvent(start, identity) {
|
|
613
|
+
return { type: "compaction_started", trigger: start.trigger, pass: start.pass, startedAt: start.startedAt, ...identity };
|
|
614
|
+
}
|
|
615
|
+
export function compactionPassReport(sink, ident) {
|
|
616
|
+
let pass;
|
|
617
|
+
return {
|
|
618
|
+
onStarted: (start) => {
|
|
619
|
+
sink.push(mintCompactionStartedEvent(start, ident()));
|
|
620
|
+
pass = start.pass;
|
|
621
|
+
},
|
|
622
|
+
get pass() {
|
|
623
|
+
return pass;
|
|
624
|
+
},
|
|
625
|
+
get end() {
|
|
626
|
+
return pass !== undefined ? { pass } : {};
|
|
627
|
+
},
|
|
628
|
+
};
|
|
629
|
+
}
|
|
604
630
|
export function countSummarizedCrossSessionMessages(branch, firstKeptEntryId) {
|
|
605
631
|
const firstKeptIdx = branch.findIndex((e) => e.id === firstKeptEntryId);
|
|
606
632
|
const summarized = firstKeptIdx >= 0 ? branch.slice(0, firstKeptIdx) : branch;
|
|
@@ -2099,24 +2099,33 @@ export type CheckpointErrorReason = "version_newer" | "env_factory_missing" | "g
|
|
|
2099
2099
|
| "installation_record_absent" | "installation_record_misaligned" | "installation_record_invalid"
|
|
2100
2100
|
/** On `checkpoint.config_refused` with `detail.code` `config.tool_face_control` — the arm prepare's own refusal carries as its `detail.reason`:
|
|
2101
2101
|
* a tool-name list the row recorded is not a list / has a member that is not a string / has a spelling no tool name can have / is not in
|
|
2102
|
-
* the one spelling a row records (`"not_a_list"`, `"not_a_string"`, `"unknown_name"`, `"unsupported_pattern"
|
|
2102
|
+
* the one spelling a row records (`"not_a_list"`, `"not_a_string"`, `"unknown_name"`, `"unsupported_pattern"`; the re-supplied config's own four lists —
|
|
2103
|
+
* `allowTools`, `excludeTools`, `deferTools`, `alwaysLoadTools` — are refused as `"not_a_list"` / `"not_a_string"`); the record of the delegation tool that
|
|
2103
2104
|
* wrote the leg's lists is not one (`"invalid_record"`); the roster the resume hands mounts a delegation tool under another name than it declares
|
|
2104
2105
|
* (`"renamed_mount"`); the process-wide table of declared names is occupied by a value that is not the engine's (`"registry_occupied"`). */
|
|
2105
|
-
| "not_a_list" | "not_a_string" | "unknown_name" | "unsupported_pattern" | "invalid_record" | "renamed_mount" | "registry_occupied"
|
|
2106
|
+
| "not_a_list" | "not_a_string" | "unknown_name" | "unsupported_pattern" | "invalid_record" | "renamed_mount" | "registry_occupied"
|
|
2107
|
+
/** On `checkpoint.config_refused` with `detail.code` `config.unknown_role` — a role seat or a tier binding the deployment wrote that the engine cannot read:
|
|
2108
|
+
* a word the model catalog does not hold (`"unknown_model_ref"`), or a value that is no model reference at all — a number, a boolean, a list
|
|
2109
|
+
* (`"not_a_model_ref"`). The sentence names the seat (`roles.consolidate`, `tiers.pro`). */
|
|
2110
|
+
| "unknown_model_ref" | "not_a_model_ref";
|
|
2106
2111
|
/**
|
|
2107
|
-
* The codes a RESUME's pre-claim configuration screen carries as `detail.code` of a `checkpoint.config_refused` — the
|
|
2108
|
-
*
|
|
2109
|
-
*
|
|
2110
|
-
* `
|
|
2111
|
-
*
|
|
2112
|
-
* `config.
|
|
2113
|
-
* `
|
|
2114
|
-
* `
|
|
2115
|
-
*
|
|
2116
|
-
*
|
|
2117
|
-
*
|
|
2112
|
+
* The codes a RESUME's pre-claim configuration screen carries as `detail.code` of a `checkpoint.config_refused` — the words prepare's
|
|
2113
|
+
* doors throw, held unchanged so one refusal reads the same on a fresh run (`TaskResult.errorCode`) and on a resume: the mode, output-style,
|
|
2114
|
+
* approval-reach, invoked-skill, attachment, recorded-mode and tool-face doors (`config.spec_retired_key` … `config.tool_face_control`, the first
|
|
2115
|
+
* nine — `detail.reason` names the tool-face arm: {@link CheckpointErrorReason}), and every value door of the config-door phase over the
|
|
2116
|
+
* re-supplied config and the deployment's deps (`configValueDoors`, the rest): the retired deps seats and caller-policy members
|
|
2117
|
+
* (`config.deps_retired_key`), the peer presence, the restore selector, the microCompact knob, the prompt selectors, `resumeAt` on a resume
|
|
2118
|
+
* (`resume_at.conflicts_resume`, `resume_at.mode_without_target` — a resume carrying `resumeAt` is refused as a conflict before its mode is
|
|
2119
|
+
* read, so `resume_at.invalid_mode` is a fresh run's word only), the containment faces, the add-only seats and the MCP / A2A `toolAxes` (`config.invalid_argument`), the three memory seats,
|
|
2120
|
+
* the auto-consolidation arming (its role key too, and the role seats and tier bindings it reads: `config.unknown_role`, `detail.reason` naming the arm), the materialize strategy and persistence flag, the lock layer (`config.locked_registry`,
|
|
2121
|
+
* `config.locked_key`), an MCP server's content class, the skills-listing seats (`config.skill_listing_budget_invalid`), the limits and the
|
|
2122
|
+
* resource-suspend knobs, the attachment modes, the governance windows, the brain-call guardrail and the retention declaration over the stores the leg
|
|
2123
|
+
* would run on (`config.retention_policy`, `config.retention_capability`), and the checkpoint store seat itself (`config.invalid_checkpoint_store`: the
|
|
2124
|
+
* retired `null` spelling, a store that declares no execution outcome), and the skills seat and the hands band (`config.invalid_argument`;
|
|
2125
|
+
* a skill's `baseDir` by its own reader, `config.skill_base_dir_invalid`). A door not entered here (the leg's model, its rebuilt
|
|
2126
|
+
* roster, its answer face, the approval seat's value) still refuses after the claim, each for a stated reason (INTEGRATION-CORE, "A resume's two doors").
|
|
2118
2127
|
*/
|
|
2119
|
-
export declare const CHECKPOINT_CONFIG_REFUSAL_CODES: readonly ["config.spec_retired_key", "config.output_style_invalid", "config.permission_mode_invalid", "config.shell_gate_value_retired", "config.approval_reach_invalid", "config.user_invoked_skill_unknown", "config.skill_deny_unreadable", "config.user_attachment_invalid", "config.tool_face_control"];
|
|
2128
|
+
export declare const CHECKPOINT_CONFIG_REFUSAL_CODES: readonly ["config.spec_retired_key", "config.output_style_invalid", "config.permission_mode_invalid", "config.shell_gate_value_retired", "config.approval_reach_invalid", "config.user_invoked_skill_unknown", "config.skill_deny_unreadable", "config.user_attachment_invalid", "config.tool_face_control", "config.deps_retired_key", "config.peer_presence_invalid", "config.tool_model_gate_invalid", "config.microcompact_invalid", "config.prompt_selector_invalid", "resume_at.conflicts_resume", "resume_at.mode_without_target", "config.read_face_invalid", "config.write_face_invalid", "config.invalid_argument", "config.memory_delegation_evidence", "config.memory_provenance", "config.memory_capture_policy", "config.auto_consolidation", "config.unknown_role", "config.tool_materialize_invalid", "config.memory_persistence_invalid", "config.tool_materialize_unreachable", "config.locked_registry", "config.locked_key", "config.mcp_content_class", "config.skill_listing_budget_invalid", "config.limit_unknown_key", "config.limit_invalid", "config.attachment_invalid", "config.usage_window_invalid", "config.brain_call_guardrail_invalid", "config.retention_policy", "config.retention_capability", "config.invalid_checkpoint_store", "config.skill_base_dir_invalid"];
|
|
2120
2129
|
export type CheckpointConfigRefusalCode = (typeof CHECKPOINT_CONFIG_REFUSAL_CODES)[number];
|
|
2121
2130
|
export declare const isCheckpointConfigRefusalCode: (word: unknown) => word is CheckpointConfigRefusalCode;
|
|
2122
2131
|
/** A typed checkpoint-store error so callers branch on `code` (mirrors `SessionError`). */
|
|
@@ -2295,13 +2304,13 @@ export declare class CheckpointError extends Error {
|
|
|
2295
2304
|
* resolve, or try to supply the minted record), or — design/252 G-7 — `"approver"` (the attribution is not a
|
|
2296
2305
|
* well-formed identifier: re-decide with a plain, bounded, control-character-free string, or omit
|
|
2297
2306
|
* it). On a `checkpoint.config_refused` `field` names which INPUT carries the refusal — `"taskConfig"`
|
|
2298
|
-
* (the re-supplied config: correct it and resume the same token)
|
|
2299
|
-
*
|
|
2307
|
+
* (the re-supplied config: correct it and resume the same token), `"deps"` (the deployment's own seat — and the policies the
|
|
2308
|
+
* caller re-supplies as inherited constraints: correct them and resume the same token) or `"inheritedGate"` (the row's record: no config reaches it; expire the row). The throw
|
|
2300
2309
|
* sites always knew which arm fired; this carries that fact instead of dropping it (ruled
|
|
2301
2310
|
* 2026-08-03, cross-repo HITL discussion). Additive and optional — `code` remains the only REQUIRED
|
|
2302
2311
|
* discriminant. */
|
|
2303
2312
|
readonly detail?: {
|
|
2304
|
-
field?: "boundCallId" | "boundInputHash" | "boundGrantDigest" | "answer" | "hostDecision" | "approver" | "taskConfig" | "inheritedGate";
|
|
2313
|
+
field?: "boundCallId" | "boundInputHash" | "boundGrantDigest" | "answer" | "hostDecision" | "approver" | "taskConfig" | "deps" | "inheritedGate";
|
|
2305
2314
|
/** On `checkpoint.config_refused` — prepare's own code for the refusal ({@link CheckpointConfigRefusalCode}), the
|
|
2306
2315
|
* word a fresh run of the same bad config reports as its `errorCode`. Absent on every other code. */
|
|
2307
2316
|
code?: CheckpointConfigRefusalCode;
|
|
@@ -2498,13 +2507,13 @@ export declare class CheckpointError extends Error {
|
|
|
2498
2507
|
* resolve, or try to supply the minted record), or — design/252 G-7 — `"approver"` (the attribution is not a
|
|
2499
2508
|
* well-formed identifier: re-decide with a plain, bounded, control-character-free string, or omit
|
|
2500
2509
|
* it). On a `checkpoint.config_refused` `field` names which INPUT carries the refusal — `"taskConfig"`
|
|
2501
|
-
* (the re-supplied config: correct it and resume the same token)
|
|
2502
|
-
*
|
|
2510
|
+
* (the re-supplied config: correct it and resume the same token), `"deps"` (the deployment's own seat — and the policies the
|
|
2511
|
+
* caller re-supplies as inherited constraints: correct them and resume the same token) or `"inheritedGate"` (the row's record: no config reaches it; expire the row). The throw
|
|
2503
2512
|
* sites always knew which arm fired; this carries that fact instead of dropping it (ruled
|
|
2504
2513
|
* 2026-08-03, cross-repo HITL discussion). Additive and optional — `code` remains the only REQUIRED
|
|
2505
2514
|
* discriminant. */
|
|
2506
2515
|
detail?: {
|
|
2507
|
-
field?: "boundCallId" | "boundInputHash" | "boundGrantDigest" | "answer" | "hostDecision" | "approver" | "taskConfig" | "inheritedGate";
|
|
2516
|
+
field?: "boundCallId" | "boundInputHash" | "boundGrantDigest" | "answer" | "hostDecision" | "approver" | "taskConfig" | "deps" | "inheritedGate";
|
|
2508
2517
|
/** On `checkpoint.config_refused` — prepare's own code for the refusal ({@link CheckpointConfigRefusalCode}), the
|
|
2509
2518
|
* word a fresh run of the same bad config reports as its `errorCode`. Absent on every other code. */
|
|
2510
2519
|
code?: CheckpointConfigRefusalCode;
|