@sema-agent/core 7.33.7 → 7.34.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 +357 -0
- 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/open-responses.js +1 -0
- package/dist/brain/openai.js +1 -0
- package/dist/brain/stream-engine.d.ts +3 -0
- package/dist/brain/stream-engine.js +2 -1
- package/dist/core/a2a.d.ts +3 -1
- package/dist/core/a2a.js +1 -1
- package/dist/core/ask-origin.d.ts +351 -154
- package/dist/core/ask-origin.js +223 -45
- 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 +50 -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 +17 -1
- package/dist/core/gate-lanes.js +111 -46
- package/dist/core/gate-outcome.d.ts +40 -7
- package/dist/core/gate-outcome.js +26 -4
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +12 -0
- package/dist/core/hooks.d.ts +50 -29
- package/dist/core/hooks.js +1 -3
- package/dist/core/mcp.js +1 -1
- package/dist/core/mode-policy.d.ts +182 -0
- package/dist/core/mode-policy.js +179 -0
- package/dist/core/oracle-isolation.d.ts +3 -3
- package/dist/core/permission-rule-model.d.ts +16 -3
- package/dist/core/permission-rule-model.js +36 -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 +35 -8
- package/dist/core/persisted-rule-arms.d.ts +10 -9
- package/dist/core/persisted-rule-arms.js +9 -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/classifier-ladder.d.ts +43 -1
- package/dist/core/runner/classifier-ladder.js +29 -0
- package/dist/core/runner/contracts.d.ts +37 -35
- package/dist/core/runner/denial-limit-arms.d.ts +30 -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/gate-exit.d.ts +30 -4
- package/dist/core/runner/gate-exit.js +28 -7
- package/dist/core/runner/permission-rule-lanes.d.ts +9 -5
- package/dist/core/runner/permission-rule-lanes.js +44 -40
- 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 +5 -3
- package/dist/core/runner/prepare-ask-lane.js +1 -1
- 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-gate-stations.d.ts +33 -6
- package/dist/core/runner/prepare-gate-stations.js +32 -19
- 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 +22 -22
- package/dist/core/runner/prepare-inherited-gate.js +37 -23
- package/dist/core/runner/prepare-park-ask.d.ts +2 -2
- package/dist/core/runner/prepare-park-ask.js +2 -1
- package/dist/core/runner/prepare-policy-chain.d.ts +22 -2
- package/dist/core/runner/prepare-policy-chain.js +39 -12
- 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 +51 -22
- 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-identity-wiring.js +1 -1
- package/dist/core/runner/run-leg.js +24 -15
- package/dist/core/runner/run-notification-lane.js +1 -0
- 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.js +3 -0
- 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 +16 -2
- package/dist/core/secret-env.js +25 -1
- package/dist/core/sensitive-path-policy.d.ts +4 -2
- package/dist/core/sensitive-path-policy.js +39 -30
- package/dist/core/spec-contract.d.ts +15 -0
- package/dist/core/spec-contract.js +19 -10
- 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 +54 -25
- package/dist/core/tool-policy.js +43 -2
- package/dist/core/tool-registry.d.ts +2 -0
- package/dist/core/tool-registry.js +3 -0
- package/dist/core/tool-spec.d.ts +8 -9
- package/dist/core/trace.d.ts +7 -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/kill-tree.js +4 -1
- package/dist/engine/execution-env/node-execution-env.d.ts +3 -0
- package/dist/engine/execution-env/node-execution-env.js +34 -35
- 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/index.d.ts +15 -5
- package/dist/index.js +12 -3
- package/dist/orchestration/run-spec.d.ts +4 -0
- package/dist/orchestration/run-spec.js +4 -3
- 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/fs-atomic.js +2 -1
- 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 +49 -0
- package/dist/tools/fs/file-read-check.js +71 -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 +42 -11
- package/dist/tools/fs/shell-read-check.js +188 -13
- 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 +3 -1
- package/test/export-surface.snapshot.json +103 -11
- package/wire-changes/core@7.33.8.json +165 -0
- package/wire-changes/core@7.34.0.json +2621 -0
|
@@ -2,28 +2,19 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
export const AUTO_MODE_DIRECTIVE = "## Auto Mode Active\n\nBias toward working without stopping for clarifying questions — when you'd normally pause to check, make the reasonable call and keep going; they'll redirect you if needed. If the user, a skill, or the shape of the task suggests they want you to ask (with AskUserQuestion or otherwise), do so. And even absent that signal, it's still fine to stop when you're genuinely blocked — unclear direction, missing input, a decision only they can make.\n\nBefore any command that could discard uncommitted work — `git checkout`/`restore`/`reset`/`clean`, `rm -rf` in the repo, restoring from a snapshot — run `git status` first and stash (with `-u` for untracked) or commit anything that's there. When staging or committing, review what's included (`git status` after a broad `git add`), and if you see anything suspicious that might reveal secrets — even if the filename looks innocuous — double-check the file's contents before pushing.";
|
|
3
3
|
export const BASH_FIRST_DIRECTIVE = "Do your work through the Bash tool wherever it can accomplish the job: read files with cat, head, or sed -n, search with grep and find, and make file changes with sed, heredocs, or short scripts, rather than using the dedicated Read, Edit, or Write tools. Fall back to a dedicated tool only when Bash genuinely cannot do the job.";
|
|
4
4
|
export const BYPASS_POSTURE_DIRECTIVE = `While bypass permissions mode is active:\n\n${BASH_FIRST_DIRECTIVE}`;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export function refuseInvalidApproverPosture(spec) {
|
|
10
|
-
if (spec.approverPosture !== undefined && !isApproverPosture(spec.approverPosture)) {
|
|
11
|
-
const e = new Error(`approverPosture must be one of ${APPROVER_POSTURES.map((w) => JSON.stringify(w)).join(", ")} when present (got ${JSON.stringify(spec.approverPosture)}) — omit it to declare no posture; an unrecognized value is refused rather than read as absence.`);
|
|
12
|
-
e.code = "config.approver_posture_invalid";
|
|
13
|
-
throw e;
|
|
14
|
-
}
|
|
15
|
-
return spec.approverPosture;
|
|
5
|
+
function postureSegmentOf(facts) {
|
|
6
|
+
if (facts.mode === "bypassPermissions" || facts.askEffective === "auto_allow")
|
|
7
|
+
return BYPASS_POSTURE_DIRECTIVE;
|
|
8
|
+
return facts.mode === "auto" ? AUTO_MODE_DIRECTIVE : undefined;
|
|
16
9
|
}
|
|
17
10
|
export function composeApprovalPostureDirective(facts, statedBodies = []) {
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
11
|
+
const owed = postureSegmentOf(facts);
|
|
12
|
+
if (owed === undefined)
|
|
20
13
|
return undefined;
|
|
21
|
-
const stated = new Set();
|
|
22
14
|
for (const body of statedBodies)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return owed.length === 0 ? undefined : owed.join("\n\n");
|
|
15
|
+
if (postureSegmentsOf(body)?.includes(owed) === true)
|
|
16
|
+
return undefined;
|
|
17
|
+
return owed;
|
|
27
18
|
}
|
|
28
19
|
const POSTURE_SEGMENTS = [AUTO_MODE_DIRECTIVE, BYPASS_POSTURE_DIRECTIVE];
|
|
29
20
|
function postureSegmentsOf(body) {
|
|
@@ -17,8 +17,13 @@ export interface AutoModeRules {
|
|
|
17
17
|
export interface BuildAutoModePromptOptions {
|
|
18
18
|
rules?: AutoModeRules;
|
|
19
19
|
/** Settings-plane deny rules (tool[/argument-pattern] strings) — rendered into the
|
|
20
|
-
* `<settings_deny_rules>` slot as the CC circumvention-catching paragraph. */
|
|
20
|
+
* `<settings_deny_rules>` slot as the CC circumvention-catching paragraph, after the engine's own lines. */
|
|
21
21
|
settingsDenyRules?: readonly string[];
|
|
22
|
+
/** The ENGINE's deny lines — its effective read-deny table as rules ({@link readDenyClassifierRules}).
|
|
23
|
+
* Rendered in the same paragraph, ahead of `settingsDenyRules`. A separate member (not pre-merged by the
|
|
24
|
+
* caller) because a persisted arming records the two halves separately: the deployment's half is compared
|
|
25
|
+
* against the redeeming deployment's own settings, the engine's half against the row's own table. */
|
|
26
|
+
derivedDenyRules?: readonly DerivedDenyRule[];
|
|
22
27
|
/** Extra session-context facts (e.g. the CC user-identity line) — appended as a
|
|
23
28
|
* `## Session Context` bullet block after the assembled document. */
|
|
24
29
|
sessionContext?: readonly string[];
|
|
@@ -28,6 +33,62 @@ export interface BuildAutoModePromptOptions {
|
|
|
28
33
|
* text may carry `$`). */
|
|
29
34
|
crossSessionMessagesRule?: string;
|
|
30
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* One engine deny line: a `Read(...)` rule in the engine's own path-rule grammar (`//` = the filesystem root,
|
|
38
|
+
* `*` within one segment, a whole-segment `**` across segments, and a rule reaches the path it names and
|
|
39
|
+
* everything under it — `parseRuleText` / `pathRuleReachOf`), plus whether ASCII letter case is ignored, which
|
|
40
|
+
* that grammar cannot spell and the paragraph states beside the rule.
|
|
41
|
+
*/
|
|
42
|
+
export interface DerivedDenyRule {
|
|
43
|
+
readonly rule: string;
|
|
44
|
+
readonly anyCase?: true;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The effective READ-deny table as the classifier's deny lines — one line per row, the row's reach in the
|
|
48
|
+
* engine's own `Read(...)` path-rule grammar.
|
|
49
|
+
*
|
|
50
|
+
* A read-deny row is a run of path SEGMENTS matched anywhere in a path — the entry itself and everything under
|
|
51
|
+
* it — and its `*` never crosses a `/` (read-deny.ts: `.ssh` refuses `~/.ssh/id_rsa` and `/srv/x/.ssh/config`
|
|
52
|
+
* alike; a `**` segment matches exactly one segment). The rule grammar reaches the path a pattern names and
|
|
53
|
+
* everything under it, so the line is `//**` + `/` + the row with every run of stars written `*` (one segment,
|
|
54
|
+
* as in the row): `.ssh` ⇒ `Read(//**\/.ssh)`, `**\/secrets/**` ⇒ `Read(//**\/*\/secrets/*)`, `*` (every
|
|
55
|
+
* path) ⇒ `Read(//**\/*)`. A row that names no segment at all (`""`, `/`) is not stated. A HOME-ANCHORED row
|
|
56
|
+
* (`~/<run>`, read-deny.ts `READ_DENY_HOME_WORD`) is the rule grammar's home word with the same reach:
|
|
57
|
+
* `~/.sema/settings.json` ⇒ `Read(~/.sema/settings.json)`.
|
|
58
|
+
*
|
|
59
|
+
* The text of a row can come from a task (a spawned task's author included) and it is spliced into a trusted
|
|
60
|
+
* prompt, so a row is stated as a rule and nothing else: every character the grammar or the prompt must not
|
|
61
|
+
* carry (see NOT_STATED) is written `*`, and an over-long pattern is cut and closed with `*` — both wider than
|
|
62
|
+
* the row, never narrower, and the result still parses as a rule. No row can start a line, leave its code
|
|
63
|
+
* span, form a tag or a second rule, or read as a sentence; the engine enforces the row as compiled either way.
|
|
64
|
+
*
|
|
65
|
+
* A row matched without regard to ASCII letter case (the deny set's default) carries `anyCase` when its stated
|
|
66
|
+
* pattern has an ASCII letter. Duplicates are folded AFTER this encoding, first seen.
|
|
67
|
+
*/
|
|
68
|
+
export declare function readDenyClassifierRules(entries: readonly {
|
|
69
|
+
readonly pattern: string;
|
|
70
|
+
readonly caseSensitive?: boolean;
|
|
71
|
+
}[], rules?: readonly {
|
|
72
|
+
readonly tool: string;
|
|
73
|
+
readonly pattern: string;
|
|
74
|
+
}[]): DerivedDenyRule[];
|
|
75
|
+
/**
|
|
76
|
+
* The `<settings_deny_rules>` paragraph for one arming, from the engine's facts — the effective deny table
|
|
77
|
+
* (`readDeny`: the compiled matcher's entries, built-in tiers + deployment + task additions; `readDenyRules`: its
|
|
78
|
+
* permission-rule rows, both faces) and the deployment's `settingsDenyRules` — exactly as {@link buildAutoModePrompt}
|
|
79
|
+
* splices it. Empty inputs ⇒ "".
|
|
80
|
+
*/
|
|
81
|
+
export declare function renderClassifierDenyRules(input: {
|
|
82
|
+
readonly readDeny?: readonly {
|
|
83
|
+
readonly pattern: string;
|
|
84
|
+
readonly caseSensitive?: boolean;
|
|
85
|
+
}[];
|
|
86
|
+
readonly readDenyRules?: readonly {
|
|
87
|
+
readonly tool: string;
|
|
88
|
+
readonly pattern: string;
|
|
89
|
+
}[];
|
|
90
|
+
readonly settingsDenyRules?: readonly string[];
|
|
91
|
+
}): string;
|
|
31
92
|
/**
|
|
32
93
|
* Assemble the classifier SYSTEM prompt (CC `KRg`, content-equivalent single string — CC splits the
|
|
33
94
|
* session-context tail into a second uncached block purely as a prompt-cache optimization).
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AUTO_MODE_DEFAULT_WINDOW_MAX_CHARS, AUTO_MODE_DEFAULT_WINDOW_MAX_ENTRIES, AUTO_MODE_DEFAULTS_SENTINEL } from "./auto-mode-defaults.js";
|
|
2
2
|
import { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./auto-mode-prompt-assets.js";
|
|
3
3
|
import { cutEnginePrefix, cutEngineSegments, flattenableUserText } from "./untrusted-text.js";
|
|
4
|
+
import { readDenyPatternRun } from "../tools/fs/read-deny.js";
|
|
4
5
|
export { AUTO_MODE_DEFAULTS_SENTINEL } from "./auto-mode-defaults.js";
|
|
5
6
|
function mergeRuleSection(user, defaultsInner) {
|
|
6
7
|
if (!user?.length)
|
|
@@ -20,10 +21,47 @@ function mergeRuleSection(user, defaultsInner) {
|
|
|
20
21
|
}
|
|
21
22
|
return out.join("\n");
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const DENY_LINE_PATTERN_MAX = 512;
|
|
25
|
+
const NOT_STATED = /[\s\p{Z}\p{Cf}`<>(){}[\]?\\\u0000-\u001f\u007f-\u009f]/u;
|
|
26
|
+
const notStated = (ch) => NOT_STATED.test(ch) || NOT_STATED.test(ch.normalize("NFKC"));
|
|
27
|
+
export function readDenyClassifierRules(entries, rules = []) {
|
|
28
|
+
const out = [];
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
const push = (line) => {
|
|
31
|
+
const key = `${line.rule}\u0000${line.anyCase === true}`;
|
|
32
|
+
if (seen.has(key))
|
|
33
|
+
return;
|
|
34
|
+
seen.add(key);
|
|
35
|
+
out.push(line);
|
|
36
|
+
};
|
|
37
|
+
for (const { pattern, caseSensitive } of entries) {
|
|
38
|
+
const { homeAnchored, run } = readDenyPatternRun(pattern);
|
|
39
|
+
const segments = run.split("/").filter((seg) => seg.length > 0);
|
|
40
|
+
if (segments.length === 0)
|
|
41
|
+
continue;
|
|
42
|
+
const stated = statedPattern(segments.join("/"));
|
|
43
|
+
push({ rule: homeAnchored ? `Read(~/${stated})` : `Read(//**/${stated})`, ...(caseSensitive !== true && /[A-Za-z]/.test(stated) ? { anyCase: true } : {}) });
|
|
44
|
+
}
|
|
45
|
+
for (const { tool, pattern } of rules) {
|
|
46
|
+
if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(tool) || pattern.length === 0)
|
|
47
|
+
continue;
|
|
48
|
+
push({ rule: `${tool}(${pattern.split("/").map((seg) => (seg === "**" ? seg : statedPattern(seg))).join("/")})` });
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function statedPattern(pattern) {
|
|
53
|
+
const chars = [...pattern].map((ch) => (notStated(ch) ? "*" : ch));
|
|
54
|
+
const cut = chars.length > DENY_LINE_PATTERN_MAX ? [...chars.slice(0, DENY_LINE_PATTERN_MAX), "*"] : chars;
|
|
55
|
+
return cut.join("").replace(/\*+/g, "*");
|
|
56
|
+
}
|
|
57
|
+
export function renderClassifierDenyRules(input) {
|
|
58
|
+
return renderSettingsDenyRules(readDenyClassifierRules(input.readDeny ?? [], input.readDenyRules ?? []), input.settingsDenyRules ?? []);
|
|
59
|
+
}
|
|
60
|
+
function renderSettingsDenyRules(derived, settings) {
|
|
61
|
+
const items = [...derived.map((d) => `\`${d.rule}\`${d.anyCase === true ? " (any ASCII letter case)" : ""}`), ...settings.map((r) => `\`${r}\``)];
|
|
62
|
+
if (items.length === 0)
|
|
25
63
|
return "";
|
|
26
|
-
return (`- User Deny Rules: The user has configured these permission deny rules: ${
|
|
64
|
+
return (`- User Deny Rules: The user has configured these permission deny rules: ${items.join(", ")}. Each rule names a tool and (optionally) an argument pattern that is already hard-blocked for that tool. ` +
|
|
27
65
|
"Block the action if it accomplishes the same effect via a different tool — e.g. using Bash with " +
|
|
28
66
|
"`python -c`, `sed -i`, `cat >`, heredocs, or similar to write or edit a file that an Edit/Write/MultiEdit deny rule covers, or otherwise routing around a deny rule by switching tools. The named tool itself is enforced separately; your job here is to catch circumvention.");
|
|
29
67
|
}
|
|
@@ -39,7 +77,7 @@ export function buildAutoModePrompt(options) {
|
|
|
39
77
|
for (const [key, re] of PAIRED_SECTIONS) {
|
|
40
78
|
out = out.replace(re, (_m, inner) => mergeRuleSection(options?.rules?.[key], inner));
|
|
41
79
|
}
|
|
42
|
-
out = out.replace("<settings_deny_rules>", () => renderSettingsDenyRules(options?.settingsDenyRules ?? []));
|
|
80
|
+
out = out.replace("<settings_deny_rules>", () => renderSettingsDenyRules(options?.derivedDenyRules ?? [], options?.settingsDenyRules ?? []));
|
|
43
81
|
const context = options?.sessionContext ?? [];
|
|
44
82
|
if (context.length > 0) {
|
|
45
83
|
out += `\n\n## Session Context\n\n${context.map((f) => `- ${f}`).join("\n")}`;
|
|
@@ -28,7 +28,8 @@ export interface AutoModeRebuildOptions {
|
|
|
28
28
|
* an affirmative claim, "this is the run's first gated action", that would simply be false. A provider
|
|
29
29
|
* that returns `[]` is DECLARING the transcript empty, which is a different statement from an engine
|
|
30
30
|
* that silently had none to show. A provider that throws fails the classification closed
|
|
31
|
-
* (`unavailable` ⇒ a
|
|
31
|
+
* (`unavailable` ⇒ on to a person where the station has an approval seat, refused with a sentence that says
|
|
32
|
+
* so where it has none), never into a blind verdict.
|
|
32
33
|
*/
|
|
33
34
|
transcript: () => readonly Message[] | Promise<readonly Message[]>;
|
|
34
35
|
}
|
|
@@ -11,6 +11,7 @@ export function rebuildAutoModeDecider(opts) {
|
|
|
11
11
|
const promptOptions = {
|
|
12
12
|
...(effective.rules !== undefined ? { rules: effective.rules } : {}),
|
|
13
13
|
...(effective.settingsDenyRules !== undefined ? { settingsDenyRules: effective.settingsDenyRules } : {}),
|
|
14
|
+
...(effective.derivedDenyRules !== undefined ? { derivedDenyRules: effective.derivedDenyRules } : {}),
|
|
14
15
|
...(effective.sessionContext !== undefined ? { sessionContext: effective.sessionContext } : {}),
|
|
15
16
|
...(effective.crossSessionMessagesRule === true ? { crossSessionMessagesRule: CROSS_SESSION_CLASSIFIER_RULE } : {}),
|
|
16
17
|
};
|
package/dist/core/auto-mode.d.ts
CHANGED
|
@@ -51,6 +51,14 @@ export declare function isAutoModeClassifierFallback(v: unknown): v is AutoModeC
|
|
|
51
51
|
* `config.auto_mode_classifier_fallback_invalid`. A typo'd knob must not read as the default: `"non"` would
|
|
52
52
|
* keep paying for the rung it meant to turn off. */
|
|
53
53
|
export declare function autoModeClassifierFallbackOf(v: unknown): AutoModeClassifierFallback;
|
|
54
|
+
/** Whether a present budget value is one the knob takes: a finite number of milliseconds in [1, 2147483647]. */
|
|
55
|
+
export declare function isAutoModeTimeoutMs(v: unknown): v is number;
|
|
56
|
+
/** Read the per-level budget knob (`RunnerDeps.autoMode.timeoutMs` / `AutoModeDeciderOptions.timeoutMs`):
|
|
57
|
+
* absent ⇒ {@link AUTO_MODE_DEFAULT_TIMEOUT_MS}; a finite number in [1, 2147483647] ⇒ itself; ANYTHING ELSE
|
|
58
|
+
* (NaN, ±Infinity, `0`, a negative, a value a host timer would fire at once, a string, an object) ⇒ a loud
|
|
59
|
+
* refusal carrying `config.auto_mode_timeout_invalid`. There is no OFF spelling: the budget bounds how long a
|
|
60
|
+
* permission gate waits, and a gate with no bound is not a setting this knob offers. */
|
|
61
|
+
export declare function autoModeTimeoutMsOf(v: unknown): number;
|
|
54
62
|
/** The classifier's verdict over one pending `ask`. Every arm may carry the classification's model
|
|
55
63
|
* attribution ({@link AutoModeClassifierRound}), present iff the decider was wired with a candidate ladder. */
|
|
56
64
|
export type AutoModeVerdict = ({
|
|
@@ -157,6 +165,12 @@ export declare function rungDidNotRule(v: AutoModeVerdict): AutoModeFallbackCaus
|
|
|
157
165
|
* NOT move keeps the CC text byte for byte.
|
|
158
166
|
*/
|
|
159
167
|
export declare function classifierUnavailableDenyMessage(toolName: string, cause: AutoModeUnavailableCause, attribution?: AutoModeClassifierRound): string;
|
|
168
|
+
/**
|
|
169
|
+
* The text an ask carries to a person when the classifier could not rule on it and the leg has an approval seat
|
|
170
|
+
* (the gate's classifier station and the inherited station): the ask's own text first — the card still says what
|
|
171
|
+
* is being asked — then why auto mode is asking at all. One writer for both stations.
|
|
172
|
+
*/
|
|
173
|
+
export declare function classifierUnavailableAskMessage(askText: string, cause: AutoModeUnavailableCause): string;
|
|
160
174
|
/**
|
|
161
175
|
* The model-facing deny text for a `parse_error` verdict — CC 2.1.259 `i7t` stem ("Auto mode could not evaluate
|
|
162
176
|
* this action and is blocking it for safety"; 2.1.250 `Ure`): the classifier answered, but not with a verdict,
|
|
@@ -256,7 +270,10 @@ export interface AutoModeDeciderOptions<T = void> {
|
|
|
256
270
|
/** Hard cap on one classification round-trip — one LADDER RUNG's DISPATCH, not the whole classification
|
|
257
271
|
* (see {@link candidates}) — and, separately, the one {@link assemble} call that precedes the walk (ONE
|
|
258
272
|
* number: a second knob would be a second answer to "how long may a classification make the gate wait").
|
|
259
|
-
*
|
|
273
|
+
* Omitted ⇒ {@link AUTO_MODE_DEFAULT_TIMEOUT_MS}. A present value that is not a finite number of at least 1
|
|
274
|
+
* and at most 2147483647 (a host timer fires at once past that) is REFUSED at construction
|
|
275
|
+
* (`config.auto_mode_timeout_invalid`, {@link autoModeTimeoutMsOf}) — never clamped, never read as the
|
|
276
|
+
* default, and there is no OFF: a permission gate always has a bound. */
|
|
260
277
|
timeoutMs?: number;
|
|
261
278
|
/**
|
|
262
279
|
* Fired once per `decide` call with the verdict the gate is about to act on and the wall time it waited
|
|
@@ -288,6 +305,10 @@ export interface AutoModeClassified {
|
|
|
288
305
|
export interface AutoModeDecider {
|
|
289
306
|
/** Never rejects. Any internal failure surfaces as `unavailable`/`parse_error` (fail-closed). */
|
|
290
307
|
decide(input: AutoModeClassifyInput, signal?: AbortSignal): Promise<AutoModeVerdict>;
|
|
308
|
+
/** The per-level budget this decider holds each rung's dispatch (and the assembly) to, in ms — the resolved
|
|
309
|
+
* {@link AutoModeDeciderOptions.timeoutMs}; what a notice about a timed-out classification names. Absent on a decider a
|
|
310
|
+
* host implements by hand. */
|
|
311
|
+
readonly timeoutMs?: number;
|
|
291
312
|
}
|
|
292
313
|
/** The per-run decider: timeout + fail-closed error mapping, nothing carried between rounds. One instance per
|
|
293
314
|
* run (it binds the run's model leg and assembled prompt); a round that failed says nothing about the next,
|
|
@@ -413,8 +434,16 @@ export interface AutoModeDenialTracker {
|
|
|
413
434
|
windowArmedAt?: number;
|
|
414
435
|
};
|
|
415
436
|
}
|
|
416
|
-
|
|
417
|
-
|
|
437
|
+
export declare function autoModeDenialLimitOf(v: unknown): Required<AutoModeDenialLimitOptions>;
|
|
438
|
+
/** The deployment face's three value knobs, read by their one readers — every leg's prepare runs it while
|
|
439
|
+
* `RunnerDeps.autoMode` is present, whatever mode the leg starts in, so a bad knob refuses the run before a steer or a
|
|
440
|
+
* parent's live ceiling can reach an arming that would have refused it. */
|
|
441
|
+
export declare function screenAutoModeKnobs(face: {
|
|
442
|
+
readonly timeoutMs?: unknown;
|
|
443
|
+
readonly classifierFallback?: unknown;
|
|
444
|
+
readonly denialLimit?: unknown;
|
|
445
|
+
}): void;
|
|
446
|
+
/** Build the per-run tracker over {@link autoModeDenialLimitOf}'s bounds (a bad knob refuses there). */
|
|
418
447
|
export declare function createAutoModeDenialTracker(opts?: AutoModeDenialLimitOptions): AutoModeDenialTracker;
|
|
419
448
|
/** The fallback ask's text — CC's sentence family, single-sourced for every mint site (the main gate
|
|
420
449
|
* and the inherited-lane arms): `Classifier denial limit exceeded, falling back to prompting: <limit
|
package/dist/core/auto-mode.js
CHANGED
|
@@ -31,6 +31,20 @@ export function autoModeClassifierFallbackOf(v) {
|
|
|
31
31
|
e.code = "config.auto_mode_classifier_fallback_invalid";
|
|
32
32
|
throw e;
|
|
33
33
|
}
|
|
34
|
+
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
35
|
+
export function isAutoModeTimeoutMs(v) {
|
|
36
|
+
return typeof v === "number" && Number.isFinite(v) && v >= 1 && v <= MAX_TIMER_DELAY_MS;
|
|
37
|
+
}
|
|
38
|
+
export function autoModeTimeoutMsOf(v) {
|
|
39
|
+
if (v === undefined)
|
|
40
|
+
return AUTO_MODE_DEFAULT_TIMEOUT_MS;
|
|
41
|
+
if (isAutoModeTimeoutMs(v))
|
|
42
|
+
return v;
|
|
43
|
+
const e = new Error(`RunnerDeps.autoMode.timeoutMs must be a number of milliseconds between 1 and ${MAX_TIMER_DELAY_MS} ` +
|
|
44
|
+
`(got ${describeRejectedValue(v)}) — omit it for the default ${AUTO_MODE_DEFAULT_TIMEOUT_MS}`);
|
|
45
|
+
e.code = "config.auto_mode_timeout_invalid";
|
|
46
|
+
throw e;
|
|
47
|
+
}
|
|
34
48
|
export const AUTO_MODE_ROUND_NOT_ASKED_CODE = "auto_mode.round_not_asked";
|
|
35
49
|
export function autoModeRoundNotAsked(reason) {
|
|
36
50
|
let detail;
|
|
@@ -70,6 +84,10 @@ export function classifierUnavailableDenyMessage(toolName, cause, attribution) {
|
|
|
70
84
|
"If it keeps failing, continue with other tasks that don't require this action and come back to it later. " +
|
|
71
85
|
"Note: reading files, searching code, and other read-only operations do not require the classifier and can still be used.");
|
|
72
86
|
}
|
|
87
|
+
export function classifierUnavailableAskMessage(askText, cause) {
|
|
88
|
+
const parenthetical = cause === "timeout" ? " (timed out)" : "";
|
|
89
|
+
return `${askText}\n\nThe auto-mode classifier is temporarily unavailable${parenthetical}, so auto mode could not judge this call; it needs an approval instead of the classifier's verdict.`;
|
|
90
|
+
}
|
|
73
91
|
export function classifierParseFailureDenyMessage(attribution) {
|
|
74
92
|
const stem = "Auto mode could not evaluate this action and is blocking it for safety";
|
|
75
93
|
return attribution === undefined ? `${stem} — the classifier's reply carried no verdict.` : `${stem} — the classifier model "${attribution.modelUsed}" replied without a verdict.`;
|
|
@@ -153,7 +171,7 @@ export function parseAutoModeResponse(text, options) {
|
|
|
153
171
|
return { kind: "block", category, reason };
|
|
154
172
|
}
|
|
155
173
|
export function createAutoModeDecider(opts) {
|
|
156
|
-
const timeoutMs = opts.timeoutMs
|
|
174
|
+
const timeoutMs = autoModeTimeoutMsOf(opts.timeoutMs);
|
|
157
175
|
if (opts.candidates !== undefined) {
|
|
158
176
|
if (opts.candidates.length === 0) {
|
|
159
177
|
throw new Error("AutoModeDeciderOptions.candidates must name at least one model — omit the member for the leg's own model, never an empty ladder");
|
|
@@ -183,7 +201,7 @@ export function createAutoModeDecider(opts) {
|
|
|
183
201
|
}
|
|
184
202
|
return verdict;
|
|
185
203
|
};
|
|
186
|
-
return { decide };
|
|
204
|
+
return { decide, timeoutMs };
|
|
187
205
|
async function decideUnreported(input, round, signal) {
|
|
188
206
|
const causes = [];
|
|
189
207
|
let verdict = { kind: "unavailable", cause: "error" };
|
|
@@ -243,42 +261,46 @@ export function createAutoModeDecider(opts) {
|
|
|
243
261
|
}
|
|
244
262
|
}
|
|
245
263
|
async function withDeadline(capMs, signal, call) {
|
|
264
|
+
if (signal?.aborted === true)
|
|
265
|
+
throw new Error("the classification was cancelled before it was dispatched");
|
|
246
266
|
let timer;
|
|
267
|
+
let onOuterAbort;
|
|
247
268
|
const inner = new AbortController();
|
|
248
|
-
const onOuterAbort = () => inner.abort();
|
|
249
|
-
if (signal !== undefined) {
|
|
250
|
-
if (signal.aborted)
|
|
251
|
-
inner.abort();
|
|
252
|
-
else
|
|
253
|
-
signal.addEventListener("abort", onOuterAbort, { once: true });
|
|
254
|
-
}
|
|
255
269
|
try {
|
|
256
270
|
return await new Promise((resolve, reject) => {
|
|
257
271
|
let settled = false;
|
|
272
|
+
const settle = (end) => {
|
|
273
|
+
if (settled)
|
|
274
|
+
return;
|
|
275
|
+
settled = true;
|
|
276
|
+
end();
|
|
277
|
+
};
|
|
278
|
+
onOuterAbort = () => {
|
|
279
|
+
inner.abort();
|
|
280
|
+
settle(() => reject(new Error("the classification was cancelled")));
|
|
281
|
+
};
|
|
282
|
+
signal?.addEventListener("abort", onOuterAbort, { once: true });
|
|
258
283
|
timer = setTimeout(() => {
|
|
259
|
-
|
|
260
|
-
settled = true;
|
|
284
|
+
settle(() => {
|
|
261
285
|
inner.abort();
|
|
262
286
|
reject(new AutoModeTimeout());
|
|
263
|
-
}
|
|
287
|
+
});
|
|
264
288
|
}, capMs);
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
});
|
|
289
|
+
let pending;
|
|
290
|
+
try {
|
|
291
|
+
pending = call(inner.signal);
|
|
292
|
+
}
|
|
293
|
+
catch (e) {
|
|
294
|
+
settle(() => reject(e));
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
pending.then((v) => settle(() => resolve(v)), (e) => settle(() => reject(e)));
|
|
276
298
|
});
|
|
277
299
|
}
|
|
278
300
|
finally {
|
|
279
301
|
if (timer !== undefined)
|
|
280
302
|
clearTimeout(timer);
|
|
281
|
-
if (signal !== undefined)
|
|
303
|
+
if (signal !== undefined && onOuterAbort !== undefined)
|
|
282
304
|
signal.removeEventListener("abort", onOuterAbort);
|
|
283
305
|
}
|
|
284
306
|
}
|
|
@@ -305,25 +327,42 @@ export function unarmedWindow(fallback) {
|
|
|
305
327
|
function countsOf(fallback) {
|
|
306
328
|
return { consecutive: fallback.consecutive, total: fallback.total, limit: fallback.limit };
|
|
307
329
|
}
|
|
308
|
-
const
|
|
309
|
-
export function
|
|
310
|
-
const
|
|
311
|
-
|
|
330
|
+
const DENIAL_LIMIT_MEMBERS = new Set(["maxConsecutive", "maxTotal", "autoDenyAfterMs"]);
|
|
331
|
+
export function autoModeDenialLimitOf(v) {
|
|
332
|
+
const refuse = (what) => {
|
|
333
|
+
const e = new Error(`RunnerDeps.autoMode.denialLimit${what}`);
|
|
334
|
+
e.code = "config.auto_mode_denial_limit_invalid";
|
|
335
|
+
throw e;
|
|
336
|
+
};
|
|
337
|
+
if (v === undefined)
|
|
338
|
+
return { ...AUTO_MODE_DENIAL_LIMIT_DEFAULTS, autoDenyAfterMs: AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS };
|
|
339
|
+
if (typeof v !== "object" || v === null || Array.isArray(v))
|
|
340
|
+
return refuse(` must be a record of bounds (got ${describeRejectedValue(v)}) — omit it for the defaults`);
|
|
341
|
+
const opts = v;
|
|
342
|
+
const stray = Object.keys(opts).find((k) => !DENIAL_LIMIT_MEMBERS.has(k));
|
|
343
|
+
if (stray !== undefined)
|
|
344
|
+
return refuse(` has no member ${JSON.stringify(stray)} — its members are maxConsecutive, maxTotal and autoDenyAfterMs`);
|
|
345
|
+
const bound = (name, dflt) => {
|
|
346
|
+
const b = opts[name];
|
|
347
|
+
if (b === undefined)
|
|
312
348
|
return dflt;
|
|
313
|
-
|
|
314
|
-
throw new Error(`RunnerDeps.autoMode.denialLimit.${name} must be a positive integer (got ${JSON.stringify(v)}) — omit it for the default ${dflt}`);
|
|
315
|
-
}
|
|
316
|
-
return v;
|
|
349
|
+
return typeof b === "number" && Number.isInteger(b) && b >= 1 ? b : refuse(`.${name} must be a positive integer (got ${describeRejectedValue(b)}) — omit it for the default ${dflt}`);
|
|
317
350
|
};
|
|
318
|
-
const
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
351
|
+
const w = opts["autoDenyAfterMs"];
|
|
352
|
+
const autoDenyAfterMs = w === undefined
|
|
353
|
+
? AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS
|
|
354
|
+
: typeof w === "number" && Number.isInteger(w) && w >= 0 && w <= MAX_TIMER_DELAY_MS
|
|
355
|
+
? w
|
|
356
|
+
: refuse(`.autoDenyAfterMs must be an integer between 0 and ${MAX_TIMER_DELAY_MS} (got ${describeRejectedValue(w)}) — 0 disarms the window; omit it for the default ${AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS}`);
|
|
357
|
+
return { maxConsecutive: bound("maxConsecutive", AUTO_MODE_DENIAL_LIMIT_DEFAULTS.maxConsecutive), maxTotal: bound("maxTotal", AUTO_MODE_DENIAL_LIMIT_DEFAULTS.maxTotal), autoDenyAfterMs };
|
|
358
|
+
}
|
|
359
|
+
export function screenAutoModeKnobs(face) {
|
|
360
|
+
autoModeTimeoutMsOf(face.timeoutMs);
|
|
361
|
+
autoModeClassifierFallbackOf(face.classifierFallback);
|
|
362
|
+
autoModeDenialLimitOf(face.denialLimit);
|
|
363
|
+
}
|
|
364
|
+
export function createAutoModeDenialTracker(opts) {
|
|
365
|
+
const { maxConsecutive, maxTotal, autoDenyAfterMs } = autoModeDenialLimitOf(opts);
|
|
327
366
|
let consecutive = 0;
|
|
328
367
|
let total = 0;
|
|
329
368
|
let timedFallbackShown = false;
|
|
@@ -7,7 +7,7 @@ import type { ReadEntry } from "../tools/fs/safety.js";
|
|
|
7
7
|
import type { RepairBundle } from "../agents/repair-loop.js";
|
|
8
8
|
import type { WorkspaceHandle } from "./remote-env.js";
|
|
9
9
|
import type { SessionPermissionRules } from "./session-policy-store.js";
|
|
10
|
-
import type {
|
|
10
|
+
import type { NestedUsageAccum, PermissionMode, ShellGateDoctrine, TaskResult } from "./types.js";
|
|
11
11
|
import type { ActiveWorktreeSession } from "../internal/harness-types.js";
|
|
12
12
|
/**
|
|
13
13
|
* design/45 — the **durable-checkpoint** primitive: cross-process / resumable `suspend`/`resume`.
|
|
@@ -130,11 +130,9 @@ export interface RiskDescriptor {
|
|
|
130
130
|
};
|
|
131
131
|
/** The tool whose call is gated (mirrors the gate's `toolName`). */
|
|
132
132
|
toolName: string;
|
|
133
|
-
/** The
|
|
134
|
-
* seat
|
|
135
|
-
*
|
|
136
|
-
* Absent for per-tool safety-axis / plain policy asks. Answers "which doctrine was live when this gate
|
|
137
|
-
* fired" from the persisted row alone — three artifact layers could not answer that in the field. */
|
|
133
|
+
/** The operator's shell floor (`"always"`), present ONLY when this ask was minted through the engine's shell
|
|
134
|
+
* seat AND a floor was in force. Absent for a shape-tier ask with no floor, and for per-tool safety-axis /
|
|
135
|
+
* plain policy asks. Answers "was an operator floor live when this gate fired" from the persisted row alone. */
|
|
138
136
|
shellGateDoctrine?: ShellGateDoctrine;
|
|
139
137
|
/** A REDACTED, length-capped ONE-LINE summary of the call (the command for a shell gate; a brief key-arg
|
|
140
138
|
* digest otherwise) for the inbox preview. NEUTRALIZED via
|
|
@@ -816,6 +814,9 @@ export type PendingAction = {
|
|
|
816
814
|
* when a person's rule also speaks — never a substitute for this one). Display metadata: no gate, CAS or
|
|
817
815
|
* resume path reads it. */
|
|
818
816
|
mandated?: true;
|
|
817
|
+
/** The PARK twin of `AskRequest.authoredBy`: the deployment's own governance layer minted this parked question — a
|
|
818
|
+
* person's under every mode, whatever grant or rule the redeeming host holds. Display / seat metadata. */
|
|
819
|
+
authoredBy?: "deployment";
|
|
819
820
|
/** The PARK twin of `AskRequest.mandate` (same derivation, same precedent): the table word the bit stands on,
|
|
820
821
|
* present only when it is the whole reason. Written only as a member of the closed set. */
|
|
821
822
|
mandate?: import("./ask-origin.js").PersistedRuleMandate;
|
|
@@ -833,10 +834,6 @@ export type PendingAction = {
|
|
|
833
834
|
* Written only when it is a member of the set; a pre-existing row reads absent (unreported).
|
|
834
835
|
* Display metadata — the resume belts keep reading the gate's own bit. */
|
|
835
836
|
origin?: import("./ask-origin.js").AskOrigin;
|
|
836
|
-
/** #688 C3 (additive, same precedent as `origin`) — the mechanism behind an `origin: "rule_store_unavailable"`
|
|
837
|
-
* (`store` / `call`, {@link import("./ask-origin.js").RULE_STORE_UNREADABLE_KINDS}). Written only when it is a
|
|
838
|
-
* member of the set; a pre-existing row reads absent. Display metadata for the durable card. */
|
|
839
|
-
ruleStoreUnreadable?: import("./ask-origin.js").RuleStoreUnreadable;
|
|
840
837
|
/**
|
|
841
838
|
* design/80 D-1 §2 (slice 1a.2): the server-minted **opaque** boundInputHash of {@link args} — a
|
|
842
839
|
* SHA-256 (hex) via {@link import("./canonical-json.js").boundInputHashOf}, computed ONCE here at
|
|
@@ -1098,7 +1095,7 @@ export interface CheckpointState {
|
|
|
1098
1095
|
* Parent effective-policy inheritance (tighten-only): the durable projection of the run's
|
|
1099
1096
|
* {@link import("./runner/contracts.js").InheritedGate}. Only the serializable DATA half persists —
|
|
1100
1097
|
* `ancestorRules` (each ancestor's session-rule snapshot: sessionId/principal/rev/rules, re-compiled on
|
|
1101
|
-
* resume against the resumed leg's env)
|
|
1098
|
+
* resume against the resumed leg's env), `shellGate` (the chain's operator floor) and `permissionMode`. The OPAQUE
|
|
1102
1099
|
* half (`parentConstraints` — live ToolPolicy/onAsk closures) CANNOT persist; `requiresParentConstraint`
|
|
1103
1100
|
* records only its PRESENCE, so the resume pre-CAS ladder can fail-loud (`resume.parent_constraint_missing`,
|
|
1104
1101
|
* checkpoint stays `pending`) when the resuming caller did not re-supply the closures via
|
|
@@ -1115,27 +1112,18 @@ export interface CheckpointState {
|
|
|
1115
1112
|
rules: SessionPermissionRules;
|
|
1116
1113
|
}>;
|
|
1117
1114
|
shellGate?: ShellGateDoctrine;
|
|
1118
|
-
/** The chain's
|
|
1119
|
-
*
|
|
1120
|
-
*
|
|
1121
|
-
*
|
|
1122
|
-
*
|
|
1123
|
-
*
|
|
1124
|
-
*
|
|
1125
|
-
*
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
*
|
|
1129
|
-
|
|
1130
|
-
/** The chain's declared APPROVER POSTURE at suspend (data half, same law as `shellGate`): the word
|
|
1131
|
-
* the suspended leg was told to render — its own seat, the one its live chain carried, or the one an
|
|
1132
|
-
* earlier suspend of the same chain recorded. A resume leg reads it beside the re-supplied seat and
|
|
1133
|
-
* the waking chain, so a redemption in another process states the same posture the suspend leg did.
|
|
1134
|
-
* It authorizes nothing (the gate never reads it) and it renders nothing on a resume leg either —
|
|
1135
|
-
* the first-turn frame that carries it is already in the restored transcript; what it feeds is the
|
|
1136
|
-
* chain the resumed leg hands its OWN children. Absent on older checkpoints and on tasks that
|
|
1137
|
-
* declared no posture (byte-identical row). */
|
|
1138
|
-
approverPosture?: ApproverPosture;
|
|
1115
|
+
/** The chain's DECLARED permission mode at suspend (data half, same law as `shellGate`): the suspended leg's
|
|
1116
|
+
* own effective declaration — its request clamped under its parent's ceiling — before the deployment's fold.
|
|
1117
|
+
* A resume leg reads it as the CEILING its re-passed spec is clamped under (tighten-only, the shell floor's
|
|
1118
|
+
* law) and re-judges the deny bits and the classifier face for its own leg: a `bypassPermissions` row does not
|
|
1119
|
+
* revive the mode where the redeeming deployment denies it, and an `auto` row arms only where a face is
|
|
1120
|
+
* wired. Absent on a task that declared nothing and inherited nothing (`default`). Rows written before this
|
|
1121
|
+
* member carried `autoModeRequested` / `approverPosture` / a doctrine `shellGate`; the resume leg migrates
|
|
1122
|
+
* them once, announced (`resume.legacy_mode_seed_migrated`), and the next suspend writes this member. */
|
|
1123
|
+
permissionMode?: PermissionMode;
|
|
1124
|
+
/** Beside a recorded `plan`: the declared word the leg had when a steer or the model's `EnterPlanMode` moved it into
|
|
1125
|
+
* plan — the mode an approved or edited `plan_review` resumes under ({@link import("./mode-policy.js").modeRequestAfterResume}). */
|
|
1126
|
+
prePlanMode?: PermissionMode;
|
|
1139
1127
|
/** Org-memory admission freeze (ruled 2026-08-05): the chain's admitted org-scope set at
|
|
1140
1128
|
* suspend (data half, plain strings). The resume leg folds it seed ∩ live (tighten-only) and
|
|
1141
1129
|
* re-runs admission under it — a resume must never widen the delegation freeze. Absent on
|
|
@@ -1950,6 +1938,8 @@ export interface CheckpointSummary {
|
|
|
1950
1938
|
* OMITTED (the {@link toolCallId} absence-shape contract), screened to the literal `true` — a pre-#1079 row
|
|
1951
1939
|
* reads absent (unreported). ECHO-ONLY; nothing gates on it. */
|
|
1952
1940
|
mandated?: true;
|
|
1941
|
+
/** The row's `PendingAction.tool_approval.authoredBy`, echoed as the one literal. ECHO-ONLY. */
|
|
1942
|
+
authoredBy?: "deployment";
|
|
1953
1943
|
/** The row's `PendingAction.tool_approval.mandate`, echoed ONLY as a member of the closed set. ECHO-ONLY. */
|
|
1954
1944
|
mandate?: import("./ask-origin.js").PersistedRuleMandate;
|
|
1955
1945
|
/** The row's `PendingAction.tool_approval.denialLimitFallback`, projected BESIDE the preview
|
|
@@ -1962,9 +1952,6 @@ export interface CheckpointSummary {
|
|
|
1962
1952
|
* value is a member of the closed set ({@link import("./ask-origin.js").isAskOrigin}) — a bad value
|
|
1963
1953
|
* reads as absent, never as a word. OMITTED when the row carries none (a pre-existing row). */
|
|
1964
1954
|
origin?: import("./ask-origin.js").AskOrigin;
|
|
1965
|
-
/** The row's `PendingAction.tool_approval.ruleStoreUnreadable` (#688 C3), echoed ONLY when the stored word is a
|
|
1966
|
-
* member of the closed set ({@link import("./ask-origin.js").isRuleStoreUnreadable}); OMITTED otherwise. */
|
|
1967
|
-
ruleStoreUnreadable?: import("./ask-origin.js").RuleStoreUnreadable;
|
|
1968
1955
|
/**
|
|
1969
1956
|
* PRESENCE ONLY — this pending `tool_approval` row's payload carries at least one DIRECTIONAL
|
|
1970
1957
|
* format control (the {@link import("./tool-policy.js").AskRequest.hasBidiControls} character class;
|
|
@@ -3027,3 +3014,31 @@ export declare class InMemoryCheckpointStore implements CheckpointStore {
|
|
|
3027
3014
|
/** Test/inspection helper: number of stored checkpoints. */
|
|
3028
3015
|
get size(): number;
|
|
3029
3016
|
}
|
|
3017
|
+
/** What {@link migrateLegacyModeSeed} read off a checkpoint row's `inheritedGate`: the mode and the floor the resume
|
|
3018
|
+
* leg inherits, and — when the row carried a pre-mode key — what was migrated and what was dropped. */
|
|
3019
|
+
export interface LegacyModeSeedMigration {
|
|
3020
|
+
permissionMode?: PermissionMode;
|
|
3021
|
+
prePlanMode?: PermissionMode;
|
|
3022
|
+
shellGate?: ShellGateDoctrine;
|
|
3023
|
+
/** Present iff the row carried a retired key (`autoModeRequested` / `approverPosture` / a doctrine `shellGate`). */
|
|
3024
|
+
migrated?: {
|
|
3025
|
+
permissionMode?: PermissionMode;
|
|
3026
|
+
droppedKeys: readonly string[];
|
|
3027
|
+
};
|
|
3028
|
+
}
|
|
3029
|
+
/**
|
|
3030
|
+
* Read a row's mode seed ONCE, migrating the pre-7.34.0 keys the row may still carry. The matrix, key by key:
|
|
3031
|
+
* - `permissionMode` present ⇒ it must be a member of the closed set (else refused, `config.permission_mode_invalid`,
|
|
3032
|
+
* without reading the retired keys); the two retired mode keys beside it are DROPPED unvalidated (the new key is
|
|
3033
|
+
* the truth), `shellGate` follows the value rule below;
|
|
3034
|
+
* - `autoModeRequested`: absent / `false` ⇒ no mode from it; `true` ⇒ `auto`; anything else ⇒ refused
|
|
3035
|
+
* (`resume.legacy_mode_seed`);
|
|
3036
|
+
* - `approverPosture`: any value ⇒ dropped (it never authorized anything; the mode comes from the request / chain,
|
|
3037
|
+
* and a `"bypass"` posture beside `autoModeRequested: true` reads `auto` — a posture never becomes an executable
|
|
3038
|
+
* bypass);
|
|
3039
|
+
* - `shellGate`: absent ⇒ none; `"always"` ⇒ kept (a live key, not a retired encoding); `"off"` / `"classify"` ⇒
|
|
3040
|
+
* dropped (no floor); anything else ⇒ refused (`resume.legacy_mode_seed`).
|
|
3041
|
+
* Idempotent: a row the resume leg writes back carries `permissionMode` and the floor only, so a second read migrates
|
|
3042
|
+
* nothing. Migration precedes any retired-key door, so a window row is read rather than refused.
|
|
3043
|
+
*/
|
|
3044
|
+
export declare function migrateLegacyModeSeed(gate: object | undefined): LegacyModeSeedMigration;
|