@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
package/dist/core/gate-lanes.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { attestClassifierDenyCause, classifierDenyCauseOf, decisionText, describeThrown } from "./tool-policy.js";
|
|
2
|
-
import {
|
|
3
|
-
import { classifierParseFailureDenyMessage,
|
|
2
|
+
import { arbitrateByMode, classifyAsk, fileCommandOptionsSentence, mandateDisclosureOf, persistedRuleMandateOf, ruleClearanceOf, safetyAxisOf, stampAskOrigin } from "./ask-origin.js";
|
|
3
|
+
import { classifierParseFailureDenyMessage, denialLimitFallbackMessage, unarmedWindow } from "./auto-mode.js";
|
|
4
|
+
import { classifierUnavailableArm } from "./classifier-unavailable-arm.js";
|
|
4
5
|
import { SETTLEMENT_NOBODY_JUDGED } from "./gate-outcome.js";
|
|
5
6
|
import { inlineUntrusted } from "./untrusted-text.js";
|
|
6
7
|
import { isRuleBehavior } from "./permission-rule-model.js";
|
|
7
8
|
import { ASK_USER_QUESTION_TOOL_NAME } from "./ask-question.js";
|
|
8
9
|
import { applyPersistedTightening, disclosedRuleSet } from "./persisted-rule-arms.js";
|
|
9
|
-
import { readOnlyShellVerdict } from "./read-only-shell.js";
|
|
10
|
+
import { CD_GIT_COMPOUND_SENTENCE, changesDirectoryBeforeGit, readOnlyShellVerdict } from "./read-only-shell.js";
|
|
11
|
+
import { readShellCommand } from "./shell-lexer.js";
|
|
10
12
|
import { COMMAND_RULE_TOOL } from "./permission-rule-model.js";
|
|
11
13
|
import { catalogRuleFaceOf } from "./tool-registry.js";
|
|
14
|
+
import { modeAcceptsFileCommand } from "./mode-policy.js";
|
|
12
15
|
import { ORG_ADJUDICATION_TIMEOUT_MS, ORG_RULE_DECISION_REASON, ORG_UNAVAILABLE_DECISION_REASON, settleOrgVerdictWithin } from "./permission-rule-org.js";
|
|
13
16
|
import { exitGate } from "./runner/gate-exit.js";
|
|
14
17
|
import { traceHookCrash } from "./gate-crash-trace.js";
|
|
@@ -165,14 +168,19 @@ export async function runGateLanes(pass) {
|
|
|
165
168
|
writeProtected: pass.writeProtected === true,
|
|
166
169
|
writeProtectionUnresolved: pass.writeProtectionUnresolved === true,
|
|
167
170
|
});
|
|
168
|
-
const askOriginFacts = (org,
|
|
171
|
+
const askOriginFacts = (org, ruleCallUnreadable) => ({
|
|
169
172
|
contentQuestion: toolName === ASK_USER_QUESTION_TOOL_NAME,
|
|
170
173
|
markedUnresolvable: markedUnresolvable(),
|
|
171
174
|
org,
|
|
172
|
-
...(
|
|
175
|
+
...(ruleCallUnreadable ? { ruleCallUnreadable } : {}),
|
|
173
176
|
tightened: pass.tightenedBy,
|
|
174
177
|
mandateMarks: mandateMarksNow(),
|
|
178
|
+
...(input.toolMarkSources !== undefined ? { toolMarkSources: input.toolMarkSources } : {}),
|
|
179
|
+
...(input.a2aIsolatePeers === true ? { a2aIsolatePeers: true } : {}),
|
|
175
180
|
});
|
|
181
|
+
const classifyStanding = (unstamped, facts) => {
|
|
182
|
+
pass.askClassification = classifyAsk(unstamped, facts);
|
|
183
|
+
};
|
|
176
184
|
let orgTightenCount = 0;
|
|
177
185
|
let orgEvidence = (() => {
|
|
178
186
|
const w = input.orgRules === undefined ? "not_wired" : "not_adjudicated";
|
|
@@ -208,18 +216,14 @@ export async function runGateLanes(pass) {
|
|
|
208
216
|
orgEvidence = { revisionAbsent: "unavailable", ruleAbsent: "unavailable" };
|
|
209
217
|
orgRealApprovalRequired = true;
|
|
210
218
|
await notifier.notifyAsync(() => input.orgRules?.onUnavailable?.({ toolName, toolCallId, message: answer.disclosures.join("; ") }), "toolGate.orgSnapshotUnavailable");
|
|
219
|
+
const orgUnavailableMessage = `tool "${toolName}" needs approval: this deployment is org-governed and cannot currently adjudicate ` +
|
|
220
|
+
`against an organization policy snapshot — every allow tightens to a real approval until it can`;
|
|
211
221
|
if (decided.action === "allow") {
|
|
212
|
-
decided = {
|
|
213
|
-
action: "ask",
|
|
214
|
-
message: `tool "${toolName}" needs approval: this deployment is org-governed and cannot currently adjudicate ` +
|
|
215
|
-
`against an organization policy snapshot — every allow tightens to a real approval until it can`,
|
|
216
|
-
decisionReason: ORG_UNAVAILABLE_DECISION_REASON,
|
|
217
|
-
requiresRealApproval: true,
|
|
218
|
-
};
|
|
222
|
+
decided = { action: "ask", message: orgUnavailableMessage, decisionReason: ORG_UNAVAILABLE_DECISION_REASON, requiresRealApproval: true };
|
|
219
223
|
orgTightenCount += 1;
|
|
220
224
|
}
|
|
221
225
|
else if (decided.action === "ask" && decided.requiresRealApproval !== true) {
|
|
222
|
-
decided = { ...decided, requiresRealApproval: true };
|
|
226
|
+
decided = { ...decided, requiresRealApproval: true, ...(pass.tightenedBy !== undefined ? { message: orgUnavailableMessage } : {}) };
|
|
223
227
|
orgTightenCount += 1;
|
|
224
228
|
}
|
|
225
229
|
return { decision: decided, org: "unavailable" };
|
|
@@ -241,23 +245,20 @@ export async function runGateLanes(pass) {
|
|
|
241
245
|
return { decision: decided, org: undefined };
|
|
242
246
|
orgTightenCount += 1;
|
|
243
247
|
orgRealApprovalRequired = true;
|
|
248
|
+
const orgRuleMessage = unreadable !== undefined
|
|
249
|
+
? `an organization policy rule (${rule}) may reach this call, which could not be read against it (${unreadable}) — a person must decide`
|
|
250
|
+
: `an organization policy rule (${rule}) requires approval for this call`;
|
|
244
251
|
return {
|
|
245
252
|
decision: decided.action === "ask"
|
|
246
|
-
?
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
message: unreadable !== undefined
|
|
250
|
-
? `an organization policy rule (${rule}) may reach this call, which could not be read against it (${unreadable}) — a person must decide`
|
|
251
|
-
: `an organization policy rule (${rule}) requires approval for this call`,
|
|
252
|
-
decisionReason: ORG_RULE_DECISION_REASON,
|
|
253
|
-
requiresRealApproval: true,
|
|
254
|
-
},
|
|
253
|
+
?
|
|
254
|
+
{ ...decided, requiresRealApproval: true, ...(decided.requiresRealApproval !== true && pass.tightenedBy !== undefined ? { message: orgRuleMessage } : {}) }
|
|
255
|
+
: { action: "ask", message: orgRuleMessage, decisionReason: ORG_RULE_DECISION_REASON, requiresRealApproval: true },
|
|
255
256
|
org: "rule",
|
|
256
257
|
};
|
|
257
258
|
};
|
|
258
259
|
const orgAnswer = await applyOrgLayer(pass.decision, pass.policyRewrite !== undefined ? pass.policyRewrite : pass.currentInput);
|
|
259
260
|
pass.decision = orgAnswer.decision;
|
|
260
|
-
const
|
|
261
|
+
const ruleCallUnreadableOf = (lane) => lane?.unreadable === true && lane.reason !== undefined;
|
|
261
262
|
const readPersistedRules = async (req) => {
|
|
262
263
|
let pendingHit;
|
|
263
264
|
try {
|
|
@@ -348,10 +349,10 @@ export async function runGateLanes(pass) {
|
|
|
348
349
|
};
|
|
349
350
|
}
|
|
350
351
|
}
|
|
351
|
-
const originFacts = askOriginFacts(orgAnswer.org,
|
|
352
|
+
const originFacts = askOriginFacts(orgAnswer.org, ruleCallUnreadableOf(laneAnswer));
|
|
352
353
|
if (pass.decision.action === "ask") {
|
|
353
|
-
const {
|
|
354
|
-
|
|
354
|
+
const stamped = { ...pass.decision, ruleEvidence: mintRuleEvidence(personalEvidence), ...(laneCoverage !== undefined ? { segmentCoverage: laneCoverage } : {}) };
|
|
355
|
+
classifyStanding(stamped, originFacts);
|
|
355
356
|
pass.decision = stampAskOrigin(stamped, originFacts);
|
|
356
357
|
}
|
|
357
358
|
if (allowLayerMayClear() && pass.decision.action === "ask" && ruleMayClear() && pass.probeBoundaryDeclined !== true && pass.req.toolName === COMMAND_RULE_TOOL) {
|
|
@@ -359,7 +360,8 @@ export async function runGateLanes(pass) {
|
|
|
359
360
|
const args = pass.req.args;
|
|
360
361
|
const command = typeof args === "object" && args !== null && !Array.isArray(args) ? args[commandParam] : undefined;
|
|
361
362
|
const backgrounded = typeof args === "object" && args !== null && args.run_in_background === true;
|
|
362
|
-
|
|
363
|
+
const shape = typeof command === "string" ? readShellCommand(command) : undefined;
|
|
364
|
+
if (typeof command === "string" && shape !== undefined && !backgrounded && readOnlyShellVerdict(command, shape, input.trackedCwd?.()).readOnly) {
|
|
363
365
|
pass.decision = {
|
|
364
366
|
action: "allow",
|
|
365
367
|
message: "the command is read-only (upstream's read-only command tables) — allowed without asking",
|
|
@@ -368,15 +370,21 @@ export async function runGateLanes(pass) {
|
|
|
368
370
|
};
|
|
369
371
|
await notifier.notifyAsync(() => input.onReadOnlyAllowed?.({ toolName: pass.req.toolName, toolCallId, command }), "toolGate.readOnlyAllowed");
|
|
370
372
|
}
|
|
373
|
+
else if (shape !== undefined && pass.tightenedBy === "shell_gate" && changesDirectoryBeforeGit(shape, input.trackedCwd?.())) {
|
|
374
|
+
pass.decision = { ...pass.decision, message: CD_GIT_COMPOUND_SENTENCE };
|
|
375
|
+
}
|
|
371
376
|
}
|
|
372
377
|
if (pass.decision.action === "ask")
|
|
373
378
|
ledger.supersede();
|
|
374
|
-
if (input.autoMode && pass.decision.action === "ask" && pass.decision.origin !== undefined &&
|
|
375
|
-
const
|
|
376
|
-
|
|
379
|
+
if (input.autoMode && pass.decision.action === "ask" && pass.decision.origin !== undefined && pass.askClassification?.row.classifierEligible === true && (input.permissionMode === undefined || input.permissionMode() === "auto")) {
|
|
380
|
+
const classifySignal = callSignal ?? input.abortSignal;
|
|
381
|
+
const heard = await input.autoMode.decider
|
|
382
|
+
.decide({ req: pass.req, askMessage: decisionText(pass.decision) }, classifySignal)
|
|
377
383
|
.catch(() => ({ kind: "unavailable", cause: "error" }));
|
|
378
|
-
|
|
379
|
-
if (verdict
|
|
384
|
+
const verdict = heard.kind === "allow" && input.permissionMode !== undefined && input.permissionMode() !== "auto" ? undefined : heard;
|
|
385
|
+
if (verdict !== undefined)
|
|
386
|
+
pass.classifierRound = verdict.classifier;
|
|
387
|
+
if (verdict?.kind === "allow") {
|
|
380
388
|
input.autoMode.denialTracking?.recordAllow();
|
|
381
389
|
pass.decision = {
|
|
382
390
|
action: "allow",
|
|
@@ -385,14 +393,17 @@ export async function runGateLanes(pass) {
|
|
|
385
393
|
...(pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}),
|
|
386
394
|
};
|
|
387
395
|
}
|
|
388
|
-
else if (verdict
|
|
389
|
-
pass.decision
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
396
|
+
else if (verdict?.kind === "unavailable") {
|
|
397
|
+
const arm = classifierUnavailableArm({ ask: pass.decision, req: pass.req, cause: verdict.cause, attribution: verdict.classifier, seat: input.autoMode.unavailable, aborted: classifySignal?.aborted === true });
|
|
398
|
+
if (arm.kind === "asked") {
|
|
399
|
+
classifyStanding(arm.ask, originFacts);
|
|
400
|
+
pass.decision = stampAskOrigin(arm.ask, originFacts);
|
|
401
|
+
pass.classifierRound = undefined;
|
|
402
|
+
}
|
|
403
|
+
else
|
|
404
|
+
pass.decision = arm.deny;
|
|
394
405
|
}
|
|
395
|
-
else {
|
|
406
|
+
else if (verdict !== undefined) {
|
|
396
407
|
const reason = verdict.kind === "block" ? (verdict.reason ? inlineUntrusted(verdict.reason) : "") : classifierParseFailureDenyMessage(verdict.classifier);
|
|
397
408
|
const category = verdict.kind === "block" && verdict.category ? inlineUntrusted(verdict.category) : "";
|
|
398
409
|
const tracked = input.autoMode.denialTracking?.recordBlock();
|
|
@@ -404,6 +415,7 @@ export async function runGateLanes(pass) {
|
|
|
404
415
|
requiresRealApproval: true,
|
|
405
416
|
denialLimitFallback: tracked.fallback,
|
|
406
417
|
};
|
|
418
|
+
classifyStanding(fallbackAsk, originFacts);
|
|
407
419
|
pass.decision = stampAskOrigin(fallbackAsk, originFacts);
|
|
408
420
|
}
|
|
409
421
|
else {
|
|
@@ -430,7 +442,7 @@ export async function runGateLanes(pass) {
|
|
|
430
442
|
pass.hookAsk === undefined &&
|
|
431
443
|
input.egress !== true &&
|
|
432
444
|
input.peerMessage !== true &&
|
|
433
|
-
|
|
445
|
+
pass.askClassification?.row.sandboxAdmission === true &&
|
|
434
446
|
pass.probeBoundaryDeclined !== true &&
|
|
435
447
|
input.sandboxAdmission.boundaryCapable !== true) {
|
|
436
448
|
const recorded = input.sandboxAdmission.askClassesOf(toolCallId);
|
|
@@ -447,6 +459,42 @@ export async function runGateLanes(pass) {
|
|
|
447
459
|
};
|
|
448
460
|
}
|
|
449
461
|
}
|
|
462
|
+
if (pass.decision.action === "ask" && input.permissionMode !== undefined && pass.askClassification !== undefined) {
|
|
463
|
+
const standing = pass.decision;
|
|
464
|
+
const mode = input.permissionMode();
|
|
465
|
+
const fileCommandByWord = modeAcceptsFileCommand(mode, toolName, pass.policyRewrite ?? pass.currentInput);
|
|
466
|
+
const verdict = arbitrateByMode(mode, pass.askClassification, { ...(standing.origin !== undefined ? { origin: standing.origin } : {}), ...(standing.mandate !== undefined ? { mandate: standing.mandate } : {}) }, { fileCommand: pass.shellWritesPlaced === true && fileCommandByWord });
|
|
467
|
+
if (verdict.kind === "ask" && fileCommandByWord && pass.shellWritesPlaced !== true && pass.shellUnplacedOptions !== undefined && pass.askClassification.classes.size === 1 && pass.askClassification.classes.has("ordinary")) {
|
|
468
|
+
pass.decision = { ...standing, message: fileCommandOptionsSentence(pass.shellUnplacedOptions.program) };
|
|
469
|
+
}
|
|
470
|
+
if (verdict.kind === "accepted") {
|
|
471
|
+
ledger.supersede();
|
|
472
|
+
pass.decision = { action: "allow", message: `accepted by the ${verdict.mode} permission mode (one of its file commands)`, decisionReason: "mode", ...(pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}) };
|
|
473
|
+
}
|
|
474
|
+
else if (verdict.kind === "released") {
|
|
475
|
+
pass.arbitration = verdict;
|
|
476
|
+
ledger.supersede();
|
|
477
|
+
await notifier.notifyAsync(() => input.onModeReleased?.({ toolName, toolCallId, release: { mode: verdict.mode, askClass: verdict.askClass, classes: verdict.classes, ...(verdict.origin !== undefined ? { origin: verdict.origin } : {}), ...(verdict.mandate !== undefined ? { mandate: verdict.mandate } : {}), ...(verdict.reason !== undefined ? { reason: verdict.reason } : {}) }, ...(pass.shellReadUnestablished === true ? { readUnestablished: true } : {}) }), "toolGate.onModeReleased");
|
|
478
|
+
pass.decision = {
|
|
479
|
+
action: "allow",
|
|
480
|
+
message: `released by the ${verdict.mode} permission mode (the ${verdict.askClass} ask was not put to anyone)`,
|
|
481
|
+
decisionReason: "mode",
|
|
482
|
+
...(pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}),
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
else if (verdict.kind === "denied_by_mode") {
|
|
486
|
+
pass.arbitration = verdict;
|
|
487
|
+
pass.deniedBy = "mode";
|
|
488
|
+
pass.denyCause = undefined;
|
|
489
|
+
pass.decision = {
|
|
490
|
+
action: "deny",
|
|
491
|
+
message: verdict.mode === "dontAsk"
|
|
492
|
+
? `refused by the dontAsk permission mode: this call needed approval (${verdict.askClass}) and the mode answers no ask; run under a mode with an approver, or change the call`
|
|
493
|
+
: `refused by the bypassPermissions permission mode: the write-protection judge could not resolve this write's target, and with nobody to ask the mode refuses rather than releases it`,
|
|
494
|
+
decisionReason: "mode",
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
}
|
|
450
498
|
const safety = safetyAxisOf({
|
|
451
499
|
egress: input.egress,
|
|
452
500
|
shellGated: input.shellGated,
|
|
@@ -466,8 +514,8 @@ export async function runGateLanes(pass) {
|
|
|
466
514
|
...(callSignal !== undefined ? { signal: callSignal } : {}),
|
|
467
515
|
...(d.action === "ask" && d.denialLimitFallback !== undefined ? { denialLimitFallback: unarmedWindow(d.denialLimitFallback) } : {}),
|
|
468
516
|
...(d.action === "ask" && d.origin !== undefined ? { origin: d.origin } : {}),
|
|
469
|
-
...(d.action === "ask" && d.ruleStoreUnreadable !== undefined ? { ruleStoreUnreadable: d.ruleStoreUnreadable } : {}),
|
|
470
517
|
...(d.action === "ask" && d.mandated === true ? { mandated: true, ...(d.mandate !== undefined ? { mandate: d.mandate } : {}) } : {}),
|
|
518
|
+
...(d.action === "ask" && d.authoredBy === "deployment" ? { authoredBy: "deployment" } : {}),
|
|
471
519
|
});
|
|
472
520
|
if (suspendAsk && pass.decision.action === "ask") {
|
|
473
521
|
const parkArgs = [pass.req, pass.currentInput, safety, undefined, realApprovalOf(pass.decision), pass.decision.action === "ask" ? pass.decision.persistedRuleShadowed : undefined, pass.decision.action === "ask" ? pass.decision.decisionReason : undefined, pass.decision.action === "ask" ? pass.decision.probeReason : undefined, pass.decision.action === "ask" ? pass.decision.probeCause : undefined, pass.decision.action === "ask" ? pass.decision.segmentCoverage : undefined, pass.decision.action === "ask" ? pass.decision.matchedAskRule : undefined, pass.decision.action === "ask" ? pass.decision.probeMandated : undefined];
|
|
@@ -642,10 +690,10 @@ export async function runGateLanes(pass) {
|
|
|
642
690
|
editDenied = recheck;
|
|
643
691
|
break;
|
|
644
692
|
}
|
|
645
|
-
let
|
|
693
|
+
let editRuleCallUnreadable = false;
|
|
646
694
|
if (input.persistedRules !== undefined && toolName !== ASK_USER_QUESTION_TOOL_NAME) {
|
|
647
695
|
const editLane = await readPersistedRules({ toolName, args: editArgs, toolCallId, ...(pass.req.face !== undefined ? { face: pass.req.face } : {}) });
|
|
648
|
-
|
|
696
|
+
editRuleCallUnreadable = ruleCallUnreadableOf(editLane);
|
|
649
697
|
const editTightened = applyPersistedTightening(recheck, editLane);
|
|
650
698
|
recheck = editTightened.decision;
|
|
651
699
|
if (editTightened.tightened === "deny")
|
|
@@ -666,10 +714,27 @@ export async function runGateLanes(pass) {
|
|
|
666
714
|
editArgs = recheck.updatedInput;
|
|
667
715
|
editRewrittenSinceHuman = true;
|
|
668
716
|
}
|
|
669
|
-
const {
|
|
670
|
-
const editAskSnapshot = { ...recheckUnstamped, ruleEvidence: mintRuleEvidence({ dotsAbsent: "not_adjudicated" }) };
|
|
717
|
+
const editAskSnapshot = { ...recheck, ruleEvidence: mintRuleEvidence({ dotsAbsent: "not_adjudicated" }) };
|
|
671
718
|
markedThisRound = undefined;
|
|
672
|
-
const
|
|
719
|
+
const editFacts = askOriginFacts(editOrg.org, editRuleCallUnreadable);
|
|
720
|
+
classifyStanding(editAskSnapshot, editFacts);
|
|
721
|
+
const editAskDecision = stampAskOrigin(editAskSnapshot, editFacts);
|
|
722
|
+
if (input.permissionMode !== undefined && pass.askClassification !== undefined) {
|
|
723
|
+
const editVerdict = arbitrateByMode(input.permissionMode(), pass.askClassification, { ...(editAskDecision.origin !== undefined ? { origin: editAskDecision.origin } : {}), ...(editAskDecision.mandate !== undefined ? { mandate: editAskDecision.mandate } : {}) });
|
|
724
|
+
if (editVerdict.kind === "released") {
|
|
725
|
+
ledger.supersede();
|
|
726
|
+
pass.arbitration = editVerdict;
|
|
727
|
+
await notifier.notifyAsync(() => input.onModeReleased?.({ toolName, toolCallId, release: { mode: editVerdict.mode, askClass: editVerdict.askClass, classes: editVerdict.classes, ...(editVerdict.origin !== undefined ? { origin: editVerdict.origin } : {}), ...(editVerdict.mandate !== undefined ? { mandate: editVerdict.mandate } : {}), ...(editVerdict.reason !== undefined ? { reason: editVerdict.reason } : {}) }, ...(pass.shellReadUnestablished === true ? { readUnestablished: true } : {}) }), "toolGate.onModeReleased");
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
if (editVerdict.kind === "denied_by_mode") {
|
|
731
|
+
ledger.supersede();
|
|
732
|
+
pass.arbitration = editVerdict;
|
|
733
|
+
editDenied = { action: "deny", message: `the approved edit for "${toolName}" raised a new approval requirement that the ${editVerdict.mode} permission mode refuses`, decisionReason: "mode" };
|
|
734
|
+
pass.deniedBy = "mode";
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
673
738
|
const editAskReq = { toolName, args: editArgs, toolCallId, ...(pass.req.face !== undefined ? { face: pass.req.face } : {}) };
|
|
674
739
|
const rr = await (callSignal !== undefined ? resolveAsk(editAskDecision, editAskReq, callSignal) : resolveAsk(editAskDecision, editAskReq));
|
|
675
740
|
ledger.fromResolved(rr, editAskDecision, toolName);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* policy composes: a policy says allow / ask / deny; this file says how the pass that consumed that
|
|
8
8
|
* verdict ended.
|
|
9
9
|
*/
|
|
10
|
-
import { type AskOrigin, type AssertAllKeysHandled } from "./ask-origin.js";
|
|
10
|
+
import { type AskClass, type AskOrigin, type AssertAllKeysHandled, type PermissionMode, type PersistedRuleMandate } from "./ask-origin.js";
|
|
11
11
|
import { type AutoModeClassifierRound } from "./auto-mode.js";
|
|
12
12
|
/**
|
|
13
13
|
* How the wait an ask was in ENDED — the closed set. One word per way a wait can end; the `who` on the
|
|
@@ -197,7 +197,8 @@ export type NobodyJudgedTableCoversEveryKind = AssertAllKeysHandled<Exclude<Sett
|
|
|
197
197
|
* - `org` — an organization policy rule denied.
|
|
198
198
|
* - `classifier` — the auto-mode classifier denied directly (its ASK-side role is `origin:
|
|
199
199
|
* "denial_limit_fallback"`).
|
|
200
|
-
* - `
|
|
200
|
+
* - `persisted_rule` — the person's persisted-rule lane refused: a deny row (live), or, on a durable resume, a deny
|
|
201
|
+
* row, a call unreadable against the rows, or a wait for them the task's cancel ended.
|
|
201
202
|
* - `compliance` — the compliance call-time lock.
|
|
202
203
|
* - `write_protection` — an approved edit was rewritten by the restriction chain onto a write-protected
|
|
203
204
|
* path that no approval covers.
|
|
@@ -206,15 +207,18 @@ export type NobodyJudgedTableCoversEveryKind = AssertAllKeysHandled<Exclude<Sett
|
|
|
206
207
|
* - `read_boundary` — the shell read station refused: a deny-listed read (refused on every seat, with no
|
|
207
208
|
* ask), a read the read-only shell face may not make, or a rewritten / re-established command whose reads
|
|
208
209
|
* no longer are what a person approved.
|
|
210
|
+
* - `mode` — the permission MODE refused a surviving ask at the gate's arbitration point: `dontAsk` turns an
|
|
211
|
+
* ask nobody is present to answer into a refusal, and `bypassPermissions` refuses a write whose protection
|
|
212
|
+
* could not be judged. The disposition carries `mode`, `askClass` and `classes` beside the word.
|
|
209
213
|
*/
|
|
210
|
-
export declare const DENIED_BY_VALUES: readonly ["policy", "hook", "org", "persisted_rule", "classifier", "
|
|
214
|
+
export declare const DENIED_BY_VALUES: readonly ["policy", "hook", "org", "persisted_rule", "classifier", "compliance", "write_protection", "ask_resolution", "read_boundary", "mode"];
|
|
211
215
|
export type DeniedBy = (typeof DENIED_BY_VALUES)[number];
|
|
212
216
|
/** Membership test for {@link DeniedBy}. */
|
|
213
217
|
export declare function isDeniedBy(v: unknown): v is DeniedBy;
|
|
214
218
|
/** Which layers may VETO an approval a person already gave (invariant I3: a `human_allowed` settlement
|
|
215
219
|
* beside a `denied` disposition names one of these). The approval-edit re-screen (hook), the policy, org
|
|
216
220
|
* and persisted-rule re-checks, and the write-protection re-judge run AFTER the person's yes; the
|
|
217
|
-
* classifier
|
|
221
|
+
* classifier and compliance run before any ask exists, and `ask_resolution` IS the
|
|
218
222
|
* settlement, not a veto of it. `persisted_rule` = a person's own standing DENY row (their settings'
|
|
219
223
|
* deny list, imported) — the personal-store sibling of `org`, and like it re-judged on an edited
|
|
220
224
|
* command. `read_boundary` vetoes too: the rewrite re-judge and the pre-spawn re-establishment run after
|
|
@@ -225,11 +229,11 @@ export declare const DENIED_BY_MAY_VETO: {
|
|
|
225
229
|
readonly org: true;
|
|
226
230
|
readonly persisted_rule: true;
|
|
227
231
|
readonly classifier: false;
|
|
228
|
-
readonly plan_mode: false;
|
|
229
232
|
readonly compliance: false;
|
|
230
233
|
readonly write_protection: true;
|
|
231
234
|
readonly ask_resolution: false;
|
|
232
235
|
readonly read_boundary: true;
|
|
236
|
+
readonly mode: false;
|
|
233
237
|
};
|
|
234
238
|
/** Compile-time fence: `never` while every deny layer has a veto row. */
|
|
235
239
|
export type VetoTableCoversEveryDeniedBy = AssertAllKeysHandled<Exclude<DeniedBy, keyof typeof DENIED_BY_MAY_VETO>>;
|
|
@@ -259,11 +263,11 @@ export declare const DENIED_BY_MAY_CARRY_CAUSE: {
|
|
|
259
263
|
readonly org: false;
|
|
260
264
|
readonly persisted_rule: false;
|
|
261
265
|
readonly classifier: true;
|
|
262
|
-
readonly plan_mode: false;
|
|
263
266
|
readonly compliance: false;
|
|
264
267
|
readonly write_protection: false;
|
|
265
268
|
readonly ask_resolution: false;
|
|
266
269
|
readonly read_boundary: false;
|
|
270
|
+
readonly mode: false;
|
|
267
271
|
};
|
|
268
272
|
/** Compile-time fence: `never` while every deny layer has a cause row. */
|
|
269
273
|
export type CauseTableCoversEveryDeniedBy = AssertAllKeysHandled<Exclude<DeniedBy, keyof typeof DENIED_BY_MAY_CARRY_CAUSE>>;
|
|
@@ -285,12 +289,37 @@ export type CauseTableCoversEveryDeniedBy = AssertAllKeysHandled<Exclude<DeniedB
|
|
|
285
289
|
export type GateDisposition = {
|
|
286
290
|
kind: "allowed";
|
|
287
291
|
classifier?: AutoModeClassifierRound;
|
|
292
|
+
released?: ModeRelease;
|
|
288
293
|
} | {
|
|
289
294
|
kind: "denied";
|
|
290
295
|
deniedBy: DeniedBy;
|
|
291
296
|
cause?: ClassifierDenyCause;
|
|
292
297
|
classifier?: AutoModeClassifierRound;
|
|
298
|
+
mode?: ModeRefusal["mode"];
|
|
299
|
+
askClass?: AskClass;
|
|
300
|
+
classes?: readonly AskClass[];
|
|
293
301
|
};
|
|
302
|
+
/**
|
|
303
|
+
* A permission MODE released a surviving ask — the allowed disposition's account of the question it did not put
|
|
304
|
+
* to anyone: the mode, the disclosed class word (`askClass`, the first member of the folded set in `ASK_CLASSES`
|
|
305
|
+
* order — one of {@link RELEASABLE_ASK_CLASSES}), the whole set, and the origin / mandate words the card would
|
|
306
|
+
* have shown. `reason: "rule_unreadable"` marks a release over a rule the call could not be read against.
|
|
307
|
+
*/
|
|
308
|
+
export interface ModeRelease {
|
|
309
|
+
mode: PermissionMode;
|
|
310
|
+
askClass: AskClass;
|
|
311
|
+
classes: readonly AskClass[];
|
|
312
|
+
origin?: AskOrigin;
|
|
313
|
+
mandate?: PersistedRuleMandate;
|
|
314
|
+
reason?: "rule_unreadable";
|
|
315
|
+
}
|
|
316
|
+
/** The two modes that refuse on their own account (`deniedBy: "mode"`): `dontAsk` every non-authority ask,
|
|
317
|
+
* `bypassPermissions` a write whose protection could not be judged. `plan` refuses nothing here — its questions ask. */
|
|
318
|
+
export interface ModeRefusal {
|
|
319
|
+
mode: "bypassPermissions" | "dontAsk";
|
|
320
|
+
askClass: AskClass;
|
|
321
|
+
classes: readonly AskClass[];
|
|
322
|
+
}
|
|
294
323
|
/**
|
|
295
324
|
* The WHOLE record of one tool-gate pass: the final disposition, plus — when this pass settled an ask —
|
|
296
325
|
* the settlement it consumed and the ask's origin. Minted ONCE (the gate's exit; the decide lane for a
|
|
@@ -310,7 +339,11 @@ export type GateDisposition = {
|
|
|
310
339
|
* I6 `disposition.classifier` present ⇒ both model ids are non-empty strings, and a `fallback` on it
|
|
311
340
|
* names a `to` that IS `modelUsed` (the ladder landed where the record says) with a cause in
|
|
312
341
|
* {@link import("./auto-mode.js").AUTO_MODE_FALLBACK_CAUSES} — a record whose `to` and `modelUsed`
|
|
313
|
-
* disagree would let a reader answer "which model decided this call?" two ways
|
|
342
|
+
* disagree would let a reader answer "which model decided this call?" two ways;
|
|
343
|
+
* I7 `disposition.released` present ⇒ `kind === "allowed"`, no settlement, `mode` is `bypassPermissions`,
|
|
344
|
+
* `askClass` is a releasable class and every member of `classes` is an ask class — a mode releases nothing
|
|
345
|
+
* a person answered and nothing the table calls immune;
|
|
346
|
+
* I8 `deniedBy === "mode"` ⇔ `mode` present (`bypassPermissions` | `dontAsk`) with `askClass` and `classes`.
|
|
314
347
|
* Nothing here is read out of an ABSENCE: an ordinary allow with no ask is `{ disposition: { kind: "allowed" } }`,
|
|
315
348
|
* a direct policy deny is `{ disposition: { kind: "denied", deniedBy: "policy" } }`, and neither carries a
|
|
316
349
|
* settlement because neither settled one.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isAskOrigin } from "./ask-origin.js";
|
|
1
|
+
import { isAskClass, isAskOrigin, isPermissionMode, RELEASABLE_ASK_CLASSES } from "./ask-origin.js";
|
|
2
2
|
import { isAutoModeFallbackCause } from "./auto-mode.js";
|
|
3
3
|
export const SETTLEMENT_KINDS = [
|
|
4
4
|
"human_allowed",
|
|
@@ -56,7 +56,7 @@ export const SETTLEMENT_NOBODY_JUDGED = {
|
|
|
56
56
|
policy_refused: true,
|
|
57
57
|
task_aborted: false,
|
|
58
58
|
};
|
|
59
|
-
export const DENIED_BY_VALUES = ["policy", "hook", "org", "persisted_rule", "classifier", "
|
|
59
|
+
export const DENIED_BY_VALUES = ["policy", "hook", "org", "persisted_rule", "classifier", "compliance", "write_protection", "ask_resolution", "read_boundary", "mode"];
|
|
60
60
|
const DENIED_BY_SET = new Set(DENIED_BY_VALUES);
|
|
61
61
|
export function isDeniedBy(v) {
|
|
62
62
|
return DENIED_BY_SET.has(v);
|
|
@@ -67,11 +67,11 @@ export const DENIED_BY_MAY_VETO = {
|
|
|
67
67
|
org: true,
|
|
68
68
|
persisted_rule: true,
|
|
69
69
|
classifier: false,
|
|
70
|
-
plan_mode: false,
|
|
71
70
|
compliance: false,
|
|
72
71
|
write_protection: true,
|
|
73
72
|
ask_resolution: false,
|
|
74
73
|
read_boundary: true,
|
|
74
|
+
mode: false,
|
|
75
75
|
};
|
|
76
76
|
export const CLASSIFIER_DENY_CAUSES = ["unavailable", "parse_error"];
|
|
77
77
|
const CLASSIFIER_DENY_CAUSE_SET = new Set(CLASSIFIER_DENY_CAUSES);
|
|
@@ -84,11 +84,11 @@ export const DENIED_BY_MAY_CARRY_CAUSE = {
|
|
|
84
84
|
org: false,
|
|
85
85
|
persisted_rule: false,
|
|
86
86
|
classifier: true,
|
|
87
|
-
plan_mode: false,
|
|
88
87
|
compliance: false,
|
|
89
88
|
write_protection: false,
|
|
90
89
|
ask_resolution: false,
|
|
91
90
|
read_boundary: false,
|
|
91
|
+
mode: false,
|
|
92
92
|
};
|
|
93
93
|
export function screenGateOutcome(g) {
|
|
94
94
|
const defects = [];
|
|
@@ -116,6 +116,28 @@ export function screenGateOutcome(g) {
|
|
|
116
116
|
else if (isDeniedBy(d.deniedBy) && !DENIED_BY_MAY_CARRY_CAUSE[d.deniedBy])
|
|
117
117
|
defects.push(`I5: a classifier cause cannot stand beside deniedBy "${d.deniedBy}"`);
|
|
118
118
|
}
|
|
119
|
+
if (d.kind === "allowed" && d.released !== undefined) {
|
|
120
|
+
const r = d.released;
|
|
121
|
+
if (s !== undefined)
|
|
122
|
+
defects.push("I7: a mode release carries no settlement — nobody answered the ask it released");
|
|
123
|
+
if (r.mode !== "bypassPermissions")
|
|
124
|
+
defects.push(`I7: a mode release names mode "${String(r.mode)}" — only bypassPermissions releases`);
|
|
125
|
+
if (!isAskClass(r.askClass) || !RELEASABLE_ASK_CLASSES.includes(r.askClass))
|
|
126
|
+
defects.push(`I7: released.askClass "${String(r.askClass)}" is not a releasable class`);
|
|
127
|
+
if (!Array.isArray(r.classes) || r.classes.length === 0 || !r.classes.every((c) => isAskClass(c)))
|
|
128
|
+
defects.push("I7: released.classes must be a non-empty list of ask classes");
|
|
129
|
+
if (r.origin !== undefined && !isAskOrigin(r.origin))
|
|
130
|
+
defects.push(`I7: released.origin "${String(r.origin)}" is outside the closed set`);
|
|
131
|
+
}
|
|
132
|
+
if (d.kind === "denied") {
|
|
133
|
+
const modeRefusal = d.deniedBy === "mode";
|
|
134
|
+
if (modeRefusal !== (d.mode !== undefined))
|
|
135
|
+
defects.push("I8: deniedBy mode and disposition.mode must be present together");
|
|
136
|
+
if (modeRefusal && (!isPermissionMode(d.mode) || (d.mode !== "bypassPermissions" && d.mode !== "dontAsk")))
|
|
137
|
+
defects.push(`I8: a mode refusal names mode "${String(d.mode)}" — only bypassPermissions, dontAsk and plan refuse`);
|
|
138
|
+
if (modeRefusal && (!isAskClass(d.askClass) || !Array.isArray(d.classes) || d.classes.length === 0 || !d.classes.every((c) => isAskClass(c))))
|
|
139
|
+
defects.push("I8: a mode refusal carries askClass and a non-empty classes list");
|
|
140
|
+
}
|
|
119
141
|
const attribution = d.classifier;
|
|
120
142
|
if (attribution !== undefined) {
|
|
121
143
|
const named = (v) => typeof v === "string" && v !== "";
|
|
@@ -119,7 +119,7 @@ export type NoticeAudience = "user" | "operator";
|
|
|
119
119
|
* the union's catalog ({@link EngineNotice.code}). The groups below say only which family a row joins,
|
|
120
120
|
* so the catalog is not written a second time here and the two cannot drift.
|
|
121
121
|
*/
|
|
122
|
-
export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.context_settings_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.write_face_swapped", "config.write_face_deployment_clamped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "config.artifact_host_invalid", "config.task_root_not_canonical", "config.write_protection_target_view_absent", "config.write_protection_unresolved", "config.execution_env_capability_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.forward_sink_failed", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "config.secret_env_scrubbed", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "classifier.fallback", "classifier.role_defaulted", "classifier.contract_broken", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "mcp.server_redialed", "workflow.governance_key_stripped", "workflow.agent_option_ignored", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "memory.consolidation_withheld", "memory.restore_withheld", "memory.project_scope_absent", "route.fallback_to_primary", "route.classifier_unroutable", "route.base_url_changed_key_unchanged", "route.response_model_mismatch", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "skills.listing_truncated", "agents.listing_truncated", "tool_result.offload_put_failed"];
|
|
122
|
+
export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.context_settings_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.write_face_swapped", "config.write_face_deployment_clamped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "config.artifact_host_invalid", "config.task_root_not_canonical", "config.write_protection_target_view_absent", "config.write_protection_unresolved", "config.execution_env_capability_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.forward_sink_failed", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "config.secret_env_scrubbed", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable", "resume.legacy_mode_seed_migrated", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "classifier.fallback", "classifier.role_defaulted", "classifier.contract_broken", "classifier.unavailable", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "mcp.server_redialed", "workflow.governance_key_stripped", "workflow.agent_option_ignored", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "memory.consolidation_withheld", "memory.restore_withheld", "memory.project_scope_absent", "route.fallback_to_primary", "route.classifier_unroutable", "route.base_url_changed_key_unchanged", "route.response_model_mismatch", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "skills.listing_truncated", "agents.listing_truncated", "tool_result.offload_put_failed"];
|
|
123
123
|
/** A code this engine mints (see {@link ENGINE_NOTICE_CODES}). NOT the type of
|
|
124
124
|
* `EngineNotice.code`, which stays `string` — a host forwarding its own notices through the same
|
|
125
125
|
* sink is a supported shape, and narrowing that field would break it. */
|
|
@@ -128,6 +128,11 @@ export const ENGINE_NOTICE_CODES = [
|
|
|
128
128
|
"config.peer_admission_out_of_range",
|
|
129
129
|
"config.peer_lane_unmounted",
|
|
130
130
|
"config.secret_env_scrubbed",
|
|
131
|
+
"config.permission_mode_unavailable",
|
|
132
|
+
"permission.ask_released_by_mode",
|
|
133
|
+
"permission.read_deny_unapplied",
|
|
134
|
+
"permission.rule_store_unavailable",
|
|
135
|
+
"resume.legacy_mode_seed_migrated",
|
|
131
136
|
"peer.inbound_disposition",
|
|
132
137
|
"peer.held_settled",
|
|
133
138
|
"peer.idle_subscription",
|
|
@@ -135,6 +140,7 @@ export const ENGINE_NOTICE_CODES = [
|
|
|
135
140
|
"classifier.fallback",
|
|
136
141
|
"classifier.role_defaulted",
|
|
137
142
|
"classifier.contract_broken",
|
|
143
|
+
"classifier.unavailable",
|
|
138
144
|
"checkpoint.execution_outcome_unrecorded",
|
|
139
145
|
"delegation.transcript_integrity",
|
|
140
146
|
"delegation.ask_unresolvable",
|
|
@@ -187,6 +193,7 @@ const NOTICE_AUDIENCE_TABLE = {
|
|
|
187
193
|
"task.user_steer_undrained": "user",
|
|
188
194
|
"task.user_followup_undrained": "user",
|
|
189
195
|
"classifier.denial_limit": "user",
|
|
196
|
+
"classifier.unavailable": "user",
|
|
190
197
|
"classifier.fallback": "operator",
|
|
191
198
|
"classifier.role_defaulted": "operator",
|
|
192
199
|
"classifier.contract_broken": "operator",
|
|
@@ -198,11 +205,16 @@ const NOTICE_AUDIENCE_TABLE = {
|
|
|
198
205
|
"approval.read_root_granted": "user",
|
|
199
206
|
"approval.read_root_grant_rejected": "user",
|
|
200
207
|
"config.durable_gate_unavailable": "user",
|
|
208
|
+
"config.permission_mode_unavailable": "user",
|
|
209
|
+
"permission.ask_released_by_mode": "user",
|
|
210
|
+
"permission.read_deny_unapplied": "user",
|
|
211
|
+
"permission.rule_store_unavailable": "user",
|
|
201
212
|
"memory.capture_opted_out": "user",
|
|
202
213
|
"memory.capture_optout_unpersisted": "user",
|
|
203
214
|
"memory.consolidation_withheld": "operator",
|
|
204
215
|
"delegation.ask_unresolvable": "user",
|
|
205
216
|
"config.autocompact_window_clamped": "operator",
|
|
217
|
+
"resume.legacy_mode_seed_migrated": "operator",
|
|
206
218
|
"config.env_timeout_discarded": "operator",
|
|
207
219
|
"config.materialize_env_discarded": "operator",
|
|
208
220
|
"config.models_swapped": "operator",
|