@sema-agent/core 5.28.0 → 5.30.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 +99 -3
- package/dist/agents/cascade.d.ts +1 -1
- package/dist/agents/observer.d.ts +3 -3
- package/dist/agents/peer-admission.d.ts +1 -1
- package/dist/agents/send-message-tool.js +2 -0
- package/dist/agents/subagent.d.ts +2 -0
- package/dist/agents/subagent.js +6 -0
- package/dist/agents/teacher.d.ts +1 -1
- package/dist/agents/teacher.js +2 -0
- package/dist/agents/verify.d.ts +6 -6
- package/dist/agents/verify.js +2 -0
- package/dist/bench/metrics.d.ts +1 -1
- package/dist/brain/retry.d.ts +1 -1
- package/dist/core/arg-summary.d.ts +1 -1
- package/dist/core/ask-class.d.ts +2 -2
- package/dist/core/ask-question.d.ts +1 -1
- package/dist/core/ask-question.js +14 -1
- package/dist/core/auto-compaction.d.ts +5 -1
- package/dist/core/auto-compaction.js +10 -1
- package/dist/core/background-shell.d.ts +5 -5
- package/dist/core/checkpoint-store.d.ts +65 -19
- package/dist/core/checkpoint-store.js +2 -1
- package/dist/core/file-snapshot-store.d.ts +1 -1
- package/dist/core/hooks.d.ts +15 -4
- package/dist/core/hooks.js +8 -2
- package/dist/core/human-input-projection.d.ts +2 -2
- package/dist/core/memory-admission.d.ts +2 -2
- package/dist/core/memory-engine/engine.d.ts +1 -1
- package/dist/core/memory-engine/file-backend.d.ts +2 -2
- package/dist/core/memory-engine/header-hints.d.ts +1 -1
- package/dist/core/memory-engine/layout.d.ts +2 -2
- package/dist/core/memory-recall.d.ts +1 -1
- package/dist/core/memory.d.ts +2 -2
- package/dist/core/oracle-isolation.d.ts +2 -2
- package/dist/core/permission-rule-consent.d.ts +1 -1
- package/dist/core/permission-rule-model.d.ts +1 -1
- package/dist/core/permission-rule-org.d.ts +1 -1
- package/dist/core/permission-rule-store.d.ts +1 -1
- package/dist/core/permission-rules.js +2 -2
- package/dist/core/runner/active-skill-scope.d.ts +1 -1
- package/dist/core/runner/memory-consolidation.d.ts +1 -1
- package/dist/core/runner/prepare-task.d.ts +29 -8
- package/dist/core/runner/prepare-task.js +118 -22
- package/dist/core/runner/runtask.d.ts +1 -1
- package/dist/core/runner/runtask.js +34 -8
- package/dist/core/runner/session-rule-policy.d.ts +4 -3
- package/dist/core/runner/teardown-bounded.d.ts +1 -1
- package/dist/core/runner/tool-disclosure.d.ts +1 -1
- package/dist/core/runner/tool-output-projection.js +1 -1
- package/dist/core/scheduler.d.ts +4 -4
- package/dist/core/sensitive-path-policy.js +5 -16
- package/dist/core/session-policy-store.d.ts +3 -3
- package/dist/core/shared-memory/normalize.d.ts +1 -1
- package/dist/core/skill-tool-specifier.d.ts +1 -1
- package/dist/core/store-contracts/tool-result-store-contract.js +23 -0
- package/dist/core/task-notification.d.ts +2 -2
- package/dist/core/task-registry-agent.d.ts +9 -9
- package/dist/core/task-registry-shared.d.ts +4 -4
- package/dist/core/tighten-task-spec.js +18 -0
- package/dist/core/tool-errors.d.ts +2 -2
- package/dist/core/tool-policy.d.ts +64 -12
- package/dist/core/tool-policy.js +68 -8
- package/dist/core/tool-result-store.d.ts +2 -2
- package/dist/core/tool-result-store.js +3 -1
- package/dist/core/trace.d.ts +1 -1
- package/dist/core/types.d.ts +68 -13
- package/dist/core/untrusted-text.d.ts +6 -0
- package/dist/core/untrusted-text.js +1 -0
- package/dist/engine/compaction/compaction.d.ts +1 -1
- package/dist/engine/harness/messages.d.ts +1 -1
- package/dist/engine/harness/types.d.ts +12 -2
- package/dist/engine/llm/types.d.ts +1 -1
- package/dist/engine/loop/types.d.ts +2 -2
- package/dist/engine/session/import-validate.d.ts +1 -1
- package/dist/engine/session/log-digest.d.ts +1 -1
- package/dist/fixtures/index.d.ts +18 -2
- package/dist/fixtures/index.js +11 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/orchestration/goal.d.ts +1 -1
- package/dist/orchestration/run-workflow-tool.d.ts +21 -0
- package/dist/orchestration/run-workflow-tool.js +6 -3
- package/dist/orchestration/workflow-primitives.d.ts +10 -1
- package/dist/orchestration/workflow-primitives.js +12 -1
- package/dist/orchestration/workflow-types.d.ts +5 -4
- package/dist/orchestration/workflow.js +11 -3
- package/dist/prompt-assembly/artifact.d.ts +1 -1
- package/dist/prompt-assembly/epoch.js +2 -0
- package/dist/prompt-assembly/packs/sema-default.js +2 -2
- package/dist/prompt-assembly/types.d.ts +4 -0
- package/dist/prompts/default.d.ts +14 -9
- package/dist/prompts/default.js +14 -4
- package/dist/scenarios/scenario-registry.d.ts +3 -3
- package/dist/stores/file/tool-result-store.d.ts +2 -2
- package/dist/tools/fs/bash-readonly-classifier.d.ts +23 -2
- package/dist/tools/fs/bash-readonly-classifier.js +11 -0
- package/dist/tools/fs/fs-bash.d.ts +8 -1
- package/dist/tools/fs/fs-bash.js +8 -3
- package/dist/tools/fs/fs-pdf.d.ts +1 -1
- package/dist/tools/fs/fs-pdf.js +2 -2
- package/dist/tools/fs/fs-read.d.ts +1 -1
- package/dist/tools/fs/fs-read.js +11 -7
- package/dist/tools/fs/fs-search-tools.d.ts +5 -3
- package/dist/tools/fs/fs-search-tools.js +15 -8
- package/dist/tools/fs/fs-shared.d.ts +6 -2
- package/dist/tools/fs/fs-shared.js +8 -3
- package/dist/tools/fs/index.d.ts +18 -0
- package/dist/tools/fs/index.js +13 -2
- package/dist/tools/fs/read-deny.d.ts +105 -0
- package/dist/tools/fs/read-deny.js +151 -0
- package/dist/tools/fs/read-face.d.ts +43 -0
- package/dist/tools/fs/read-face.js +38 -0
- package/dist/tools/fs/repo-map.d.ts +3 -1
- package/dist/tools/fs/repo-map.js +11 -5
- package/dist/tools/fs/safety.d.ts +34 -12
- package/dist/tools/fs/safety.js +88 -7
- package/dist/tools/fs/search.d.ts +54 -5
- package/dist/tools/fs/search.js +103 -21
- package/dist/tools/scheduler-tools.d.ts +1 -1
- package/dist/tools/task-list.d.ts +1 -1
- package/dist/tools/web.d.ts +3 -3
- package/package.json +1 -1
|
@@ -647,6 +647,37 @@ export interface CheckpointState {
|
|
|
647
647
|
/** design/44: the hand's read-file state (content hashes), serialized from the hands-toolkit closure
|
|
648
648
|
* (NOT part of Prepared — §15.2 net-add #8). Without it a resumed `edit_file` is rejected "not read". */
|
|
649
649
|
readFileState?: Array<[string, ReadEntry]>;
|
|
650
|
+
/**
|
|
651
|
+
* design/199 — the READ-face containment state at suspend (v9,
|
|
652
|
+
* {@link FACE_CHECKPOINT_VERSION}): the RESOLVED face plus the NORMALIZED deny-set ADDITIONS in
|
|
653
|
+
* force (deployment ∪ task layers; the built-in table is engine code, never frozen here — a newer
|
|
654
|
+
* engine's built-ins apply on resume as everywhere else). Written ONLY when the posture is
|
|
655
|
+
* non-default (face "open", or additions present) — an absent section IS the default posture and
|
|
656
|
+
* resumes as roots + built-ins on any worker. Resume folds STRICTER-WINS: face = roots unless the
|
|
657
|
+
* section says open AND the live resolution says open; deny additions = frozen ∪ live (add-only,
|
|
658
|
+
* monotone — a resume can never widen what the cut leg could read).
|
|
659
|
+
*
|
|
660
|
+
* The design's r3/r4 rounds carried a third key (the workspace domain at cut) to evaluate
|
|
661
|
+
* domain-scoped deny entries under their frozen domain; the D-4 ruling (2026-08-13) removed the
|
|
662
|
+
* scoped tier entirely, leaving that key without a consumer — it is deliberately NOT persisted
|
|
663
|
+
* (dead fields in a durable format are the drift surface the liveness gate exists to catch).
|
|
664
|
+
*/
|
|
665
|
+
readFace?: {
|
|
666
|
+
face: "open" | "roots";
|
|
667
|
+
denyEntries?: Array<{
|
|
668
|
+
pattern: string;
|
|
669
|
+
caseSensitive: boolean;
|
|
670
|
+
}>;
|
|
671
|
+
/** codex r2 — REDUNDANT governance marker: `true` iff the row's gate was minted WITH a
|
|
672
|
+
* {@link RealApprovalGateBit}. v8's bit-required invariant was version-keyed and v9 (face)
|
|
673
|
+
* outranks v8 in the stamp, so without this a v9 GOVERNED row whose `gate.realApproval` member
|
|
674
|
+
* was dropped in storage would pass pre-CAS and be redeemable on an unwired worker — the exact
|
|
675
|
+
* #130/#120 corruption/downgrade hole, reopened one version up. The resume pre-CAS ladder
|
|
676
|
+
* refuses a v9+ row whose section says `realApproval: true` while the gate carries no
|
|
677
|
+
* well-formed bit (same protection level as v8: a coherent rewrite of BOTH sites remains out of
|
|
678
|
+
* scope for any in-row invariant). Absent = the row was minted ungoverned. */
|
|
679
|
+
realApproval?: true;
|
|
680
|
+
};
|
|
650
681
|
/**
|
|
651
682
|
* design/78 Slice-1: the SAFE-tier self-repair loop's durable state (`failureTrace`/`diagnostics`/
|
|
652
683
|
* `rejectedHypotheses`/`attemptCount`/`oracleTier` — all JSON/`structuredClone`-safe, no fn/Date). Set ONLY
|
|
@@ -784,7 +815,7 @@ export interface CheckpointState {
|
|
|
784
815
|
/** THIS task's OWN admitted org verdict at suspend (adversarial-review adoption, 2026-08-05):
|
|
785
816
|
* distinct from {@link admittedOrgScopes} (the incoming PARENT constraint). The resume leg
|
|
786
817
|
* applies it as the SESSION freeze on the final admitted projection — regardless of origin
|
|
787
|
-
* (deployment-origin scopes
|
|
818
|
+
* (deployment-origin scopes circumvent the chain freeze, never the session freeze) — so a session's
|
|
788
819
|
* verdict can only NARROW across suspend/resume: re-adjudication still runs every leg (a
|
|
789
820
|
* revoked grant refuses the resume), but a policy widened mid-session cannot widen THIS
|
|
790
821
|
* session's mount or the chain its children inherit. Present (possibly EMPTY — an adjudicated
|
|
@@ -797,7 +828,7 @@ export interface CheckpointState {
|
|
|
797
828
|
* suspend leg did not hold); a scope string = the explicit grant. Present iff
|
|
798
829
|
* {@link ownAdmittedOrgScopes} is (same adjudication record). */
|
|
799
830
|
ownAdmittedOrgWriteScope?: string | null;
|
|
800
|
-
/** The MONOTONIC org-governance provenance bit (
|
|
831
|
+
/** The MONOTONIC org-governance provenance bit (falsification-style round 5): `true` when any leg of
|
|
801
832
|
* the suspended tree ran with an org-admission surface (or inherited the bit). A resume worker
|
|
802
833
|
* missing the surface still treats the leg as governed — org-shaped scopes respelled outside
|
|
803
834
|
* the v2 contract are refused, even when every admitted set en route was EMPTY. Absent on
|
|
@@ -901,7 +932,7 @@ export declare const RESOURCE_CHECKPOINT_VERSION = 2;
|
|
|
901
932
|
* `boundInputHash`) whose enforcement lives ENTIRELY in the resuming worker's resume path. A pre-D-1 worker
|
|
902
933
|
* (released 1.100.0: `MAX_SUPPORTED`=2, and its resume code has ZERO binding logic) would otherwise resume a
|
|
903
934
|
* D-1-minted v1 checkpoint and execute the pending tool with NO decision-action verification — the exact
|
|
904
|
-
* "approve vendor-A $5 → execute vendor-B $5000"
|
|
935
|
+
* "approve vendor-A $5 → execute vendor-B $5000" circumvent the binding exists to prevent (council BLOCKER #1).
|
|
905
936
|
* Stamping these at **v3 (> the old worker's MAX of 2)** forces a pre-D-1 worker to reject them PRE-CAS
|
|
906
937
|
* (`unsupported_version`, stays `pending`, retried on a binding-enforcing worker) instead of silently voiding
|
|
907
938
|
* the binding. A resource_limit suspend keeps stamping v2; a pre-binding (legacy 1.100.0) checkpoint is v1.
|
|
@@ -923,7 +954,7 @@ export declare const BINDING_CHECKPOINT_VERSION = 3;
|
|
|
923
954
|
*/
|
|
924
955
|
export declare const TOKEN_CHECKPOINT_VERSION = 5;
|
|
925
956
|
/**
|
|
926
|
-
* Org-memory admission state (
|
|
957
|
+
* Org-memory admission state (falsification-style round 2, 2026-08-05 — the BINDING_CHECKPOINT_VERSION
|
|
927
958
|
* precedent replayed a fourth time): the schema version a suspend stamps when its state carries
|
|
928
959
|
* org-admission freeze fields (`inheritedGate.admittedOrgScopes` / `ownAdmittedOrgScopes` /
|
|
929
960
|
* `ownAdmittedOrgWriteScope` — the adjudicated-empty record included). The enforcement — the
|
|
@@ -937,7 +968,7 @@ export declare const TOKEN_CHECKPOINT_VERSION = 5;
|
|
|
937
968
|
export declare const ORG_ADMISSION_CHECKPOINT_VERSION = 6;
|
|
938
969
|
/**
|
|
939
970
|
* F-012 (2026-08-09 — the ORG_ADMISSION_CHECKPOINT_VERSION precedent replayed a fifth time, codex
|
|
940
|
-
*
|
|
971
|
+
* falsification-style finding): the schema version a suspend stamps when its state carries the F-012 durable
|
|
941
972
|
* enforcement fields — `inheritedGate.constraintChain`/`constraintDigest` (the frozen ancestor
|
|
942
973
|
* projection chain the resume-edit re-adjudication executes) or `delegationProvenance` (the monotonic
|
|
943
974
|
* provenance aggregate a resume re-seeds). Both enforcements live ENTIRELY in the resuming worker: a
|
|
@@ -963,14 +994,29 @@ export declare const F012_CHECKPOINT_VERSION = 7;
|
|
|
963
994
|
* stays true: rows whose gate carries no realApproval bit keep their historic stamps everywhere.
|
|
964
995
|
*/
|
|
965
996
|
export declare const REAL_APPROVAL_CHECKPOINT_VERSION = 8;
|
|
997
|
+
/**
|
|
998
|
+
* design/199 (2026-08-13 — the precedent replayed a seventh time): the schema version a suspend
|
|
999
|
+
* stamps when its state carries the READ-FACE section ({@link CheckpointState.readFace} — the
|
|
1000
|
+
* resolved open face and/or deny-set additions). The enforcement lives ENTIRELY in the resuming
|
|
1001
|
+
* worker (the stricter-wins face fold + the deny-entry union at prepare): a pre-199 worker
|
|
1002
|
+
* (MAX_SUPPORTED=8) would accept the row, ignore the section, and resume the leg under its own
|
|
1003
|
+
* defaults — silently DROPPING the frozen deny additions (a widening), and re-deriving the face
|
|
1004
|
+
* from live config alone. Stamping v9 forces it to reject PRE-CAS (`unsupported_version`, stays
|
|
1005
|
+
* `pending`, retried on an enforcing worker). A row whose task ran the default posture (face roots,
|
|
1006
|
+
* no additions) carries NO section and keeps the historic stamps — such a resume on ANY worker
|
|
1007
|
+
* folds to roots + built-ins, which is exactly the semantics it was cut under.
|
|
1008
|
+
*/
|
|
1009
|
+
export declare const FACE_CHECKPOINT_VERSION = 9;
|
|
966
1010
|
/** The highest {@link Checkpoint.version} `runner.resume` will act on; a higher one is rejected pre-CAS with
|
|
967
1011
|
* {@link CheckpointError} `unsupported_version` (the checkpoint stays `pending`, retryable on a newer worker).
|
|
968
1012
|
* Raised to 8 for realApproval-bearing gates — this worker reads v1 (legacy human), v2 (resource), v3
|
|
969
1013
|
* (binding human/irreversible_ask), v4 (a pre-164 row, refused only when it carries the retired
|
|
970
1014
|
* wall-clock allocation), v5 (token-allocation-bearing resource/approval), v6 (org-admission freeze
|
|
971
|
-
* state), v7 (F-012 constraint-chain / delegation-provenance enforcement state)
|
|
972
|
-
* non-budgetable `realApproval` gate bit with its org-origin resume semantics)
|
|
973
|
-
|
|
1015
|
+
* state), v7 (F-012 constraint-chain / delegation-provenance enforcement state), v8 (a
|
|
1016
|
+
* non-budgetable `realApproval` gate bit with its org-origin resume semantics) and v9 (the
|
|
1017
|
+
* design/199 read-face section: resolved face + deny-set additions, folded stricter-wins on
|
|
1018
|
+
* resume). */
|
|
1019
|
+
export declare const MAX_SUPPORTED_CHECKPOINT_VERSION = 9;
|
|
974
1020
|
/**
|
|
975
1021
|
* Read a checkpoint's schema version, defaulting an absent field to **legacy `0`** (a 1.67-era checkpoint
|
|
976
1022
|
* written before the field existed — it carries no `workspaceHandle`, so resuming it the v1 way is safe).
|
|
@@ -1022,7 +1068,7 @@ export declare function debitLedger(prior: ResourceLedger | undefined, slice: {
|
|
|
1022
1068
|
* per-slice window / walltime). A resumed slice's effective `maxCostUsd` is `min(window, this)`. NB: `0` is a
|
|
1023
1069
|
* VALID, exhausted ceiling — NOT "unlimited" (only `undefined` is unlimited). Any spend then immediately trips
|
|
1024
1070
|
* `overBudget`, and the run loop fails an exhausted resume fast (runtask). Never falsy-test this value (a
|
|
1025
|
-
* `if (remaining)` would skip a legitimate 0 ceiling and silently
|
|
1071
|
+
* `if (remaining)` would skip a legitimate 0 ceiling and silently circumvent the budget). */
|
|
1026
1072
|
export declare function remainingBudgetMicroUsd(ledger: ResourceLedger | undefined): number | undefined;
|
|
1027
1073
|
/** design/164 (the TOKEN sibling of {@link remainingBudgetMicroUsd}): tokens the NEXT slice may still
|
|
1028
1074
|
* spend = `totalTokens − spentTokens`, never negative. `undefined` when no token total is set (the run is
|
|
@@ -1270,7 +1316,7 @@ export interface Checkpoint {
|
|
|
1270
1316
|
* `durableApproval` (an unattended safety suspend, a non-ask gate, or a legacy pre-1.389 checkpoint — the
|
|
1271
1317
|
* consumer then falls back to the human-kind scope-only inference for legacy rows and injects NOTHING for
|
|
1272
1318
|
* unattended parks, so a spawn that never opted in can never gain the opt-in through a resume). Engine-
|
|
1273
|
-
* minted from the task's own spec — a worker/tool cannot
|
|
1319
|
+
* minted from the task's own spec — a worker/tool cannot self-declare it (`durableApproval` never rides tool args).
|
|
1274
1320
|
*/
|
|
1275
1321
|
durableApproval?: {
|
|
1276
1322
|
scope: string;
|
|
@@ -1442,7 +1488,7 @@ export declare class CheckpointError extends Error {
|
|
|
1442
1488
|
* rather than evicting: an already-accepted operator instruction is never dropped to make room —
|
|
1443
1489
|
* the caller delivers or removes a parked steer first. Nothing is written on this path. */
|
|
1444
1490
|
| "steering.queue_full"
|
|
1445
|
-
/** design/171 §6.3 (
|
|
1491
|
+
/** design/171 §6.3 (falsification review adoption): the append reused an `inputId` already parked on
|
|
1446
1492
|
* this checkpoint, but with DIFFERENT content. Re-appending an IDENTICAL payload stays the
|
|
1447
1493
|
* idempotent no-op a retry needs; a differing one is refused, because swallowing it would lose the
|
|
1448
1494
|
* second operator's instruction — the exact silent loss the queue replaced. Retry with a fresh id.
|
|
@@ -1450,7 +1496,7 @@ export declare class CheckpointError extends Error {
|
|
|
1450
1496
|
| "steering.duplicate_input_id"
|
|
1451
1497
|
/** design/144 §3 (governance-gate purity, fail-closed pre-CAS): a `wake` outcome was supplied for a
|
|
1452
1498
|
* checkpoint that awaits a PENDING GATE DECISION (tool approval / dry-run review / plan review /
|
|
1453
|
-
* resource-limit continue). Wake is a pure un-park verb and must never
|
|
1499
|
+
* resource-limit continue). Wake is a pure un-park verb and must never circumvent an approval — resume
|
|
1454
1500
|
* the checkpoint through its own decide entry instead (the error message names it). */
|
|
1455
1501
|
| "wake.gate_pending"
|
|
1456
1502
|
/** design/144 §3 (anti spin-wake): a `wake` outcome carried no `message` and the checkpoint has no
|
|
@@ -1498,7 +1544,7 @@ export declare class CheckpointError extends Error {
|
|
|
1498
1544
|
field?: "boundCallId" | "boundInputHash" | "answer" | "settledBy";
|
|
1499
1545
|
/** WHICH pre-CAS refusal arm fired, where one `code` covers several (requested 2026-08-10: a
|
|
1500
1546
|
* deployment retry policy needs to tell "a newer worker can redeem this row" from "this row is
|
|
1501
|
-
* damaged/
|
|
1547
|
+
* damaged/caller-declared and no worker ever will" — blanket-retriable and gate-shape heuristics were
|
|
1502
1548
|
* both refuted downstream, so the throw site carries the fact it always knew). Closed set;
|
|
1503
1549
|
* additive and optional like `field` — `code` remains the only REQUIRED discriminant.
|
|
1504
1550
|
* · `unsupported_version` arms: `"version_newer"` (retryable on a newer worker),
|
|
@@ -1507,7 +1553,7 @@ export declare class CheckpointError extends Error {
|
|
|
1507
1553
|
* · `invalid_outcome` pre-CAS row-integrity arms: `"real_approval_damaged"`,
|
|
1508
1554
|
* `"real_approval_forged"`, `"constraint_chain_missing"` (all terminal for the row's
|
|
1509
1555
|
* current bytes — no worker version redeems a damaged row). */
|
|
1510
|
-
reason?: "version_newer" | "env_factory_missing" | "governed_unwired" | "real_approval_damaged" | "real_approval_forged" | "constraint_chain_missing";
|
|
1556
|
+
reason?: "version_newer" | "env_factory_missing" | "governed_unwired" | "real_approval_damaged" | "real_approval_forged" | "read_face_damaged" | "read_face_forged" | "constraint_chain_missing";
|
|
1511
1557
|
} | undefined;
|
|
1512
1558
|
constructor(code: "checkpoint.already_exists" | "checkpoint.already_resolved" | "checkpoint.not_found"
|
|
1513
1559
|
/** `runner.resume` was handed an {@link ResumeOutcome} whose `gate` arm does not match the
|
|
@@ -1563,7 +1609,7 @@ export declare class CheckpointError extends Error {
|
|
|
1563
1609
|
* rather than evicting: an already-accepted operator instruction is never dropped to make room —
|
|
1564
1610
|
* the caller delivers or removes a parked steer first. Nothing is written on this path. */
|
|
1565
1611
|
| "steering.queue_full"
|
|
1566
|
-
/** design/171 §6.3 (
|
|
1612
|
+
/** design/171 §6.3 (falsification review adoption): the append reused an `inputId` already parked on
|
|
1567
1613
|
* this checkpoint, but with DIFFERENT content. Re-appending an IDENTICAL payload stays the
|
|
1568
1614
|
* idempotent no-op a retry needs; a differing one is refused, because swallowing it would lose the
|
|
1569
1615
|
* second operator's instruction — the exact silent loss the queue replaced. Retry with a fresh id.
|
|
@@ -1571,7 +1617,7 @@ export declare class CheckpointError extends Error {
|
|
|
1571
1617
|
| "steering.duplicate_input_id"
|
|
1572
1618
|
/** design/144 §3 (governance-gate purity, fail-closed pre-CAS): a `wake` outcome was supplied for a
|
|
1573
1619
|
* checkpoint that awaits a PENDING GATE DECISION (tool approval / dry-run review / plan review /
|
|
1574
|
-
* resource-limit continue). Wake is a pure un-park verb and must never
|
|
1620
|
+
* resource-limit continue). Wake is a pure un-park verb and must never circumvent an approval — resume
|
|
1575
1621
|
* the checkpoint through its own decide entry instead (the error message names it). */
|
|
1576
1622
|
| "wake.gate_pending"
|
|
1577
1623
|
/** design/144 §3 (anti spin-wake): a `wake` outcome carried no `message` and the checkpoint has no
|
|
@@ -1619,7 +1665,7 @@ export declare class CheckpointError extends Error {
|
|
|
1619
1665
|
field?: "boundCallId" | "boundInputHash" | "answer" | "settledBy";
|
|
1620
1666
|
/** WHICH pre-CAS refusal arm fired, where one `code` covers several (requested 2026-08-10: a
|
|
1621
1667
|
* deployment retry policy needs to tell "a newer worker can redeem this row" from "this row is
|
|
1622
|
-
* damaged/
|
|
1668
|
+
* damaged/caller-declared and no worker ever will" — blanket-retriable and gate-shape heuristics were
|
|
1623
1669
|
* both refuted downstream, so the throw site carries the fact it always knew). Closed set;
|
|
1624
1670
|
* additive and optional like `field` — `code` remains the only REQUIRED discriminant.
|
|
1625
1671
|
* · `unsupported_version` arms: `"version_newer"` (retryable on a newer worker),
|
|
@@ -1628,7 +1674,7 @@ export declare class CheckpointError extends Error {
|
|
|
1628
1674
|
* · `invalid_outcome` pre-CAS row-integrity arms: `"real_approval_damaged"`,
|
|
1629
1675
|
* `"real_approval_forged"`, `"constraint_chain_missing"` (all terminal for the row's
|
|
1630
1676
|
* current bytes — no worker version redeems a damaged row). */
|
|
1631
|
-
reason?: "version_newer" | "env_factory_missing" | "governed_unwired" | "real_approval_damaged" | "real_approval_forged" | "constraint_chain_missing";
|
|
1677
|
+
reason?: "version_newer" | "env_factory_missing" | "governed_unwired" | "real_approval_damaged" | "real_approval_forged" | "read_face_damaged" | "read_face_forged" | "constraint_chain_missing";
|
|
1632
1678
|
} | undefined);
|
|
1633
1679
|
}
|
|
1634
1680
|
/**
|
|
@@ -1880,7 +1926,7 @@ export interface CheckpointStore {
|
|
|
1880
1926
|
* REJECTED with a typed {@link CheckpointError} `steering.invalid_content` (mirroring runtask.ts's trusted-
|
|
1881
1927
|
* steer reject) — a dirty steer NEVER enters {@link CheckpointState}, so the persisted state stays clean
|
|
1882
1928
|
* regardless of `trusted`. (The untrusted RESUME-injection path ALSO sanitizes the text as untrusted data;
|
|
1883
|
-
* this persist-time reject is the belt that keeps a
|
|
1929
|
+
* this persist-time reject is the belt that keeps a caller-declared close tag out of the durable record on BOTH
|
|
1884
1930
|
* paths.) Text over {@link MAX_PENDING_STEER_CHARS} is rejected with the same code (HRD-APV-4 — the
|
|
1885
1931
|
* steer is re-delivered into the model context on every resume, so it is capped like every other
|
|
1886
1932
|
* persisted model-facing field on this row; rejected rather than truncated because cutting an
|
|
@@ -136,7 +136,8 @@ export const TOKEN_CHECKPOINT_VERSION = 5;
|
|
|
136
136
|
export const ORG_ADMISSION_CHECKPOINT_VERSION = 6;
|
|
137
137
|
export const F012_CHECKPOINT_VERSION = 7;
|
|
138
138
|
export const REAL_APPROVAL_CHECKPOINT_VERSION = 8;
|
|
139
|
-
export const
|
|
139
|
+
export const FACE_CHECKPOINT_VERSION = 9;
|
|
140
|
+
export const MAX_SUPPORTED_CHECKPOINT_VERSION = 9;
|
|
140
141
|
export function checkpointVersionOf(cp) {
|
|
141
142
|
return cp.version ?? 0;
|
|
142
143
|
}
|
|
@@ -72,7 +72,7 @@ export interface FileSnapshotStore {
|
|
|
72
72
|
/**
|
|
73
73
|
* 2c session-sync ([277]): STORE a single content-addressed blob — the symmetric WRITE side of {@link getBlob},
|
|
74
74
|
* for a two-phase PUSH (upload blobs, THEN import the manifest that references them). VERIFIES content-address
|
|
75
|
-
* integrity (`sha256(bytes) === hash`) so a corrupt/mismatched upload can never
|
|
75
|
+
* integrity (`sha256(bytes) === hash`) so a corrupt/mismatched upload can never contaminate the store (a later
|
|
76
76
|
* getBlob/restore would otherwise return wrong content); a mismatch → `read_failed`, nothing stored. IMMUTABLE +
|
|
77
77
|
* content-addressed: a repeat putBlob for the same hash is a no-op (the bytes are identical by definition).
|
|
78
78
|
* Returns a {@link FileSnapshotResult} (NOT void) so the integrity failure is encodable under the never-throw
|
package/dist/core/hooks.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { type AskClass } from "./ask-class.js";
|
|
|
8
8
|
*
|
|
9
9
|
* - {@link Hooks.preToolUse} runs before a tool executes. It may **rewrite** the args (an `allow` with
|
|
10
10
|
* `updatedInput`), **restrict** the call (`deny`/`ask`), and/or inject `additionalContext`. It is a
|
|
11
|
-
* pre-filter only: a hook's `allow` does NOT
|
|
11
|
+
* pre-filter only: a hook's `allow` does NOT circumvent the tool-policy gate — the policy still runs on
|
|
12
12
|
* the (possibly rewritten) args and has the final say (load-bearing invariant; see {@link runToolGate}).
|
|
13
13
|
* - {@link Hooks.postToolUse} runs after a tool executes. It may replace the result content
|
|
14
14
|
* (`updatedOutput`) and/or append `additionalContext`.
|
|
@@ -55,7 +55,7 @@ export interface Hooks {
|
|
|
55
55
|
* JSON on the ordinary path, which clones whole; the residual is reachable only through values a
|
|
56
56
|
* DEPLOYMENT put there (its own rewrite, or driving `runToolGate` directly), and the face and the
|
|
57
57
|
* deployment are one trust principal — this seam helps a deployment keep its own declaration, it is not
|
|
58
|
-
* a boundary against an
|
|
58
|
+
* a boundary against an untrusted party.
|
|
59
59
|
*
|
|
60
60
|
* If a declared-observational face RETURNS SOMETHING ANYWAY, the return is REFUSED, not obeyed: the
|
|
61
61
|
* call proceeds as if the hook had no opinion (`undefined`), and the deployment's `onError` face is
|
|
@@ -142,7 +142,7 @@ export interface Hooks {
|
|
|
142
142
|
* plan-mode write-deny. Closes the observation blind spot where a blocked call fires NEITHER postToolUse
|
|
143
143
|
* NOR postToolUseFailure (R3 scoping). Does NOT fire for:
|
|
144
144
|
* - a **durable suspend** (the ask path — CC surfaces that via a `can_use_tool` control_request);
|
|
145
|
-
* - a **PreToolUse hook deny** (CC-exact: "PreToolUse hook denies
|
|
145
|
+
* - a **PreToolUse hook deny** (CC-exact: "PreToolUse hook denies circumvention canUseTool and are not
|
|
146
146
|
* covered here").
|
|
147
147
|
* DOES fire for a **PreToolUse hook CRASH** (RB-442, `source:"hook"`): a throwing hook is intercepted by
|
|
148
148
|
* the ENGINE, which then makes its OWN fail-closed deny decision. The R5 exclusion above covers the
|
|
@@ -724,13 +724,20 @@ export interface ToolGateInput {
|
|
|
724
724
|
* - `unavailable`/`parse_error` → FAIL-CLOSED: the ask continues down the ORIGINAL chain
|
|
725
725
|
* (durable suspend / onAsk / headless auto-deny) exactly as if auto mode were absent
|
|
726
726
|
* (CC `automode-unavailable`/`automode-parsing-error` — "NOT a policy decision").
|
|
727
|
-
* SCOPE (declared semantic): this covers
|
|
727
|
+
* SCOPE (declared semantic): this covers every surviving ask INCLUDING the deterministic egress/
|
|
728
728
|
* irreversibility safety tightens — auto mode is the deployment's explicit choice to let the
|
|
729
729
|
* classifier be the "explicit ask-resolution" for this session (its rule set covers exactly those
|
|
730
730
|
* action classes: outbound writes, irreversible operations, and sensitive-value handling). A
|
|
731
731
|
* deployment that wants human-only
|
|
732
732
|
* resolution for safety asks simply does not arm auto mode. The classifier can never AUTO-ALLOW by
|
|
733
733
|
* failing — only an affirmative `<block>no</block>` allows.
|
|
734
|
+
* EXCLUDED from that scope (the ask flows the ORIGINAL chain untouched, exactly as if this field
|
|
735
|
+
* were absent for that one call): an org-constrained ask (design/182 §7.4, either org arm), the
|
|
736
|
+
* reserved question tool, a call MARKED unresolvable (design/153), a hook-raised or hook-promoted
|
|
737
|
+
* ask (`decisionReason:"hook"`), and an ask carrying `matchedAskRule` (an explicit `ask` permission
|
|
738
|
+
* rule matched — #213: a person's standing "ask me each time" is not classifier hesitation). The
|
|
739
|
+
* last two are the same pair the persisted-rule lane refuses, so the gate's two ask→allow seams
|
|
740
|
+
* answer provenance questions identically.
|
|
734
741
|
*/
|
|
735
742
|
autoMode?: {
|
|
736
743
|
decider: import("./auto-mode.js").AutoModeDecider;
|
|
@@ -769,6 +776,10 @@ export interface ToolGateInput {
|
|
|
769
776
|
* tool's own egress/irreversibility marks are structural requirements, not classifier hesitation
|
|
770
777
|
* ("allow rules silence the classifier's questions, never a mandated one"). When a rule matches
|
|
771
778
|
* but cannot clear, the surviving ask discloses it (message + `persistedRuleShadowed`).
|
|
779
|
+
* · #213: an ask carrying `matchedAskRule` (an explicit `ask` permission rule matched this call)
|
|
780
|
+
* is never consumed — a person's standing "ask me each time" outranks the memory of one past
|
|
781
|
+
* yes, across lanes exactly as the DSL's own deny > ask > allow order says within one. Same
|
|
782
|
+
* disclosure as the mandated family when a rule matches anyway.
|
|
772
783
|
* What it DOES consume is the classify-DOCTRINE shell ask (the coarse tier "maybe" — the
|
|
773
784
|
* don't-ask-again main case this feature exists for) and unmarked bare asks. Consuming such an ask
|
|
774
785
|
* also skips the park it would have minted; that IS what a standing approval means, and the
|
package/dist/core/hooks.js
CHANGED
|
@@ -354,7 +354,7 @@ export async function runToolGate(input) {
|
|
|
354
354
|
req.toolName !== ASK_USER_QUESTION_TOOL_NAME &&
|
|
355
355
|
input.isMarkedUnresolvable?.(input.event.toolCallId) !== true) {
|
|
356
356
|
const hit = await input.persistedRules.admits(req).catch(() => undefined);
|
|
357
|
-
if (hit !== undefined && persistedRuleMandate === undefined) {
|
|
357
|
+
if (hit !== undefined && persistedRuleMandate === undefined && decision.matchedAskRule === undefined) {
|
|
358
358
|
decision = {
|
|
359
359
|
action: "allow",
|
|
360
360
|
message: `a persisted allow rule (${hit}) covers this call`,
|
|
@@ -365,7 +365,11 @@ export async function runToolGate(input) {
|
|
|
365
365
|
}
|
|
366
366
|
else if (hit !== undefined) {
|
|
367
367
|
const shownRule = inlineUntrusted(hit, 200);
|
|
368
|
-
const mandateNoun = persistedRuleMandate === "operator_always"
|
|
368
|
+
const mandateNoun = persistedRuleMandate === "operator_always"
|
|
369
|
+
? "this deployment mandates per-call confirmation for shell commands (shellGate: always)"
|
|
370
|
+
: persistedRuleMandate !== undefined
|
|
371
|
+
? "this tool carries egress/irreversibility marks (a mandated confirmation a rule cannot clear)"
|
|
372
|
+
: "an explicit ask rule matched this call (a person's ask-me-each-time outranks a standing allow rule)";
|
|
369
373
|
decision = {
|
|
370
374
|
...decision,
|
|
371
375
|
persistedRuleShadowed: shownRule,
|
|
@@ -376,6 +380,8 @@ export async function runToolGate(input) {
|
|
|
376
380
|
if (input.autoMode &&
|
|
377
381
|
!orgRealApprovalRequired &&
|
|
378
382
|
decision.action === "ask" &&
|
|
383
|
+
decision.decisionReason !== "hook" &&
|
|
384
|
+
decision.matchedAskRule === undefined &&
|
|
379
385
|
req.toolName !== ASK_USER_QUESTION_TOOL_NAME &&
|
|
380
386
|
input.isMarkedUnresolvable?.(input.event.toolCallId) !== true) {
|
|
381
387
|
const verdict = await input.autoMode.decider
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
*
|
|
19
19
|
* The label goes through the same two neutralizers as the external notification's `from="…"` header
|
|
20
20
|
* (`inlineUntrusted` + `attrEscape` — one-line, capped, break-out-defused, quote-escaped), so a
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* untrusted id can never close the `[from "…"]` frame, escape an enclosing `<system-reminder>`, or
|
|
22
|
+
* carry across a fake `(unverified)`-free identity. The projection runs BEFORE trust framing at every
|
|
23
23
|
* call site, so the annotation always sits INSIDE the frame it attributes (a trusted frame's
|
|
24
24
|
* reminder wrapper, an untrusted frame's fence) and can never be separated from its text.
|
|
25
25
|
*/
|
|
@@ -50,7 +50,7 @@ export interface MemoryAdmissionInput {
|
|
|
50
50
|
* request-origin scope outside it refuses the prepare; a deployment-origin scope outside it is
|
|
51
51
|
* dropped and disclosed; a write grant absent from it collapses to read-only. */
|
|
52
52
|
priorOwnVerdict: OwnOrgAdmissionVerdict | undefined;
|
|
53
|
-
/** The monotonic governance-provenance bit (
|
|
53
|
+
/** The monotonic governance-provenance bit (falsification-style round 5): true when this leg's TREE is
|
|
54
54
|
* org-governed — the deployment surface is configured here, or ANY ancestor/prior leg recorded
|
|
55
55
|
* the bit (`InheritedGate.orgAdmissionGoverned`, persisted on checkpoints). Counts as governance
|
|
56
56
|
* evidence even when every admitted set en route is EMPTY: a governed parent's org-less child,
|
|
@@ -82,7 +82,7 @@ export interface MemoryAdmissionOutcome {
|
|
|
82
82
|
* (an older-shape chain must never read as "unconstrained"); a top-level leg simply has no chain
|
|
83
83
|
* constraint. The SESSION freeze (the leg's own suspend-time verdict) is a separate input to
|
|
84
84
|
* {@link admitMemoryScopes} (`priorOwnVerdict`) — it must constrain the FINAL admitted projection
|
|
85
|
-
* regardless of origin (deployment-origin scopes
|
|
85
|
+
* regardless of origin (deployment-origin scopes circumvent the chain freeze, not the session freeze),
|
|
86
86
|
* so it cannot ride this chain-side fold.
|
|
87
87
|
*/
|
|
88
88
|
export declare function foldAdmissionFreeze(input: {
|
|
@@ -37,7 +37,7 @@ export declare const MEMORY_RECALL_DISCIPLINE = "Before answering questions abou
|
|
|
37
37
|
* CC-verbatim `# Memory` instruction stays byte-identical to its sha256-pinned capture. Appended
|
|
38
38
|
* only when a WRITE channel exists (prepare-memory gates it) — the lines it teaches are frontmatter
|
|
39
39
|
* the MODEL writes; the engine has no write arm for them (writing usage evidence for the model
|
|
40
|
-
* would
|
|
40
|
+
* would self-declare the very account the three-tier discipline forbids forging).
|
|
41
41
|
*/
|
|
42
42
|
export declare const MEMORY_PREFERENCE_DISCIPLINE = "When the user confirms a stored preference or fact still holds, refresh that entry's `last-confirmed: <YYYY-MM-DD>` frontmatter line (add it when absent). When you save a preference, add an `applies-when: <context>` frontmatter line naming when it applies. Both are plain frontmatter lines \u2014 write them yourself; nothing fills them in for you.";
|
|
43
43
|
/**
|
|
@@ -323,7 +323,7 @@ export declare class FileMemoryEngineBackend implements MemoryBackend {
|
|
|
323
323
|
applyPatches(patches: readonly NotePatch[]): Promise<PatchReport>;
|
|
324
324
|
private txnLockDir;
|
|
325
325
|
/**
|
|
326
|
-
*
|
|
326
|
+
* 证伪式复审 L1/L2/L3 — the mkdir mutex, hardened (same shape as layout.ts' acquireSidecarLock, but
|
|
327
327
|
* fail-closed timings and an async wait):
|
|
328
328
|
* - L2: every acquisition writes an OWNER TOKEN (`<lock>/owner`, crypto-random — never a
|
|
329
329
|
* timestamp) immediately after mkdir. Release only removes the lock while the token is still
|
|
@@ -338,7 +338,7 @@ export declare class FileMemoryEngineBackend implements MemoryBackend {
|
|
|
338
338
|
* crashed with `remaining stale > waitMs` no longer strands every waiter in an all-throw window.
|
|
339
339
|
*/
|
|
340
340
|
private acquireTxnLock;
|
|
341
|
-
/**
|
|
341
|
+
/** 证伪式复审 L2 — 提交点归属复验: called immediately before the JOURNAL write (the persistent commit
|
|
342
342
|
* point). A holder that stalled past the stale deadline (slow disk / GC pause / long txn) and was
|
|
343
343
|
* stolen from must ABORT — its staged tmps carry no committed state and the stealer's journal
|
|
344
344
|
* recovery is now authoritative; committing anyway would overwrite the stealer's transaction. */
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* This accessor is the ONLY consumer-side reading: pure, lenient, and closed over bad values —
|
|
12
12
|
* a malformed line reads as FIELD ABSENT, never an error and never a partial adoption. Nothing in
|
|
13
13
|
* the engine writes these lines; the model does (the preference-discipline paragraph teaches it),
|
|
14
|
-
* because an engine-written confirmation would
|
|
14
|
+
* because an engine-written confirmation would self-declare the usage evidence it is supposed to record.
|
|
15
15
|
*/
|
|
16
16
|
/** The parsed v2 hints. Every field is absent unless a well-formed line was found. */
|
|
17
17
|
export interface V2HeaderHints {
|
|
@@ -133,7 +133,7 @@ export declare function claimRootScope(controlDir: string, scope: string): strin
|
|
|
133
133
|
/** Register `scope` in the durable registry (idempotent) and return its home dir under `memoryDir`.
|
|
134
134
|
* H5 fail-closed: a dir-name collision with a DIFFERENT registered scope throws — two scopes must
|
|
135
135
|
* never share one directory (silent cross-scope merge). The collision test has TWO arms:
|
|
136
|
-
* - exact: two scopes whose labels are the SAME string (hash collision /
|
|
136
|
+
* - exact: two scopes whose labels are the SAME string (hash collision / caller-declared registry);
|
|
137
137
|
* - case-fold (2026-08-12): two labels differing ONLY in letter case are distinct strings to this
|
|
138
138
|
* registry yet ONE physical directory on a case-folding volume — cross-scope bleed the exact arm
|
|
139
139
|
* cannot see. The fold arm compares the FINAL dir labels (ASCII by construction, so toLowerCase
|
|
@@ -473,7 +473,7 @@ export declare function appendChallengeEvents(controlDir: string, events: Readon
|
|
|
473
473
|
*
|
|
474
474
|
* `eventCount` rides along for the same reason it does on {@link appendChallengeEvents}: a resolve
|
|
475
475
|
* APPENDS an event too, so it crosses the size bound exactly as a challenge does (independent
|
|
476
|
-
*
|
|
476
|
+
* falsification review, verified — hanging the disclosure off the challenge writer alone leaves a ledger
|
|
477
477
|
* that crossed the bound on a resolve silent until the next challenge, which may never come).
|
|
478
478
|
* `undefined` when nothing was appended. */
|
|
479
479
|
export declare function resolveChallengeEvent(controlDir: string, entryId: string, generation: number, reason: string, now: () => number, requestId?: string): {
|
|
@@ -162,7 +162,7 @@ export declare function composeSelectiveBody(manifestText: string, selected: Mem
|
|
|
162
162
|
/** A `(scope, id)` composite key for the multi-scope durable de-dup (design/84 Seam A decision 5). Note ids
|
|
163
163
|
* are only unique WITHIN a scope, so the cross-scope `priorSurfacedKeys`/`surfacedKeys` set MUST be keyed on
|
|
164
164
|
* both. Core owns the FORMAT so a caller can't drift the wire format (decision 7) — the encoding is OPAQUE
|
|
165
|
-
* and length-prefixes the scope so a scope/id containing the separator cannot
|
|
165
|
+
* and length-prefixes the scope so a scope/id containing the separator cannot self-declare a collision.
|
|
166
166
|
*
|
|
167
167
|
* ONE-WAY by design (codex MINOR (d)): the key is only ever PRODUCED here (`surfacedKeys`) and compared by
|
|
168
168
|
* STRING EQUALITY against the caller's persisted set ({@link LayeredRecallOptions.priorSurfacedKeys}, applied
|
package/dist/core/memory.d.ts
CHANGED
|
@@ -194,7 +194,7 @@ export interface StructuredNoteInput {
|
|
|
194
194
|
export interface MemoryNoteHeader {
|
|
195
195
|
/** Store contract: ids must be SINGLE-LINE whitespace-free tokens (uuid-like). The manifest renders
|
|
196
196
|
* `- {id}: …` lines and the side-query matches selected ids by string equality — an id containing
|
|
197
|
-
* a newline could
|
|
197
|
+
* a newline could self-declare manifest lines, and a renderer-side rewrite would break the equality match,
|
|
198
198
|
* so the contract sits on the store (search 1.95.1 review #2). */
|
|
199
199
|
id: string;
|
|
200
200
|
/** A one-line summary used as the relevance signal (derived from the note's first sentence when untyped). */
|
|
@@ -306,7 +306,7 @@ export declare function enforceSecretWriteGate(body: string): void;
|
|
|
306
306
|
* (rendered as `[[name]]` cross-references) and `description` (rendered into the recall manifest + injected
|
|
307
307
|
* into the side-query sub-prompt) are ALSO persisted and surfaced, so a secret hidden there
|
|
308
308
|
* (`appendStructured({ body: "safe", description: "sk_live_…" })`) would otherwise be durably stored and
|
|
309
|
-
* leaked into every later same-scope manifest — a real
|
|
309
|
+
* leaked into every later same-scope manifest — a real circumvention of the body-only scan. `links` slugs are NOT
|
|
310
310
|
* scanned: they are short `[[name]]`-style reference tokens (no credential body shape clears the
|
|
311
311
|
* conservative detector), and scanning them adds FP surface for no realistic gain. Fail-closed: the first
|
|
312
312
|
* field that carries a high-confidence credential throws {@link MemoryGateError}`("memory.secret_detected")`.
|
|
@@ -46,7 +46,7 @@ export interface OracleIsolationVerdict {
|
|
|
46
46
|
export interface GraderEnv {
|
|
47
47
|
/** The isolated env the oracle grades in (service-provisioned; a {@link import("./remote-env.js").RemoteExecutionEnv} in production). */
|
|
48
48
|
env: ExecutionEnv;
|
|
49
|
-
/** Paths the oracle must be protected behind — probed for writability through the WORKER's bash
|
|
49
|
+
/** Paths the oracle must be protected behind — probed for writability through the WORKER's bash circumvention surface. */
|
|
50
50
|
immutableOraclePaths: string[];
|
|
51
51
|
/** Provenance brand — ONLY the control-plane factory sets this. Never derived from worker state. */
|
|
52
52
|
provenance: "control_plane";
|
|
@@ -72,7 +72,7 @@ export interface AssertOracleIsolationOptions {
|
|
|
72
72
|
* `candidate_only`); the class is what gates auto-accept (the 2-of-3 asymmetry).
|
|
73
73
|
*
|
|
74
74
|
* **(C) BASH WRITE-PROBE (necessary-not-sufficient, cheap early signal)** — for each `immutableOraclePath`, run
|
|
75
|
-
* a write probe through the WORKER env's `exec` (the bash
|
|
75
|
+
* a write probe through the WORKER env's `exec` (the bash circumvention surface — see {@link probeCommand}), against
|
|
76
76
|
* the path canonicalized via {@link canonicalizeTarget}. The probe PASSES only if the write returns a non-zero
|
|
77
77
|
* exit code or an exec error (read-only mount / permission denied / not supported). Any SUCCESS (RC 0) ⇒ the
|
|
78
78
|
* oracle dir is writable from the worker ⇒ `protected:false`. A probe THROW / abort / timeout ⇒ fail-closed
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* ## The boundary this does NOT claim
|
|
24
24
|
*
|
|
25
25
|
* The confirmation transfer is an in-process call. A deployment must reach it only from its authenticated
|
|
26
|
-
* approval channel, and a deployment that shares filesystem credentials with the user can
|
|
26
|
+
* approval channel, and a deployment that shares filesystem credentials with the user can circumvent all of
|
|
27
27
|
* this by editing a file backend's file. That is the settings-file trust model, stated rather than
|
|
28
28
|
* defended against: for a file backend, host = user, no more and no less.
|
|
29
29
|
*/
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*
|
|
19
19
|
* The floor is `parseLeadingCommandName` — the one simple-command parser already shared by the
|
|
20
20
|
* read-only classifier, the reversibility probe, the coarse command-name policy and the skill tool
|
|
21
|
-
* specifier. A second tokenizer would drift, and drift on a loosening face shows up as a
|
|
21
|
+
* specifier. A second tokenizer would drift, and drift on a loosening face shows up as a circumvention rather
|
|
22
22
|
* than as a test failure.
|
|
23
23
|
*
|
|
24
24
|
* ## Normalization order is load-bearing
|
|
@@ -203,7 +203,7 @@ export interface EffectivePermissionRule {
|
|
|
203
203
|
/**
|
|
204
204
|
* design/182 §7.3 — the introspection face: every personal rule with its effective status under the org
|
|
205
205
|
* overlay. An org deny does not delete a personal rule — the personal rule is the user's approval
|
|
206
|
-
* record, and the org's power is to OVERRIDE it, not to
|
|
206
|
+
* record, and the org's power is to OVERRIDE it, not to self-declare its removal — so a shadowed rule reports
|
|
207
207
|
* `shadowed-by-org` and comes back by itself when the org deny is withdrawn. A rule whose every add is
|
|
208
208
|
* tombstoned reports `removed` (its tombstone identity is still visible in the store).
|
|
209
209
|
*
|
|
@@ -159,7 +159,7 @@ export interface RuleSyncJoinDelta {
|
|
|
159
159
|
* What a write may say. Authorization discriminates on the DELTA SHAPE, not on a full snapshot: only the
|
|
160
160
|
* add arm can introduce a dot, and the delete arm carries a tombstone and no adds. A backend additionally
|
|
161
161
|
* REFUSES at runtime any delete that would introduce a new add dot — structure and runtime check together,
|
|
162
|
-
* so "pick the delete arm and
|
|
162
|
+
* so "pick the delete arm and carry across an add" is neither expressible nor accepted. The `sync-join` arm's
|
|
163
163
|
* safety does not rest on hiding the type (ruled 2026-08-10: the backend contract IS exported for
|
|
164
164
|
* out-of-repo store twins) — it rests on every inbound record inside it passing the single validator
|
|
165
165
|
* again AT THE BACKEND: the fifth door of design/179 §4's validator list closes here, not at the
|
|
@@ -287,11 +287,11 @@ export function createPermissionRulePolicy(rules, opts) {
|
|
|
287
287
|
return { action: "deny", message: ruleMessage("denied", paramDeny.ruleText, paramDeny.source) };
|
|
288
288
|
}
|
|
289
289
|
if (entry.bare.ask) {
|
|
290
|
-
return { action: "ask", message: ruleMessage("flagged", entry.bare.ask.ruleText, entry.bare.ask.source) };
|
|
290
|
+
return { action: "ask", message: ruleMessage("flagged", entry.bare.ask.ruleText, entry.bare.ask.source), matchedAskRule: entry.bare.ask.ruleText };
|
|
291
291
|
}
|
|
292
292
|
const paramAsk = matchParamRules(entry.param.ask, req.args, caps.maxScalarValueChars);
|
|
293
293
|
if (paramAsk) {
|
|
294
|
-
return { action: "ask", message: ruleMessage("flagged", paramAsk.ruleText, paramAsk.source) };
|
|
294
|
+
return { action: "ask", message: ruleMessage("flagged", paramAsk.ruleText, paramAsk.source), matchedAskRule: paramAsk.ruleText };
|
|
295
295
|
}
|
|
296
296
|
if (entry.bare.allow) {
|
|
297
297
|
return { action: "allow" };
|
|
@@ -34,7 +34,7 @@ export declare class ActiveSkillScope {
|
|
|
34
34
|
* - DENY any tool not in the INTERSECTION of every active frame's `allowTools`.
|
|
35
35
|
* - For a WRITE tool with an `allowPaths` constraint on ANY active frame, DENY a write whose
|
|
36
36
|
* canonicalized target is outside every listed prefix (reuses {@link canonicalizeTarget}, so a
|
|
37
|
-
* symlink can't
|
|
37
|
+
* symlink can't carry across a write outside the allowed paths).
|
|
38
38
|
* - A frame whose manifest could not be resolved (`kind:"unresolved"`) → DENY-ALL (fail-closed: no
|
|
39
39
|
* allowlist means nothing is allowed — NOT the bare task policy).
|
|
40
40
|
* When NO frame is active, the policy returns `allow` for everything (it only ever subtracts), leaving
|
|
@@ -27,7 +27,7 @@ export declare const DEFAULT_CONSOLIDATION_MAX_NOTES = 50;
|
|
|
27
27
|
export declare const DEFAULT_CONSOLIDATION_TIMEOUT_SEC = 30;
|
|
28
28
|
/** Normalize a note body for the EXACT-restatement check in the `<lo` near-dup cheap path. Case- + whitespace-
|
|
29
29
|
* insensitive ONLY (lower-case, collapse whitespace runs, trim); it PRESERVES every symbol/punctuation so it can
|
|
30
|
-
* NOT false-equate semantically-distinct facts. (service [365]
|
|
30
|
+
* NOT false-equate semantically-distinct facts. (service [365] falsification-style: the earlier `[^\p{L}\p{N}]`-stripping
|
|
31
31
|
* normalization made "C#"≡"C", "delta +5"≡"delta -5", "100%"≡"100" — and a textual-CONTAINMENT test on top of it
|
|
32
32
|
* also mis-judged "rate limit 1000" ⊇ "rate limit 100" (word boundary). A fire-on-every-note, no-LLM path must not
|
|
33
33
|
* make a semantic-subset call, so the cheap path now folds ONLY an exact restatement; every real near-dup defers
|