@sema-agent/core 7.33.8 → 7.35.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 +462 -3
- package/dist/agents/cross-session-judge.d.ts +16 -7
- package/dist/agents/cross-session-judge.js +18 -15
- package/dist/agents/peer-session-drain.d.ts +4 -0
- package/dist/agents/peer-session-drain.js +5 -2
- package/dist/agents/repair-loop.d.ts +1 -1
- package/dist/agents/subagent.d.ts +3 -0
- package/dist/agents/subagent.js +12 -3
- package/dist/brain/anthropic.js +1 -0
- package/dist/brain/errors.d.ts +23 -1
- package/dist/brain/errors.js +19 -2
- package/dist/brain/open-responses.js +1 -0
- package/dist/brain/openai.js +1 -0
- package/dist/brain/status-sink.d.ts +7 -1
- package/dist/brain/stream-engine.d.ts +3 -0
- package/dist/brain/stream-engine.js +2 -1
- package/dist/brain/stream-shared.d.ts +3 -1
- package/dist/brain/stream-shared.js +16 -4
- package/dist/core/a2a.d.ts +3 -1
- package/dist/core/a2a.js +1 -1
- package/dist/core/ask-origin.d.ts +379 -154
- package/dist/core/ask-origin.js +224 -45
- package/dist/core/ask-question.js +2 -1
- package/dist/core/ask-unresolvable-notice.d.ts +5 -2
- package/dist/core/ask-unresolvable-notice.js +4 -3
- package/dist/core/auto-mode-arming.d.ts +17 -4
- package/dist/core/auto-mode-arming.js +31 -6
- package/dist/core/auto-mode-defaults.d.ts +16 -7
- package/dist/core/auto-mode-defaults.js +2 -1
- package/dist/core/auto-mode-directive.d.ts +18 -30
- package/dist/core/auto-mode-directive.js +9 -18
- package/dist/core/auto-mode-prompt.d.ts +62 -1
- package/dist/core/auto-mode-prompt.js +42 -4
- package/dist/core/auto-mode-rebuild.d.ts +2 -1
- package/dist/core/auto-mode-rebuild.js +1 -0
- package/dist/core/auto-mode.d.ts +32 -3
- package/dist/core/auto-mode.js +80 -41
- package/dist/core/checkpoint-store.d.ts +54 -35
- package/dist/core/checkpoint-store.js +58 -3
- package/dist/core/child-clamp-seats.js +1 -0
- package/dist/core/classifier-unavailable-arm.d.ts +57 -0
- package/dist/core/classifier-unavailable-arm.js +17 -0
- package/dist/core/effective-path-target.d.ts +12 -0
- package/dist/core/effective-path-target.js +18 -7
- package/dist/core/engine-notice.d.ts +53 -2
- package/dist/core/fs-write-gate-policy.d.ts +5 -1
- package/dist/core/fs-write-gate-policy.js +4 -1
- package/dist/core/gate-fold.js +19 -3
- package/dist/core/gate-lanes.js +116 -50
- package/dist/core/gate-outcome.d.ts +40 -7
- package/dist/core/gate-outcome.js +26 -4
- package/dist/core/governance-codes.d.ts +18 -2
- package/dist/core/governance-codes.js +23 -1
- package/dist/core/hooks.d.ts +53 -29
- package/dist/core/hooks.js +1 -3
- package/dist/core/mcp.js +1 -1
- package/dist/core/message-utils.d.ts +2 -1
- package/dist/core/message-utils.js +2 -1
- package/dist/core/mode-policy.d.ts +185 -0
- package/dist/core/mode-policy.js +184 -0
- package/dist/core/oracle-isolation.d.ts +3 -3
- package/dist/core/permission-rule-model.d.ts +20 -3
- package/dist/core/permission-rule-model.js +40 -3
- package/dist/core/permission-rule-provider.d.ts +22 -11
- package/dist/core/permission-rule-provider.js +17 -7
- package/dist/core/permission-rules.d.ts +23 -1
- package/dist/core/permission-rules.js +56 -9
- package/dist/core/persisted-rule-arms.d.ts +14 -10
- package/dist/core/persisted-rule-arms.js +13 -6
- package/dist/core/present-plan-tool.d.ts +1 -1
- package/dist/core/present-plan-tool.js +3 -3
- package/dist/core/read-only-shell.d.ts +19 -19
- package/dist/core/read-only-shell.js +38 -12
- package/dist/core/retired-keys.d.ts +9 -3
- package/dist/core/retired-keys.js +5 -1
- package/dist/core/runner/active-skill-scope.js +5 -4
- package/dist/core/runner/assemble-result.js +2 -2
- package/dist/core/runner/classifier-ladder.d.ts +43 -1
- package/dist/core/runner/classifier-ladder.js +29 -0
- package/dist/core/runner/contracts.d.ts +62 -38
- package/dist/core/runner/denial-limit-arms.d.ts +35 -7
- package/dist/core/runner/denial-limit-arms.js +15 -9
- package/dist/core/runner/env-capability-census.js +1 -1
- package/dist/core/runner/file-history-request.d.ts +26 -0
- package/dist/core/runner/file-history-request.js +90 -0
- package/dist/core/runner/gate-exit.d.ts +30 -4
- package/dist/core/runner/gate-exit.js +28 -7
- package/dist/core/runner/permission-rule-lanes.d.ts +11 -6
- package/dist/core/runner/permission-rule-lanes.js +55 -48
- package/dist/core/runner/persisted-path-rows.d.ts +64 -0
- package/dist/core/runner/persisted-path-rows.js +89 -0
- package/dist/core/runner/prepare-artifact.d.ts +5 -1
- package/dist/core/runner/prepare-artifact.js +9 -3
- package/dist/core/runner/prepare-ask-lane.d.ts +6 -4
- package/dist/core/runner/prepare-ask-lane.js +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +39 -13
- package/dist/core/runner/prepare-caps-and-workflow.js +58 -29
- package/dist/core/runner/prepare-file-history.d.ts +9 -3
- package/dist/core/runner/prepare-file-history.js +4 -90
- package/dist/core/runner/prepare-gate-stations.d.ts +37 -9
- package/dist/core/runner/prepare-gate-stations.js +46 -29
- package/dist/core/runner/prepare-hands-readface.d.ts +40 -17
- package/dist/core/runner/prepare-hands-readface.js +64 -53
- package/dist/core/runner/prepare-inherited-gate.d.ts +30 -23
- package/dist/core/runner/prepare-inherited-gate.js +45 -25
- package/dist/core/runner/prepare-park-ask.d.ts +6 -4
- package/dist/core/runner/prepare-park-ask.js +7 -5
- package/dist/core/runner/prepare-policy-chain.d.ts +25 -4
- package/dist/core/runner/prepare-policy-chain.js +203 -180
- package/dist/core/runner/prepare-protocol-tools.d.ts +28 -0
- package/dist/core/runner/prepare-protocol-tools.js +13 -3
- package/dist/core/runner/prepare-run-refs.d.ts +6 -6
- package/dist/core/runner/prepare-run-refs.js +9 -4
- package/dist/core/runner/prepare-safety-scan.d.ts +5 -0
- package/dist/core/runner/prepare-safety-scan.js +19 -3
- package/dist/core/runner/prepare-suspend-saga.d.ts +5 -5
- package/dist/core/runner/prepare-suspend-saga.js +6 -6
- package/dist/core/runner/prepare-task.js +56 -24
- package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -6
- package/dist/core/runner/prepare-wiring-manifest.js +5 -3
- package/dist/core/runner/read-root-grant.d.ts +9 -0
- package/dist/core/runner/read-root-grant.js +12 -0
- package/dist/core/runner/retired-runner-deps.js +1 -0
- package/dist/core/runner/run-brain-sinks.js +3 -1
- package/dist/core/runner/run-identity-wiring.js +1 -1
- package/dist/core/runner/run-leg.js +29 -18
- package/dist/core/runner/run-notification-lane.d.ts +3 -0
- package/dist/core/runner/run-notification-lane.js +3 -1
- package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
- package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
- package/dist/core/runner/run-turn-boundary.js +1 -1
- package/dist/core/runner/runtask.d.ts +1 -0
- package/dist/core/runner/runtask.js +8 -11
- package/dist/core/runner/session-mode-seats.d.ts +48 -0
- package/dist/core/runner/session-mode-seats.js +73 -0
- package/dist/core/runner/session-rule-policy.js +3 -2
- package/dist/core/runner/stream-mode-verb.d.ts +27 -0
- package/dist/core/runner/stream-mode-verb.js +19 -0
- package/dist/core/runner/turn-attachments.d.ts +3 -3
- package/dist/core/runner/turn-attachments.js +1 -1
- package/dist/core/runner/write-protection-station.d.ts +7 -1
- package/dist/core/runner/write-protection-station.js +6 -3
- package/dist/core/runner-deps.d.ts +31 -34
- package/dist/core/runtime-caps.d.ts +12 -3
- package/dist/core/safety-axis-vocab.d.ts +2 -2
- package/dist/core/safety-axis-vocab.js +1 -1
- package/dist/core/secret-env.d.ts +30 -58
- package/dist/core/secret-env.js +149 -586
- package/dist/core/sensitive-path-policy.d.ts +4 -2
- package/dist/core/sensitive-path-policy.js +41 -31
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -1
- package/dist/core/task-event.d.ts +1 -1
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +41 -89
- package/dist/core/task-stream.d.ts +16 -0
- package/dist/core/terminal-cause.js +1 -0
- package/dist/core/tighten-task-spec.d.ts +2 -2
- package/dist/core/tighten-task-spec.js +12 -14
- package/dist/core/tool-catalog-entries.js +4 -3
- package/dist/core/tool-policy.d.ts +84 -30
- package/dist/core/tool-policy.js +76 -16
- package/dist/core/tool-registry.d.ts +38 -0
- package/dist/core/tool-registry.js +25 -1
- package/dist/core/tool-roster.d.ts +17 -2
- package/dist/core/tool-roster.js +11 -1
- package/dist/core/tool-spec.d.ts +8 -9
- package/dist/core/trace.d.ts +18 -6
- package/dist/core/types.d.ts +2 -1
- package/dist/core/wiring-manifest.d.ts +31 -5
- package/dist/core/wiring-manifest.js +28 -1
- package/dist/core/write-protect.d.ts +11 -1
- package/dist/core/write-protect.js +26 -15
- package/dist/engine/execution-env/node-execution-env.d.ts +6 -1
- package/dist/engine/execution-env/node-execution-env.js +16 -3
- package/dist/engine/harness/agent-harness.js +2 -1
- package/dist/engine/harness/types.d.ts +6 -7
- package/dist/engine/llm/types.d.ts +2 -4
- package/dist/engine/loop/types.d.ts +7 -0
- package/dist/engine/lsp/node-lsp-manager.js +1 -1
- package/dist/index.d.ts +18 -8
- package/dist/index.js +15 -6
- package/dist/orchestration/workflow-governance.js +1 -1
- package/dist/orchestration/workflow-primitives.d.ts +1 -1
- package/dist/orchestration/workflow-size-guideline.js +2 -2
- package/dist/stores/file/index.d.ts +5 -3
- package/dist/stores/file/session-store.d.ts +8 -2
- package/dist/stores/file/session-store.js +5 -5
- package/dist/tools/artifact/artifact-tool.d.ts +17 -0
- package/dist/tools/artifact/artifact-tool.js +16 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +28 -7
- package/dist/tools/fs/bash-readonly-classifier.js +88 -0
- package/dist/tools/fs/containment-face.d.ts +9 -3
- package/dist/tools/fs/containment-face.js +2 -0
- package/dist/tools/fs/file-read-check.d.ts +50 -0
- package/dist/tools/fs/file-read-check.js +79 -0
- package/dist/tools/fs/fs-bash.d.ts +3 -3
- package/dist/tools/fs/fs-read.d.ts +1 -0
- package/dist/tools/fs/fs-search-tools.d.ts +2 -2
- package/dist/tools/fs/fs-search-tools.js +4 -4
- package/dist/tools/fs/fs-shared.js +20 -6
- package/dist/tools/fs/fs-write.d.ts +4 -3
- package/dist/tools/fs/fs-write.js +9 -9
- package/dist/tools/fs/index.d.ts +39 -3
- package/dist/tools/fs/index.js +38 -15
- package/dist/tools/fs/read-deny-home.d.ts +93 -0
- package/dist/tools/fs/read-deny-home.js +225 -0
- package/dist/tools/fs/read-deny.d.ts +176 -34
- package/dist/tools/fs/read-deny.js +268 -61
- package/dist/tools/fs/safety.d.ts +6 -1
- package/dist/tools/fs/safety.js +18 -5
- package/dist/tools/fs/search.d.ts +3 -0
- package/dist/tools/fs/search.js +47 -9
- package/dist/tools/fs/shell-read-check.d.ts +52 -13
- package/dist/tools/fs/shell-read-check.js +199 -20
- package/dist/tools/fs/shell-read-establish.d.ts +7 -3
- package/dist/tools/fs/shell-read-establish.js +134 -21
- package/dist/tools/fs/shell-read-identity.d.ts +7 -0
- package/dist/tools/fs/shell-read-identity.js +21 -0
- package/dist/tools/fs/shell-read-judge.d.ts +92 -32
- package/dist/tools/fs/shell-read-judge.js +43 -18
- package/dist/tools/fs/shell-read-plan.d.ts +4 -0
- package/dist/tools/fs/shell-read-plan.js +26 -4
- package/dist/tools/fs/shell-read-write-targets.d.ts +73 -0
- package/dist/tools/fs/shell-read-write-targets.js +173 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +117 -11
- package/wire-changes/core@7.34.0.json +2621 -0
- package/wire-changes/core@7.35.0.json +584 -0
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AskOrigin, type AssertAllKeysHandled } from "./ask-origin.js";
|
|
2
2
|
import { type ReadRootGrantScreen } from "./read-root-grant-screen.js";
|
|
3
3
|
import { type Settlement } from "./gate-outcome.js";
|
|
4
4
|
/** A tool call presented to a policy before it executes. */
|
|
5
5
|
/** design/388 B6 — the per-call face (see {@link ToolCallRequest.face}). */
|
|
6
6
|
export interface ToolCallFace {
|
|
7
|
+
/** Every name a tool-name rule may spell this tool by: the tool's wire name first, then each alias that resolves to
|
|
8
|
+
* it on the leg's roster (`rosterToolName`). A rule reaches the call iff its tool name is on this list — read it
|
|
9
|
+
* through {@link ruleNamesOf}, never off `toolName` alone. Absent on a face rebuilt from a park row that predates it
|
|
10
|
+
* (the call's own name is then the list). */
|
|
11
|
+
names?: readonly string[];
|
|
7
12
|
family: import("./tool-face.js").ToolFamily;
|
|
8
13
|
pathTarget?: import("./tool-face.js").ToolRosterPathTarget;
|
|
9
14
|
capabilityId: string;
|
|
@@ -12,6 +17,8 @@ export interface ToolCallFace {
|
|
|
12
17
|
implementationRevision: string;
|
|
13
18
|
}
|
|
14
19
|
export interface ToolCallRequest {
|
|
20
|
+
/** The tool's wire name — the Runner's gate resolves a call made under an alias to the wire name of the tool it
|
|
21
|
+
* dispatches to before any policy runs (`rosterToolName`); the names a rule may use for it are {@link ruleNamesOf}. */
|
|
15
22
|
toolName: string;
|
|
16
23
|
args: unknown;
|
|
17
24
|
toolCallId: string;
|
|
@@ -325,9 +332,8 @@ export type PermissionResult = {
|
|
|
325
332
|
/** This ask is MANDATED — no persisted allow rule and no STANDING ANSWER (a release remembered from an
|
|
326
333
|
* earlier call, at any scope, a session included) may retire it; only an answer produced for THIS call
|
|
327
334
|
* clears it — a person, a hook, a deployment's blanket `onAsk: "allow"` seat, or the live deciders the
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
* blanket seat. ENGINE-STAMPED at the gate's single origin stamp from the ONE derivation
|
|
335
|
+
* ask's class row admits ({@link import("./ask-origin.js").MODE_ARBITRATION}); {@link requiresRealApproval}
|
|
336
|
+
* is the stronger demand that also refuses the blanket seat. ENGINE-STAMPED at the gate's single origin stamp from the ONE derivation
|
|
331
337
|
* the approval card's rule-offer word also reads ({@link import("./ask-origin.js").ruleClearanceOf},
|
|
332
338
|
* which lists the doors). TIGHTENING-ONLY: a layer below the mint may not clear one a layer above
|
|
333
339
|
* set, and a policy self-declaring it only opts its own ask OUT of automatic answering. Carried
|
|
@@ -376,19 +382,34 @@ export type PermissionResult = {
|
|
|
376
382
|
* fallback / the reserved question tool / a marked call / plain policy). ENGINE-STAMPED inside the
|
|
377
383
|
* gate at the same single stamp point as `ruleEvidence`, derived from what the gate's own layers
|
|
378
384
|
* did ({@link import("./ask-origin.js").askOriginOf}) — a policy that self-declares it is overwritten
|
|
379
|
-
* there, so the member is never a channel a policy can loosen through.
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
* decision is caller-composable; every ask the gate hands to its resolver carries it. */
|
|
385
|
+
* there, so the member is never a channel a policy can loosen through. A DISCLOSURE word: the lanes
|
|
386
|
+
* read the ask's class set ({@link import("./ask-origin.js").classifyAsk}), an approval surface may
|
|
387
|
+
* render the card by it. Optional on the TYPE only because a decision is caller-composable; every ask
|
|
388
|
+
* the gate hands to its resolver carries it. */
|
|
384
389
|
origin?: import("./ask-origin.js").AskOrigin;
|
|
385
|
-
/**
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
|
|
391
|
-
|
|
390
|
+
/** WHICH integrity guard minted this ask — the unverifiable recursive delete or the session-transcript
|
|
391
|
+
* write. ENGINE-STAMPED by the two guard policies; a fold-internal fact the class derivation reads
|
|
392
|
+
* ({@link import("./ask-origin.js").classifyAsk}) so the two guards, which mint the same
|
|
393
|
+
* `requiresRealApproval` ask, are told apart by a fact and not by their prose. Never copied onto the
|
|
394
|
+
* approval request or the park row. */
|
|
395
|
+
guard?: "unverifiable_delete" | "transcript_write" | "artifact_shared_read";
|
|
396
|
+
/** The call could not be READ against a rule that may reach it — a content-form deny row, a content-form
|
|
397
|
+
* ask row, or a persisted-rule row (the lexer's word). ENGINE-STAMPED at the three unreadable mint sites;
|
|
398
|
+
* the class derivation reads it as `rule_unreadable` (a mode may release it, a person may clear it, no
|
|
399
|
+
* automatic lane may). It replaces the former practice of stamping `matchedAskRule` on an unreadable
|
|
400
|
+
* read: a rule that COULD NOT be matched is not a rule that matched. Fold-internal; not on the request. */
|
|
401
|
+
ruleUnreadable?: "content_deny" | "content_ask" | "persisted_call";
|
|
402
|
+
/** The ask was authored by the DEPLOYMENT's own governance (an operator's must-ask list, a managed command
|
|
403
|
+
* policy's default ask) — a fact a host's policy states on its own ask so the class derivation reads it
|
|
404
|
+
* as `operator_forced`: a permission mode releases nothing an operator mandated, and `dontAsk` does not
|
|
405
|
+
* settle it. Public: a host composing such a policy sets it; {@link combinePolicies} carries it onto the
|
|
406
|
+
* surviving ask (tighten-only). Not on the approval request — the card shows `origin` / `mandate`. */
|
|
407
|
+
authoredBy?: "deployment";
|
|
408
|
+
/** The classes of the OTHER asks a policy fold collapsed into this one
|
|
409
|
+
* ({@link import("./ask-origin.js").decisionAskClassesOf} per contributing ask, unioned), so a member the
|
|
410
|
+
* fold does not copy — a guard mark, an unreadable-rule fact, a bare real-approval bit — is not lost behind
|
|
411
|
+
* the ask that won the slot. ENGINE-WRITTEN by {@link combinePolicies}; fold-internal, never on the request. */
|
|
412
|
+
foldedAskClasses?: readonly import("./ask-origin.js").AskClass[];
|
|
392
413
|
} | {
|
|
393
414
|
action: "deny";
|
|
394
415
|
updatedInput?: unknown;
|
|
@@ -495,7 +516,44 @@ export interface ToolPolicy {
|
|
|
495
516
|
* describes the policy must read that bit, not the field's presence.
|
|
496
517
|
*/
|
|
497
518
|
readonly projection?: ToolPolicyProjection;
|
|
519
|
+
/** The policy's path DENY rules that speak for a face of the engine's deny table (a `Read(<pattern>)` / `Edit(<pattern>)`
|
|
520
|
+
* deny compiled by `createPermissionRulePolicy`), as rows the prepared leg joins to its read and write tables — so the
|
|
521
|
+
* shell's reads and writes and the traversals meet the rule, not only the tool it names. Carried through
|
|
522
|
+
* {@link combinePolicies}; read with {@link toolPolicyPathDenyRules}. Deny-only: a row can only refuse more. */
|
|
523
|
+
readonly pathDenyRules?: readonly import("../tools/fs/read-deny.js").PathRuleDenyRow[];
|
|
524
|
+
/** Does this policy guard a write to this ABSOLUTE target — would it refuse the write if a write tool made it? The
|
|
525
|
+
* policy's answer for a writer it cannot see as a call (the shell's file commands), read by the shell station's
|
|
526
|
+
* `writesPlaced` fact alone: a guarded target is not placed, so no permission mode answers the command and its ask
|
|
527
|
+
* reaches a person — it is not refused. A PURE judgment: no side effect, no audit record, no hook, no approver; `true`
|
|
528
|
+
* on a target it cannot judge. Carried through {@link combinePolicies} (any member's `true` is the answer). */
|
|
529
|
+
readonly writeTargetGuard?: (target: string, signal?: AbortSignal) => Promise<boolean>;
|
|
530
|
+
/** The raw tool-name lists a name-keyed policy was built from — the prepare-time policy-name audit's feed (a retired
|
|
531
|
+
* name refuses the run, an unmounted one is advised). Carried through {@link combinePolicies} (the union of the
|
|
532
|
+
* members' groups); read with {@link toolPolicyNameSets}. */
|
|
533
|
+
readonly nameSets?: readonly ToolPolicyNameSets[];
|
|
498
534
|
}
|
|
535
|
+
/**
|
|
536
|
+
* The members a policy carries beside `check` — one table for every seat that carries them: {@link wrapToolPolicy} reads
|
|
537
|
+
* each by name off the wrapped policy, {@link combinePolicies} folds each. The fence makes a new {@link ToolPolicy} member
|
|
538
|
+
* a compile error until the table names it.
|
|
539
|
+
*/
|
|
540
|
+
declare const TOOL_POLICY_CARRIED_MEMBERS: readonly ["projection", "pathDenyRules", "writeTargetGuard", "nameSets"];
|
|
541
|
+
type ToolPolicyCarriedMember = (typeof TOOL_POLICY_CARRIED_MEMBERS)[number];
|
|
542
|
+
export type ToolPolicyCarriesEveryMember = AssertAllKeysHandled<Exclude<Exclude<keyof ToolPolicy, "check">, ToolPolicyCarriedMember>>;
|
|
543
|
+
/**
|
|
544
|
+
* The ONE way to wrap a policy: the wrapper decides through `check`; every member {@link ToolPolicy} declares beside it
|
|
545
|
+
* (projection, name sets, path deny rows, write-target guard — the carried-member table) rides as `inner` holds it, read
|
|
546
|
+
* by name, so a class instance's accessor or a non-enumerable member rides too — a `{ check }` literal drops them, and
|
|
547
|
+
* engine stations the wrapper never sees read each one. The carried members describe what `inner` refuses, so the wrapper
|
|
548
|
+
* must refuse at least that (an observer, a tightener); one that releases what `inner` refuses is a policy of its own.
|
|
549
|
+
* The ask-class brand is not carried: it attests the replaced `check`.
|
|
550
|
+
*/
|
|
551
|
+
export declare function wrapToolPolicy<P extends ToolPolicy>(inner: P, check: ToolPolicy["check"]): Pick<P, ToolPolicyCarriedMember> & ToolPolicy;
|
|
552
|
+
/** Every path deny row the given policies carry (combined policies included), in order — the deny tables' rule rows. */
|
|
553
|
+
export declare function toolPolicyPathDenyRules(policies: readonly (ToolPolicy | undefined)[]): readonly import("../tools/fs/read-deny.js").PathRuleDenyRow[];
|
|
554
|
+
/** The one write-target guard the given policies carry together (combined policies included): `true` when any member
|
|
555
|
+
* guards the target, a member that throws counting as guarding it. `undefined` when none carries one. */
|
|
556
|
+
export declare function toolPolicyWriteTargetGuard(policies: readonly (ToolPolicy | undefined)[]): ToolPolicy["writeTargetGuard"];
|
|
499
557
|
/**
|
|
500
558
|
* One deterministic DENY component of a {@link ToolPolicyProjection} (F-012 L1). All fields are plain
|
|
501
559
|
* serializable data — a component round-trips JSON/`structuredClone` unchanged, which is what lets a
|
|
@@ -574,10 +632,7 @@ export declare function namespacedCoveringHit(covering: readonly string[], toolN
|
|
|
574
632
|
* trusting a re-supplied policy's execution half for the frozen deny data: the projection persisted
|
|
575
633
|
* at suspend is the authority for what the chain refused, whatever a re-supplied closure claims.
|
|
576
634
|
*/
|
|
577
|
-
export declare function checkToolPolicyProjection(projection: Pick<ToolPolicyProjection, "components">, req: {
|
|
578
|
-
toolName: string;
|
|
579
|
-
args: unknown;
|
|
580
|
-
}): Extract<PermissionResult, {
|
|
635
|
+
export declare function checkToolPolicyProjection(projection: Pick<ToolPolicyProjection, "components">, req: Pick<ToolCallRequest, "toolName" | "args" | "face">): Extract<PermissionResult, {
|
|
581
636
|
action: "deny";
|
|
582
637
|
}> | undefined;
|
|
583
638
|
/**
|
|
@@ -751,7 +806,7 @@ export interface ToolPolicyNameSets {
|
|
|
751
806
|
* here silently WIDENS (the tool runs without ever asking), so it is audited too. */
|
|
752
807
|
readonly ask?: readonly string[];
|
|
753
808
|
}
|
|
754
|
-
/** A policy that
|
|
809
|
+
/** A policy that states its name lists ({@link ToolPolicy.nameSets} set). Composition-transparent:
|
|
755
810
|
* {@link combinePolicies} (and therefore {@link tightenTaskSpec}) unions children's groups onto the
|
|
756
811
|
* combined policy, so metadata survives the deployment's tighten/compose path. */
|
|
757
812
|
export type NamedToolPolicy = ToolPolicy & {
|
|
@@ -910,10 +965,10 @@ export declare const COARSE_SHELL_TOOLS: string[];
|
|
|
910
965
|
* you MUST compose explicitly: `combinePolicies(deploymentBaseline, createCoarseCommandNamePolicy(…))`
|
|
911
966
|
* (deny-wins — both policies' denies still fire). {@link tightenTaskSpec} does this for you.
|
|
912
967
|
*
|
|
913
|
-
*
|
|
914
|
-
* `"maybe"` tier is the
|
|
968
|
+
* On the full shell face a plain `ask` from this policy stays a plain ask: the shell seat's
|
|
969
|
+
* `"maybe"` tier is the shape probe's instrument, not a risk label, so the park is the budgetable kind
|
|
915
970
|
* (`human` under `durableApproval`) unless the probe mandated the call (an out-of-root read) or could not
|
|
916
|
-
* answer. A command the
|
|
971
|
+
* answer. A command the shape tier's OWN tighten refuses (`rm -rf …` with no policy asking first) still parks
|
|
917
972
|
* non-budgetable — the safety axis is judged per call by `safetyAxisOf`.
|
|
918
973
|
*/
|
|
919
974
|
export declare function createCoarseCommandNamePolicy(opts: {
|
|
@@ -1299,6 +1354,11 @@ export interface AskRequest {
|
|
|
1299
1354
|
* gate, never caller/worker-settable; tightening-only.
|
|
1300
1355
|
*/
|
|
1301
1356
|
readonly mandated?: true;
|
|
1357
|
+
/** The ask was minted by the deployment's own governance layer (the {@link PermissionResult} ask arm's member of the same
|
|
1358
|
+
* name, carried through every request mint and onto the park row): a person's question under every mode — no persisted
|
|
1359
|
+
* rule, standing answer or mode release retires it. A host approval seat that settles asks from a session grant reads
|
|
1360
|
+
* it to keep its hands off. Filled by the gate, never caller/worker-settable. */
|
|
1361
|
+
readonly authoredBy?: "deployment";
|
|
1302
1362
|
/** WHICH word of the closed {@link import("./ask-origin.js").PERSISTED_RULE_MANDATES} set {@link mandated} stands
|
|
1303
1363
|
* on — present ⇔ that table word is the bit's WHOLE reason (`probe_mandate` = a read outside the session's
|
|
1304
1364
|
* directories). ABSENT beside `mandated: true` = another door holds too (governance, a hook, an ancestor's
|
|
@@ -1317,12 +1377,6 @@ export interface AskRequest {
|
|
|
1317
1377
|
* that forwards a decision's member here does not compile, which is the point: the window is the
|
|
1318
1378
|
* route's fact, and forwarding it silently is how a configured window becomes a wait with none. */
|
|
1319
1379
|
readonly denialLimitFallback?: import("./auto-mode.js").DenialLimitFallback;
|
|
1320
|
-
/** #688 C3 (additive) — the wire twin of the {@link PermissionResult} ask-arm member of the same name: which
|
|
1321
|
-
* mechanism stands behind an `origin: "rule_store_unavailable"` (`store` / `call`,
|
|
1322
|
-
* {@link import("./ask-origin.js").RULE_STORE_UNREADABLE_KINDS}). Present ⇔ that origin; a consumer that
|
|
1323
|
-
* renders "the rule store is unreachable" vs "this command could not be read against your rules" branches
|
|
1324
|
-
* here, never on the message text. Filled by the gate's own carry station from the engine-stamped decision. */
|
|
1325
|
-
readonly ruleStoreUnreadable?: import("./ask-origin.js").RuleStoreUnreadable;
|
|
1326
1380
|
/** (additive) WHICH AUTHORITY raised this ask — the wire twin of the {@link PermissionResult} ask-arm
|
|
1327
1381
|
* member of the same name, one word from the closed {@link import("./ask-origin.js").AskOrigin} set.
|
|
1328
1382
|
* The gate's own mint station copies the engine-stamped word; the three inherited-lane stations
|
package/dist/core/tool-policy.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { brandPolicyAskClass } from "./ask-class.js";
|
|
4
|
+
import { decisionAskClassesOf, headlessDenialMessage } from "./ask-origin.js";
|
|
4
5
|
import { ownReadRootCandidate, readRootGrantSeat } from "./read-root-grant-screen.js";
|
|
5
6
|
import { decidersNote, settledByPerson } from "./gate-outcome.js";
|
|
6
7
|
import { sanitizeAutoModeArmingRecipe } from "./auto-mode-arming.js";
|
|
7
8
|
import { join, normalize as normalizePath, sep } from "node:path";
|
|
8
9
|
import { BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName } from "../tools/fs/index.js";
|
|
9
10
|
import { protectivePathTargetOf } from "./tool-registry.js";
|
|
11
|
+
import { ruleNamesOf, toolNameSetHas } from "./tool-roster.js";
|
|
10
12
|
import { boundInputHashOf } from "./canonical-json.js";
|
|
11
13
|
import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY } from "./untrusted-text.js";
|
|
12
14
|
import { describeRejectedValue } from "./tool-search-setting.js";
|
|
@@ -121,6 +123,41 @@ export const ASK_EVIDENCE_ABSENCE_VALUES = ["not_wired", "not_adjudicated", "una
|
|
|
121
123
|
export function decisionText(d) {
|
|
122
124
|
return d.message;
|
|
123
125
|
}
|
|
126
|
+
const TOOL_POLICY_CARRIED_MEMBERS = ["projection", "pathDenyRules", "writeTargetGuard", "nameSets"];
|
|
127
|
+
function carriedMembersOf(p) {
|
|
128
|
+
const out = {};
|
|
129
|
+
const carry = (k) => {
|
|
130
|
+
const v = p[k];
|
|
131
|
+
if (v !== undefined)
|
|
132
|
+
out[k] = v;
|
|
133
|
+
};
|
|
134
|
+
for (const k of TOOL_POLICY_CARRIED_MEMBERS)
|
|
135
|
+
carry(k);
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
export function wrapToolPolicy(inner, check) {
|
|
139
|
+
return { ...carriedMembersOf(inner), check };
|
|
140
|
+
}
|
|
141
|
+
export function toolPolicyPathDenyRules(policies) {
|
|
142
|
+
return policies.flatMap((p) => (Array.isArray(p?.pathDenyRules) ? p.pathDenyRules : []));
|
|
143
|
+
}
|
|
144
|
+
export function toolPolicyWriteTargetGuard(policies) {
|
|
145
|
+
const guards = policies.flatMap((p) => (typeof p?.writeTargetGuard === "function" ? [p.writeTargetGuard] : []));
|
|
146
|
+
if (guards.length === 0)
|
|
147
|
+
return undefined;
|
|
148
|
+
return async (target, signal) => {
|
|
149
|
+
for (const guard of guards) {
|
|
150
|
+
try {
|
|
151
|
+
if ((await guard(target, signal)) !== false)
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return false;
|
|
159
|
+
};
|
|
160
|
+
}
|
|
124
161
|
export function namespacedCoveringEntries(entries) {
|
|
125
162
|
return (entries ?? []).filter(isNamespacedCoveringRuleName);
|
|
126
163
|
}
|
|
@@ -128,20 +165,21 @@ export function namespacedCoveringHit(covering, toolName) {
|
|
|
128
165
|
return covering.length > 0 && covering.some((e) => namespacedRuleNameCovers(e, toolName));
|
|
129
166
|
}
|
|
130
167
|
export function checkToolPolicyProjection(projection, req) {
|
|
168
|
+
const names = ruleNamesOf(req);
|
|
131
169
|
for (const c of projection.components) {
|
|
132
170
|
if (c.kind === "tool_deny") {
|
|
133
|
-
if (c.names.includes(
|
|
171
|
+
if (names.some((n) => c.names.includes(n)) || namespacedCoveringHit(namespacedCoveringEntries(c.names), req.toolName)) {
|
|
134
172
|
return { action: "deny", message: `tool "${req.toolName}" is denied by a frozen inherited policy projection` };
|
|
135
173
|
}
|
|
136
174
|
continue;
|
|
137
175
|
}
|
|
138
176
|
if (c.kind === "tool_allowlist") {
|
|
139
|
-
if (!c.names.includes(
|
|
177
|
+
if (!names.some((n) => c.names.includes(n)) && !namespacedCoveringHit(namespacedCoveringEntries(c.names), req.toolName)) {
|
|
140
178
|
return { action: "deny", message: `tool "${req.toolName}" is not in a frozen inherited policy projection's allowlist` };
|
|
141
179
|
}
|
|
142
180
|
continue;
|
|
143
181
|
}
|
|
144
|
-
if (!c.tools.includes(
|
|
182
|
+
if (!names.some((n) => c.tools.includes(n)))
|
|
145
183
|
continue;
|
|
146
184
|
const command = req.args?.command;
|
|
147
185
|
if (typeof command !== "string") {
|
|
@@ -350,10 +388,10 @@ export function createAllowDenyPolicy(opts) {
|
|
|
350
388
|
nameSets: [{ ...(opts.allow ? { allow: [...opts.allow] } : {}), ...(opts.deny ? { deny: [...opts.deny] } : {}) }],
|
|
351
389
|
check(req) {
|
|
352
390
|
const toolName = req.toolName;
|
|
353
|
-
if (deny
|
|
391
|
+
if (toolNameSetHas(deny, req) || namespacedCoveringHit(denyCovering, toolName)) {
|
|
354
392
|
return { action: "deny", message: `tool "${req.toolName}" is denied by policy` };
|
|
355
393
|
}
|
|
356
|
-
if (allow && !allow
|
|
394
|
+
if (allow && !toolNameSetHas(allow, req) && !namespacedCoveringHit(allowCovering, toolName)) {
|
|
357
395
|
return { action: "deny", message: `tool "${req.toolName}" is not in the allowlist` };
|
|
358
396
|
}
|
|
359
397
|
return ALLOW;
|
|
@@ -390,10 +428,10 @@ export function createApprovalPolicy(opts) {
|
|
|
390
428
|
],
|
|
391
429
|
async check(req, signal) {
|
|
392
430
|
const toolName = req.toolName;
|
|
393
|
-
if (deny
|
|
431
|
+
if (toolNameSetHas(deny, req) || namespacedCoveringHit(denyCovering, toolName)) {
|
|
394
432
|
return { action: "deny", message: `tool "${req.toolName}" is denied by policy` };
|
|
395
433
|
}
|
|
396
|
-
if (need
|
|
434
|
+
if (toolNameSetHas(need, req) || namespacedCoveringHit(needCovering, toolName)) {
|
|
397
435
|
const attest = (d, settlement) => attestEngineSettlement(d, { settlement, origin: "policy" }, req);
|
|
398
436
|
if (signal?.aborted) {
|
|
399
437
|
return attest({ action: "deny", message: `approval aborted for "${req.toolName}" (run or turn interrupted)` }, settledByNobody("task_aborted"));
|
|
@@ -443,7 +481,7 @@ export function createApprovalPolicy(opts) {
|
|
|
443
481
|
}
|
|
444
482
|
return attest({ action: "deny", message: `approval denied for "${req.toolName}"` }, settledByPerson("human_refused", undefined));
|
|
445
483
|
}
|
|
446
|
-
if (opts.denyByDefault && !auto
|
|
484
|
+
if (opts.denyByDefault && !toolNameSetHas(auto, req) && !namespacedCoveringHit(autoCovering, toolName)) {
|
|
447
485
|
return { action: "deny", message: `tool "${req.toolName}" requires explicit allow` };
|
|
448
486
|
}
|
|
449
487
|
return ALLOW;
|
|
@@ -452,6 +490,8 @@ export function createApprovalPolicy(opts) {
|
|
|
452
490
|
}
|
|
453
491
|
export function combinePolicies(...policies) {
|
|
454
492
|
const nameSets = policies.flatMap(toolPolicyNameSets);
|
|
493
|
+
const pathDenyRules = toolPolicyPathDenyRules(policies);
|
|
494
|
+
const writeTargetGuard = toolPolicyWriteTargetGuard(policies);
|
|
455
495
|
const childProjections = policies.map((p) => p.projection);
|
|
456
496
|
const projectedComponents = [];
|
|
457
497
|
let combinedRemainder = false;
|
|
@@ -469,9 +509,14 @@ export function combinePolicies(...policies) {
|
|
|
469
509
|
const combinedProjection = childProjections.some((p) => p !== undefined)
|
|
470
510
|
? { components: projectedComponents, requiresLiveRemainder: combinedRemainder }
|
|
471
511
|
: undefined;
|
|
512
|
+
const folded = {
|
|
513
|
+
projection: combinedProjection,
|
|
514
|
+
nameSets: nameSets.length > 0 ? nameSets : undefined,
|
|
515
|
+
pathDenyRules: pathDenyRules.length > 0 ? pathDenyRules : undefined,
|
|
516
|
+
writeTargetGuard,
|
|
517
|
+
};
|
|
472
518
|
return {
|
|
473
|
-
...(
|
|
474
|
-
...(nameSets.length > 0 ? { nameSets } : {}),
|
|
519
|
+
...carriedMembersOf(folded),
|
|
475
520
|
async check(req, signal) {
|
|
476
521
|
let asked;
|
|
477
522
|
let current = req;
|
|
@@ -482,6 +527,8 @@ export function combinePolicies(...policies) {
|
|
|
482
527
|
let probeMandateSeen = false;
|
|
483
528
|
let mandateSeen = false;
|
|
484
529
|
let fallbackSeen;
|
|
530
|
+
let authoredBySeen = false;
|
|
531
|
+
const contributors = [];
|
|
485
532
|
for (const p of policies) {
|
|
486
533
|
const d = refuseOutOfContractDecision(await p.check(current, signal));
|
|
487
534
|
if (d.action === "deny") {
|
|
@@ -516,6 +563,10 @@ export function combinePolicies(...policies) {
|
|
|
516
563
|
mandateSeen = true;
|
|
517
564
|
if (d.action === "ask" && d.denialLimitFallback !== undefined && fallbackSeen === undefined)
|
|
518
565
|
fallbackSeen = d.denialLimitFallback;
|
|
566
|
+
if (d.action === "ask" && d.authoredBy === "deployment")
|
|
567
|
+
authoredBySeen = true;
|
|
568
|
+
if (d.action === "ask")
|
|
569
|
+
contributors.push(d);
|
|
519
570
|
}
|
|
520
571
|
if (asked) {
|
|
521
572
|
const merged = rewrite?.updatedInput;
|
|
@@ -523,7 +574,14 @@ export function combinePolicies(...policies) {
|
|
|
523
574
|
const withMark = probeMandateSeen && withRuleAsk.probeMandated !== true ? { ...withRuleAsk, probeMandated: true } : withRuleAsk;
|
|
524
575
|
const withMandate = mandateSeen && withMark.mandated !== true ? { ...withMark, mandated: true } : withMark;
|
|
525
576
|
const withFallback = fallbackSeen !== undefined && withMandate.denialLimitFallback === undefined ? { ...withMandate, denialLimitFallback: fallbackSeen, requiresRealApproval: true } : withMandate;
|
|
526
|
-
|
|
577
|
+
const withAuthor = authoredBySeen && withFallback.authoredBy === undefined ? { ...withFallback, authoredBy: "deployment" } : withFallback;
|
|
578
|
+
const folded = new Set(withAuthor.foldedAskClasses ?? []);
|
|
579
|
+
for (const c of contributors)
|
|
580
|
+
if (c !== asked)
|
|
581
|
+
for (const k of decisionAskClassesOf(c))
|
|
582
|
+
folded.add(k);
|
|
583
|
+
const withClasses = folded.size > 0 ? { ...withAuthor, foldedAskClasses: [...folded] } : withAuthor;
|
|
584
|
+
return merged !== undefined ? { ...withClasses, updatedInput: merged } : withClasses;
|
|
527
585
|
}
|
|
528
586
|
const allowed = rewrite ?? ALLOW;
|
|
529
587
|
if (!anyRecord)
|
|
@@ -563,7 +621,7 @@ export function createCoarseCommandNamePolicy(opts) {
|
|
|
563
621
|
return brandPolicyAskClass({
|
|
564
622
|
projection,
|
|
565
623
|
check(req) {
|
|
566
|
-
if (!shellTools
|
|
624
|
+
if (!toolNameSetHas(shellTools, req))
|
|
567
625
|
return ALLOW;
|
|
568
626
|
const command = req.args?.command;
|
|
569
627
|
if (typeof command !== "string") {
|
|
@@ -817,7 +875,7 @@ export function createUnverifiableDeletePolicy(opts) {
|
|
|
817
875
|
const shellTools = canonicalToolNameSet(opts?.tools);
|
|
818
876
|
return brandPolicyAskClass({
|
|
819
877
|
check(req) {
|
|
820
|
-
if (!shellTools
|
|
878
|
+
if (!toolNameSetHas(shellTools, req))
|
|
821
879
|
return ALLOW;
|
|
822
880
|
const command = req.args?.command;
|
|
823
881
|
if (typeof command !== "string")
|
|
@@ -829,6 +887,7 @@ export function createUnverifiableDeletePolicy(opts) {
|
|
|
829
887
|
action: "ask",
|
|
830
888
|
decisionReason: "safety",
|
|
831
889
|
requiresRealApproval: true,
|
|
890
|
+
guard: "unverifiable_delete",
|
|
832
891
|
message: `Unverifiable recursive delete (fail-closed unless cleared): ${finding}. ` +
|
|
833
892
|
`Re-run the delete with the resolved literal path written into the command itself ` +
|
|
834
893
|
`(or assign the variable in the same command, e.g. \`DIR=/exact/path; rm -rf "$DIR"\`) so the target can be verified.`,
|
|
@@ -905,6 +964,7 @@ export function createTranscriptIntegrityPolicy(opts) {
|
|
|
905
964
|
action: "ask",
|
|
906
965
|
decisionReason: "safety",
|
|
907
966
|
requiresRealApproval: true,
|
|
967
|
+
guard: "transcript_write",
|
|
908
968
|
message: `Session-transcript write (fail-closed unless cleared): ${what}. Session transcripts (the .jsonl files ` +
|
|
909
969
|
`under the agent data dir's sessions/ directory) are harness-written session state, not agent working ` +
|
|
910
970
|
`files — modifying or deleting them tampers with the run's own audit trail. Reading them (ls/cat/grep ` +
|
|
@@ -912,8 +972,7 @@ export function createTranscriptIntegrityPolicy(opts) {
|
|
|
912
972
|
});
|
|
913
973
|
return brandPolicyAskClass({
|
|
914
974
|
check(req) {
|
|
915
|
-
|
|
916
|
-
if (shellTools.has(toolName)) {
|
|
975
|
+
if (toolNameSetHas(shellTools, req)) {
|
|
917
976
|
const command = req.args?.command;
|
|
918
977
|
if (typeof command !== "string")
|
|
919
978
|
return ALLOW;
|
|
@@ -956,6 +1015,7 @@ export function isLiveApproverSeat(onAsk) {
|
|
|
956
1015
|
return typeof onAsk === "function";
|
|
957
1016
|
}
|
|
958
1017
|
export const DURABLE_MANDATE_SOURCES = ["force_durable_gate", "durable_approval"];
|
|
1018
|
+
Object.freeze(DURABLE_MANDATE_SOURCES);
|
|
959
1019
|
export function durableMandateSourceOf(args) {
|
|
960
1020
|
if (args.forceDurableGate)
|
|
961
1021
|
return "force_durable_gate";
|
|
@@ -1250,7 +1310,7 @@ async function resolveAskArms(req, onAsk, signal, onLateSettlement) {
|
|
|
1250
1310
|
if (onAsk === undefined || onAsk === "deny") {
|
|
1251
1311
|
return {
|
|
1252
1312
|
action: "deny",
|
|
1253
|
-
message:
|
|
1313
|
+
message: headlessDenialMessage(req.toolName, req.message),
|
|
1254
1314
|
decisionReason: "mode",
|
|
1255
1315
|
settlement: settledByNobody("no_approver"),
|
|
1256
1316
|
};
|
|
@@ -28,6 +28,8 @@ export declare function pathConfinableWriteToolNames(): ReadonlySet<string>;
|
|
|
28
28
|
export declare function skillScopeWriteToolNames(): ReadonlySet<string>;
|
|
29
29
|
/** A-8 — every definition that names a path target at all (read or write): the file-tool glob lane. */
|
|
30
30
|
export declare function pathTargetingToolNames(): ReadonlySet<string>;
|
|
31
|
+
/** The tools whose declared path target is a READ (a file read, a search root): the file read station's membership. */
|
|
32
|
+
export declare function readTargetToolNames(): ReadonlySet<string>;
|
|
31
33
|
/** A-10 — the micro-compaction whitelist (`compactable: true`). */
|
|
32
34
|
export declare function compactableToolNames(): ReadonlySet<string>;
|
|
33
35
|
/** A-5 — the aggregate-budget exemptions (`budgetExempt: true`). */
|
|
@@ -46,9 +48,40 @@ export interface ToolCallLike {
|
|
|
46
48
|
args: unknown;
|
|
47
49
|
face?: {
|
|
48
50
|
pathTarget?: ToolCallPathTarget;
|
|
51
|
+
names?: readonly string[];
|
|
49
52
|
} | undefined;
|
|
50
53
|
}
|
|
51
54
|
type CallPathTarget = ToolCallPathTarget;
|
|
55
|
+
/**
|
|
56
|
+
* The names a tool-name rule may spell this call's tool by — the face's `names` when they include the call's name,
|
|
57
|
+
* else the call's name alone (a bare request, or a face minted for another name). Every name-keyed matcher and the
|
|
58
|
+
* protective path floor read a rule's tool name against THIS list, which is the roster's one canonicalization seen
|
|
59
|
+
* from the call's side: a rule written against an alias and one written against the wire name reach the same calls.
|
|
60
|
+
*/
|
|
61
|
+
export declare function ruleNamesOf(req: Pick<ToolCallLike, "toolName" | "face">): readonly string[];
|
|
62
|
+
/** Does the call answer to `name` — is it one of the call's names ({@link ruleNamesOf})? The engine's TIGHTENS keyed on
|
|
63
|
+
* one of its own tool names read this, so a tool that wears the name under any of its names is tightened (the
|
|
64
|
+
* fail-safe direction); a grant or a routing keyed on an engine tool name reads the wire name. */
|
|
65
|
+
export declare function callAnswersTo(req: Pick<ToolCallLike, "toolName" | "face">, name: string): boolean;
|
|
66
|
+
/** Does a set of rule tool names name this call's tool ({@link ruleNamesOf})? */
|
|
67
|
+
export declare function toolNameSetHas(set: {
|
|
68
|
+
has(name: string): boolean;
|
|
69
|
+
}, req: Pick<ToolCallLike, "toolName" | "face">): boolean;
|
|
70
|
+
/** Does `set` hold any of the call's names ({@link ruleNamesOf})? The read of a MEMBERSHIP tighten keyed on the engine's
|
|
71
|
+
* own tool names (the egress set, a read station's tools, the compliance veto): a tool is held under every name it
|
|
72
|
+
* answers to. A grant or a routing keyed on an engine tool name reads the wire name instead. */
|
|
73
|
+
export declare function anyNamed(set: {
|
|
74
|
+
has(name: string): boolean;
|
|
75
|
+
}, names: readonly string[]): boolean;
|
|
76
|
+
/** The first of the call's names ({@link ruleNamesOf}, the spelling first) that `map` holds an entry for, with the entry.
|
|
77
|
+
* The read of a SEAT keyed on the engine's own tool names (a tier, a mark's sources): every key of one seat is read under
|
|
78
|
+
* the ONE name this returns — a seat is never assembled from two names' entries. */
|
|
79
|
+
export declare function firstNamed<V>(map: {
|
|
80
|
+
get(name: string): V | undefined;
|
|
81
|
+
}, names: readonly string[]): {
|
|
82
|
+
readonly name: string;
|
|
83
|
+
readonly value: V;
|
|
84
|
+
} | undefined;
|
|
52
85
|
/**
|
|
53
86
|
* design/388 B6/B17 — the path target a PROTECTIVE fence reads (a deny-list judging a write's target: sensitive paths,
|
|
54
87
|
* frozen paths, write protection, the transcript gate; edit tracking): the object face's own declaration first, the
|
|
@@ -56,6 +89,11 @@ type CallPathTarget = ToolCallPathTarget;
|
|
|
56
89
|
* face that declares none. Reading the catalog for an undeclared same-name tool can only TIGHTEN a deny-list (it adds
|
|
57
90
|
* a slot to judge — the pre-388 posture, never wider), which is exactly why this floor is wrong for a grant.
|
|
58
91
|
*
|
|
92
|
+
* The floor reads EVERY name the call answers to (`ruleNamesOf`: the face's `names`, else the call's name alone) in
|
|
93
|
+
* the face's order — the name the call was made under first, then the wire name, then the other aliases — and the
|
|
94
|
+
* first name the catalog declares a target for wins: a call made under a catalog name and resolved to a tool that
|
|
95
|
+
* declares none keeps THAT name's slot, also when the tool answers to a second catalog name with another slot.
|
|
96
|
+
*
|
|
59
97
|
* #635: "can only tighten" is true of the SLOT and false of the RESOLUTION, so the floor hands over the first and
|
|
60
98
|
* defaults the second (`pathTargetSlotOnly`). Telling a fence that a FOREIGN tool of the same name resolves its slot
|
|
61
99
|
* against the project root is a claim about someone else's implementation: get it wrong and the judged path moves to
|
|
@@ -31,6 +31,9 @@ export function skillScopeWriteToolNames() {
|
|
|
31
31
|
export function pathTargetingToolNames() {
|
|
32
32
|
return names((e) => e.face.pathTarget !== undefined);
|
|
33
33
|
}
|
|
34
|
+
export function readTargetToolNames() {
|
|
35
|
+
return names((e) => e.face.pathTarget?.access === "read");
|
|
36
|
+
}
|
|
34
37
|
export function compactableToolNames() {
|
|
35
38
|
return names((e) => e.face.compactable === true);
|
|
36
39
|
}
|
|
@@ -48,10 +51,31 @@ export function pathTargetValue(args, pt) {
|
|
|
48
51
|
}
|
|
49
52
|
return undefined;
|
|
50
53
|
}
|
|
54
|
+
export function ruleNamesOf(req) {
|
|
55
|
+
const names = req.face?.names;
|
|
56
|
+
return names !== undefined && names.includes(req.toolName) ? names : [req.toolName];
|
|
57
|
+
}
|
|
58
|
+
export function callAnswersTo(req, name) {
|
|
59
|
+
return ruleNamesOf(req).includes(name);
|
|
60
|
+
}
|
|
61
|
+
export function toolNameSetHas(set, req) {
|
|
62
|
+
return anyNamed(set, ruleNamesOf(req));
|
|
63
|
+
}
|
|
64
|
+
export function anyNamed(set, names) {
|
|
65
|
+
return names.some((n) => set.has(n));
|
|
66
|
+
}
|
|
67
|
+
export function firstNamed(map, names) {
|
|
68
|
+
for (const name of names) {
|
|
69
|
+
const value = map.get(name);
|
|
70
|
+
if (value !== undefined)
|
|
71
|
+
return { name, value };
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
51
75
|
export function protectivePathTargetOf(call) {
|
|
52
76
|
if (call.face?.pathTarget !== undefined)
|
|
53
77
|
return call.face.pathTarget;
|
|
54
|
-
const floor =
|
|
78
|
+
const floor = ruleNamesOf(call).map((n) => pathTargetOf(n)).find((pt) => pt !== undefined);
|
|
55
79
|
return floor === undefined || call.face === undefined ? floor : pathTargetSlotOnly(floor);
|
|
56
80
|
}
|
|
57
81
|
export const BARE_REQUEST_CODE = "policy.bare_request";
|
|
@@ -249,7 +249,19 @@ export type ToolRosterDelta = Static<typeof ToolRosterDelta>;
|
|
|
249
249
|
/** The ONE name→row resolution every per-call reader shares (the call face, the park identity): the wire name first,
|
|
250
250
|
* then an alias — the same order the dispatcher resolves a model's call. */
|
|
251
251
|
export declare function rosterEntryNamed(roster: ToolRoster, toolName: string): ToolRosterEntry | undefined;
|
|
252
|
-
/**
|
|
252
|
+
/**
|
|
253
|
+
* The ONE tool-name canonicalization: the wire name of the row `name` resolves to on the roster ({@link rosterEntryNamed}
|
|
254
|
+
* — the wire name first, then an alias, the dispatcher's order), or `name` itself when no row answers to it. The gate
|
|
255
|
+
* reads a call's name through it before any policy runs, and a rule's tool name reaches a call exactly when it
|
|
256
|
+
* canonicalizes to the same wire name (`ToolCallFace.names` is that set, minted here) — so a rule written against
|
|
257
|
+
* an alias and one written against the wire name are one rule.
|
|
258
|
+
*/
|
|
259
|
+
export declare function rosterToolName(roster: ToolRoster | undefined, name: string): string;
|
|
260
|
+
export { anyNamed, callAnswersTo, firstNamed, ruleNamesOf, toolNameSetHas } from "./tool-registry.js";
|
|
261
|
+
/** design/388 B6 — the per-call face a gated request carries, read off a roster snapshot by name (aliases resolve).
|
|
262
|
+
* `names` leads with the name the face was minted under (the model's spelling at the gate), then the wire name and the
|
|
263
|
+
* other aliases: a reader that must pick ONE of the call's names (the protective path floor) picks the spelling first,
|
|
264
|
+
* as the same call was judged before name resolution. */
|
|
253
265
|
export declare function toolCallFaceOf(roster: ToolRoster | undefined, toolName: string): import("./tool-policy.js").ToolCallFace | undefined;
|
|
254
266
|
/** design/388 B18 — the identity members a park row carries, read off a roster snapshot by name; an empty object when
|
|
255
267
|
* the name is not on the snapshot (the row then reads pre-identity and the resume takes the name-only path). */
|
|
@@ -268,6 +280,7 @@ export declare function parkedToolIdentity(roster: ToolRoster | undefined, toolN
|
|
|
268
280
|
export declare function parkedToolFace(roster: ToolRoster | undefined, toolName: string): {
|
|
269
281
|
family?: ToolFamily;
|
|
270
282
|
pathTarget?: ToolRosterEntry["pathTarget"];
|
|
283
|
+
names?: readonly string[];
|
|
271
284
|
};
|
|
272
285
|
/** The call face a park row carries — present iff the row was minted with BOTH its face members and its four
|
|
273
286
|
* identity members (one mint writes all of them; a row missing any is pre-388 and yields undefined, never a
|
|
@@ -275,6 +288,7 @@ export declare function parkedToolFace(roster: ToolRoster | undefined, toolName:
|
|
|
275
288
|
export declare function parkedCallFaceOf(pending: {
|
|
276
289
|
family?: ToolFamily;
|
|
277
290
|
pathTarget?: ToolRosterPathTarget;
|
|
291
|
+
names?: readonly string[];
|
|
278
292
|
capabilityId?: string;
|
|
279
293
|
contractId?: string;
|
|
280
294
|
shapeDigest?: string;
|
|
@@ -321,7 +335,8 @@ export interface RosterBoundMembers {
|
|
|
321
335
|
cardId?: string;
|
|
322
336
|
}
|
|
323
337
|
/** The typed refusal a member over its bound mints — the door's two codes, and the mint's own for the members no
|
|
324
|
-
* declaration door names (`modelGate`, the derived `cardId`).
|
|
338
|
+
* declaration door names (`modelGate`, the derived `cardId`). Spelled out as literals (a consumer's tooling reads
|
|
339
|
+
* the words); each is a member of {@link ToolDeclarationCode}, which every mint below satisfies. */
|
|
325
340
|
export type RosterBoundCode = "config.tool_name_too_long" | "config.tool_contract_too_long" | "config.tool_roster_bound";
|
|
326
341
|
/**
|
|
327
342
|
* design/388 B20 / #626 ① — the ONE bound judge over a row's identity members: a problem `{ code, message }`, or
|
package/dist/core/tool-roster.js
CHANGED
|
@@ -74,13 +74,22 @@ export const ToolRosterDelta = Type.Object({
|
|
|
74
74
|
export function rosterEntryNamed(roster, toolName) {
|
|
75
75
|
return roster.entries.find((r) => r.name === toolName) ?? roster.entries.find((r) => r.aliases.includes(toolName));
|
|
76
76
|
}
|
|
77
|
+
export function rosterToolName(roster, name) {
|
|
78
|
+
return (roster === undefined ? undefined : rosterEntryNamed(roster, name)?.name) ?? name;
|
|
79
|
+
}
|
|
80
|
+
function namesResolvingTo(roster, e) {
|
|
81
|
+
return [e.name, ...e.aliases.filter((a) => a !== e.name && rosterToolName(roster, a) === e.name)];
|
|
82
|
+
}
|
|
83
|
+
export { anyNamed, callAnswersTo, firstNamed, ruleNamesOf, toolNameSetHas } from "./tool-registry.js";
|
|
77
84
|
export function toolCallFaceOf(roster, toolName) {
|
|
78
85
|
if (roster === undefined)
|
|
79
86
|
return undefined;
|
|
80
87
|
const e = rosterEntryNamed(roster, toolName);
|
|
81
88
|
if (e === undefined)
|
|
82
89
|
return undefined;
|
|
90
|
+
const resolving = namesResolvingTo(roster, e);
|
|
83
91
|
return {
|
|
92
|
+
names: resolving.includes(toolName) ? [toolName, ...resolving.filter((n) => n !== toolName)] : resolving,
|
|
84
93
|
family: e.family,
|
|
85
94
|
...(e.pathTarget !== undefined ? { pathTarget: e.pathTarget } : {}),
|
|
86
95
|
capabilityId: e.capabilityId,
|
|
@@ -103,12 +112,13 @@ export function parkedToolFace(roster, toolName) {
|
|
|
103
112
|
const e = rosterEntryNamed(roster, toolName);
|
|
104
113
|
if (e === undefined)
|
|
105
114
|
return {};
|
|
106
|
-
return { family: e.family, ...(e.pathTarget !== undefined ? { pathTarget: e.pathTarget } : {}) };
|
|
115
|
+
return { family: e.family, ...(e.pathTarget !== undefined ? { pathTarget: e.pathTarget } : {}), names: namesResolvingTo(roster, e) };
|
|
107
116
|
}
|
|
108
117
|
export function parkedCallFaceOf(pending) {
|
|
109
118
|
if (pending.family === undefined || pending.capabilityId === undefined || pending.contractId === undefined || pending.shapeDigest === undefined || pending.implementationRevision === undefined)
|
|
110
119
|
return undefined;
|
|
111
120
|
return {
|
|
121
|
+
...(pending.names !== undefined ? { names: pending.names } : {}),
|
|
112
122
|
family: pending.family,
|
|
113
123
|
...(pending.pathTarget !== undefined ? { pathTarget: pending.pathTarget } : {}),
|
|
114
124
|
capabilityId: pending.capabilityId,
|