@sema-agent/core 7.33.8 → 7.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +462 -3
- package/dist/agents/cross-session-judge.d.ts +16 -7
- package/dist/agents/cross-session-judge.js +18 -15
- package/dist/agents/peer-session-drain.d.ts +4 -0
- package/dist/agents/peer-session-drain.js +5 -2
- package/dist/agents/repair-loop.d.ts +1 -1
- package/dist/agents/subagent.d.ts +3 -0
- package/dist/agents/subagent.js +12 -3
- package/dist/brain/anthropic.js +1 -0
- package/dist/brain/errors.d.ts +23 -1
- package/dist/brain/errors.js +19 -2
- package/dist/brain/open-responses.js +1 -0
- package/dist/brain/openai.js +1 -0
- package/dist/brain/status-sink.d.ts +7 -1
- package/dist/brain/stream-engine.d.ts +3 -0
- package/dist/brain/stream-engine.js +2 -1
- package/dist/brain/stream-shared.d.ts +3 -1
- package/dist/brain/stream-shared.js +16 -4
- package/dist/core/a2a.d.ts +3 -1
- package/dist/core/a2a.js +1 -1
- package/dist/core/ask-origin.d.ts +379 -154
- package/dist/core/ask-origin.js +224 -45
- package/dist/core/ask-question.js +2 -1
- package/dist/core/ask-unresolvable-notice.d.ts +5 -2
- package/dist/core/ask-unresolvable-notice.js +4 -3
- package/dist/core/auto-mode-arming.d.ts +17 -4
- package/dist/core/auto-mode-arming.js +31 -6
- package/dist/core/auto-mode-defaults.d.ts +16 -7
- package/dist/core/auto-mode-defaults.js +2 -1
- package/dist/core/auto-mode-directive.d.ts +18 -30
- package/dist/core/auto-mode-directive.js +9 -18
- package/dist/core/auto-mode-prompt.d.ts +62 -1
- package/dist/core/auto-mode-prompt.js +42 -4
- package/dist/core/auto-mode-rebuild.d.ts +2 -1
- package/dist/core/auto-mode-rebuild.js +1 -0
- package/dist/core/auto-mode.d.ts +32 -3
- package/dist/core/auto-mode.js +80 -41
- package/dist/core/checkpoint-store.d.ts +54 -35
- package/dist/core/checkpoint-store.js +58 -3
- package/dist/core/child-clamp-seats.js +1 -0
- package/dist/core/classifier-unavailable-arm.d.ts +57 -0
- package/dist/core/classifier-unavailable-arm.js +17 -0
- package/dist/core/effective-path-target.d.ts +12 -0
- package/dist/core/effective-path-target.js +18 -7
- package/dist/core/engine-notice.d.ts +53 -2
- package/dist/core/fs-write-gate-policy.d.ts +5 -1
- package/dist/core/fs-write-gate-policy.js +4 -1
- package/dist/core/gate-fold.js +19 -3
- package/dist/core/gate-lanes.js +116 -50
- package/dist/core/gate-outcome.d.ts +40 -7
- package/dist/core/gate-outcome.js +26 -4
- package/dist/core/governance-codes.d.ts +18 -2
- package/dist/core/governance-codes.js +23 -1
- package/dist/core/hooks.d.ts +53 -29
- package/dist/core/hooks.js +1 -3
- package/dist/core/mcp.js +1 -1
- package/dist/core/message-utils.d.ts +2 -1
- package/dist/core/message-utils.js +2 -1
- package/dist/core/mode-policy.d.ts +185 -0
- package/dist/core/mode-policy.js +184 -0
- package/dist/core/oracle-isolation.d.ts +3 -3
- package/dist/core/permission-rule-model.d.ts +20 -3
- package/dist/core/permission-rule-model.js +40 -3
- package/dist/core/permission-rule-provider.d.ts +22 -11
- package/dist/core/permission-rule-provider.js +17 -7
- package/dist/core/permission-rules.d.ts +23 -1
- package/dist/core/permission-rules.js +56 -9
- package/dist/core/persisted-rule-arms.d.ts +14 -10
- package/dist/core/persisted-rule-arms.js +13 -6
- package/dist/core/present-plan-tool.d.ts +1 -1
- package/dist/core/present-plan-tool.js +3 -3
- package/dist/core/read-only-shell.d.ts +19 -19
- package/dist/core/read-only-shell.js +38 -12
- package/dist/core/retired-keys.d.ts +9 -3
- package/dist/core/retired-keys.js +5 -1
- package/dist/core/runner/active-skill-scope.js +5 -4
- package/dist/core/runner/assemble-result.js +2 -2
- package/dist/core/runner/classifier-ladder.d.ts +43 -1
- package/dist/core/runner/classifier-ladder.js +29 -0
- package/dist/core/runner/contracts.d.ts +62 -38
- package/dist/core/runner/denial-limit-arms.d.ts +35 -7
- package/dist/core/runner/denial-limit-arms.js +15 -9
- package/dist/core/runner/env-capability-census.js +1 -1
- package/dist/core/runner/file-history-request.d.ts +26 -0
- package/dist/core/runner/file-history-request.js +90 -0
- package/dist/core/runner/gate-exit.d.ts +30 -4
- package/dist/core/runner/gate-exit.js +28 -7
- package/dist/core/runner/permission-rule-lanes.d.ts +11 -6
- package/dist/core/runner/permission-rule-lanes.js +55 -48
- package/dist/core/runner/persisted-path-rows.d.ts +64 -0
- package/dist/core/runner/persisted-path-rows.js +89 -0
- package/dist/core/runner/prepare-artifact.d.ts +5 -1
- package/dist/core/runner/prepare-artifact.js +9 -3
- package/dist/core/runner/prepare-ask-lane.d.ts +6 -4
- package/dist/core/runner/prepare-ask-lane.js +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +39 -13
- package/dist/core/runner/prepare-caps-and-workflow.js +58 -29
- package/dist/core/runner/prepare-file-history.d.ts +9 -3
- package/dist/core/runner/prepare-file-history.js +4 -90
- package/dist/core/runner/prepare-gate-stations.d.ts +37 -9
- package/dist/core/runner/prepare-gate-stations.js +46 -29
- package/dist/core/runner/prepare-hands-readface.d.ts +40 -17
- package/dist/core/runner/prepare-hands-readface.js +64 -53
- package/dist/core/runner/prepare-inherited-gate.d.ts +30 -23
- package/dist/core/runner/prepare-inherited-gate.js +45 -25
- package/dist/core/runner/prepare-park-ask.d.ts +6 -4
- package/dist/core/runner/prepare-park-ask.js +7 -5
- package/dist/core/runner/prepare-policy-chain.d.ts +25 -4
- package/dist/core/runner/prepare-policy-chain.js +203 -180
- package/dist/core/runner/prepare-protocol-tools.d.ts +28 -0
- package/dist/core/runner/prepare-protocol-tools.js +13 -3
- package/dist/core/runner/prepare-run-refs.d.ts +6 -6
- package/dist/core/runner/prepare-run-refs.js +9 -4
- package/dist/core/runner/prepare-safety-scan.d.ts +5 -0
- package/dist/core/runner/prepare-safety-scan.js +19 -3
- package/dist/core/runner/prepare-suspend-saga.d.ts +5 -5
- package/dist/core/runner/prepare-suspend-saga.js +6 -6
- package/dist/core/runner/prepare-task.js +56 -24
- package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -6
- package/dist/core/runner/prepare-wiring-manifest.js +5 -3
- package/dist/core/runner/read-root-grant.d.ts +9 -0
- package/dist/core/runner/read-root-grant.js +12 -0
- package/dist/core/runner/retired-runner-deps.js +1 -0
- package/dist/core/runner/run-brain-sinks.js +3 -1
- package/dist/core/runner/run-identity-wiring.js +1 -1
- package/dist/core/runner/run-leg.js +29 -18
- package/dist/core/runner/run-notification-lane.d.ts +3 -0
- package/dist/core/runner/run-notification-lane.js +3 -1
- package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
- package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
- package/dist/core/runner/run-turn-boundary.js +1 -1
- package/dist/core/runner/runtask.d.ts +1 -0
- package/dist/core/runner/runtask.js +8 -11
- package/dist/core/runner/session-mode-seats.d.ts +48 -0
- package/dist/core/runner/session-mode-seats.js +73 -0
- package/dist/core/runner/session-rule-policy.js +3 -2
- package/dist/core/runner/stream-mode-verb.d.ts +27 -0
- package/dist/core/runner/stream-mode-verb.js +19 -0
- package/dist/core/runner/turn-attachments.d.ts +3 -3
- package/dist/core/runner/turn-attachments.js +1 -1
- package/dist/core/runner/write-protection-station.d.ts +7 -1
- package/dist/core/runner/write-protection-station.js +6 -3
- package/dist/core/runner-deps.d.ts +31 -34
- package/dist/core/runtime-caps.d.ts +12 -3
- package/dist/core/safety-axis-vocab.d.ts +2 -2
- package/dist/core/safety-axis-vocab.js +1 -1
- package/dist/core/secret-env.d.ts +30 -58
- package/dist/core/secret-env.js +149 -586
- package/dist/core/sensitive-path-policy.d.ts +4 -2
- package/dist/core/sensitive-path-policy.js +41 -31
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -1
- package/dist/core/task-event.d.ts +1 -1
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +41 -89
- package/dist/core/task-stream.d.ts +16 -0
- package/dist/core/terminal-cause.js +1 -0
- package/dist/core/tighten-task-spec.d.ts +2 -2
- package/dist/core/tighten-task-spec.js +12 -14
- package/dist/core/tool-catalog-entries.js +4 -3
- package/dist/core/tool-policy.d.ts +84 -30
- package/dist/core/tool-policy.js +76 -16
- package/dist/core/tool-registry.d.ts +38 -0
- package/dist/core/tool-registry.js +25 -1
- package/dist/core/tool-roster.d.ts +17 -2
- package/dist/core/tool-roster.js +11 -1
- package/dist/core/tool-spec.d.ts +8 -9
- package/dist/core/trace.d.ts +18 -6
- package/dist/core/types.d.ts +2 -1
- package/dist/core/wiring-manifest.d.ts +31 -5
- package/dist/core/wiring-manifest.js +28 -1
- package/dist/core/write-protect.d.ts +11 -1
- package/dist/core/write-protect.js +26 -15
- package/dist/engine/execution-env/node-execution-env.d.ts +6 -1
- package/dist/engine/execution-env/node-execution-env.js +16 -3
- package/dist/engine/harness/agent-harness.js +2 -1
- package/dist/engine/harness/types.d.ts +6 -7
- package/dist/engine/llm/types.d.ts +2 -4
- package/dist/engine/loop/types.d.ts +7 -0
- package/dist/engine/lsp/node-lsp-manager.js +1 -1
- package/dist/index.d.ts +18 -8
- package/dist/index.js +15 -6
- package/dist/orchestration/workflow-governance.js +1 -1
- package/dist/orchestration/workflow-primitives.d.ts +1 -1
- package/dist/orchestration/workflow-size-guideline.js +2 -2
- package/dist/stores/file/index.d.ts +5 -3
- package/dist/stores/file/session-store.d.ts +8 -2
- package/dist/stores/file/session-store.js +5 -5
- package/dist/tools/artifact/artifact-tool.d.ts +17 -0
- package/dist/tools/artifact/artifact-tool.js +16 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +28 -7
- package/dist/tools/fs/bash-readonly-classifier.js +88 -0
- package/dist/tools/fs/containment-face.d.ts +9 -3
- package/dist/tools/fs/containment-face.js +2 -0
- package/dist/tools/fs/file-read-check.d.ts +50 -0
- package/dist/tools/fs/file-read-check.js +79 -0
- package/dist/tools/fs/fs-bash.d.ts +3 -3
- package/dist/tools/fs/fs-read.d.ts +1 -0
- package/dist/tools/fs/fs-search-tools.d.ts +2 -2
- package/dist/tools/fs/fs-search-tools.js +4 -4
- package/dist/tools/fs/fs-shared.js +20 -6
- package/dist/tools/fs/fs-write.d.ts +4 -3
- package/dist/tools/fs/fs-write.js +9 -9
- package/dist/tools/fs/index.d.ts +39 -3
- package/dist/tools/fs/index.js +38 -15
- package/dist/tools/fs/read-deny-home.d.ts +93 -0
- package/dist/tools/fs/read-deny-home.js +225 -0
- package/dist/tools/fs/read-deny.d.ts +176 -34
- package/dist/tools/fs/read-deny.js +268 -61
- package/dist/tools/fs/safety.d.ts +6 -1
- package/dist/tools/fs/safety.js +18 -5
- package/dist/tools/fs/search.d.ts +3 -0
- package/dist/tools/fs/search.js +47 -9
- package/dist/tools/fs/shell-read-check.d.ts +52 -13
- package/dist/tools/fs/shell-read-check.js +199 -20
- package/dist/tools/fs/shell-read-establish.d.ts +7 -3
- package/dist/tools/fs/shell-read-establish.js +134 -21
- package/dist/tools/fs/shell-read-identity.d.ts +7 -0
- package/dist/tools/fs/shell-read-identity.js +21 -0
- package/dist/tools/fs/shell-read-judge.d.ts +92 -32
- package/dist/tools/fs/shell-read-judge.js +43 -18
- package/dist/tools/fs/shell-read-plan.d.ts +4 -0
- package/dist/tools/fs/shell-read-plan.js +26 -4
- package/dist/tools/fs/shell-read-write-targets.d.ts +73 -0
- package/dist/tools/fs/shell-read-write-targets.js +173 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +117 -11
- package/wire-changes/core@7.34.0.json +2621 -0
- package/wire-changes/core@7.35.0.json +584 -0
|
@@ -1,6 +1,22 @@
|
|
|
1
|
+
import { posix } from "node:path";
|
|
1
2
|
import { readShellCommand } from "./shell-lexer.js";
|
|
2
3
|
import { FIND_ACTION_PRIMARIES, FIND_NEWER_PRIMARY, FIND_VALUE_PRIMARIES, FLAG_VALUE_ACCEPTS, READ_ONLY_BARE_ONLY, READ_ONLY_BARE_PROGRAMS, READ_ONLY_COMMAND_TABLE, READ_ONLY_ENV_NAMES, READ_ONLY_EXACT_FORMS, READ_ONLY_GLOB_PROGRAMS, XARGS_READ_ONLY_TARGETS, dockerRetargetDanger, } from "./read-only-shell-table.js";
|
|
3
4
|
const NOT = (reason) => ({ readOnly: false, reason });
|
|
5
|
+
export const CD_GIT_COMPOUND_SENTENCE = "This command changes directory before running a version-control command, which can pick up untrusted hooks or repository configuration from the target directory. Approve only if you trust it.";
|
|
6
|
+
export function changesDirectoryBeforeGit(shape, cwd) {
|
|
7
|
+
const programs = shape.segments.map((s) => peelBuiltinPrefix(s.argv.slice(leadingAssignmentCount(s.argv)).map((w) => w.text))[0] ?? "");
|
|
8
|
+
if (shape.segments.length < 2 || !programs.includes("cd") || !programs.includes("git"))
|
|
9
|
+
return false;
|
|
10
|
+
const cds = shape.segments.filter((_, i) => programs[i] === "cd");
|
|
11
|
+
const [cd] = cds;
|
|
12
|
+
if (cwd === undefined || cds.length !== 1 || cd === undefined || cd.redirections.length > 0 || cd.argv.length !== 2 || cd.argv[0].raw !== "cd")
|
|
13
|
+
return true;
|
|
14
|
+
const target = cd.argv[1];
|
|
15
|
+
if (target.expands !== false || !/^(?:\.\.?(?:\/|$)|\/)/.test(target.text) || /[$`*?[\\]/.test(target.raw))
|
|
16
|
+
return true;
|
|
17
|
+
const trim = (p) => p.replace(/\/+$/, "") || "/";
|
|
18
|
+
return trim(posix.resolve(cwd, target.text)) !== trim(cwd);
|
|
19
|
+
}
|
|
4
20
|
const READ_ONLY = { readOnly: true };
|
|
5
21
|
const BARE_PROGRAMS = new Set(READ_ONLY_BARE_PROGRAMS.filter((p) => !p.includes(" ")));
|
|
6
22
|
const BARE_MULTIWORD = READ_ONLY_BARE_PROGRAMS.filter((p) => p.includes(" ")).map((p) => p.split(" "));
|
|
@@ -23,15 +39,10 @@ const PRINTF_ESCAPE = new RegExp(`%[^%a-zA-Z]*${PRINTF_LENGTH}\\\\[0-7xX]`);
|
|
|
23
39
|
const PRINTF_NUMERIC = new RegExp(`%[-+ 0#']*[0-9.*]*${PRINTF_LENGTH}[diouxXeEfFgGaAn]`);
|
|
24
40
|
const JQ_DANGER_FLAG = /^['"]?-[a-zA-Z]*[fL]/;
|
|
25
41
|
const JQ_DANGER_TEXT = /--from-file|--rawfile|--slurpfile|--run-tests|--library-path|\benv\b|\$ENV\b|\binclude\b|\bimport\b/;
|
|
26
|
-
export function readOnlyShellVerdict(command, shape = readShellCommand(command)) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return NOT("no command");
|
|
31
|
-
if (shape.backgrounded)
|
|
32
|
-
return NOT("a background `&` defers execution past the approval-time reading");
|
|
33
|
-
if (shape.strayRedirection)
|
|
34
|
-
return NOT("a redirection on a piece that runs no program — the shell still opens its target");
|
|
42
|
+
export function readOnlyShellVerdict(command, shape = readShellCommand(command), cwd) {
|
|
43
|
+
const line = lineStructureObjection(shape);
|
|
44
|
+
if (line !== undefined)
|
|
45
|
+
return NOT(line);
|
|
35
46
|
for (const seg of shape.segments) {
|
|
36
47
|
if (seg.unreadable !== undefined)
|
|
37
48
|
return NOT(seg.unreadable);
|
|
@@ -46,9 +57,8 @@ export function readOnlyShellVerdict(command, shape = readShellCommand(command))
|
|
|
46
57
|
return NOT("a shell-specific expansion spelling (`=cmd`, `~[…]`)");
|
|
47
58
|
}
|
|
48
59
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return NOT("a compound that changes directory and runs git");
|
|
60
|
+
if (changesDirectoryBeforeGit(shape, cwd))
|
|
61
|
+
return NOT(CD_GIT_REASON);
|
|
52
62
|
for (const seg of shape.segments) {
|
|
53
63
|
const verdict = segmentVerdict(seg);
|
|
54
64
|
if (!verdict.readOnly)
|
|
@@ -56,6 +66,22 @@ export function readOnlyShellVerdict(command, shape = readShellCommand(command))
|
|
|
56
66
|
}
|
|
57
67
|
return READ_ONLY;
|
|
58
68
|
}
|
|
69
|
+
const CD_GIT_REASON = "a compound that changes directory and runs git";
|
|
70
|
+
function lineStructureObjection(shape) {
|
|
71
|
+
if (shape.grouped)
|
|
72
|
+
return "a subshell, group or control structure — not a flat list of simple commands";
|
|
73
|
+
if (shape.segments.length === 0)
|
|
74
|
+
return "no command";
|
|
75
|
+
if (shape.backgrounded)
|
|
76
|
+
return "a background `&` defers execution past the approval-time reading";
|
|
77
|
+
if (shape.strayRedirection)
|
|
78
|
+
return "a redirection on a piece that runs no program — the shell still opens its target";
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
export function readOnlyLineObjection(command, cwd) {
|
|
82
|
+
const shape = readShellCommand(command);
|
|
83
|
+
return lineStructureObjection(shape) ?? (changesDirectoryBeforeGit(shape, cwd) ? CD_GIT_REASON : undefined);
|
|
84
|
+
}
|
|
59
85
|
function expansionKind(w) {
|
|
60
86
|
if (w.expands === false)
|
|
61
87
|
return "none";
|
|
@@ -12,8 +12,14 @@ export declare function retiredKeyOf(bag: object, table: Readonly<Record<string,
|
|
|
12
12
|
replacement: string;
|
|
13
13
|
} | undefined;
|
|
14
14
|
/**
|
|
15
|
-
* The
|
|
16
|
-
* instead of vanishing silently — a seat that is silently
|
|
17
|
-
* the bag's type name for the sentence (`RunnerDeps`,
|
|
15
|
+
* The retired-key door: a retired seat still wired by a deployment is refused BY NAME (`config.deps_retired_key`
|
|
16
|
+
* on a deps bag, `config.spec_retired_key` on a `TaskSpec`) instead of vanishing silently — a seat that is silently
|
|
17
|
+
* dropped reads to the caller as wired. `owner` is the bag's type name for the sentence (`RunnerDeps`,
|
|
18
|
+
* `RunWorkflowToolDeps`, `TaskSpec`).
|
|
18
19
|
*/
|
|
19
20
|
export declare function assertNoRetiredKeys(bag: object, table: Readonly<Record<string, string>>, owner: string): void;
|
|
21
|
+
/** The RETIRED `TaskSpec` keys, each with the sentence that names its replacement — the table the spec door reads
|
|
22
|
+
* ({@link assertNoRetiredKeys} with owner `TaskSpec`). A caller still writing one of these is refused by name
|
|
23
|
+
* (`config.spec_retired_key`), never read as absence: a mode a caller believes it declared must not silently run
|
|
24
|
+
* as another. */
|
|
25
|
+
export declare const RETIRED_TASK_SPEC_KEYS: Readonly<Record<string, string>>;
|
|
@@ -10,6 +10,10 @@ export function assertNoRetiredKeys(bag, table, owner) {
|
|
|
10
10
|
if (hit === undefined)
|
|
11
11
|
return;
|
|
12
12
|
const e = new Error(`${owner}.${hit.key} is retired and is not read by this engine — ${hit.replacement}. Refused rather than ignored: a seat that is silently dropped reads to the deployment as wired.`);
|
|
13
|
-
e.code = "config.deps_retired_key";
|
|
13
|
+
e.code = owner === "TaskSpec" ? "config.spec_retired_key" : "config.deps_retired_key";
|
|
14
14
|
throw e;
|
|
15
15
|
}
|
|
16
|
+
export const RETIRED_TASK_SPEC_KEYS = Object.freeze({
|
|
17
|
+
autoModeRequested: 'the permission mode is one key now — write `permissionMode: "auto"`',
|
|
18
|
+
approverPosture: 'a posture never authorized anything; drop the key (the engine renders the posture from the effective mode). Releasing asks is a separate decision: `permissionMode: "bypassPermissions"`',
|
|
19
|
+
});
|
|
@@ -3,6 +3,7 @@ import { pathTargetValue } from "../tool-registry.js";
|
|
|
3
3
|
import { pathTargetBaseOf } from "../effective-path-target.js";
|
|
4
4
|
import { PATH_WRITE_TOOLS, isWithin } from "./session-rule-policy.js";
|
|
5
5
|
import { parseSkillToolEntry, skillSpecifierRejection } from "../skill-tool-specifier.js";
|
|
6
|
+
import { ruleNamesOf } from "../tool-roster.js";
|
|
6
7
|
export class ActiveSkillScope {
|
|
7
8
|
frames = [];
|
|
8
9
|
push(frame) {
|
|
@@ -22,7 +23,7 @@ export function createActiveSkillScopePolicy(opts) {
|
|
|
22
23
|
const { scope, env, rootPath, toolEffects } = opts;
|
|
23
24
|
return {
|
|
24
25
|
async check(req, signal) {
|
|
25
|
-
const
|
|
26
|
+
const names = ruleNamesOf(req);
|
|
26
27
|
const frames = scope.active();
|
|
27
28
|
if (frames.length === 0)
|
|
28
29
|
return { action: "allow" };
|
|
@@ -47,7 +48,7 @@ export function createActiveSkillScopePolicy(opts) {
|
|
|
47
48
|
}
|
|
48
49
|
return { manifest: m, byName };
|
|
49
50
|
});
|
|
50
|
-
if (perFrame.length === 0 || perFrame.some((f) => !f.byName.has(
|
|
51
|
+
if (perFrame.length === 0 || perFrame.some((f) => !names.some((n) => f.byName.has(n)))) {
|
|
51
52
|
const ids = manifests.map((m) => m.lineageId).join(", ");
|
|
52
53
|
return {
|
|
53
54
|
action: "deny",
|
|
@@ -56,7 +57,7 @@ export function createActiveSkillScopePolicy(opts) {
|
|
|
56
57
|
};
|
|
57
58
|
}
|
|
58
59
|
for (const frame of perFrame) {
|
|
59
|
-
const alternatives = frame.byName.get(
|
|
60
|
+
const alternatives = names.flatMap((n) => frame.byName.get(n) ?? []);
|
|
60
61
|
const rejections = [];
|
|
61
62
|
let admitted = false;
|
|
62
63
|
for (const entry of alternatives) {
|
|
@@ -79,7 +80,7 @@ export function createActiveSkillScopePolicy(opts) {
|
|
|
79
80
|
const declaredTarget = req.face?.pathTarget;
|
|
80
81
|
const skillScopeEligible = declaredTarget !== undefined && declaredTarget.skillScopeEligible === true;
|
|
81
82
|
if (pathConstrainingActive && !skillScopeEligible) {
|
|
82
|
-
const eff = toolEffects?.get(toolName) ?? "write";
|
|
83
|
+
const eff = toolEffects?.get(req.toolName) ?? "write";
|
|
83
84
|
if (eff !== "read") {
|
|
84
85
|
const ids = manifests.filter((m) => m.allowPaths?.length).map((m) => m.lineageId).join(", ");
|
|
85
86
|
return {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
2
|
-
import { extractErrorCode, stripErrorCodePrefix } from "../../brain/errors.js";
|
|
2
|
+
import { extractErrorCode, isProviderEchoShape, stripErrorCodePrefix } from "../../brain/errors.js";
|
|
3
3
|
import { terminalProjection } from "./terminal-projection.js";
|
|
4
4
|
import { haltConsumed } from "./halt-attribution.js";
|
|
5
5
|
import { providerReportedModel } from "../message-utils.js";
|
|
@@ -127,7 +127,7 @@ export function terminalCauseOf(final, stats, flags) {
|
|
|
127
127
|
if (final.isApiErrorMessage === true) {
|
|
128
128
|
apiFailure = {
|
|
129
129
|
...(final.apiErrorStatus !== undefined ? { status: final.apiErrorStatus } : {}),
|
|
130
|
-
...(final.requestId
|
|
130
|
+
...(typeof final.requestId === "string" && isProviderEchoShape(final.requestId) ? { requestId: final.requestId } : {}),
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
133
|
const lifted = extractErrorCode(message);
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
*/
|
|
35
35
|
import type { AssistantMessage, Model } from "../../internal/llm.js";
|
|
36
36
|
import { type AutoModeClassified } from "../auto-mode.js";
|
|
37
|
-
import type {
|
|
37
|
+
import type { ClassifierUnavailableInfo, ClassifierUnavailableSeat } from "../classifier-unavailable-arm.js";
|
|
38
|
+
import type { EngineNotice, RunnerDeps } from "../types.js";
|
|
38
39
|
/**
|
|
39
40
|
* The rungs a classification walks, in order: the classifier seat, and — under `classifierFallback: "main"` (the
|
|
40
41
|
* default) — the run's main model behind it. The knob is screened by `autoModeClassifierFallbackOf`, here and
|
|
@@ -108,6 +109,47 @@ export declare function announceClassifierRoleDefaulted(args: {
|
|
|
108
109
|
sessionId?: string;
|
|
109
110
|
runId?: string;
|
|
110
111
|
}): void;
|
|
112
|
+
/**
|
|
113
|
+
* The `classifier.unavailable` notice — a classification that could not RULE (the classifier leg failed, or its
|
|
114
|
+
* per-level budget ran out) and where the call went: `"asked"` = on down the ask chain to this leg's approval seat (a
|
|
115
|
+
* person or a durable park decides; the handed-on ask carries `requiresRealApproval`, so no automatic lane answers it),
|
|
116
|
+
* `"denied"` = refused (no approval seat: nobody to ask). Audience `user`: the refusal is the session user's own
|
|
117
|
+
* call, and on a lane with no approval face (`-p`) this line is the only place they hear why it did not run. Once
|
|
118
|
+
* per classification that could not answer, from both classifier stations (the leg's gate and an ancestor's frozen
|
|
119
|
+
* classifier in the policy fold) — a call judged by more than one classifier can carry one line per classifier. It is never a block: the denial limit does not count it, so this line is not a step toward the
|
|
120
|
+
* limit's card either.
|
|
121
|
+
*
|
|
122
|
+
* `detail: { toolName, toolCallId, cause, disposition, sessionId?, runId? }` — `cause` is the
|
|
123
|
+
* `AutoModeUnavailableCause` word (`timeout` / `error`). The sentence names the seat the station forked on — `leg` (this
|
|
124
|
+
* run's own approval seat) or `ancestor` (the frozen approver of the ancestor layer whose classifier it was) — and, on a
|
|
125
|
+
* timeout, the budget the classifier actually held (`budgetMs`, the decider's {@link AutoModeDecider.timeoutMs}; a
|
|
126
|
+
* hand-built decider states none, and the sentence names the knob alone).
|
|
127
|
+
*/
|
|
128
|
+
export declare function classifierUnavailableNotice(args: ClassifierUnavailableInfo & ClassifierUnavailableStation & {
|
|
129
|
+
sessionId?: string;
|
|
130
|
+
runId?: string;
|
|
131
|
+
}): EngineNotice;
|
|
132
|
+
/** A classifier station's fork facts, wired once per leg: whether an ask the station hands on reaches a person, and
|
|
133
|
+
* the `classifier.unavailable` notice through the one throat. Both stations (the gate, the inherited fold) take it. */
|
|
134
|
+
export declare function classifierUnavailableSeat(args: ClassifierUnavailableStation & {
|
|
135
|
+
approvalSeat: boolean;
|
|
136
|
+
onNotice: RunnerDeps["onNotice"];
|
|
137
|
+
sessionId?: string;
|
|
138
|
+
runId?: string;
|
|
139
|
+
}): ClassifierUnavailableSeat;
|
|
140
|
+
/** Which station forked (`leg` — the leg's gate, on its own approval seat; `ancestor` — an ancestor's frozen classifier in
|
|
141
|
+
* the policy fold, on that ancestor's frozen approver) and the per-level budget its classifier held, when stated.
|
|
142
|
+
*
|
|
143
|
+
* ONE question at both stations — does the route a handed-on ask takes from HERE end at a person — asked of two routes:
|
|
144
|
+
* the leg's gate hands its ask down the leg's own chain (`askReachesPerson(wiring_manifest.ask)`: a live approver, or the
|
|
145
|
+
* durable park); the inherited station resolves at the ancestor's frozen approver, whose only road to a person is being
|
|
146
|
+
* a live function (`isLiveApproverSeat`: a blanket or absent seat there ends in a refusal, and the leg's park is reached
|
|
147
|
+
* only through a live approver's `unavailable`). Reading the leg's route at the inherited station would hand the ask to a
|
|
148
|
+
* frozen seat that cannot answer it. */
|
|
149
|
+
export interface ClassifierUnavailableStation {
|
|
150
|
+
station: "leg" | "ancestor";
|
|
151
|
+
budgetMs?: number;
|
|
152
|
+
}
|
|
111
153
|
/**
|
|
112
154
|
* The classifier's REASONING SEAT, read off the model a classification is actually asked on — ONE predicate:
|
|
113
155
|
* does the wire SPELL off ({@link thinkingOffSpelled})? Yes ⇒ `off` + the verdict-sized cap (the request pays
|
|
@@ -94,6 +94,35 @@ export function announceClassifierRoleDefaulted(args) {
|
|
|
94
94
|
...(args.sessionId !== undefined ? { sessionId: args.sessionId } : {}),
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
|
+
export function classifierUnavailableNotice(args) {
|
|
98
|
+
const whose = args.station === "ancestor" ? "the classifier of the ancestor layer that asked" : "the classifier";
|
|
99
|
+
const why = args.cause === "timeout"
|
|
100
|
+
? `${whose} did not answer within its per-level budget (${args.budgetMs !== undefined ? `${args.budgetMs} ms, ` : ""}RunnerDeps.autoMode.timeoutMs)`
|
|
101
|
+
: `${whose} could not run (error)`;
|
|
102
|
+
const next = args.disposition === "asked"
|
|
103
|
+
? args.station === "ancestor"
|
|
104
|
+
? "so the call was handed on to that layer's approver rather than refused by the classifier."
|
|
105
|
+
: "so the call was handed on to a person (a live approver or a durable park) rather than refused by the classifier."
|
|
106
|
+
: (args.station === "ancestor" ? "and that layer has no live approver" : "and this run has no approval seat") +
|
|
107
|
+
", so the call was refused and did not run." +
|
|
108
|
+
(args.cause === "timeout" ? " A deployment on a slow gateway raises the budget." : "");
|
|
109
|
+
return {
|
|
110
|
+
code: "classifier.unavailable",
|
|
111
|
+
message: `auto mode could not judge "${args.toolName}": ${why}, ${next}`,
|
|
112
|
+
detail: {
|
|
113
|
+
toolName: args.toolName,
|
|
114
|
+
toolCallId: args.toolCallId,
|
|
115
|
+
cause: args.cause,
|
|
116
|
+
disposition: args.disposition,
|
|
117
|
+
...(args.sessionId !== undefined ? { sessionId: args.sessionId } : {}),
|
|
118
|
+
...(args.runId !== undefined ? { runId: args.runId } : {}),
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function classifierUnavailableSeat(args) {
|
|
123
|
+
const { approvalSeat, onNotice, sessionId, runId, station, budgetMs } = args;
|
|
124
|
+
return { approvalSeat, onUnavailable: (info) => deliverEngineNotice(onNotice, classifierUnavailableNotice({ ...info, station, ...(budgetMs !== undefined ? { budgetMs } : {}), ...(sessionId !== undefined ? { sessionId } : {}), ...(runId !== undefined ? { runId } : {}) })) };
|
|
125
|
+
}
|
|
97
126
|
function classifierFallbackAnnouncer(args) {
|
|
98
127
|
const announced = new Set();
|
|
99
128
|
return (attribution) => {
|
|
@@ -22,6 +22,11 @@ import type { Model } from "../../internal/llm.js";
|
|
|
22
22
|
import type { WorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
23
23
|
import type { ToolManifestRow } from "../../prompt-assembly/tool-catalog.js";
|
|
24
24
|
import type { CwdRef, ContainmentFace } from "../../tools/fs/index.js";
|
|
25
|
+
/** A leg's WRITE face: the face in force (read per call, following the mode's face) and whether a seat or a clamp DECLARED the fence (a checkpoint row records it; a resume keeps it). */
|
|
26
|
+
export interface WriteFaceSeat {
|
|
27
|
+
readonly inForce: () => ContainmentFace;
|
|
28
|
+
readonly fenced: boolean;
|
|
29
|
+
}
|
|
25
30
|
import type { MaterializedA2a } from "../a2a.js";
|
|
26
31
|
import type { CompactionForkContext, CompactionPhaseDurations, MaybeCompactOptions, RapidRefillState } from "../auto-compaction.js";
|
|
27
32
|
import type { AutoModeArmingRecipe } from "../auto-mode-arming.js";
|
|
@@ -47,12 +52,14 @@ import type { SystemInjectionPriority, TaskNotificationPayload } from "../task-n
|
|
|
47
52
|
import type { OnAsk, PermissionResult, ResolvedAsk, ToolCallRequest, ToolPolicy } from "../tool-policy.js";
|
|
48
53
|
import type { RemoteExecutionEnv, SnapshotId, WorkspaceHandle } from "../remote-env.js";
|
|
49
54
|
import type { ToolDisclosureManifest } from "../trace.js";
|
|
50
|
-
import type { AgentDefinition,
|
|
55
|
+
import type { AgentDefinition, Brain, CompactOutcome, ModelRef, ModelRoles, NestedUsageAccum, PermissionMode, RunnerDeps, ShellGateDoctrine, TaskEvent, TaskEventIdentity, TaskResult, TaskSpec, TaskStream, ToolActivity, ToolEffect } from "../types.js";
|
|
56
|
+
import type { PermissionModeController } from "../mode-policy.js";
|
|
51
57
|
import type { WiringManifest } from "../wiring-manifest.js";
|
|
52
58
|
import type { ActiveSkillFrame } from "./active-skill-scope.js";
|
|
53
59
|
import type { BudgetAxis } from "./assemble-result.js";
|
|
54
60
|
import type { GitStatusLaneRef } from "./git-status-frame.js";
|
|
55
61
|
import type { SessionReadFileStates } from "./session-read-file-states.js";
|
|
62
|
+
import type { LegModeCeiling, SessionModeSeats } from "./session-mode-seats.js";
|
|
56
63
|
import type { BlockedRef, OutputRef, SkillListingEntry } from "./synthetic-tools.js";
|
|
57
64
|
import type { createAttachmentState, WriteFamily } from "./turn-attachments.js";
|
|
58
65
|
import type { cacheFamilyOf } from "./usage-accounting.js";
|
|
@@ -275,10 +282,11 @@ export interface Prepared {
|
|
|
275
282
|
reminderDisclosureCounts: import("../reminder-disclosure.js").ReminderDisclosureCounts;
|
|
276
283
|
/** Whether the full shell is on this leg's roster (the inherited-gate phase's bit, asserted against the mount). */
|
|
277
284
|
fullShellReachable: boolean;
|
|
278
|
-
/** The run's
|
|
279
|
-
*
|
|
280
|
-
* reminder; nothing else reads them, and no gate decision keys on either. */
|
|
285
|
+
/** The run's approval-posture facts as prepared — the effective permission mode and the ask seam's effective
|
|
286
|
+
* answer. The first user turn renders them into the engine's own marked reminder; no gate decision keys on them. */
|
|
281
287
|
approvalPosture: import("../auto-mode-directive.js").ApprovalPostureFacts;
|
|
288
|
+
/** The leg's live permission-mode seat: the effective mode every mode consumer reads, the steer verb writes. */
|
|
289
|
+
permissionMode: PermissionModeController;
|
|
282
290
|
/** The ISOLATION-AWARE working-tree root for this task (a worktree's cwd when `isolation: "worktree"`, else
|
|
283
291
|
* `deps.rootPath ?? executionEnv.cwd`) — the same value the hands/LSP/policy/restore use. The Runner's
|
|
284
292
|
* rewind/snapshot path MUST key off THIS, not `deps.rootPath`, or a worktree-isolated turn snapshots the base
|
|
@@ -413,9 +421,9 @@ export interface Prepared {
|
|
|
413
421
|
denyNarrowingPolicy?: ToolPolicy;
|
|
414
422
|
/** The person's own persisted-rule lane (deny/ask rows), re-adjudicated on RESUME at the same
|
|
415
423
|
* station as the deny-narrowing layers: a deny row added while the call was parked vetoes the execution
|
|
416
|
-
* (`deniedBy: "persisted_rule"`)
|
|
417
|
-
*
|
|
418
|
-
* no rule store is wired. */
|
|
424
|
+
* (`deniedBy: "persisted_rule"`), and so does a call the lane could not read against the rows (re-issue and
|
|
425
|
+
* re-approve); an ask row does not (the approval is its answer), and a store that could not be read
|
|
426
|
+
* contributes no row. Absent when no rule store is wired. */
|
|
419
427
|
persistedRuleLane?: {
|
|
420
428
|
adjudicate: (req: ToolCallRequest) => Promise<import("../hooks.js").PersistedRuleAnswer>;
|
|
421
429
|
};
|
|
@@ -436,6 +444,8 @@ export interface Prepared {
|
|
|
436
444
|
};
|
|
437
445
|
/** Removes the `spec.signal` abort listener on task end (else a long-lived signal leaks listeners). */
|
|
438
446
|
releaseSignal: () => void;
|
|
447
|
+
/** The leg has stopped: its children's mode ceiling moves to the session's current leg (called once its loop settled). */
|
|
448
|
+
endModeCeiling: () => void;
|
|
439
449
|
/**
|
|
440
450
|
* End-of-leg sweep for questions this leg's gate routed to a person. Called once by the
|
|
441
451
|
* run loop at task end, on every exit path. A binding that is still present means a person ANSWERED and
|
|
@@ -1019,12 +1029,6 @@ export interface Prepared {
|
|
|
1019
1029
|
* `drain` and calls `releaseRun` with it at the run's terminal. */
|
|
1020
1030
|
runIdent: string;
|
|
1021
1031
|
};
|
|
1022
|
-
/** The live plan-mode flag (`enter_plan_mode` flips it, run-local one-way). Exposed
|
|
1023
|
-
* so the run loop's plan-mode attachment producer reads the SAME flag the write-deny enforces —
|
|
1024
|
-
* never a second source of truth. Always present (`active:false` when plan mode is unused). */
|
|
1025
|
-
planModeRef: {
|
|
1026
|
-
active: boolean;
|
|
1027
|
-
};
|
|
1028
1032
|
/** The ctx `requestStopAfterTurn` primitive's run-local mirror (one-way, set when a tool asked for a
|
|
1029
1033
|
* clean stop after the current turn). The harness latch itself is private to the loop; the turn
|
|
1030
1034
|
* boundary reads THIS to know that no next request follows — a boundary bundle collected there
|
|
@@ -1271,6 +1275,9 @@ export interface PrepareResume {
|
|
|
1271
1275
|
/** The §4.bis correctness-state snapshot to re-seed (activeTools / outputRef / nestedStats /
|
|
1272
1276
|
* readFileState). */
|
|
1273
1277
|
seed: CheckpointState;
|
|
1278
|
+
/** The decision this resume carries (the gate word and, for a review, approve / edit / reject) — read by the mode seed
|
|
1279
|
+
* (`modeRequestAfterResume`: an approved or edited `plan_review` exits a recorded plan to its pre-plan word). Absent on a prepare that resumes nothing. */
|
|
1280
|
+
outcome?: ResumeRun["outcome"];
|
|
1274
1281
|
/** How many times this task already suspended (the resumed checkpoint's
|
|
1275
1282
|
* `suspendCount`). The next suspend mints `priorSuspendCount + 1`; past `maxSuspends` it fails
|
|
1276
1283
|
* (`suspend.loop`) instead of re-suspending. Absent/0 ⇒ no prior suspends. */
|
|
@@ -1316,11 +1323,12 @@ export interface PrepareResume {
|
|
|
1316
1323
|
* Parent effective-policy inheritance (tighten-only, {@link RunInternals.inheritedGate}): the spawning
|
|
1317
1324
|
* chain's already-evaluated FINAL gate, split into two halves —
|
|
1318
1325
|
*
|
|
1319
|
-
* - **Data half** (`ancestorRules`, `shellGate`): serializable snapshots. Each ancestor's per-session
|
|
1326
|
+
* - **Data half** (`ancestorRules`, `shellGate`, `permissionMode`): serializable snapshots. Each ancestor's per-session
|
|
1320
1327
|
* permission rules ride as (sessionId, principal, rev, rules) so the child's prepare can re-read them
|
|
1321
1328
|
* LIVE (rev-monotonic — a lagging replica never loosens the snapshot) and re-compile them against the
|
|
1322
|
-
* CHILD's own env/root/effects as extra deny-narrowing layers. `shellGate`
|
|
1323
|
-
* (
|
|
1329
|
+
* CHILD's own env/root/effects as extra deny-narrowing layers. `shellGate` is the operator floor
|
|
1330
|
+
* (`"always"`): a child inherits it and cannot lift it. `permissionMode` is the ceiling a child's own
|
|
1331
|
+
* declaration is clamped under.
|
|
1324
1332
|
* - **Opaque half** (`parentConstraints`): each ancestor's live caller `ToolPolicy` plus its FROZEN
|
|
1325
1333
|
* `onAsk`, ancestors first — and, for a hook-wired ancestor, a SECOND entry: its PreToolUse screening
|
|
1326
1334
|
* face folded into a `ToolPolicy` (`createPreToolUseConstraintPolicy`), carrying its own frozen
|
|
@@ -1346,30 +1354,29 @@ export interface InheritedGate {
|
|
|
1346
1354
|
gen?: number;
|
|
1347
1355
|
rules: SessionPermissionRules;
|
|
1348
1356
|
}>;
|
|
1349
|
-
/** The chain's
|
|
1357
|
+
/** The chain's shell floor (`"always"`) — a child inherits it; its own spec cannot lift it. */
|
|
1350
1358
|
shellGate?: ShellGateDoctrine;
|
|
1351
1359
|
/**
|
|
1352
|
-
* The chain's
|
|
1353
|
-
*
|
|
1354
|
-
*
|
|
1355
|
-
*
|
|
1356
|
-
*
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
*
|
|
1361
|
-
* the
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
*
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
approverPosture?: ApproverPosture;
|
|
1360
|
+
* The chain's EFFECTIVE permission mode at spawn (data half, session-wide like every mode): the CEILING a
|
|
1361
|
+
* child's own declaration is clamped under (`TaskSpec.permissionMode` absent ⇒ inherit; declared ⇒ adopted only
|
|
1362
|
+
* where the rank table admits it, else folded and disclosed). Persists on the checkpoint's data half
|
|
1363
|
+
* (`CheckpointState.inheritedGate.permissionMode`) so a redemption in another process resumes under the same
|
|
1364
|
+
* declared mode — and re-judges the deployment's deny bits for its own leg.
|
|
1365
|
+
*/
|
|
1366
|
+
permissionMode?: PermissionMode;
|
|
1367
|
+
/**
|
|
1368
|
+
* The parent's live mode ceiling (opaque half; never serialized): the parent leg's mode while that leg runs, its
|
|
1369
|
+
* SESSION's current leg after it — the child's effective mode is DERIVED from it on every read, so a parent's steer
|
|
1370
|
+
* reaches a running child on the child's next arbitration without a forwarding frame, and the session's next leg
|
|
1371
|
+
* reaches a child that outlived the leg that spawned it (a session-scoped background child). `sessionId` is the
|
|
1372
|
+
* parent's, the address of the user notice a folded child declaration is announced on. `hold` (the engine's own
|
|
1373
|
+
* ceiling): the child is re-judged at each move of the ceiling, and a fold that produces is announced as at prepare.
|
|
1374
|
+
*/
|
|
1375
|
+
permissionModeCeiling?: {
|
|
1376
|
+
readonly current: PermissionMode;
|
|
1377
|
+
readonly sessionId: string;
|
|
1378
|
+
readonly hold?: LegModeCeiling["hold"];
|
|
1379
|
+
};
|
|
1373
1380
|
/**
|
|
1374
1381
|
* Org-memory admission freeze (ruled 2026-08-05): the spawning chain's FROZEN admitted org-scope
|
|
1375
1382
|
* set — every org memory scope the parent actually mounted (deployment-origin + admitted request).
|
|
@@ -1459,6 +1466,14 @@ export interface InheritedGate {
|
|
|
1459
1466
|
* the one every gate consults, here and on every descendant.
|
|
1460
1467
|
*/
|
|
1461
1468
|
arming?: AutoModeArmingRecipe;
|
|
1469
|
+
/**
|
|
1470
|
+
* The ancestor's live mode ceiling (opaque, never serialized; the chain's `permissionModeCeiling`): the classifier
|
|
1471
|
+
* answers the ancestor's asks only while this reads `auto` — else they go to the frozen approver. Absent (a
|
|
1472
|
+
* re-supplied entry) ⇒ the classifier answers as recorded.
|
|
1473
|
+
*/
|
|
1474
|
+
liveMode?: {
|
|
1475
|
+
readonly current: PermissionMode;
|
|
1476
|
+
};
|
|
1462
1477
|
};
|
|
1463
1478
|
/**
|
|
1464
1479
|
* Set ONLY on an entry whose `policy` is the folded form of an ancestor's PreToolUse screening face
|
|
@@ -1993,6 +2008,13 @@ export interface RunInternals {
|
|
|
1993
2008
|
* channel, same posture as every other field here.
|
|
1994
2009
|
*/
|
|
1995
2010
|
sessionReadStates?: SessionReadFileStates;
|
|
2011
|
+
/**
|
|
2012
|
+
* The Runner's per-session permission-mode seats ({@link SessionModeSeats}): the chain a leg hands its children
|
|
2013
|
+
* carries its SESSION's seat as the children's ceiling, and each leg binds itself to that seat once its prepare
|
|
2014
|
+
* commits — so a session-scoped background child follows the session's current leg. Always set by the Runner's own
|
|
2015
|
+
* prepare call (overriding any caller value); absent on a standalone prepareTask, which mints a seat of its own.
|
|
2016
|
+
*/
|
|
2017
|
+
sessionModeSeats?: SessionModeSeats;
|
|
1996
2018
|
/** Trusted system-injection sink (`Runner.runLocked` wires it to the live TaskStream queue plus the
|
|
1997
2019
|
* current harness lanes; never a public TaskSpec field), ADDRESSED TO THE SESSION rather than to this
|
|
1998
2020
|
* run: a producer wired here (a background shell, a watch, a delegation settle) keeps the closure long
|
|
@@ -2780,6 +2802,8 @@ export interface LiveHandle {
|
|
|
2780
2802
|
runId: string;
|
|
2781
2803
|
hookTimeoutMs: number;
|
|
2782
2804
|
hookIdentity: HookInvocationIdentity;
|
|
2805
|
+
/** The leg's live permission-mode seat (`TaskStream.setPermissionMode` steers it). */
|
|
2806
|
+
permissionMode: PermissionModeController;
|
|
2783
2807
|
}
|
|
2784
2808
|
/** The run body's backstop CARRIER: the effective ids and the post-prepare
|
|
2785
2809
|
* observations the run body publishes as they are minted, so the stream layer's failure backstop names the
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { type AutoModeDecider, type AutoModeDenialLimitOptions, type AutoModeDenialTracker, type DenialLimitFallback, type UnarmedDenialLimitFallback } from "../auto-mode.js";
|
|
2
2
|
import { type AutoModeArmingRecipe } from "../auto-mode-arming.js";
|
|
3
|
+
import { type ClassifierUnavailableSeat } from "../classifier-unavailable-arm.js";
|
|
3
4
|
import { type PermissionResult, type ResolvedAsk, type ToolCallRequest } from "../tool-policy.js";
|
|
4
5
|
import { type EngineNotice } from "../types.js";
|
|
5
|
-
import { type AskOrigin, type
|
|
6
|
+
import { type AskOrigin, type AskOriginFacts } from "../ask-origin.js";
|
|
6
7
|
import type { Prepared, StopForDenialLimit } from "./contracts.js";
|
|
7
8
|
/**
|
|
8
9
|
* The classifier DENIAL-LIMIT arms of the tool gate's inherited (delegation) lane, extracted
|
|
@@ -26,6 +27,10 @@ export interface InheritedAutoMode {
|
|
|
26
27
|
decider: AutoModeDecider;
|
|
27
28
|
denialTracking?: AutoModeDenialTracker;
|
|
28
29
|
arming?: AutoModeArmingRecipe;
|
|
30
|
+
/** The ancestor's live mode seat: the decider answers only while it reads `auto` (absent ⇒ as recorded). */
|
|
31
|
+
liveMode?: {
|
|
32
|
+
readonly current: import("../ask-origin.js").PermissionMode;
|
|
33
|
+
};
|
|
29
34
|
}
|
|
30
35
|
/**
|
|
31
36
|
* A re-supplied chain entry (a cross-process redemption rebuilt its ancestor's classifier from the
|
|
@@ -83,13 +88,29 @@ export declare function createDenialLimitStop(opts: {
|
|
|
83
88
|
export type InheritedAsk = Extract<PermissionResult, {
|
|
84
89
|
action: "ask";
|
|
85
90
|
}>;
|
|
91
|
+
/**
|
|
92
|
+
* The delegation half of the live gate's classifier eligibility: the SAME class row (`classifyAsk` over the
|
|
93
|
+
* ask's own members) answers for the FROZEN classifier — an ask raised by a hook, minted by an
|
|
94
|
+
* explicit `ask` permission rule, or already a denial-limit fallback is exactly as out of the frozen
|
|
95
|
+
* decider's reach as the live one's, because the person's / hook's standing decision travels with the
|
|
96
|
+
* frozen entry and a frozen decider resolving it would re-open the same hole one process over. The
|
|
97
|
+
* three GATE-HELD facts of the derivation are not this station's to judge: an ancestor's frozen
|
|
98
|
+
* classifier runs inside the policy fold, BEFORE the live gate's org layer has spoken and before its
|
|
99
|
+
* question-tool and marked-call lanes run, so they read as absent here and those asks meet their
|
|
100
|
+
* exclusions downstream, where the facts exist. Excluded asks fall through to the frozen approver
|
|
101
|
+
* chain, the delegation lane's original chain. The word is derived inside {@link judgeInheritedClassifier}
|
|
102
|
+
* — once off the incoming ask for the exclusion and the unchanged-ask branches, once off the re-spoken
|
|
103
|
+
* ask this station mints at the bound — and threaded to the request mint, never re-read off the
|
|
104
|
+
* caller-owned object there.
|
|
105
|
+
* The MANDATE marks are empty for the same reason — no tool seat of this task, no tighten of its own;
|
|
106
|
+
* what the ancestor's ask carries on ITSELF still answers, read off the decision by the one predicate.
|
|
107
|
+
*/
|
|
108
|
+
export declare const INHERITED_STATION_FACTS: AskOriginFacts;
|
|
86
109
|
/** The members of a surviving ask that ride onto the approval request with a COMPUTED value — what the
|
|
87
110
|
* four request mint stations (the gate's own and the three inherited-lane ones) spread after the seats
|
|
88
111
|
* they spell themselves: the ask's origin word and its denial-limit fallback. */
|
|
89
112
|
export interface AskRequestCarry {
|
|
90
113
|
origin?: AskOrigin;
|
|
91
|
-
/** Rides only from the gate's OWN station (the inherited stations' facts never read a rule store). */
|
|
92
|
-
ruleStoreUnreadable?: RuleStoreUnreadable;
|
|
93
114
|
denialLimitFallback?: DenialLimitFallback;
|
|
94
115
|
}
|
|
95
116
|
/** The GATE's own mint station: both members are read off the surviving decision, which is the gate's
|
|
@@ -126,8 +147,8 @@ export type InheritedClassifierJudgment<A extends InheritedAsk> =
|
|
|
126
147
|
* unchanged-ask branches it is the SAME read the exclusion judged by (a caller-owned decision with a
|
|
127
148
|
* stateful accessor cannot answer the exclusion with one word and the card with another); on the
|
|
128
149
|
* bound branch it is derived from the re-spoken ask this station minted (its own object). */
|
|
129
|
-
/** `ask` is the incoming ask unchanged (the caller's own object)
|
|
130
|
-
* it is a `deny` whose attested cause reads `unavailable`. */
|
|
150
|
+
/** `ask` is the incoming ask unchanged (the caller's own object), or — an UNAVAILABLE classification with a live frozen
|
|
151
|
+
* approver — that ask with the classifier sentence appended; without one it is a `deny` whose attested cause reads `unavailable`. */
|
|
131
152
|
| {
|
|
132
153
|
kind: "resolve";
|
|
133
154
|
ask: A;
|
|
@@ -142,8 +163,9 @@ export type InheritedClassifierJudgment<A extends InheritedAsk> =
|
|
|
142
163
|
* gate would have produced; `block` AT the bound ⇒ the ask is re-spoken in the classifier's voice as
|
|
143
164
|
* the fallback (`decisionReason:"classifier"`, `requiresRealApproval`, the member carrying counts +
|
|
144
165
|
* window) for the arm to resolve at the frozen approver; `parse_error` ⇒ the same block path with the CC
|
|
145
|
-
* parse-failure sentence (counted); `unavailable` ⇒
|
|
146
|
-
*
|
|
166
|
+
* parse-failure sentence (counted); `unavailable` ⇒ the gate's own exit (`classifierUnavailableArm`) on the
|
|
167
|
+
* frozen approver — a live one resolves the incoming ask, none ⇒ a DENY that says so (CC 2.1.250 form), never
|
|
168
|
+
* counted; excluded / no classifier ⇒ the incoming ask, unchanged. The member may ALREADY be on the incoming
|
|
147
169
|
* ask (a deeper ancestor's fallback, minted inside the nested fold — the case the exclusion names):
|
|
148
170
|
* it rides to the approver and the headless arm unchanged, whichever tracker minted it; only a
|
|
149
171
|
* tracker that counted THIS streak is reset by the person's later allow (`mintedHere`).
|
|
@@ -160,6 +182,10 @@ export type InheritedClassifierJudgment<A extends InheritedAsk> =
|
|
|
160
182
|
*/
|
|
161
183
|
export declare function judgeInheritedClassifier<A extends InheritedAsk>(opts: {
|
|
162
184
|
autoMode: InheritedAutoMode | undefined;
|
|
185
|
+
/** The leg's live permission mode (the gate's seat): under `dontAsk` / `bypassPermissions` / `plan` the decider is not
|
|
186
|
+
* dispatched (the mode's arbitration decides first). Absent ⇒ a decider on the chain is dispatched (subject to the
|
|
187
|
+
* entry's own `autoMode.liveMode`). */
|
|
188
|
+
liveMode?: () => import("../ask-origin.js").PermissionMode;
|
|
163
189
|
ask: A;
|
|
164
190
|
req: ToolCallRequest;
|
|
165
191
|
signal: AbortSignal;
|
|
@@ -167,6 +193,8 @@ export declare function judgeInheritedClassifier<A extends InheritedAsk>(opts: {
|
|
|
167
193
|
/** The rewrite the arm's allow would have carried (the ask's own `updatedInput`, the re-check's edited
|
|
168
194
|
* args) — handed IN so the allow is minted here, attested, once; absent ⇒ a bare allow. */
|
|
169
195
|
allowRewrite?: unknown;
|
|
196
|
+
/** An `unavailable` verdict's fork facts (`classifierUnavailableArm`): is the frozen approver live; who hears it. */
|
|
197
|
+
unavailableSeat?: ClassifierUnavailableSeat;
|
|
170
198
|
}): Promise<InheritedClassifierJudgment<A>>;
|
|
171
199
|
/** The resolver's answer as the settlement reads it. */
|
|
172
200
|
export type FallbackResolution = Pick<ResolvedAsk, "action" | "settlement" | "approverUnavailable">;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { classifierParseFailureDenyMessage,
|
|
1
|
+
import { classifierParseFailureDenyMessage, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, unarmedWindow } from "../auto-mode.js";
|
|
2
2
|
import { sanitizeAutoModeArmingRecipe, tightenDenialLimit } from "../auto-mode-arming.js";
|
|
3
|
+
import { classifierUnavailableArm } from "../classifier-unavailable-arm.js";
|
|
3
4
|
import { attestClassifierDenyCause, attestClassifierRound } from "../tool-policy.js";
|
|
4
5
|
import { SETTLEMENT_NOBODY_JUDGED } from "../gate-outcome.js";
|
|
5
6
|
import { deliverEngineNotice } from "../types.js";
|
|
6
7
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
7
|
-
import { askOriginOf,
|
|
8
|
+
import { askOriginOf, classifyAsk } from "../ask-origin.js";
|
|
8
9
|
export function attachRebuiltDenialTrackers(entries, denialLimit) {
|
|
9
10
|
if (entries === undefined)
|
|
10
11
|
return undefined;
|
|
@@ -58,7 +59,7 @@ export function createDenialLimitStop(opts) {
|
|
|
58
59
|
};
|
|
59
60
|
return { gateStopRef, stopForDenialLimit };
|
|
60
61
|
}
|
|
61
|
-
const INHERITED_STATION_FACTS = { contentQuestion: false, markedUnresolvable: false, org: undefined, tightened: undefined, mandateMarks: {} };
|
|
62
|
+
export const INHERITED_STATION_FACTS = { contentQuestion: false, markedUnresolvable: false, org: undefined, tightened: undefined, mandateMarks: {} };
|
|
62
63
|
function fallbackCarry(fallback, liveApprover, tracker) {
|
|
63
64
|
if (fallback === undefined)
|
|
64
65
|
return {};
|
|
@@ -67,7 +68,6 @@ function fallbackCarry(fallback, liveApprover, tracker) {
|
|
|
67
68
|
export function gateAskCarry(decision, liveApprover, tracker) {
|
|
68
69
|
return {
|
|
69
70
|
...(decision.origin !== undefined ? { origin: decision.origin } : {}),
|
|
70
|
-
...(decision.ruleStoreUnreadable !== undefined ? { ruleStoreUnreadable: decision.ruleStoreUnreadable } : {}),
|
|
71
71
|
...fallbackCarry(decision.denialLimitFallback, liveApprover, tracker),
|
|
72
72
|
};
|
|
73
73
|
}
|
|
@@ -80,22 +80,28 @@ export function inheritedAskCarry(judged, liveApprover, tracker) {
|
|
|
80
80
|
export async function judgeInheritedClassifier(opts) {
|
|
81
81
|
const { autoMode, ask, req } = opts;
|
|
82
82
|
const origin = askOriginOf(ask, INHERITED_STATION_FACTS);
|
|
83
|
-
|
|
83
|
+
const classifierStandsDown = () => {
|
|
84
|
+
const live = opts.liveMode?.();
|
|
85
|
+
return live === "dontAsk" || live === "bypassPermissions" || live === "plan" || (autoMode?.liveMode !== undefined && autoMode.liveMode.current !== "auto");
|
|
86
|
+
};
|
|
87
|
+
if (autoMode === undefined || classifierStandsDown() || !classifyAsk(ask, INHERITED_STATION_FACTS).row.classifierEligible) {
|
|
84
88
|
return { kind: "resolve", ask, fallback: ask.denialLimitFallback, mintedHere: false, origin };
|
|
85
89
|
}
|
|
86
90
|
const verdict = await autoMode.decider
|
|
87
91
|
.decide({ req, ...(ask.message !== undefined ? { askMessage: ask.message } : {}) }, opts.signal)
|
|
88
92
|
.catch(() => ({ kind: "unavailable", cause: "error" }));
|
|
93
|
+
if (verdict.kind === "allow" && classifierStandsDown())
|
|
94
|
+
return { kind: "resolve", ask, fallback: ask.denialLimitFallback, mintedHere: false, origin };
|
|
89
95
|
const attributed = (d) => (verdict.classifier !== undefined ? attestClassifierRound(d, verdict.classifier, req) : d);
|
|
90
96
|
if (verdict.kind === "allow") {
|
|
91
97
|
autoMode.denialTracking?.recordAllow();
|
|
92
98
|
return { kind: "allow", result: attributed({ action: "allow", ...(opts.allowRewrite !== undefined ? { updatedInput: opts.allowRewrite } : {}) }) };
|
|
93
99
|
}
|
|
94
100
|
if (verdict.kind === "unavailable") {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
};
|
|
101
|
+
const arm = classifierUnavailableArm({ ask, req, cause: verdict.cause, seat: opts.unavailableSeat, aborted: opts.signal.aborted });
|
|
102
|
+
if (arm.kind === "asked")
|
|
103
|
+
return { kind: "resolve", ask: arm.ask, fallback: ask.denialLimitFallback, mintedHere: false, origin };
|
|
104
|
+
return { kind: "deny", result: attributed(arm.deny) };
|
|
99
105
|
}
|
|
100
106
|
const reason = verdict.kind === "block" ? (verdict.reason ? inlineUntrusted(verdict.reason) : "") : classifierParseFailureDenyMessage(verdict.classifier);
|
|
101
107
|
const category = verdict.kind === "block" && verdict.category ? inlineUntrusted(verdict.category) : "";
|