@sema-agent/server 7.117.0 → 7.119.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/MIGRATION.md +182 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +58 -28
- package/deploy/sema-up/README.md +2 -0
- package/deploy/sema-up/chart/templates/minio.yaml +2 -1
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +1 -0
- package/deploy/sema-up/chart/templates/server.yaml +1 -0
- package/deploy/sema-up/chart/values.yaml +7 -1
- package/deploy/sema-up/cluster-up.sh +20 -8
- package/deploy/sema-up/kube-up.sh +29 -16
- package/deploy/sema-up/lib/buckets.sh +21 -0
- package/deploy/sema-up/lib/health.sh +33 -0
- package/deploy/sema-up/sema-up.sh +14 -9
- package/deploy/sema-up/smoke.sh +16 -6
- package/deploy/sema-up/stack/compose.yaml +1 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -1
- package/dist/approval-ask-audit-store.js +2 -7
- package/dist/approval-card.d.ts +40 -10
- package/dist/approval-card.js +11 -1
- package/dist/approval.d.ts +55 -17
- package/dist/approval.js +74 -33
- package/dist/audit.d.ts +12 -1
- package/dist/audit.js +62 -2
- package/dist/auto-mode-face.d.ts +2 -3
- package/dist/bake-runner/main.js +1 -6
- package/dist/bake-runner/protocol.d.ts +18 -0
- package/dist/bake-runner/protocol.js +22 -2
- package/dist/bake-runner/runner.d.ts +12 -8
- package/dist/bake-runner/runner.js +5 -9
- package/dist/boot/config-center.js +1 -1
- package/dist/boot/coordinators.js +4 -1
- package/dist/boot/device-lane.d.ts +3 -3
- package/dist/boot/device-lane.js +1 -2
- package/dist/boot/leader.d.ts +4 -4
- package/dist/boot/leader.js +1 -2
- package/dist/boot/parked-revive-gate.d.ts +4 -6
- package/dist/boot/parked-revive-gate.js +2 -6
- package/dist/boot/resolve-spec.js +9 -11
- package/dist/boot/runner-deps.js +2 -2
- package/dist/boot/stage-07-capability-layer.js +7 -3
- package/dist/boot/stage-10-http-server.js +3 -2
- package/dist/boot/stores.js +12 -7
- package/dist/capabilities/center-plugins.d.ts +2 -0
- package/dist/capabilities/center-plugins.js +24 -6
- package/dist/capabilities/code-review-council.js +8 -11
- package/dist/capabilities/hands-lane.d.ts +25 -0
- package/dist/capabilities/hands-lane.js +1 -1
- package/dist/capabilities/mcp-probe.d.ts +2 -2
- package/dist/capabilities/scenarios.d.ts +34 -7
- package/dist/capabilities/scenarios.js +33 -6
- package/dist/config-catalog.js +3 -3
- package/dist/config.d.ts +14 -18
- package/dist/config.js +15 -26
- package/dist/deployment-governance.d.ts +5 -7
- package/dist/deployment-governance.js +4 -5
- package/dist/host-decision.d.ts +114 -31
- package/dist/host-decision.js +39 -2
- package/dist/http/admission.js +12 -6
- package/dist/http/approval-sweeps.js +1 -1
- package/dist/http/mcp-dial-deadline.d.ts +35 -0
- package/dist/http/mcp-dial-deadline.js +21 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +11 -5
- package/dist/http/memory-compliance-failure-reply.js +17 -8
- package/dist/http/memory-store-refusal-reply.d.ts +14 -11
- package/dist/http/memory-store-refusal-reply.js +16 -5
- package/dist/http/resume-legs.js +8 -3
- package/dist/http/routes/approvals-assistant.d.ts +25 -3
- package/dist/http/routes/approvals-assistant.js +27 -26
- package/dist/http/routes/capabilities.js +6 -3
- package/dist/http/routes/mcp-reconnect.d.ts +17 -0
- package/dist/http/routes/mcp-reconnect.js +6 -1
- package/dist/http/routes/memory-bundle.js +1 -1
- package/dist/http/routes/memory-compliance.js +1 -1
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/memory-origin.js +1 -1
- package/dist/http/routes/runs.js +47 -33
- package/dist/http/routes/sessions.d.ts +1 -1
- package/dist/http/routes/sessions.js +23 -22
- package/dist/http/routes/workflows.js +2 -0
- package/dist/http/wire-types.d.ts +17 -13
- package/dist/images/manifest.d.ts +7 -1
- package/dist/images/manifest.js +18 -16
- package/dist/memory-store-refusal.d.ts +38 -18
- package/dist/memory-store-refusal.js +25 -7
- package/dist/model-unreachable-notice.d.ts +1 -1
- package/dist/namespaced-rule-name.d.ts +46 -41
- package/dist/namespaced-rule-name.js +36 -22
- package/dist/observability/corrupt-read-seat.d.ts +4 -0
- package/dist/observability/corrupt-read-seat.js +1 -0
- package/dist/observability/fail-open.d.ts +19 -3
- package/dist/observability/fail-open.js +19 -3
- package/dist/operator-ask.d.ts +6 -2
- package/dist/orchestration/workflow-agent-session-index.d.ts +82 -59
- package/dist/orchestration/workflow-agent-session-index.js +80 -14
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -0
- package/dist/orchestration/workflow-completion-inbox.js +39 -17
- package/dist/orchestration/workflow-notify-journal.d.ts +1 -1
- package/dist/orchestration/workflow-notify-journal.js +7 -2
- package/dist/outbound-dispatcher.d.ts +29 -20
- package/dist/outbound-dispatcher.js +105 -16
- package/dist/parked-decide.d.ts +43 -48
- package/dist/parked-decide.js +54 -16
- package/dist/permission-rule-vocab.d.ts +4 -3
- package/dist/permission-rule-vocab.js +1 -1
- package/dist/plugins/approval-ask-store-sql.d.ts +17 -20
- package/dist/plugins/approval-ask-store-sql.js +0 -3
- package/dist/plugins/approval-exemption-store.d.ts +4 -0
- package/dist/plugins/approval-exemption-store.js +1 -0
- package/dist/plugins/blob-backend.d.ts +13 -0
- package/dist/plugins/blob-backend.js +26 -0
- package/dist/plugins/local-session-store.d.ts +78 -47
- package/dist/plugins/local-session-store.js +152 -122
- package/dist/plugins/one-time-migrations.d.ts +11 -0
- package/dist/plugins/one-time-migrations.js +128 -4
- package/dist/plugins/pg-pool.js +1 -1
- package/dist/plugins/snapshot-bucket-presence.d.ts +36 -0
- package/dist/plugins/snapshot-bucket-presence.js +40 -0
- package/dist/plugins/store-backend.d.ts +17 -6
- package/dist/plugins/store-backend.js +46 -16
- package/dist/plugins/store-open-wait.d.ts +33 -0
- package/dist/plugins/store-open-wait.js +73 -0
- package/dist/plugins/tidb-pool.js +1 -1
- package/dist/plugins/web-search.d.ts +20 -1
- package/dist/plugins/web-search.js +39 -10
- package/dist/plugins/workflow-run-store-sql.d.ts +7 -6
- package/dist/plugins/workflow-run-store-sql.js +5 -7
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +1 -1
- package/dist/rule-entry-list.d.ts +35 -46
- package/dist/rule-entry-list.js +18 -26
- package/dist/rules-consent.js +3 -0
- package/dist/run-local.js +5 -3
- package/dist/runs.d.ts +1 -1
- package/dist/security.d.ts +5 -3
- package/dist/security.js +4 -0
- package/dist/session-policy-wire.d.ts +13 -12
- package/dist/session-policy-wire.js +11 -11
- package/dist/spec-fields.d.ts +13 -11
- package/dist/spec-fields.js +4 -7
- package/dist/standing-answer-grant.d.ts +14 -2
- package/dist/standing-answer-grant.js +8 -1
- package/dist/store-posture.d.ts +14 -6
- package/dist/store-posture.js +7 -4
- package/dist/task-settings.d.ts +27 -55
- package/dist/task-settings.js +13 -21
- package/dist/tool-approval.d.ts +133 -138
- package/dist/tool-approval.js +175 -66
- package/dist/trace/core-keyset-guard.d.ts +5 -5
- package/dist/trace/redact.d.ts +6 -0
- package/dist/trace/redact.js +9 -9
- package/dist/trace/self-minted-wire.d.ts +16 -2
- package/dist/trace/self-minted-wire.js +7 -0
- package/dist/user-input-ledger.d.ts +30 -14
- package/dist/user-input-ledger.js +25 -14
- package/package.json +2 -2
- package/scripts/drain-check-local.mjs +74 -3
- package/scripts/drain-check.mysql.sql +43 -17
- package/scripts/drain-check.pg.sql +46 -20
package/dist/tool-approval.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { contentKindOf } from "./approval-content-kind.js";
|
|
2
|
+
import { heldAskOf, liveSettledBy, readDecidedBy, retiredDecisionKeyFacts, DECIDED_BY_OUTSIDE_CLOSED_SET, POLICY_CANNOT_ALLOW_PERSON_ASK } from "./host-decision.js";
|
|
2
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
|
-
import { uuidv7, MAX_RULE_TEXT_CHARS, READ_ROOT_CANDIDATE_DIR_MAX, parseRuleText, pathConfinableWriteToolNames } from "@sema-agent/core";
|
|
4
|
+
import { uuidv7, classifyAsk, foldArbitrationRow, policyCannotAnswer, screenAskClassMembers, MAX_RULE_TEXT_CHARS, READ_ROOT_CANDIDATE_DIR_MAX, parseRuleText, pathConfinableWriteToolNames } from "@sema-agent/core";
|
|
4
5
|
import { precheckCardRuleText } from "./rules-consent.js";
|
|
5
6
|
import { redactDeep, redactSecrets, redactSecretsWithArms } from "./trace/redact.js";
|
|
6
7
|
import { hasBidiControls } from "./text-bidi.js";
|
|
@@ -9,11 +10,13 @@ import { createLogger } from "./observability/logger.js";
|
|
|
9
10
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
10
11
|
import { deriveAskId, deriveAskScopedBatchId, deriveBatchId, MAX_DECISION_NOTE_CHARS } from "./approval-ask-machine.js";
|
|
11
12
|
import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readProbeReason, readReadRootCandidate, readRuleEvidence, readAllowRuleShadowed, readRuleOffersAbsence, readRuleOffersAbsenceSeat, readParkRowTrueSeat, readAskClassPair, readClosedWordSeat, RuleOfferRawSchema, backfillLegacyBatchMemberKind, screenUncoveredDetailSeat, } from "./approval-card.js";
|
|
12
|
-
import {
|
|
13
|
+
import { POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth } from "./plugins/approval-ask-store-sql.js";
|
|
13
14
|
import { settleByPolicy } from "./approval-policy-settlement.js";
|
|
14
15
|
import { governanceAskMarksFor, runWithGovernanceAskScope } from "./governance-ask-marks.js";
|
|
15
16
|
import { governanceOriginOf } from "./runtime-governance.js";
|
|
16
|
-
import { answerGrantsBeyondCall } from "./standing-answer-grant.js";
|
|
17
|
+
import { answerGrantsBeyondCall, grantBindingOf } from "./standing-answer-grant.js";
|
|
18
|
+
import { operatorAsk } from "./operator-ask.js";
|
|
19
|
+
import { APPROVAL_EXEMPTED_EVENT } from "./plugins/approval-exemption-store.js";
|
|
17
20
|
const defaultLogger = createLogger();
|
|
18
21
|
export const APPROVAL_GATE_KINDS = ["human", "irreversible_ask"];
|
|
19
22
|
export function isApprovalGateKind(k) {
|
|
@@ -53,7 +56,7 @@ function durableRuleMaterial(material) {
|
|
|
53
56
|
export const ASK_DECISION_CONSUMER_ABSENT = "absent";
|
|
54
57
|
const MAX_INFLIGHT_TERMINAL_CLAIMS = 2;
|
|
55
58
|
const DURABLE_CONVERGE_BACKOFF_MS = [50, 100, 200, 400, 800, 1600];
|
|
56
|
-
const DURABLE_CONVERGE_READ_TIMEOUT_MS = 2_000;
|
|
59
|
+
export const DURABLE_CONVERGE_READ_TIMEOUT_MS = 2_000;
|
|
57
60
|
const DURABLE_CALL_TIMEOUT_MS = 5_000;
|
|
58
61
|
const TERMINAL_LADDER_WALL_MS = DURABLE_CALL_TIMEOUT_MS + DURABLE_CONVERGE_READ_TIMEOUT_MS + DURABLE_CONVERGE_BACKOFF_MS.reduce((a, b) => a + b, 0);
|
|
59
62
|
const TERMINAL_CLAIM_CAP_POLL_MS = 50;
|
|
@@ -81,19 +84,68 @@ const FS_WRITE_TOOLS = pathConfinableWriteToolNames();
|
|
|
81
84
|
function sessionAllowCategory(toolName) {
|
|
82
85
|
return FS_WRITE_TOOLS.has(toolName) ? "fs-write" : `tool:${toolName}`;
|
|
83
86
|
}
|
|
84
|
-
function sessionAllowKey(owner, sessionId, category) {
|
|
85
|
-
return JSON.stringify([owner, sessionId, category]);
|
|
87
|
+
function sessionAllowKey(owner, sessionId, category, binding) {
|
|
88
|
+
return JSON.stringify([owner, sessionId, category, binding.kind === "bound" ? binding.digest : null]);
|
|
86
89
|
}
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
function askStandingAnswerFacts(req, governanceForced, deploymentListAsks) {
|
|
91
|
+
const classes = screenAskClassMembers(req.askClass, req.classes)?.classes;
|
|
92
|
+
return {
|
|
93
|
+
governanceForced,
|
|
94
|
+
requiresRealApproval: req.requiresRealApproval === true,
|
|
95
|
+
mandated: req.mandated === true,
|
|
96
|
+
grantBinding: grantBindingOf(req),
|
|
97
|
+
classes,
|
|
98
|
+
deploymentListQuestion: carriesDeploymentListQuestion(deploymentListAsks, classes),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const NO_GATE_FACTS = { contentQuestion: false, markedUnresolvable: false, org: undefined, tightened: undefined, mandateMarks: {} };
|
|
102
|
+
const DEPLOYMENT_LIST_ASK_CLASSES = new Set(classifyAsk(operatorAsk("APPROVAL_REQUIRE", ""), NO_GATE_FACTS).classes);
|
|
103
|
+
export function carriesDeploymentListQuestion(deploymentListAsks, classes) {
|
|
104
|
+
return deploymentListAsks && classes !== undefined && classes.some((c) => DEPLOYMENT_LIST_ASK_CLASSES.has(c));
|
|
105
|
+
}
|
|
106
|
+
export function policyAskStandingAnswerFacts(req, ask, deploymentListQuestion) {
|
|
107
|
+
return {
|
|
108
|
+
governanceForced: false,
|
|
109
|
+
requiresRealApproval: ask.requiresRealApproval === true,
|
|
110
|
+
mandated: ask.mandated === true,
|
|
111
|
+
grantBinding: grantBindingOf(req),
|
|
112
|
+
classes: [...classifyAsk(ask, NO_GATE_FACTS).classes],
|
|
113
|
+
deploymentListQuestion,
|
|
114
|
+
};
|
|
89
115
|
}
|
|
90
|
-
export function
|
|
116
|
+
export function standingAnswerHome(facts) {
|
|
117
|
+
if (facts.governanceForced || facts.requiresRealApproval || facts.mandated || facts.classes === undefined)
|
|
118
|
+
return "none";
|
|
119
|
+
if (facts.deploymentListQuestion) {
|
|
120
|
+
return facts.grantBinding.kind === "none" && facts.classes.every((c) => DEPLOYMENT_LIST_ASK_CLASSES.has(c)) ? "server_store" : "none";
|
|
121
|
+
}
|
|
122
|
+
return facts.grantBinding.kind !== "unreadable" && foldArbitrationRow(new Set(facts.classes)).ruleMayClear ? "seat" : "none";
|
|
123
|
+
}
|
|
124
|
+
export function standingAnswerDurablyRecordable(facts) {
|
|
125
|
+
const home = standingAnswerHome(facts);
|
|
126
|
+
switch (home) {
|
|
127
|
+
case "none":
|
|
128
|
+
return false;
|
|
129
|
+
case "seat":
|
|
130
|
+
return facts.grantBinding.kind === "none";
|
|
131
|
+
case "server_store":
|
|
132
|
+
return true;
|
|
133
|
+
default: {
|
|
134
|
+
const unhandled = home;
|
|
135
|
+
throw new Error(`unhandled standing-answer home: ${String(unhandled)}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export function parkRowStandingAnswerFacts(pending, gate, governance, deploymentListAsks) {
|
|
91
140
|
const riskDescriptor = "riskDescriptor" in gate ? gate.riskDescriptor : undefined;
|
|
141
|
+
const classes = screenAskClassMembers(pending.askClass, pending.classes)?.classes;
|
|
92
142
|
return {
|
|
93
143
|
governanceForced: governanceOriginOf({ riskDescriptor }, governance) === true,
|
|
94
144
|
requiresRealApproval: readParkRowTrueSeat(pending, "requiresRealApproval"),
|
|
95
145
|
mandated: readParkRowTrueSeat(pending, "mandated") || mandatedFromSeat(readRuleOffersAbsenceSeat(pending)),
|
|
96
|
-
|
|
146
|
+
grantBinding: grantBindingOf(pending),
|
|
147
|
+
classes,
|
|
148
|
+
deploymentListQuestion: carriesDeploymentListQuestion(deploymentListAsks, classes),
|
|
97
149
|
};
|
|
98
150
|
}
|
|
99
151
|
function pendingEntryStandingAnswerFacts(entry) {
|
|
@@ -101,7 +153,9 @@ function pendingEntryStandingAnswerFacts(entry) {
|
|
|
101
153
|
governanceForced: entry.governanceForced === true,
|
|
102
154
|
requiresRealApproval: entry.requiresRealApproval === true,
|
|
103
155
|
mandated: entry.mandated === true,
|
|
104
|
-
|
|
156
|
+
grantBinding: entry.grantBinding,
|
|
157
|
+
classes: entry.classes,
|
|
158
|
+
deploymentListQuestion: entry.deploymentListQuestion === true,
|
|
105
159
|
};
|
|
106
160
|
}
|
|
107
161
|
function mandatedFromSeat(seat) {
|
|
@@ -118,29 +172,15 @@ export const PERSIST_RULE_TEXT_ERROR = `persistRule.rule must be a non-empty str
|
|
|
118
172
|
export const PERSIST_RULE_EDITED_FLAG_ERROR = "persistRule.edited must be a boolean when present";
|
|
119
173
|
export const PERSIST_RULE_BATCH_INDEX_ERROR = "persistRule.batchOfferIndex must be a non-negative integer when present";
|
|
120
174
|
export const PERSIST_RULE_BATCH_EXCLUSIVE_ERROR = "persistRule.batchOfferIndex is mutually exclusive with persistRule.rule / persistRule.edited — send exactly one arm";
|
|
121
|
-
export const SETTLED_BY_MUST_BE_STRING = "settledBy must be a string — the settlement source is one word from the engine's closed set, and this layer judges only the SHAPE (which word it is, the engine decides)";
|
|
122
|
-
export function respondSettledBy(parsed) {
|
|
123
|
-
return parsed.settledBy;
|
|
124
|
-
}
|
|
125
|
-
export const SETTLED_BY_DENY_ONLY = 'settledBy is only meaningful with decision "deny" — only a person approves an action that EXECUTES, so a self-reported settlement source on an allow is a contradiction the engine refuses; the refusal would arrive too late to undo this door\'s own side effects (a session allow-all grant, a persisted rule, a DECIDED row that replays as a plain approval), so it is refused here, before the decision';
|
|
126
|
-
export const SETTLED_BY_TOO_WIDE = `settledBy must be 1-${MAX_SETTLED_BY_CHARS} characters — the settlement source is one word from the engine's closed set and lands in a fixed-width column on the decision row`;
|
|
127
|
-
export function judgeSettledByBody(settledBy, decision, directDoorActive) {
|
|
128
|
-
if (settledBy === undefined)
|
|
129
|
-
return { kind: "absent" };
|
|
130
|
-
if (directDoorActive)
|
|
131
|
-
return { kind: "not_in_proof" };
|
|
132
|
-
if (decision !== "deny")
|
|
133
|
-
return { kind: "deny_only" };
|
|
134
|
-
if (typeof settledBy !== "string")
|
|
135
|
-
return { kind: "not_string" };
|
|
136
|
-
if (!settledByFitsColumn(settledBy))
|
|
137
|
-
return { kind: "too_wide" };
|
|
138
|
-
return { kind: "accepted", settledBy };
|
|
139
|
-
}
|
|
140
175
|
export const READ_ROOT_GRANT_ALLOW_ONLY = 'readRootGrant is only meaningful with decision "allow" — it is the card\'s "Yes, and add directory" answer (allow this call and add the card\'s readRootCandidate.dir to this run\'s read directories)';
|
|
141
176
|
export const READ_ROOT_GRANT_SHAPE = `readRootGrant must be the card's readRootCandidate.dir sent back byte for byte — a non-empty string of at most ${READ_ROOT_CANDIDATE_DIR_MAX} characters`;
|
|
142
177
|
export const PERSIST_RULE_SCOPE_REFUSED = "this endpoint does not accept a rule scope — the server mints it from where the approval happened (send neither `scope` nor `persistRule.scope`)";
|
|
143
178
|
export function parseToolApprovalResponse(body) {
|
|
179
|
+
const retired = retiredDecisionKeyFacts(body);
|
|
180
|
+
const parsed = parseToolApprovalFields(body, retired);
|
|
181
|
+
return parsed.ok || retired === undefined ? parsed : { ...parsed, ...retired.extra };
|
|
182
|
+
}
|
|
183
|
+
function parseToolApprovalFields(body, retired) {
|
|
144
184
|
if (body === null || typeof body !== "object" || Array.isArray(body))
|
|
145
185
|
return { ok: false, error: "body must be an object" };
|
|
146
186
|
const d = body.decision;
|
|
@@ -183,23 +223,19 @@ export function parseToolApprovalResponse(body) {
|
|
|
183
223
|
}
|
|
184
224
|
note = rawNote;
|
|
185
225
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
case "
|
|
192
|
-
return { ok: false, error:
|
|
193
|
-
case "too_wide":
|
|
194
|
-
return { ok: false, error: SETTLED_BY_TOO_WIDE };
|
|
195
|
-
case "absent":
|
|
196
|
-
break;
|
|
226
|
+
if (retired !== undefined)
|
|
227
|
+
return { ok: false, error: retired.error };
|
|
228
|
+
const decidedByReading = readDecidedBy(body.decidedBy, false);
|
|
229
|
+
let decidedBy;
|
|
230
|
+
switch (decidedByReading.kind) {
|
|
231
|
+
case "outside_closed_set":
|
|
232
|
+
return { ok: false, error: DECIDED_BY_OUTSIDE_CLOSED_SET };
|
|
197
233
|
case "accepted":
|
|
198
|
-
|
|
234
|
+
decidedBy = decidedByReading.decidedBy;
|
|
199
235
|
break;
|
|
200
236
|
default: {
|
|
201
|
-
const unreachable =
|
|
202
|
-
throw new Error(`unhandled
|
|
237
|
+
const unreachable = decidedByReading;
|
|
238
|
+
throw new Error(`unhandled decidedBy reading: ${JSON.stringify(unreachable)}`);
|
|
203
239
|
}
|
|
204
240
|
}
|
|
205
241
|
const rawGrant = body.readRootGrant;
|
|
@@ -214,7 +250,7 @@ export function parseToolApprovalResponse(body) {
|
|
|
214
250
|
...(d !== "deny" && u !== undefined ? { updatedInput: u } : {}),
|
|
215
251
|
...(persistRule !== undefined ? { persistRule } : {}),
|
|
216
252
|
...(note !== undefined ? { note } : {}),
|
|
217
|
-
|
|
253
|
+
decidedBy,
|
|
218
254
|
...(readRootGrant !== undefined ? { readRootGrant } : {}),
|
|
219
255
|
};
|
|
220
256
|
}
|
|
@@ -310,9 +346,10 @@ function replayTerminalAskRow(row) {
|
|
|
310
346
|
return false;
|
|
311
347
|
}
|
|
312
348
|
export function outcomeOfDecidedRow(row) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
349
|
+
const allow = row.decision === "approve";
|
|
350
|
+
const source = row.settledBy !== null ? { settledBy: row.settledBy } : {};
|
|
351
|
+
const edit = allow && row.updatedInput !== undefined ? { updatedInput: row.updatedInput } : {};
|
|
352
|
+
return row.settledBy === null && !("updatedInput" in edit) ? allow : { allow, ...edit, ...source };
|
|
316
353
|
}
|
|
317
354
|
function settleArgsOfDecided(outcome) {
|
|
318
355
|
if (typeof outcome === "boolean")
|
|
@@ -379,6 +416,7 @@ export class ToolApprovalCoordinator {
|
|
|
379
416
|
parkedRedeem;
|
|
380
417
|
askAudit;
|
|
381
418
|
sessionGrants;
|
|
419
|
+
deploymentListAsks;
|
|
382
420
|
shutdownDraining = false;
|
|
383
421
|
asksInFlight = 0;
|
|
384
422
|
parkCommits;
|
|
@@ -400,6 +438,10 @@ export class ToolApprovalCoordinator {
|
|
|
400
438
|
this.parkTombstonePerPrincipalMax = opts?.parkTombstonePerPrincipalMax ?? DEFAULT_PARK_TOMBSTONE_PER_PRINCIPAL_MAX;
|
|
401
439
|
this.askAudit = opts?.askAudit;
|
|
402
440
|
this.sessionGrants = opts?.sessionGrants;
|
|
441
|
+
if (opts?.deploymentListAsks !== undefined && opts.sessionGrants === undefined) {
|
|
442
|
+
throw new Error("ToolApprovalCoordinator: deploymentListAsks needs sessionGrants — the deployment list's standing answers live in the session exemption store");
|
|
443
|
+
}
|
|
444
|
+
this.deploymentListAsks = opts?.deploymentListAsks;
|
|
403
445
|
this.parkCommits = opts?.parkCommits;
|
|
404
446
|
this.parkCommits?.onParkCommitted((calls) => this.noteParkCommitted(calls));
|
|
405
447
|
}
|
|
@@ -751,19 +793,30 @@ export class ToolApprovalCoordinator {
|
|
|
751
793
|
return (this.governanceAskMarks ?? governanceAskMarksFor(this.streamKey(origin.owner, origin.sessionId, origin.taskId))).isMarked(req.toolCallId);
|
|
752
794
|
}
|
|
753
795
|
async standingGrantSettles(origin, req, governanceForced) {
|
|
754
|
-
|
|
755
|
-
|
|
796
|
+
const facts = askStandingAnswerFacts(req, governanceForced, this.deploymentListAsks?.(req) === true);
|
|
797
|
+
const home = standingAnswerHome(facts);
|
|
798
|
+
switch (home) {
|
|
799
|
+
case "seat":
|
|
800
|
+
break;
|
|
801
|
+
case "none":
|
|
802
|
+
case "server_store":
|
|
803
|
+
return false;
|
|
804
|
+
default: {
|
|
805
|
+
const unhandled = home;
|
|
806
|
+
throw new Error(`unhandled standing-answer home: ${String(unhandled)}`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
756
809
|
const sessionId = origin.sessionId;
|
|
757
810
|
if (!sessionId)
|
|
758
811
|
return false;
|
|
759
|
-
const live = this.allowAllSessions.has(sessionAllowKey(origin.owner, sessionId, sessionAllowCategory(req.toolName)));
|
|
760
|
-
const durable = !live && (await this.durableGrantHas(sessionId, req.toolName));
|
|
812
|
+
const live = this.allowAllSessions.has(sessionAllowKey(origin.owner, sessionId, sessionAllowCategory(req.toolName), facts.grantBinding));
|
|
813
|
+
const durable = !live && !answerGrantsBeyondCall(req) && (await this.durableGrantHas(sessionId, req.toolName));
|
|
761
814
|
if (!live && !durable)
|
|
762
815
|
return false;
|
|
763
816
|
if (!(await this.sessionGrantUncontestedByRow(origin, req)))
|
|
764
817
|
return false;
|
|
765
818
|
if (durable)
|
|
766
|
-
defaultLogger.info(
|
|
819
|
+
defaultLogger.info(APPROVAL_EXEMPTED_EVENT, { sessionId, toolName: req.toolName, seat: "approval_seat" });
|
|
767
820
|
return true;
|
|
768
821
|
}
|
|
769
822
|
async durableGrantHas(sessionId, canonicalToolName) {
|
|
@@ -1044,6 +1097,7 @@ export class ToolApprovalCoordinator {
|
|
|
1044
1097
|
if (signal?.aborted)
|
|
1045
1098
|
return false;
|
|
1046
1099
|
const governanceForced = this.governanceForcedOf(origin, req);
|
|
1100
|
+
const deploymentListAsks = this.deploymentListAsks?.(req) === true;
|
|
1047
1101
|
if (await this.standingGrantSettles(origin, req, governanceForced))
|
|
1048
1102
|
return true;
|
|
1049
1103
|
const aliveCtxs = ctxs.filter((c) => !c.abortSignal?.aborted);
|
|
@@ -1071,6 +1125,7 @@ export class ToolApprovalCoordinator {
|
|
|
1071
1125
|
let effectiveGovernanceForced = governanceForced;
|
|
1072
1126
|
let ruleLaneMaterial = this.buildRuleLaneMaterial(req, origin.owner, governanceForced, origin.sessionId);
|
|
1073
1127
|
const mandateWord = readClosedWordSeat(req, "mandate");
|
|
1128
|
+
const noRulingCause = readClosedWordSeat(req, "classifierNoRuling");
|
|
1074
1129
|
const originWord = typeof req.origin === "string" && req.origin.length > 0 ? req.origin : undefined;
|
|
1075
1130
|
const askClassPair = readAskClassPair(req);
|
|
1076
1131
|
const frame = {
|
|
@@ -1098,7 +1153,9 @@ export class ToolApprovalCoordinator {
|
|
|
1098
1153
|
...(governanceForced ? { governanceForced: true } : {}),
|
|
1099
1154
|
...(req.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
1100
1155
|
...(req.mandated === true ? { mandated: true } : {}),
|
|
1101
|
-
...(
|
|
1156
|
+
...(standingAnswerHome(askStandingAnswerFacts(req, governanceForced, deploymentListAsks)) === "none" ? { rememberRefused: true } : {}),
|
|
1157
|
+
...(typeof req.boundGrantDigest === "string" ? { boundGrantDigest: req.boundGrantDigest, ...(req.boundGrants !== undefined ? { boundGrants: req.boundGrants } : {}) } : {}),
|
|
1158
|
+
...(noRulingCause !== undefined ? { classifierNoRuling: noRulingCause } : {}),
|
|
1102
1159
|
...(mandateWord !== undefined ? { mandate: mandateWord } : {}),
|
|
1103
1160
|
...(originWord !== undefined ? { origin: originWord } : {}),
|
|
1104
1161
|
...(askClassPair ?? {}),
|
|
@@ -1306,11 +1363,12 @@ export class ToolApprovalCoordinator {
|
|
|
1306
1363
|
return unattended === "unavailable" ? unattended : shapeDeny(unattended);
|
|
1307
1364
|
}
|
|
1308
1365
|
if (settled.allowed) {
|
|
1309
|
-
if (settled.updatedInput === undefined && settled.readRootGrant === undefined)
|
|
1366
|
+
if (hostSettled === undefined && settled.updatedInput === undefined && settled.readRootGrant === undefined)
|
|
1310
1367
|
return true;
|
|
1311
1368
|
return {
|
|
1312
1369
|
allow: true,
|
|
1313
1370
|
...(settled.updatedInput !== undefined ? { updatedInput: settled.updatedInput } : {}),
|
|
1371
|
+
...(hostSettled !== undefined ? { settledBy: hostSettled } : {}),
|
|
1314
1372
|
...(settled.readRootGrant !== undefined ? { readRootGrant: settled.readRootGrant } : {}),
|
|
1315
1373
|
};
|
|
1316
1374
|
}
|
|
@@ -1623,12 +1681,17 @@ export class ToolApprovalCoordinator {
|
|
|
1623
1681
|
return shapeOutcome(await allowedP);
|
|
1624
1682
|
}
|
|
1625
1683
|
}
|
|
1626
|
-
const
|
|
1684
|
+
const entryGrantBinding = grantBindingOf(req);
|
|
1685
|
+
const entryClasses = askClassPair?.classes;
|
|
1686
|
+
const entryDeploymentListQuestion = carriesDeploymentListQuestion(deploymentListAsks, entryClasses);
|
|
1687
|
+
const rememberRefused = standingAnswerHome(pendingEntryStandingAnswerFacts({
|
|
1627
1688
|
...(effectiveGovernanceForced ? { governanceForced: true } : {}),
|
|
1628
1689
|
...(req.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
1629
1690
|
...(req.mandated === true ? { mandated: true } : {}),
|
|
1630
|
-
|
|
1631
|
-
|
|
1691
|
+
grantBinding: entryGrantBinding,
|
|
1692
|
+
classes: entryClasses,
|
|
1693
|
+
...(entryDeploymentListQuestion ? { deploymentListQuestion: true } : {}),
|
|
1694
|
+
})) === "none";
|
|
1632
1695
|
if (rememberRefused)
|
|
1633
1696
|
frame.rememberRefused = true;
|
|
1634
1697
|
else
|
|
@@ -1648,6 +1711,9 @@ export class ToolApprovalCoordinator {
|
|
|
1648
1711
|
...(origin.sessionId ? { sessionId: origin.sessionId } : {}),
|
|
1649
1712
|
frame,
|
|
1650
1713
|
toolName: req.toolName,
|
|
1714
|
+
grantBinding: entryGrantBinding,
|
|
1715
|
+
...(entryClasses !== undefined ? { classes: entryClasses } : {}),
|
|
1716
|
+
...(entryDeploymentListQuestion ? { deploymentListQuestion: true } : {}),
|
|
1651
1717
|
registeredAtMs: Date.now(),
|
|
1652
1718
|
expiresAtMs: persistedExpiresAtMs,
|
|
1653
1719
|
...(ruleLaneMaterial !== undefined ? { ruleLane: ruleLaneMaterial } : {}),
|
|
@@ -1777,7 +1843,7 @@ export class ToolApprovalCoordinator {
|
|
|
1777
1843
|
respond(id, principal, body, httpReq) {
|
|
1778
1844
|
const parsed = parseToolApprovalResponse(body);
|
|
1779
1845
|
if (!parsed.ok)
|
|
1780
|
-
return { status: 400, body: { error: parsed.error, errorCode: "request.body_shape" } };
|
|
1846
|
+
return { status: 400, body: { error: parsed.error, errorCode: "request.body_shape", ...(parsed.field !== undefined ? { field: parsed.field } : {}) } };
|
|
1781
1847
|
const entry = this.pending.get(id);
|
|
1782
1848
|
if (!entry || (entry.owner !== null && entry.owner !== principal)) {
|
|
1783
1849
|
return this.respondLate(id, principal, parsed, httpReq);
|
|
@@ -1785,14 +1851,43 @@ export class ToolApprovalCoordinator {
|
|
|
1785
1851
|
const preRule = this.precheckEditedRuleBeforeDecision(entry, parsed);
|
|
1786
1852
|
if (preRule !== undefined)
|
|
1787
1853
|
return preRule;
|
|
1854
|
+
if (parsed.value !== "deny" && policyCannotAnswer(heldAskOf(entry.requiresRealApproval === true), parsed.decidedBy)) {
|
|
1855
|
+
return { status: 400, body: { error: POLICY_CANNOT_ALLOW_PERSON_ASK, errorCode: "request.body_shape" } };
|
|
1856
|
+
}
|
|
1788
1857
|
const ruleEligibility = { eligible: true };
|
|
1789
|
-
const
|
|
1858
|
+
const decided = this.askStore && entry.askId !== undefined && entry.batchId !== undefined
|
|
1790
1859
|
? this.respondWithCas(this.askStore, entry.askId, entry.batchId, id, entry, parsed, ruleEligibility)
|
|
1791
1860
|
: this.finishRespond(id, entry, parsed);
|
|
1861
|
+
const settled = this.recordDeploymentListAnswer(decided, entry, parsed, principal, ruleEligibility);
|
|
1792
1862
|
if (parsed.persistRule === undefined)
|
|
1793
1863
|
return settled;
|
|
1794
1864
|
return this.persistRuleAfterDecision(settled, entry, parsed.persistRule, parsed.updatedInput !== undefined, ruleEligibility);
|
|
1795
1865
|
}
|
|
1866
|
+
recordDeploymentListAnswer(decided, entry, parsed, principal, eligibility) {
|
|
1867
|
+
const store = this.sessionGrants;
|
|
1868
|
+
const sessionId = entry.sessionId;
|
|
1869
|
+
if (parsed.value !== "allow_session" || !sessionId || store === undefined)
|
|
1870
|
+
return decided;
|
|
1871
|
+
if (standingAnswerHome(pendingEntryStandingAnswerFacts(entry)) !== "server_store")
|
|
1872
|
+
return decided;
|
|
1873
|
+
return (async () => {
|
|
1874
|
+
const result = await decided;
|
|
1875
|
+
if (result.status !== 200)
|
|
1876
|
+
return result;
|
|
1877
|
+
if (!eligibility.eligible)
|
|
1878
|
+
return { ...result, body: { ...result.body, rememberApplied: false } };
|
|
1879
|
+
let rememberApplied = false;
|
|
1880
|
+
try {
|
|
1881
|
+
await this.withStoreDeadline(store.grant(sessionId, entry.toolName, principal ?? entry.owner), "sessionGrants.grant(deployment-list)", DURABLE_CALL_TIMEOUT_MS);
|
|
1882
|
+
rememberApplied = true;
|
|
1883
|
+
defaultLogger.info("approval_exemption_granted", { sessionId, toolName: entry.toolName, grantedBy: principal ?? entry.owner });
|
|
1884
|
+
}
|
|
1885
|
+
catch (err) {
|
|
1886
|
+
defaultLogger.warn("approval_exemption_grant_failed", { sessionId, toolName: entry.toolName, error: String(err).slice(0, 200) });
|
|
1887
|
+
}
|
|
1888
|
+
return { ...result, body: { ...result.body, rememberApplied } };
|
|
1889
|
+
})();
|
|
1890
|
+
}
|
|
1796
1891
|
precheckEditedRuleBeforeDecision(entry, parsed) {
|
|
1797
1892
|
const persistRule = parsed.persistRule;
|
|
1798
1893
|
if (persistRule === undefined || persistRule.kind !== "text" || !persistRule.edited)
|
|
@@ -1945,7 +2040,7 @@ export class ToolApprovalCoordinator {
|
|
|
1945
2040
|
gateBoundInputHash: row.gateBoundInputHash,
|
|
1946
2041
|
decision,
|
|
1947
2042
|
...(parsed.note !== undefined ? { note: parsed.note } : {}),
|
|
1948
|
-
|
|
2043
|
+
decidedBy: parsed.decidedBy,
|
|
1949
2044
|
...(parsed.updatedInput !== undefined && parsed.value !== "deny" ? { updatedInput: parsed.updatedInput } : {}),
|
|
1950
2045
|
...(principal !== undefined ? { principal } : {}),
|
|
1951
2046
|
...(httpReq !== undefined ? { httpReq } : {}),
|
|
@@ -2183,7 +2278,7 @@ export class ToolApprovalCoordinator {
|
|
|
2183
2278
|
decision: parsed.value === "deny" ? "deny" : "approve",
|
|
2184
2279
|
...(parsed.note !== undefined ? { decisionNote: parsed.note } : {}),
|
|
2185
2280
|
...(parsed.value !== "deny" && parsed.updatedInput !== undefined ? { updatedInput: parsed.updatedInput } : {}),
|
|
2186
|
-
...(
|
|
2281
|
+
...((sb) => (sb !== undefined ? { settledBy: sb } : {}))(liveSettledBy(parsed.decidedBy)),
|
|
2187
2282
|
}), "decideAsk", DURABLE_CALL_TIMEOUT_MS, (late) => {
|
|
2188
2283
|
if (late.ok)
|
|
2189
2284
|
this.notifyExternalDecision(askId, outcomeOfDecidedRow(late.row));
|
|
@@ -2235,7 +2330,21 @@ export class ToolApprovalCoordinator {
|
|
|
2235
2330
|
}
|
|
2236
2331
|
finishRespond(id, entry, parsed, opts) {
|
|
2237
2332
|
let rememberApplied;
|
|
2238
|
-
const
|
|
2333
|
+
const home = standingAnswerHome(pendingEntryStandingAnswerFacts(entry));
|
|
2334
|
+
let grantable;
|
|
2335
|
+
switch (home) {
|
|
2336
|
+
case "seat":
|
|
2337
|
+
grantable = true;
|
|
2338
|
+
break;
|
|
2339
|
+
case "none":
|
|
2340
|
+
case "server_store":
|
|
2341
|
+
grantable = false;
|
|
2342
|
+
break;
|
|
2343
|
+
default: {
|
|
2344
|
+
const unhandled = home;
|
|
2345
|
+
throw new Error(`unhandled standing-answer home: ${String(unhandled)}`);
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2239
2348
|
if (parsed.value === "allow_session")
|
|
2240
2349
|
rememberApplied = Boolean(entry.sessionId) && grantable;
|
|
2241
2350
|
if (parsed.value === "allow_session" && entry.sessionId && grantable) {
|
|
@@ -2244,10 +2353,10 @@ export class ToolApprovalCoordinator {
|
|
|
2244
2353
|
if (oldest !== undefined)
|
|
2245
2354
|
this.allowAllSessions.delete(oldest);
|
|
2246
2355
|
}
|
|
2247
|
-
this.allowAllSessions.add(sessionAllowKey(entry.owner, entry.sessionId, sessionAllowCategory(entry.toolName)));
|
|
2356
|
+
this.allowAllSessions.add(sessionAllowKey(entry.owner, entry.sessionId, sessionAllowCategory(entry.toolName), entry.grantBinding));
|
|
2248
2357
|
}
|
|
2249
2358
|
const allowed = parsed.value !== "deny";
|
|
2250
|
-
const settleArgs = opts?.settleOutcome !== undefined ? settleArgsOfDecided(opts.settleOutcome) : { allowed, updatedInput: parsed.updatedInput, settledBy:
|
|
2359
|
+
const settleArgs = opts?.settleOutcome !== undefined ? settleArgsOfDecided(opts.settleOutcome) : { allowed, updatedInput: parsed.updatedInput, settledBy: liveSettledBy(parsed.decidedBy) };
|
|
2251
2360
|
const readRootGrant = opts?.settleOutcome === undefined || opts.settleOutcomeIsThisRequest === true ? parsed.readRootGrant : undefined;
|
|
2252
2361
|
entry.settle(settleArgs.allowed, settleArgs.allowed ? "allowed" : "denied", settleArgs.updatedInput, settleArgs.settledBy, settleArgs.allowed ? undefined : parsed.note, readRootGrant);
|
|
2253
2362
|
const updatedInputDelivered = opts?.updatedInputDelivered ?? true;
|
|
@@ -2279,7 +2388,7 @@ export class ToolApprovalCoordinator {
|
|
|
2279
2388
|
...(e.requiresRealApproval ? { requiresRealApproval: true } : {}),
|
|
2280
2389
|
...(e.governanceForced ? { governanceForced: true } : {}),
|
|
2281
2390
|
...(e.mandated ? { mandated: true } : {}),
|
|
2282
|
-
...(
|
|
2391
|
+
...(standingAnswerHome(pendingEntryStandingAnswerFacts(e)) === "none" ? { rememberRefused: true } : {}),
|
|
2283
2392
|
...(e.mandate !== undefined ? { mandate: e.mandate } : {}),
|
|
2284
2393
|
...(e.origin !== undefined ? { origin: e.origin } : {}),
|
|
2285
2394
|
...(e.originTaskId !== undefined ? { fromSubagent: true, originTaskId: e.originTaskId } : {}),
|
|
@@ -43,7 +43,7 @@ type _GuardBgNotifReverse = AssertAllKeysHandled<Exclude<BgNotifProjected | BgNo
|
|
|
43
43
|
type RosterProjected = "name" | "agentId" | "sessionId" | "toolUseId" | "owner" | "scope" | "sessionScoped" | "rootSessionId" | "model" | "modelFallback" | "createdAt";
|
|
44
44
|
type _GuardRoster = AssertAllKeysHandled<Exclude<keyof RosterEntry, RosterProjected>>;
|
|
45
45
|
type _GuardRosterReverse = AssertAllKeysHandled<Exclude<RosterProjected, keyof RosterEntry>>;
|
|
46
|
-
type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "allowRuleShadowed" | "probeCause" | "probeReason" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "readRootCandidate" | "mandated" | "mandate" | "askClass" | "classes";
|
|
46
|
+
type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "allowRuleShadowed" | "probeCause" | "probeReason" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "readRootCandidate" | "mandated" | "mandate" | "askClass" | "classes" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling";
|
|
47
47
|
type AskExcluded = "preview" | "principal" | "riskAxes" | "boundInputHash" | "isDelegatedChild" | "hasBidiControls" | "previewWithheld" | "execCwd" | "authoredBy";
|
|
48
48
|
type _GuardAsk = AssertAllKeysHandled<Exclude<keyof AskRequest, AskProjected | AskExcluded>>;
|
|
49
49
|
type _GuardAskReverse = AssertAllKeysHandled<Exclude<AskProjected | AskExcluded, keyof AskRequest>>;
|
|
@@ -136,7 +136,7 @@ type _GuardPermissionTraceReverse = AssertAllKeysHandled<Exclude<PermissionTrace
|
|
|
136
136
|
/** `T` 非 `never` 时收敛到 `never`(空集 ⇒ 残留 `"EMPTY"` ⇒ tsc 红)。 */
|
|
137
137
|
type AssertNonEmpty<T> = [T] extends [never] ? "EMPTY" : never;
|
|
138
138
|
type _GuardPermissionTraceNonEmpty = AssertAllKeysHandled<AssertNonEmpty<PermissionTraceKind>>;
|
|
139
|
-
type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "mandated" | "mandate";
|
|
139
|
+
type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "mandated" | "mandate" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling";
|
|
140
140
|
type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceTaskId" | "contentKind" | "restoreMode" | "checkpointId" | "previewWithheld" | "authoredBy";
|
|
141
141
|
type _GuardInboxSummary = AssertAllKeysHandled<Exclude<keyof CheckpointSummary, SummaryProjected | SummaryExcluded>>;
|
|
142
142
|
type _GuardInboxSummaryReverse = AssertAllKeysHandled<Exclude<SummaryProjected | SummaryExcluded, keyof CheckpointSummary>>;
|
|
@@ -144,11 +144,11 @@ type ParkArmKeys = keyof Extract<PendingAction, {
|
|
|
144
144
|
kind: "tool_approval";
|
|
145
145
|
}>;
|
|
146
146
|
type ParkArmQueueProjected = "toolName" | "toolCallId" | "boundInputHash" | "ruleOffers" | "hasBidiControls" | keyof import("../approval-card.js").ParkRowFacts;
|
|
147
|
-
type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCallIds" | "capabilityId" | "contractId" | "shapeDigest" | "implementationRevision" | "rosterDigest" | "family" | "pathTarget" | "names" | "preview" | "previewWithheld" | "execCwd" | "denialLimitFallback" | "authoredBy" | "boundGrantDigest";
|
|
147
|
+
type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCallIds" | "capabilityId" | "contractId" | "shapeDigest" | "implementationRevision" | "rosterDigest" | "family" | "pathTarget" | "names" | "preview" | "previewWithheld" | "execCwd" | "denialLimitFallback" | "authoredBy" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling";
|
|
148
148
|
type _GuardParkArmQueue = AssertAllKeysHandled<Exclude<ParkArmKeys, ParkArmQueueProjected | ParkArmQueueExcluded>>;
|
|
149
149
|
type _GuardParkArmQueueReverse = AssertAllKeysHandled<Exclude<ParkArmQueueProjected | ParkArmQueueExcluded, ParkArmKeys>>;
|
|
150
150
|
type _NoDoubleBookParkArmQueue = AssertAllKeysHandled<Extract<ParkArmQueueProjected, ParkArmQueueExcluded>>;
|
|
151
|
-
type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "writeFace" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "permissionMode" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends" | "skillListingBudgetFraction" | "skillListingMaxDescChars" | "excludeAllTools" | "fileHistory" | "userInvokedSkills" | "allowedTools";
|
|
151
|
+
type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "approvalReach" | "writeFace" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "permissionMode" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends" | "skillListingBudgetFraction" | "skillListingMaxDescChars" | "excludeAllTools" | "fileHistory" | "userInvokedSkills" | "allowedTools";
|
|
152
152
|
type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence" | "outputStyle";
|
|
153
153
|
type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
|
|
154
154
|
type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
|
|
@@ -199,7 +199,7 @@ type WorkflowRunStripped = "sourceTaskId" | "originatingSessionId" | "effectiveA
|
|
|
199
199
|
type _GuardWorkflowRun = AssertAllKeysHandled<Exclude<keyof WorkflowRun, WorkflowRunForwarded | WorkflowRunStripped>>;
|
|
200
200
|
type _GuardWorkflowRunReverse = AssertAllKeysHandled<Exclude<WorkflowRunForwarded | WorkflowRunStripped, keyof WorkflowRun>>;
|
|
201
201
|
type ParkRowKeys = keyof NonNullable<WorkflowRun["parks"]>[number];
|
|
202
|
-
type ParkRowProjected = "callKey" | "sessionId" | "originRunId" | "originUnconfirmed";
|
|
202
|
+
type ParkRowProjected = "callKey" | "sessionId" | "originRunId" | "originUnconfirmed" | "parkedBy" | "generation";
|
|
203
203
|
type ParkRowWithheld = "token";
|
|
204
204
|
type _GuardParkRow = AssertAllKeysHandled<Exclude<ParkRowKeys, ParkRowProjected | ParkRowWithheld>>;
|
|
205
205
|
type _NoDoubleBookParkRow = AssertAllKeysHandled<Extract<ParkRowProjected, ParkRowWithheld>>;
|
package/dist/trace/redact.d.ts
CHANGED
|
@@ -50,6 +50,7 @@ import { stripFormatCharacters } from "@sema-agent/core";
|
|
|
50
50
|
* 现役消费方:`src/tool-approval.ts` 的 `durableRuleMaterial`(规则车道素材闸)与语料的 FP=0 格。
|
|
51
51
|
*/
|
|
52
52
|
export declare const normalizeForRedaction: typeof stripFormatCharacters;
|
|
53
|
+
export declare const REDACTION_TOKEN_RE_SRC: string;
|
|
53
54
|
export declare function armPoolBudgetForTest(): {
|
|
54
55
|
spanCount: number;
|
|
55
56
|
coveredPositions: number;
|
|
@@ -163,6 +164,11 @@ export declare function redactionSpansForTest(text: string, opts?: {
|
|
|
163
164
|
* 让下游看到"这里有东西被省略了",而不是丢一个空对象或炸掉整条流。
|
|
164
165
|
*/
|
|
165
166
|
export declare function redactDeep(value: unknown): unknown;
|
|
167
|
+
/** {@link redactDeep} 的**串叶处置可注入**形:同一只环 / 深度守卫的遍历,只有「一个串叶怎么脱」由调用方给。
|
|
168
|
+
* `redactDeep` 本身就是 `leaf = redactSecrets` 的那一档(行为逐字不变)。读面要的是另一档 —— 串叶走
|
|
169
|
+
* `redactModelText`(认出凭据形才改写字节,不含凭据形的串逐字节不变),遍历不该因此再写第二份。
|
|
170
|
+
* `leaf` 必须是纯的串→串函数且**自己不抛**(抛了就穿出本遍历)。 */
|
|
171
|
+
export declare function redactDeepWith(value: unknown, leaf: (s: string) => string): unknown;
|
|
166
172
|
/**
|
|
167
173
|
* A REDACTED, length-BOUNDED string preview of a structured value — for surfacing a pending tool call's `args`
|
|
168
174
|
* in the assistant inbox ("what is this ask about") WITHOUT leaking secrets or dumping an unbounded payload (a
|
package/dist/trace/redact.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { displayUrlOf, stripFormatCharacters } from "@sema-agent/core";
|
|
2
2
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
3
3
|
export const normalizeForRedaction = stripFormatCharacters;
|
|
4
|
-
const REDACTION_TOKEN_RE_SRC = String.raw `«redacted(?::[a-z-]{1,16}){0,2}»`;
|
|
4
|
+
export const REDACTION_TOKEN_RE_SRC = String.raw `«redacted(?::[a-z-]{1,16}){0,2}»`;
|
|
5
5
|
const ARM_VALUE_CLASSES = [];
|
|
6
6
|
const atLeast = (cls, min) => {
|
|
7
7
|
ARM_VALUE_CLASSES.push(cls);
|
|
@@ -1797,11 +1797,14 @@ export function redactionSpansForTest(text, opts) {
|
|
|
1797
1797
|
}
|
|
1798
1798
|
const REDACT_MAX_DEPTH = 64;
|
|
1799
1799
|
export function redactDeep(value) {
|
|
1800
|
-
return
|
|
1800
|
+
return redactDeepWith(value, redactSecrets);
|
|
1801
1801
|
}
|
|
1802
|
-
function
|
|
1802
|
+
export function redactDeepWith(value, leaf) {
|
|
1803
|
+
return redactDeepInner(value, 0, new WeakSet(), leaf);
|
|
1804
|
+
}
|
|
1805
|
+
function redactDeepInner(value, depth, seen, leaf) {
|
|
1803
1806
|
if (typeof value === "string")
|
|
1804
|
-
return
|
|
1807
|
+
return leaf(value);
|
|
1805
1808
|
if (!value || typeof value !== "object")
|
|
1806
1809
|
return value;
|
|
1807
1810
|
if (seen.has(value))
|
|
@@ -1811,11 +1814,8 @@ function redactDeepInner(value, depth, seen) {
|
|
|
1811
1814
|
seen.add(value);
|
|
1812
1815
|
try {
|
|
1813
1816
|
if (Array.isArray(value))
|
|
1814
|
-
return value.map((v) => redactDeepInner(v, depth + 1, seen));
|
|
1815
|
-
|
|
1816
|
-
for (const [k, v] of Object.entries(value))
|
|
1817
|
-
out[k] = redactDeepInner(v, depth + 1, seen);
|
|
1818
|
-
return out;
|
|
1817
|
+
return value.map((v) => redactDeepInner(v, depth + 1, seen, leaf));
|
|
1818
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, redactDeepInner(v, depth + 1, seen, leaf)]));
|
|
1819
1819
|
}
|
|
1820
1820
|
finally {
|
|
1821
1821
|
seen.delete(value);
|
|
@@ -19,9 +19,9 @@ export declare const SELF_MINTED_WIRE: {
|
|
|
19
19
|
};
|
|
20
20
|
};
|
|
21
21
|
/**
|
|
22
|
-
* S-928(7.110.0):`engine_notice` `task.user_steer_undrained` 上**本仓铸**的那一臂。词表属主是 core(`
|
|
22
|
+
* S-928(7.110.0):`engine_notice` `task.user_steer_undrained` 上**本仓铸**的那一臂。词表属主是 core(`USER_INPUT_UNDRAINED_CAUSES`,两族共用;
|
|
23
23
|
* `leg_end` 由引擎铸、逐字过境,不在本表);本表登记的是「这个值自哪一版起由本服务出现在 wire 上」。`satisfies Record<CrashedCause, …>`:
|
|
24
|
-
* core 删掉 `crashed` ⇒ `CrashedCause` 成 never ⇒
|
|
24
|
+
* core 删掉 `crashed` ⇒ `CrashedCause` 成 never ⇒ 本行编译红。码按受理时的车道选(`user-input-ledger.ts` `acceptedInputKindOf`,车道 = core `injectionLaneOf`,码 = core `USER_INPUT_UNDRAINED_FAMILIES`):本行 = steer 车道。
|
|
25
25
|
*/
|
|
26
26
|
readonly userSteerUndrainedServerCause: {
|
|
27
27
|
readonly at: "engine_notice task.user_steer_undrained detail.cause(本服务铸的臂;下一条同会话 run 开通告口那一拍)";
|
|
@@ -32,6 +32,16 @@ export declare const SELF_MINTED_WIRE: {
|
|
|
32
32
|
};
|
|
33
33
|
};
|
|
34
34
|
};
|
|
35
|
+
/** S-1122(7.118.0,core 7.44.0 #1362):同一臂在 follow-up 车道(`later` 档)上的那一族。7.110.0–7.117.x 的这类丢失报在 steer 一族里。 */
|
|
36
|
+
readonly userFollowupUndrainedServerCause: {
|
|
37
|
+
readonly at: "engine_notice task.user_followup_undrained detail.cause(本服务铸的臂;`later` 档受理的崩溃丢失,下一条同会话 run 开通告口那一拍)";
|
|
38
|
+
readonly members: {
|
|
39
|
+
crashed: {
|
|
40
|
+
attr: string;
|
|
41
|
+
since: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
};
|
|
35
45
|
/**
|
|
36
46
|
* S-961(7.111.0):`wiring_manifest.instructions.selected[]` 的成员值 = 指令文件链的成员(相对 cwd 的拼写)。属主是 `project-memory.ts`
|
|
37
47
|
* 的 `INSTRUCTION_FILE_CHAIN`([ref] 序:CLAUDE 族先、AGENTS 族后,首中即选)—— 链加 / 删一员 ⇒ 本行 `satisfies` 编译红,登记跟着动。
|
|
@@ -95,6 +105,10 @@ export declare const SELF_MINTED_WIRE: {
|
|
|
95
105
|
readonly attr: 409;
|
|
96
106
|
readonly since: "7.101.0";
|
|
97
107
|
};
|
|
108
|
+
readonly "memory.scope_home_not_directory": {
|
|
109
|
+
readonly attr: 409;
|
|
110
|
+
readonly since: "7.118.0";
|
|
111
|
+
};
|
|
98
112
|
readonly "memory.erasure_evidence_unavailable": {
|
|
99
113
|
readonly attr: 409;
|
|
100
114
|
readonly since: "7.108.0";
|
|
@@ -12,6 +12,12 @@ export const SELF_MINTED_WIRE = {
|
|
|
12
12
|
crashed: { attr: "-", since: "7.110.0" },
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
|
+
userFollowupUndrainedServerCause: {
|
|
16
|
+
at: "engine_notice task.user_followup_undrained detail.cause(本服务铸的臂;`later` 档受理的崩溃丢失,下一条同会话 run 开通告口那一拍)",
|
|
17
|
+
members: {
|
|
18
|
+
crashed: { attr: "-", since: "7.118.0" },
|
|
19
|
+
},
|
|
20
|
+
},
|
|
15
21
|
wiringInstructionsSelected: {
|
|
16
22
|
at: "wiring_manifest instructions.selected[](本腿 loader 选中的链成员;段缺席 = 本腿无陈述)",
|
|
17
23
|
members: {
|
|
@@ -34,6 +40,7 @@ export const SELF_MINTED_WIRE = {
|
|
|
34
40
|
members: {
|
|
35
41
|
"config.memory_erasure_request": { attr: 400, since: "7.52.0" },
|
|
36
42
|
"memory.target_outside_store": { attr: 409, since: "7.101.0" },
|
|
43
|
+
"memory.scope_home_not_directory": { attr: 409, since: "7.118.0" },
|
|
37
44
|
"memory.erasure_evidence_unavailable": { attr: 409, since: "7.108.0" },
|
|
38
45
|
"memory.erasure_selector_mismatch": { attr: 409, since: "7.108.0" },
|
|
39
46
|
"memory.erasure_census_incomplete": { attr: 409, since: "7.108.0" },
|