@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
|
@@ -305,7 +305,7 @@ export function summarizeCheckpoint(cp) {
|
|
|
305
305
|
...(hasBidi ? { hasBidiControls: true } : {}),
|
|
306
306
|
};
|
|
307
307
|
}
|
|
308
|
-
export const CHECKPOINT_CONFIG_REFUSAL_CODES = Object.freeze(["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"]);
|
|
308
|
+
export const CHECKPOINT_CONFIG_REFUSAL_CODES = Object.freeze(["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"]);
|
|
309
309
|
export const isCheckpointConfigRefusalCode = (word) => CHECKPOINT_CONFIG_REFUSAL_CODES.some((w) => w === word);
|
|
310
310
|
export class CheckpointError extends Error {
|
|
311
311
|
code;
|
|
@@ -399,13 +399,13 @@ export function resolveCheckpointStore(spec, deps) {
|
|
|
399
399
|
throw checkpointStoreExecutionUndeclared("TaskSpec.checkpointStore", spec.checkpointStore);
|
|
400
400
|
return spec.checkpointStore;
|
|
401
401
|
}
|
|
402
|
-
if (deps.checkpointStore !== undefined && deps.checkpointStore
|
|
402
|
+
if (deps.checkpointStore !== undefined && deps.checkpointStore?.execution?.outcome !== true) {
|
|
403
403
|
throw checkpointStoreExecutionUndeclared("RunnerDeps.checkpointStore", deps.checkpointStore);
|
|
404
404
|
}
|
|
405
405
|
return deps.checkpointStore;
|
|
406
406
|
}
|
|
407
407
|
export function checkpointStoreExecutionUndeclared(seat, store) {
|
|
408
|
-
const ctor = store
|
|
408
|
+
const ctor = store?.constructor?.name;
|
|
409
409
|
const name = typeof ctor === "string" && ctor.length > 0 && ctor !== "Object" ? ` (${ctor})` : "";
|
|
410
410
|
const e = new Error(`${seat}: the wired CheckpointStore${name} does not declare \`execution = { outcome: true }\` — the execution-outcome ` +
|
|
411
411
|
"record (recordExecutionOutcome, and reopen refusing a recorded row) is a required member of the store contract; " +
|
package/dist/core/compliance.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describeRejectedValue } from "./tool-search-setting.js";
|
|
1
2
|
export const COMPLIANCE_CAPABILITIES = Object.freeze(["mcp_servers", "workflows", "web_fetch", "org_memory_mount"]);
|
|
2
3
|
export const BUILTIN_COMPLIANCE_DENIES = {
|
|
3
4
|
hipaa: ["web_fetch", "org_memory_mount"],
|
|
@@ -26,7 +27,8 @@ export function resolveComplianceDenies(posture) {
|
|
|
26
27
|
export function complianceCapabilityProblem(cap, owner) {
|
|
27
28
|
if (COMPLIANCE_CAPABILITIES.some((member) => member === cap))
|
|
28
29
|
return undefined;
|
|
29
|
-
|
|
30
|
+
const shown = (typeof cap === "object" && cap !== null) || typeof cap === "function" ? describeRejectedValue(cap) : `"${String(cap)}"`;
|
|
31
|
+
return (`${owner} contains ${shown}, not a member of the closed capability set ` +
|
|
30
32
|
`(${COMPLIANCE_CAPABILITIES.join(", ")}) — a misspelled deny must never silently not-enforce.`);
|
|
31
33
|
}
|
|
32
34
|
export const WEB_FETCH_TOOL_NAME = "WebFetch";
|
|
@@ -245,17 +245,36 @@ export declare function readCallTarget(env: ExecutionEnv | undefined, call: Tool
|
|
|
245
245
|
readonly root?: string | undefined;
|
|
246
246
|
readonly cwd?: string | undefined;
|
|
247
247
|
}, signal?: AbortSignal, leads?: ReadonlyMap<string, string | undefined>): Promise<CallTargetReading>;
|
|
248
|
+
/** The gate's entry records the loop's reading of this call's arguments against the face it stamps on the call. */
|
|
249
|
+
export declare function stampArgsReading(face: object, read: (args: unknown) => unknown): void;
|
|
248
250
|
/**
|
|
249
|
-
* `
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
251
|
+
* `args` as the called tool will run them — its argument preparation and schema coercions, the reading the loop gave the
|
|
252
|
+
* model's call — for the call `req` belongs to; `args` itself when nothing was stamped (a bare request outside a Runner:
|
|
253
|
+
* a host judging a call reads it the tool's way first). Every seat that adopts a rewrite (a hook's, a policy's, a
|
|
254
|
+
* person's edit) takes it through here before anything judges it, so what is judged is what runs. A value the reading
|
|
255
|
+
* leaves as it is comes back as ITSELF (the same object: a target reading taken for it still describes it).
|
|
256
|
+
*/
|
|
257
|
+
export declare function argsAsTheToolReads(req: {
|
|
258
|
+
readonly face?: object | undefined;
|
|
259
|
+
}, args: unknown): unknown;
|
|
260
|
+
/** `args` through one call's reading `read` (the face-less seat: the resumed call reads through its live tool): ITSELF when the
|
|
261
|
+
* reading leaves its bytes as they are, else the reading's value, recorded as read from `args`. */
|
|
262
|
+
export declare function argsReadBy(read: (args: unknown) => unknown, args: unknown): unknown;
|
|
263
|
+
/** The value `args` was read from, when a reading moved it; otherwise `args` itself. */
|
|
264
|
+
export declare function argsReadFrom(args: unknown): unknown;
|
|
265
|
+
/**
|
|
266
|
+
* `req` with its arguments replaced by `args` AS THE TOOL READS THEM ({@link argsAsTheToolReads}) and — when `req` carries
|
|
267
|
+
* a reading of its own target — a reading of the NEW args taken the same way (same environment, same root / cwd, same
|
|
268
|
+
* face). Every seat that rewrites a request's arguments and hands it on (a composition's rewrite cascade, a re-judgment of
|
|
269
|
+
* the final or an edited form) goes through here, so the next layer never judges a request its reading does not describe
|
|
270
|
+
* nor arguments the tool would run differently. `signal` is the CURRENT call's: a reading never carries one call's
|
|
271
|
+
* cancellation into another's.
|
|
254
272
|
*/
|
|
255
273
|
export declare function withArgsReading<R extends {
|
|
256
274
|
readonly args: unknown;
|
|
257
275
|
readonly targetReading?: CallTargetReading | undefined;
|
|
258
|
-
|
|
276
|
+
readonly face?: object | undefined;
|
|
277
|
+
}>(req: R, args: unknown, signal: AbortSignal | undefined): Promise<R>;
|
|
259
278
|
/** The request's target reading, when it describes the request's own `args` (the object it was taken for); otherwise
|
|
260
279
|
* nothing — a rule lane then judges the lexical view it always judged. Every rule lane reads the reading through here. */
|
|
261
280
|
export declare function callTargetReadingOf(req: {
|
|
@@ -3,6 +3,7 @@ import { declaredPathTargetOf, pathTargetValue, protectivePathTargetOf } from ".
|
|
|
3
3
|
import { symlinkChainHops } from "./path-identity.js";
|
|
4
4
|
import { DEFAULT_GATE_AWAIT_TIMEOUT_MS, withGateTimeout } from "./gate-bounded-await.js";
|
|
5
5
|
import { thrownText } from "./thrown-value.js";
|
|
6
|
+
import { canonicalize } from "./canonical-json.js";
|
|
6
7
|
export function pathTargetBaseOf(pt, bases) {
|
|
7
8
|
return (pt?.base ?? "cwd") === "root" ? bases.root : (bases.cwd ?? bases.root);
|
|
8
9
|
}
|
|
@@ -146,10 +147,31 @@ export async function readCallTarget(env, call, signal, leads) {
|
|
|
146
147
|
REREAD.set(reading, (args, rereadSignal) => readCallTarget(env, { ...call, args }, rereadSignal, leads));
|
|
147
148
|
return reading;
|
|
148
149
|
}
|
|
149
|
-
|
|
150
|
+
const ARGS_READING = new WeakMap();
|
|
151
|
+
export function stampArgsReading(face, read) {
|
|
152
|
+
ARGS_READING.set(face, read);
|
|
153
|
+
}
|
|
154
|
+
export function argsAsTheToolReads(req, args) {
|
|
155
|
+
const read = req.face === undefined ? undefined : ARGS_READING.get(req.face);
|
|
156
|
+
return read === undefined ? args : argsReadBy(read, args);
|
|
157
|
+
}
|
|
158
|
+
export function argsReadBy(read, args) {
|
|
159
|
+
const out = read(args);
|
|
160
|
+
if (out === args || canonicalize(out) === canonicalize(args))
|
|
161
|
+
return args;
|
|
162
|
+
if (typeof out === "object" && out !== null)
|
|
163
|
+
READ_FROM.set(out, args);
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
const READ_FROM = new WeakMap();
|
|
167
|
+
export function argsReadFrom(args) {
|
|
168
|
+
return typeof args === "object" && args !== null && READ_FROM.has(args) ? READ_FROM.get(args) : args;
|
|
169
|
+
}
|
|
170
|
+
export async function withArgsReading(req, args, signal) {
|
|
171
|
+
const read = argsAsTheToolReads(req, args);
|
|
150
172
|
const reading = callTargetReadingOf(req);
|
|
151
173
|
const reread = reading === undefined ? undefined : REREAD.get(reading);
|
|
152
|
-
return reread === undefined ?
|
|
174
|
+
return reread === undefined ? { ...req, args: read } : { ...req, args: read, targetReading: await reread(read, signal) };
|
|
153
175
|
}
|
|
154
176
|
function viewFrom(v) {
|
|
155
177
|
if (v.lexical === undefined)
|
|
@@ -68,7 +68,7 @@ export interface EngineNotice {
|
|
|
68
68
|
* as usual. Audience `user`; once per prepared task for the skill and request lists (`detail.sessionId` present), once
|
|
69
69
|
* per Runner for the deployment's (at construction, no session). `detail: { sessionId?, entries: [{ source, entry,
|
|
70
70
|
* reason, skill?, list? }] }` (`source` ∈ `skill` / `request` / `deployment`; `skill` and `list` only on a skill's;
|
|
71
|
-
* `reason` ∈ {@link import("./allow-entry.js").ALLOW_ENTRY_INERT_REASONS}).
|
|
71
|
+
* `reason` ∈ {@link import("./allow-entry.js").ALLOW_ENTRY_INERT_REASONS}; `entry` is the entry as written — the message spells it — when it is at most `NOTICE_RULE_TEXT_MAX` (1 024) code points; a longer one is cut to 1 024 plus the truncation mark, 1 025 in all).
|
|
72
72
|
* - `"mods.registration_refused"` — an extension registration was not mounted: a registration admitted before it
|
|
73
73
|
* answered `{ refuse }` on `plugin.register`, or failed while judging it (fail-closed). The run goes on without it.
|
|
74
74
|
* Audience `user` (the seat is in `detail`). Once per refused registration per run. `detail: { name, seat, tier,
|
|
@@ -439,7 +439,18 @@ export interface EngineNotice {
|
|
|
439
439
|
* complete absolute path (only a foreign writer or a hand edit makes one; every engine write face refuses it): they
|
|
440
440
|
* take no part in this run's checks — a deny or ask row among them protects nothing — until they are rewritten or
|
|
441
441
|
* removed. Once per run, by whichever read met them first. `detail: { rows, count, sessionId, runId }` — `rows`
|
|
442
|
-
* the first eight `{ rule, scopeRoot }
|
|
442
|
+
* the first eight `{ rule, scopeRoot }`, `count` all of them; `rule` is the rule text as the store served it, through the same reading
|
|
443
|
+
* as `permission.allowed_tool_not_granted`'s `entries[].entry` (the original, not a spelling of it, when it is at most 1 024 code points;
|
|
444
|
+
* a longer one is cut to 1 024 plus the truncation mark, 1 025 in all), `scopeRoot` neutralized and bounded. Audience `user`.
|
|
445
|
+
* - `"config.session_rules_earlier_grammar"` — a session's stored permission rules (`RunnerDeps.sessionPolicyStore`) hold
|
|
446
|
+
* entries the rule grammar they were written in reads as nothing: an allow entry admits nothing, and a `toolDeny` entry
|
|
447
|
+
* this engine's grammar reads as no rule makes those rules refuse every call until they are rewritten. The message names
|
|
448
|
+
* the session, the bucket, the entries (the first five, the rest counted), the reader's word and the way out. Audience
|
|
449
|
+
* `user`. Once per distinct entry set per bucket per rule layer (the run's own rules, each ancestor's inherited ones).
|
|
450
|
+
* `detail: { bucket, storedGrammar, entries: [{ entry, reader }], rewrite: { by, stampsGrammar }, rulesSessionId,
|
|
451
|
+
* sessionId }` — `entries` every one, `entry` through the same reading as `permission.allowed_tool_not_granted`'s;
|
|
452
|
+
* `reader` ∈ `no_rule` / `named_no_tool`; `rewrite.by` = `operator_write_without_rev`; `rulesSessionId` the session
|
|
453
|
+
* whose rules they are (an ancestor's on a delegated child), `sessionId` the run that read them.
|
|
443
454
|
* - `"config.durable_gate_unavailable"` — a durable-approval MANDATE is in force on a leg with NO
|
|
444
455
|
* checkpoint store: the per-principal `forceDurableGate` entitlement, or the task's own
|
|
445
456
|
* `durableApproval` opt-in with no live approver seat (`durableMandateSourceOf`, the predicate the
|
package/dist/core/gate-fold.js
CHANGED
|
@@ -8,6 +8,7 @@ import { createGateDeadline, awaitWriteProtectionJudgment, gateWaitFailureOf, wi
|
|
|
8
8
|
import { shellReadFaceWords } from "../tools/fs/shell-read-face.js";
|
|
9
9
|
import { failedJudgmentDeny } from "./runner/policy-throw.js";
|
|
10
10
|
import { thrownText } from "./thrown-value.js";
|
|
11
|
+
import { argsAsTheToolReads } from "./effective-path-target.js";
|
|
11
12
|
import { foldHookAsk, refuseOutOfContractDecision } from "./tool-policy.js";
|
|
12
13
|
import { applyUnansweredSilence, readStationAnswer, silencedBy } from "./shell-read-silence.js";
|
|
13
14
|
import { asksUnstated, permissionSeatAt, unstatedAskMessage } from "./mode-policy.js";
|
|
@@ -37,10 +38,10 @@ export async function runGateFold(pass) {
|
|
|
37
38
|
pass.classifierRound = attributed;
|
|
38
39
|
}
|
|
39
40
|
if ((pass.decision.action === "allow" || pass.decision.action === "ask") && pass.decision.updatedInput !== undefined) {
|
|
40
|
-
pass.policyRewrite = pass.decision.updatedInput;
|
|
41
|
+
pass.policyRewrite = argsAsTheToolReads(pass.req, pass.decision.updatedInput);
|
|
41
42
|
}
|
|
42
43
|
else if (pass.hookAsk?.updatedInput !== undefined) {
|
|
43
|
-
pass.policyRewrite = pass.hookAsk.updatedInput;
|
|
44
|
+
pass.policyRewrite = argsAsTheToolReads(pass.req, pass.hookAsk.updatedInput);
|
|
44
45
|
}
|
|
45
46
|
if (pass.hookAsk !== undefined && pass.decision.action !== "deny") {
|
|
46
47
|
pass.decision = foldHookAsk(pass.decision, pass.hookAsk);
|
package/dist/core/gate-lanes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { withArgsReading } from "./effective-path-target.js";
|
|
1
|
+
import { argsAsTheToolReads, withArgsReading } from "./effective-path-target.js";
|
|
2
2
|
import { attest, attestationOf, decisionText, foldHookAsk, ownArgs, refusalOf, refuseOutOfContractDecision } from "./tool-policy.js";
|
|
3
3
|
import { arbitrateByMode, classifyAsk, decisionAskClassesOf, fileCommandOptionsSentence, persistedRuleMandateOf, ruleClearanceOf, safetyAxisOf, stampAskOrigin } from "./ask-origin.js";
|
|
4
4
|
import { classifierParseFailureDenyMessage, denialLimitFallbackMessage, rungDidNotRule, unarmedWindow } from "./auto-mode.js";
|
|
@@ -16,7 +16,7 @@ import { catalogRuleFaceOf } from "./tool-registry.js";
|
|
|
16
16
|
import { callFaceSeat } from "./tool-roster.js";
|
|
17
17
|
import { heldBy, wireOf } from "./tool-name-readings.js";
|
|
18
18
|
import { asksUnstated, modeAcceptsFileCommand, permissionSeatAt, unstatedAskMessage } from "./mode-policy.js";
|
|
19
|
-
import { ORG_RULE_DECISION_REASON, ORG_UNAVAILABLE_DECISION_REASON } from "./permission-rule-org.js";
|
|
19
|
+
import { ORG_RULE_DECISION_REASON, ORG_UNAVAILABLE_DECISION_REASON, orgUnreadableClause } from "./permission-rule-org.js";
|
|
20
20
|
import { exitGate, passAskFace, unownedArgsMessage } from "./runner/gate-exit.js";
|
|
21
21
|
import { readOrgRuleLane, readPersistedRuleLane } from "./rule-lane-reads.js";
|
|
22
22
|
import { traceHookCrash } from "./gate-crash-trace.js";
|
|
@@ -137,14 +137,14 @@ export async function runGateLanes(pass) {
|
|
|
137
137
|
orgTightenCount += 1;
|
|
138
138
|
pass.deniedBy = "org";
|
|
139
139
|
pass.denyCause = undefined;
|
|
140
|
-
return { decision: refusalOf({ action: "deny", message: `an organization policy rule (${rule}) denies this call`, decisionReason: ORG_RULE_DECISION_REASON }, args), org: undefined };
|
|
140
|
+
return { decision: refusalOf({ action: "deny", message: `an organization policy rule (${rule}) denies this call${unreadable !== undefined ? `, ${orgUnreadableClause(unreadable)}` : ""}`, decisionReason: ORG_RULE_DECISION_REASON }, args), org: undefined };
|
|
141
141
|
}
|
|
142
142
|
if (decided.action === "deny")
|
|
143
143
|
return { decision: decided, org: undefined };
|
|
144
144
|
orgTightenCount += 1;
|
|
145
145
|
orgRealApprovalRequired = true;
|
|
146
146
|
const orgRuleMessage = unreadable !== undefined
|
|
147
|
-
? `an organization policy rule (${rule}) may reach this call,
|
|
147
|
+
? `an organization policy rule (${rule}) may reach this call, ${orgUnreadableClause(unreadable)} — a person must decide`
|
|
148
148
|
: `an organization policy rule (${rule}) requires approval for this call`;
|
|
149
149
|
return {
|
|
150
150
|
decision: decided.action === "ask"
|
|
@@ -160,8 +160,7 @@ export async function runGateLanes(pass) {
|
|
|
160
160
|
const orgAnswer = await applyOrgLayer(pass.decision, finalArgs);
|
|
161
161
|
pass.decision = orgAnswer.decision;
|
|
162
162
|
const readPersistedRules = (req) => readPersistedRuleLane(input.persistedRules, req, input.abortSignal);
|
|
163
|
-
const
|
|
164
|
-
const laneReq = Object.freeze(reread !== undefined ? await reread : { ...pass.req, ...(pass.policyRewrite !== undefined ? { args: pass.policyRewrite } : {}) });
|
|
163
|
+
const laneReq = Object.freeze(pass.policyRewrite !== undefined ? await withArgsReading(pass.req, pass.policyRewrite, pass.callSignal ?? input.abortSignal) : { ...pass.req });
|
|
165
164
|
const laneAnswer = input.persistedRules !== undefined && wireOf("lanes.persistedLaneSkip", pass.req) !== ASK_USER_QUESTION_TOOL_NAME ? await readPersistedRules(laneReq) : undefined;
|
|
166
165
|
{
|
|
167
166
|
const tightened = applyPersistedTightening(pass.decision, laneAnswer, laneReq);
|
|
@@ -558,7 +557,7 @@ export async function runGateLanes(pass) {
|
|
|
558
557
|
pass.deniedBy = "policy";
|
|
559
558
|
break;
|
|
560
559
|
}
|
|
561
|
-
editArgs = taken.value;
|
|
560
|
+
editArgs = argsAsTheToolReads(pass.req, taken.value);
|
|
562
561
|
if (round >= 3) {
|
|
563
562
|
editDenied = {
|
|
564
563
|
action: "deny",
|
|
@@ -589,7 +588,7 @@ export async function runGateLanes(pass) {
|
|
|
589
588
|
editHookAsk = ask;
|
|
590
589
|
}
|
|
591
590
|
if (hr.updatedInput !== undefined) {
|
|
592
|
-
editArgs = hr.updatedInput;
|
|
591
|
+
editArgs = argsAsTheToolReads(pass.req, hr.updatedInput);
|
|
593
592
|
editRewrittenSinceHuman = true;
|
|
594
593
|
}
|
|
595
594
|
}
|
|
@@ -622,7 +621,7 @@ export async function runGateLanes(pass) {
|
|
|
622
621
|
}
|
|
623
622
|
}
|
|
624
623
|
if (recheck.updatedInput !== undefined) {
|
|
625
|
-
editArgs = recheck.updatedInput;
|
|
624
|
+
editArgs = argsAsTheToolReads(pass.req, recheck.updatedInput);
|
|
626
625
|
editRewrittenSinceHuman = true;
|
|
627
626
|
}
|
|
628
627
|
const editOrg = await applyOrgLayer(recheck, editArgs);
|
|
@@ -632,8 +631,7 @@ export async function runGateLanes(pass) {
|
|
|
632
631
|
break;
|
|
633
632
|
}
|
|
634
633
|
let editLane;
|
|
635
|
-
const
|
|
636
|
-
const editLaneReq = Object.freeze(editReread !== undefined ? await editReread : { toolName, args: editArgs, toolCallId, ...(pass.req.face !== undefined ? { face: pass.req.face } : {}) });
|
|
634
|
+
const editLaneReq = Object.freeze(await withArgsReading(pass.req, editArgs, callSignal ?? input.abortSignal));
|
|
637
635
|
if (input.persistedRules !== undefined && wireOf("lanes.persistedEditSkip", pass.req) !== ASK_USER_QUESTION_TOOL_NAME) {
|
|
638
636
|
editLane = await readPersistedRules(editLaneReq);
|
|
639
637
|
const editTightened = applyPersistedTightening(recheck, editLane, editLaneReq);
|
|
@@ -653,7 +651,7 @@ export async function runGateLanes(pass) {
|
|
|
653
651
|
}
|
|
654
652
|
if (recheck.action === "allow") {
|
|
655
653
|
if (recheck.updatedInput !== undefined) {
|
|
656
|
-
editArgs = recheck.updatedInput;
|
|
654
|
+
editArgs = argsAsTheToolReads(pass.req, recheck.updatedInput);
|
|
657
655
|
editRewrittenSinceHuman = true;
|
|
658
656
|
}
|
|
659
657
|
if (editRewrittenSinceHuman && asksUnstated(recheck, permissionSeatAt(input, editArgs)?.seat, input.permissionMode?.())) {
|
|
@@ -662,7 +660,7 @@ export async function runGateLanes(pass) {
|
|
|
662
660
|
}
|
|
663
661
|
}
|
|
664
662
|
if (recheck.updatedInput !== undefined) {
|
|
665
|
-
editArgs = recheck.updatedInput;
|
|
663
|
+
editArgs = argsAsTheToolReads(pass.req, recheck.updatedInput);
|
|
666
664
|
editRewrittenSinceHuman = true;
|
|
667
665
|
}
|
|
668
666
|
let editFacts;
|
|
@@ -336,12 +336,17 @@ export type CauseTableCoversEveryDeniedBy = AssertAllKeysHandled<Exclude<DeniedB
|
|
|
336
336
|
* allow or deny) and from BOTH stations (this gate's own classifier step, and an ANCESTOR's frozen
|
|
337
337
|
* classifier reached through the inherited policy chain, whose allow and deny carry it out on the same seat
|
|
338
338
|
* that carries the FORM): which model it was for, which one answered, and — only when the ladder moved —
|
|
339
|
-
* the rung it left and why.
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
339
|
+
* the rung it left and why. It is the classification's account, not the settlement's: a classification that
|
|
340
|
+
* RULED keeps it whoever then decided the call — a call whose question a person then answered (an ask a
|
|
341
|
+
* station after the classifier raised, or a block at the DENIAL-LIMIT bound, which becomes an ask) carries
|
|
342
|
+
* it beside that person's settlement on {@link GateOutcome}; who decided is read from the settlement
|
|
343
|
+
* (`human_allowed`, `policy_allowed`, …), never from this member's presence. ABSENT reads UNKNOWN, never
|
|
344
|
+
* "the seat answered" (that is `fallback` absent WITH the member present), and a consumer cannot tell its
|
|
345
|
+
* three causes apart from the record alone: no classification happened; one ran on a decider given no
|
|
346
|
+
* candidate ladder and can name no model (a hand-built decider; a parked ancestor's cross-process rebuild,
|
|
347
|
+
* whose single fresh leg has none); or one did not rule (it could not run, or replied with no verdict) and
|
|
348
|
+
* handed the question to the approval seat, whose settlement then names the resolution that ask reached.
|
|
349
|
+
* Additive. Invariant I6 ({@link screenGateOutcome}) screens its shape. */
|
|
345
350
|
export type GateDisposition = {
|
|
346
351
|
kind: "allowed";
|
|
347
352
|
classifier?: AutoModeClassifierRound;
|
|
@@ -138,7 +138,7 @@ export type NoticeAudience = "user" | "operator";
|
|
|
138
138
|
* the union's catalog ({@link EngineNotice.code}). The groups below say only which family a row joins,
|
|
139
139
|
* so the catalog is not written a second time here and the two cannot drift.
|
|
140
140
|
*/
|
|
141
|
-
export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.context_settings_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.write_face_swapped", "config.write_face_deployment_clamped", "config.read_deny_swapped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "config.artifact_host_invalid", "config.task_root_not_canonical", "config.write_protection_target_view_absent", "config.path_view_unresolved", "config.execution_env_capability_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.forward_sink_failed", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "config.secret_env_scrubbed", "config.credential_not_withheld", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable", "permission.rule_row_dropped", "permission.allowed_tool_not_granted", "mods.registration_refused", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "classifier.fallback", "classifier.role_defaulted", "classifier.contract_broken", "classifier.unavailable", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "mcp.server_redialed", "workflow.governance_key_stripped", "workflow.agent_option_ignored", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "memory.consolidation_withheld", "memory.restore_withheld", "memory.project_scope_absent", "route.fallback_to_primary", "route.classifier_unroutable", "route.credential_origin_mismatch", "route.response_model_mismatch", "route.tool_call_id_folded", "route.finish_reason_unknown", "route.stream_end_ignored", "route.extra_body_reserved_dropped", "context.tool_turn_repaired", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "task.tool_turn_incomplete", "approval.read_root_granted", "approval.read_root_grant_rejected", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "skills.listing_truncated", "agents.listing_truncated", "tool_result.offload_put_failed", "tool_result.media_refused", "instructions.source_changed", "session.records_skipped", "memory.layer_locked_legacy", "server.model_unreachable", "permission.entry_mode_clamped"];
|
|
141
|
+
export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.context_settings_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.write_face_swapped", "config.write_face_deployment_clamped", "config.read_deny_swapped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "config.artifact_host_invalid", "config.task_root_not_canonical", "config.write_protection_target_view_absent", "config.path_view_unresolved", "config.execution_env_capability_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.forward_sink_failed", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "config.secret_env_scrubbed", "config.credential_not_withheld", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable", "permission.rule_row_dropped", "config.session_rules_earlier_grammar", "permission.allowed_tool_not_granted", "mods.registration_refused", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "classifier.fallback", "classifier.role_defaulted", "classifier.contract_broken", "classifier.unavailable", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "mcp.server_redialed", "workflow.governance_key_stripped", "workflow.agent_option_ignored", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "memory.consolidation_withheld", "memory.restore_withheld", "memory.project_scope_absent", "route.fallback_to_primary", "route.classifier_unroutable", "route.credential_origin_mismatch", "route.response_model_mismatch", "route.tool_call_id_folded", "route.finish_reason_unknown", "route.stream_end_ignored", "route.extra_body_reserved_dropped", "context.tool_turn_repaired", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "task.tool_turn_incomplete", "approval.read_root_granted", "approval.read_root_grant_rejected", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "skills.listing_truncated", "agents.listing_truncated", "tool_result.offload_put_failed", "tool_result.media_refused", "instructions.source_changed", "session.records_skipped", "memory.layer_locked_legacy", "server.model_unreachable", "permission.entry_mode_clamped"];
|
|
142
142
|
/** A catalogued code (see {@link ENGINE_NOTICE_CODES}); {@link NOTICE_MINTER} says who mints it. NOT the type of
|
|
143
143
|
* `EngineNotice.code`, which stays `string` — a host forwarding its own notices through the same
|
|
144
144
|
* sink is a supported shape, and narrowing that field would break it. */
|
|
@@ -187,6 +187,7 @@ declare const NOTICE_AUDIENCE_TABLE: {
|
|
|
187
187
|
readonly "permission.read_deny_unapplied": "user";
|
|
188
188
|
readonly "permission.rule_store_unavailable": "user";
|
|
189
189
|
readonly "permission.rule_row_dropped": "user";
|
|
190
|
+
readonly "config.session_rules_earlier_grammar": "user";
|
|
190
191
|
readonly "permission.allowed_tool_not_granted": "user";
|
|
191
192
|
readonly "mods.registration_refused": "user";
|
|
192
193
|
readonly "memory.capture_opted_out": "user";
|
|
@@ -309,6 +310,7 @@ declare const NOTICE_MINTER_TABLE: {
|
|
|
309
310
|
readonly "permission.read_deny_unapplied": "engine";
|
|
310
311
|
readonly "permission.rule_store_unavailable": "engine";
|
|
311
312
|
readonly "permission.rule_row_dropped": "engine";
|
|
313
|
+
readonly "config.session_rules_earlier_grammar": "engine";
|
|
312
314
|
readonly "permission.allowed_tool_not_granted": "engine";
|
|
313
315
|
readonly "mods.registration_refused": "engine";
|
|
314
316
|
readonly "peer.inbound_disposition": "engine";
|
|
@@ -151,6 +151,7 @@ export const ENGINE_NOTICE_CODES = Object.freeze([
|
|
|
151
151
|
"permission.read_deny_unapplied",
|
|
152
152
|
"permission.rule_store_unavailable",
|
|
153
153
|
"permission.rule_row_dropped",
|
|
154
|
+
"config.session_rules_earlier_grammar",
|
|
154
155
|
"permission.allowed_tool_not_granted",
|
|
155
156
|
"mods.registration_refused",
|
|
156
157
|
"peer.inbound_disposition",
|
|
@@ -246,6 +247,7 @@ const NOTICE_AUDIENCE_TABLE = {
|
|
|
246
247
|
"permission.read_deny_unapplied": "user",
|
|
247
248
|
"permission.rule_store_unavailable": "user",
|
|
248
249
|
"permission.rule_row_dropped": "user",
|
|
250
|
+
"config.session_rules_earlier_grammar": "user",
|
|
249
251
|
"permission.allowed_tool_not_granted": "user",
|
|
250
252
|
"mods.registration_refused": "user",
|
|
251
253
|
"memory.capture_opted_out": "user",
|
|
@@ -349,6 +351,7 @@ const NOTICE_MINTER_TABLE = {
|
|
|
349
351
|
"permission.read_deny_unapplied": "engine",
|
|
350
352
|
"permission.rule_store_unavailable": "engine",
|
|
351
353
|
"permission.rule_row_dropped": "engine",
|
|
354
|
+
"config.session_rules_earlier_grammar": "engine",
|
|
352
355
|
"permission.allowed_tool_not_granted": "engine",
|
|
353
356
|
"mods.registration_refused": "engine",
|
|
354
357
|
"peer.inbound_disposition": "engine",
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Total over what a deployment can hand the seat: absent passes; a value that is not a plain record, a member the record does not
|
|
3
|
+
* have, or a member holding a value it does not take refuses by name (`config.invalid_argument`, `detail.field` = `RunnerDeps.hands`
|
|
4
|
+
* or `RunnerDeps.hands.<member>`) — a band setting the engine cannot read is never run as if it were absent. A member holding
|
|
5
|
+
* `undefined` is absence, as on every optional seat.
|
|
6
|
+
*/
|
|
7
|
+
export declare function screenHandsBandOptions(value: unknown): void;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const HANDS_BAND_MEMBERS = {
|
|
2
|
+
bashReadonlyAllow: { takes: "a list of command-name strings", ok: (v) => Array.isArray(v) && v.every((c) => typeof c === "string") },
|
|
3
|
+
commitCoAuthor: { takes: "a string or false", ok: (v) => typeof v === "string" || v === false },
|
|
4
|
+
readImageDownsampler: { takes: "a function or false", ok: (v) => typeof v === "function" || v === false },
|
|
5
|
+
beforeWrite: { takes: "a function", ok: (v) => typeof v === "function" },
|
|
6
|
+
autoBackgroundOnTimeout: { takes: "a boolean", ok: (v) => typeof v === "boolean" },
|
|
7
|
+
};
|
|
8
|
+
const kindOf = (v) => (v === null ? "null" : Array.isArray(v) ? "an array" : typeof v);
|
|
9
|
+
export function screenHandsBandOptions(value) {
|
|
10
|
+
if (value === undefined)
|
|
11
|
+
return;
|
|
12
|
+
const refuse = (field, problem) => {
|
|
13
|
+
const e = new Error(`${field} ${problem} — a hands band setting the engine cannot read is refused rather than ignored.`);
|
|
14
|
+
e.code = "config.invalid_argument";
|
|
15
|
+
e.detail = { field };
|
|
16
|
+
throw e;
|
|
17
|
+
};
|
|
18
|
+
const proto = typeof value === "object" && value !== null ? Object.getPrototypeOf(value) : undefined;
|
|
19
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || (proto !== Object.prototype && proto !== null)) {
|
|
20
|
+
refuse("RunnerDeps.hands", `must be a plain record of hands band settings (got ${kindOf(value)})`);
|
|
21
|
+
}
|
|
22
|
+
for (const key of Object.keys(value)) {
|
|
23
|
+
const v = value[key];
|
|
24
|
+
if (v === undefined)
|
|
25
|
+
continue;
|
|
26
|
+
if (!Object.hasOwn(HANDS_BAND_MEMBERS, key))
|
|
27
|
+
refuse(`RunnerDeps.hands.${key}`, `is not a hands band setting (the settings: ${Object.keys(HANDS_BAND_MEMBERS).join(", ")})`);
|
|
28
|
+
const member = HANDS_BAND_MEMBERS[key];
|
|
29
|
+
if (!member.ok(v))
|
|
30
|
+
refuse(`RunnerDeps.hands.${key}`, `must be ${member.takes} (got ${kindOf(v)})`);
|
|
31
|
+
}
|
|
32
|
+
}
|
package/dist/core/hooks.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ActorAssertion, DocumentContent, ImageContent, TextContent } from "../internal/llm.js";
|
|
2
2
|
import type { ExecutionEnv, FileError, Result, SessionTreeEntry } from "../internal/harness-types.js";
|
|
3
3
|
import type { DecisionReason, PermissionResult, ResolvedAsk, ToolCallFace, ToolCallRequest, ToolPolicy } from "./tool-policy.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type CallTargetStamp } from "./effective-path-target.js";
|
|
5
5
|
import type { GateOutcome } from "./gate-outcome.js";
|
|
6
6
|
import { type PreToolUseScreening } from "./runner/gate-exit.js";
|
|
7
7
|
export { normalizeOrgGateVerdict, normalizePersistedRuleHit } from "./rule-lane-reads.js";
|
|
@@ -629,9 +629,9 @@ export interface ParkAttemptFailed {
|
|
|
629
629
|
*/
|
|
630
630
|
export type OrgGateVerdict = {
|
|
631
631
|
status: "available";
|
|
632
|
-
/** The org rule that speaks for this call, if any. Deny outranks ask; there is no allow bucket. An
|
|
633
|
-
* `
|
|
634
|
-
*
|
|
632
|
+
/** The org rule that speaks for this call, if any. Deny outranks ask; there is no allow bucket. An `unreadable` member says why
|
|
633
|
+
* `rule` could not be judged against the call (the message names it): the call could not be READ against it (the lexer's reason;
|
|
634
|
+
* `behavior` is `ask`), or the row is one the org layer does not read, standing at its strictest reading (`deny`, or `ask`). */
|
|
635
635
|
verdict?: {
|
|
636
636
|
behavior: "deny" | "ask";
|
|
637
637
|
rule: string;
|
|
@@ -727,11 +727,13 @@ export interface ToolGateInput extends PermissionSeatInputs, SkillGateSeats, All
|
|
|
727
727
|
onNotifyError?: (failure: import("./safe-notify.js").SafeNotifyFailure) => void;
|
|
728
728
|
/** The call. `toolName` is the name every policy, hook and name-keyed table of this gate reads — the wire name of the
|
|
729
729
|
* tool the call dispatches to: the Runner's station resolves a call made under an alias through the roster
|
|
730
|
-
* (`rosterToolName`) before this gate runs; a host driving the gate resolves it the same way.
|
|
730
|
+
* (`rosterToolName`) before this gate runs; a host driving the gate resolves it the same way. `readArguments` = the loop's
|
|
731
|
+
* reading of the call's args: stamped on the call's face, every rewrite this gate adopts is read through it (`argsAsTheToolReads`). */
|
|
731
732
|
event: {
|
|
732
733
|
toolCallId: string;
|
|
733
734
|
toolName: string;
|
|
734
735
|
input: Record<string, unknown>;
|
|
736
|
+
readArguments?: (args: unknown) => unknown;
|
|
735
737
|
};
|
|
736
738
|
/** The run/leg identity envelope, put verbatim on every {@link PreToolUseEvent} this
|
|
737
739
|
* gate call builds and on every {@link PermissionDeniedPayload} it emits. The Runner supplies its
|
package/dist/core/hooks.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { argsAsTheToolReads, stampArgsReading } from "./effective-path-target.js";
|
|
2
|
+
import { abortedSeatDeny, decisionText, describeThrown, isEngineRefusal, ownArgs, ownedReading, refuseOutOfContractDecision, unaskedSeatAnswer } from "./tool-policy.js";
|
|
2
3
|
import { createSettlementLedger, engineFailClosedExit, hookDenyExit, unownedArgsExit } from "./runner/gate-exit.js";
|
|
3
4
|
import { classicEventBase, dispatchClassic, ENGINE_CORE_ANSWER, engineCore, failedHandlerClause, judgedInputOf, observePermissionDenied, recordScreeningChain } from "./mods/stations.js";
|
|
4
5
|
import { runGateFold } from "./gate-fold.js";
|
|
@@ -235,7 +236,8 @@ export async function runToolGate(input) {
|
|
|
235
236
|
screening,
|
|
236
237
|
afterFold: runGateLanes,
|
|
237
238
|
currentInput: args,
|
|
238
|
-
req: Object.freeze({ toolName, args, toolCallId, ...(() => { const f = input.toolFaceOf?.(toolName);
|
|
239
|
+
req: Object.freeze({ toolName, args, toolCallId, ...(() => { const f = input.toolFaceOf?.(toolName); const read = event.readArguments; if (f !== undefined && read !== undefined)
|
|
240
|
+
stampArgsReading(f, ownedReading(read)); return f !== undefined ? { face: f } : {}; })() }),
|
|
239
241
|
preToolContext: [],
|
|
240
242
|
hookAsk: undefined,
|
|
241
243
|
parkFailed: undefined,
|
|
@@ -261,7 +263,7 @@ export async function runToolGate(input) {
|
|
|
261
263
|
return hookDenyExit(pass, screening.deniedReason(r));
|
|
262
264
|
}
|
|
263
265
|
if (r.updatedInput !== undefined) {
|
|
264
|
-
pass.currentInput = r.updatedInput;
|
|
266
|
+
pass.currentInput = argsAsTheToolReads(pass.req, r.updatedInput);
|
|
265
267
|
}
|
|
266
268
|
if (r.action === "ask") {
|
|
267
269
|
pass.hookAsk = r;
|