@sema-agent/core 7.50.1 → 7.51.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 +275 -0
- package/dist/agents/child-failure.d.ts +5 -16
- package/dist/agents/child-failure.js +4 -24
- package/dist/agents/subagent-steps.js +2 -2
- package/dist/agents/subagent.d.ts +4 -13
- package/dist/agents/subagent.js +94 -204
- package/dist/agents/tool-filter.d.ts +2 -2
- package/dist/brain/anthropic.d.ts +1 -1
- package/dist/brain/anthropic.js +7 -5
- package/dist/brain/open-responses.d.ts +1 -1
- package/dist/brain/open-responses.js +3 -2
- package/dist/brain/openai.d.ts +1 -1
- package/dist/brain/openai.js +3 -2
- package/dist/brain/request-route.d.ts +2 -2
- package/dist/brain/route-adjudicator.d.ts +16 -7
- package/dist/brain/route-adjudicator.js +23 -7
- package/dist/brain/route-conformance.js +23 -5
- package/dist/brain/stream-engine.js +9 -3
- package/dist/core/a2a.js +6 -8
- package/dist/core/allow-entry.d.ts +45 -26
- package/dist/core/allow-entry.js +48 -24
- package/dist/core/arg-summary.d.ts +8 -85
- package/dist/core/arg-summary.js +4 -386
- package/dist/core/ask-origin.d.ts +35 -33
- package/dist/core/ask-origin.js +9 -11
- package/dist/core/checkpoint-requirements.d.ts +6 -0
- package/dist/core/checkpoint-requirements.js +1 -0
- package/dist/core/checkpoint-store.d.ts +15 -34
- package/dist/core/checkpoint-store.js +5 -4
- package/dist/core/classifier-unavailable-arm.d.ts +12 -8
- package/dist/core/classifier-unavailable-arm.js +7 -2
- package/dist/core/credential-vocabulary.d.ts +3 -2
- package/dist/core/credential-vocabulary.js +27 -18
- package/dist/core/delegation-frames.d.ts +40 -15
- package/dist/core/delegation-frames.js +34 -0
- package/dist/core/engine-notice.d.ts +35 -11
- package/dist/core/gate-fold.js +9 -7
- package/dist/core/gate-lanes.js +6 -4
- package/dist/core/gate-outcome.d.ts +87 -11
- package/dist/core/gate-outcome.js +171 -61
- package/dist/core/governance-codes.d.ts +7 -1
- package/dist/core/governance-codes.js +9 -0
- package/dist/core/hooks.d.ts +4 -4
- package/dist/core/hooks.js +4 -3
- package/dist/core/mcp-connection-pool.d.ts +2 -0
- package/dist/core/mcp-connection-pool.js +2 -1
- package/dist/core/mcp-failure.js +2 -1
- package/dist/core/mcp-server-spec.d.ts +27 -5
- package/dist/core/mcp.d.ts +13 -38
- package/dist/core/mcp.js +28 -23
- package/dist/core/media-byte-cap.d.ts +25 -0
- package/dist/core/media-byte-cap.js +18 -2
- package/dist/core/memory-engine/consolidation-driver.js +1 -1
- package/dist/core/mode-policy.d.ts +25 -1
- package/dist/core/mode-policy.js +28 -13
- package/dist/core/own-property-receipt.d.ts +18 -0
- package/dist/core/own-property-receipt.js +39 -0
- package/dist/core/permission-rule-model.d.ts +32 -11
- package/dist/core/permission-rule-model.js +12 -7
- package/dist/core/persisted-rule-arms.d.ts +3 -3
- package/dist/core/prompt-section-seat.d.ts +54 -0
- package/dist/core/prompt-section-seat.js +49 -0
- package/dist/core/protocol-http-auth.d.ts +44 -0
- package/dist/core/protocol-http-auth.js +45 -0
- package/dist/core/read-face-seats.d.ts +30 -0
- package/dist/core/read-face-seats.js +38 -0
- package/dist/core/redaction-passes.d.ts +82 -0
- package/dist/core/redaction-passes.js +378 -0
- package/dist/core/remote-env.d.ts +4 -3
- package/dist/core/resume-startup-cause.d.ts +56 -0
- package/dist/core/resume-startup-cause.js +43 -0
- package/dist/core/rule-denial-sentence.d.ts +2 -2
- package/dist/core/runner/announce-once-ledger.js +2 -2
- package/dist/core/runner/classifier-ladder.d.ts +17 -13
- package/dist/core/runner/classifier-ladder.js +12 -10
- package/dist/core/runner/construction-doors.js +9 -20
- package/dist/core/runner/contracts.d.ts +4 -4
- package/dist/core/runner/gate-exit.js +1 -1
- package/dist/core/runner/memory-consolidation-write-judge.d.ts +53 -9
- package/dist/core/runner/memory-consolidation-write-judge.js +39 -1
- package/dist/core/runner/permission-rule-lanes.d.ts +8 -13
- package/dist/core/runner/person-path-rows.d.ts +4 -3
- package/dist/core/runner/policy-throw.js +4 -4
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -3
- package/dist/core/runner/prepare-caps-and-workflow.js +8 -6
- package/dist/core/runner/prepare-gate-stations.js +3 -3
- package/dist/core/runner/prepare-hands-readface.js +1 -19
- package/dist/core/runner/prepare-inherited-gate.js +10 -9
- package/dist/core/runner/prepare-policy-chain.d.ts +3 -5
- package/dist/core/runner/prepare-policy-chain.js +10 -13
- package/dist/core/runner/prepare-project-context.d.ts +4 -4
- package/dist/core/runner/prepare-project-context.js +2 -1
- package/dist/core/runner/prepare-prompt-assembly.js +1 -0
- package/dist/core/runner/prepare-prompt-inputs.d.ts +1 -1
- package/dist/core/runner/prepare-prompt-inputs.js +4 -4
- package/dist/core/runner/prepare-protocol-tools.d.ts +4 -3
- package/dist/core/runner/prepare-protocol-tools.js +3 -2
- package/dist/core/runner/prepare-task.js +6 -4
- package/dist/core/runner/prepare-turn-wiring.d.ts +16 -13
- package/dist/core/runner/prepare-turn-wiring.js +8 -7
- package/dist/core/runner/run-harness-handlers.js +2 -2
- package/dist/core/runner/run-leg.js +10 -9
- package/dist/core/runner/run-settle-and-teardown.js +5 -5
- package/dist/core/runner/run-turn-boundary.js +3 -2
- package/dist/core/runner/runtask.d.ts +1 -2
- package/dist/core/runner/runtask.js +8 -12
- package/dist/core/runner/session-rule-policy.d.ts +15 -11
- package/dist/core/runner/session-rule-policy.js +43 -33
- package/dist/core/runner/stream-lifecycle-verbs.js +2 -2
- package/dist/core/runner/stream-settle-backstop.js +2 -1
- package/dist/core/runner/sub-agent-deny-lists.d.ts +12 -8
- package/dist/core/runner/sub-agent-deny-lists.js +23 -12
- package/dist/core/runner/terminal-account.js +3 -3
- package/dist/core/runner/tool-output-projection.js +3 -8
- package/dist/core/runner/tool-result-media-seat.d.ts +19 -0
- package/dist/core/runner/tool-result-media-seat.js +13 -0
- package/dist/core/runner/turn-attachments.d.ts +13 -12
- package/dist/core/runner/turn-attachments.js +11 -7
- package/dist/core/runner-deps.d.ts +4 -3
- package/dist/core/safety-merge-corpus.js +21 -21
- package/dist/core/secret-env.js +1 -1
- package/dist/core/session-policy-store.d.ts +89 -72
- package/dist/core/session-policy-store.js +100 -93
- package/dist/core/shell-read-silence.d.ts +38 -4
- package/dist/core/shell-read-silence.js +102 -0
- package/dist/core/side-query.d.ts +7 -8
- package/dist/core/skill-grant-binding.d.ts +32 -11
- package/dist/core/skill-grant-binding.js +11 -3
- package/dist/core/skill-grant.d.ts +4 -3
- package/dist/core/skill-grant.js +5 -4
- package/dist/core/skills-directory.d.ts +12 -1
- package/dist/core/skills-directory.js +17 -8
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +3 -0
- package/dist/core/swappable-deps.d.ts +15 -1
- package/dist/core/swappable-deps.js +25 -1
- package/dist/core/synthetic-read-verdict.d.ts +16 -14
- package/dist/core/synthetic-read-verdict.js +28 -8
- package/dist/core/task-notification.d.ts +13 -3
- package/dist/core/task-notification.js +12 -1
- package/dist/core/task-spec.d.ts +14 -15
- package/dist/core/tool-name-readings.d.ts +0 -4
- package/dist/core/tool-name-readings.js +0 -1
- package/dist/core/tool-policy.d.ts +40 -9
- package/dist/core/tool-policy.js +25 -7
- package/dist/core/types.d.ts +3 -3
- package/dist/core/types.js +1 -1
- package/dist/core/untrusted-egress.d.ts +60 -11
- package/dist/core/untrusted-egress.js +120 -9
- package/dist/core/untrusted-text.js +16 -0
- package/dist/core/wiring-manifest.d.ts +22 -8
- package/dist/core/wiring-manifest.js +12 -4
- package/dist/engine/harness/agent-harness.d.ts +2 -1
- package/dist/engine/harness/agent-harness.js +4 -1
- package/dist/engine/harness/types.d.ts +3 -0
- package/dist/engine/llm/types.d.ts +7 -9
- package/dist/engine/loop/agent-loop.d.ts +8 -3
- package/dist/engine/loop/agent-loop.js +26 -9
- package/dist/engine/loop/types.d.ts +10 -0
- package/dist/index.d.ts +17 -5
- package/dist/index.js +14 -5
- package/dist/orchestration/run-workflow-tool.js +8 -8
- package/dist/orchestration/workflow.js +2 -2
- package/dist/prompt-assembly/assemble.d.ts +3 -1
- package/dist/prompt-assembly/assemble.js +6 -0
- package/dist/prompts/cowork.d.ts +42 -0
- package/dist/prompts/cowork.js +529 -0
- package/dist/prompts/default.d.ts +14 -1
- package/dist/prompts/default.js +3 -0
- package/dist/stores/file/session-policy-store.d.ts +6 -3
- package/dist/stores/file/session-policy-store.js +5 -16
- package/dist/tools/artifact/artifact-tool.js +2 -0
- package/dist/tools/fs/image-admission.d.ts +57 -4
- package/dist/tools/fs/image-admission.js +85 -0
- package/dist/tools/fs/path-containment.d.ts +2 -0
- package/dist/tools/fs/path-containment.js +5 -0
- package/dist/tools/fs/shell-read-check.d.ts +31 -6
- package/dist/tools/fs/shell-read-check.js +2 -4
- package/dist/tools/fs/shell-read-environment.d.ts +10 -25
- package/dist/tools/fs/shell-read-environment.js +6 -18
- package/dist/tools/fs/shell-read-establish.d.ts +3 -2
- package/dist/tools/fs/shell-read-establish.js +5 -5
- package/dist/tools/fs/shell-read-judge.d.ts +38 -8
- package/dist/tools/fs/shell-read-judge.js +12 -10
- package/dist/tools/web.js +2 -2
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +94 -10
- package/wire-changes/core@7.50.2.json +42 -0
- package/wire-changes/core@7.51.0.json +2421 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isAskClass, isAskOrigin, isModeReleaseReason, isPermissionMode, ASK_FACES, releasableAskClassesFor } from "./ask-origin.js";
|
|
1
|
+
import { isAskClass, isAskOrigin, isModeReleaseReason, isPermissionMode, isPersistedRuleMandate, ASK_FACES, releasableAskClassesFor } from "./ask-origin.js";
|
|
2
2
|
import { isAutoModeFallbackCause } from "./auto-mode.js";
|
|
3
3
|
export const SETTLEMENT_KINDS = Object.freeze([
|
|
4
4
|
"human_allowed",
|
|
@@ -108,76 +108,186 @@ export const DENIED_BY_MAY_CARRY_CAUSE = {
|
|
|
108
108
|
read_boundary: false,
|
|
109
109
|
mode: false,
|
|
110
110
|
};
|
|
111
|
-
|
|
111
|
+
const retired = (word, retiredIn) => Object.freeze({ word, retiredIn });
|
|
112
|
+
export const RETIRED_MEMBERS = Object.freeze({
|
|
113
|
+
askClass: Object.freeze([retired("write_protection_unresolved", "7.49.0"), retired("governance_unavailable", "7.51.0")]),
|
|
114
|
+
modeReleaseReason: Object.freeze([retired("write_protection_unresolved", "7.49.0"), retired("rule_unreadable", "7.50.0")]),
|
|
115
|
+
askOrigin: Object.freeze([retired("unresolvable", "7.14.0")]),
|
|
116
|
+
persistedRuleMandate: Object.freeze([retired("write_protection_unresolved", "7.49.0")]),
|
|
117
|
+
deniedBy: Object.freeze([retired("plan_mode", "7.34.0")]),
|
|
118
|
+
settlementKind: Object.freeze([]),
|
|
119
|
+
settlementParty: Object.freeze([]),
|
|
120
|
+
classifierDenyCause: Object.freeze([]),
|
|
121
|
+
permissionMode: Object.freeze([]),
|
|
122
|
+
autoModeFallbackCause: Object.freeze([]),
|
|
123
|
+
});
|
|
124
|
+
const LIVE_MEMBER = Object.freeze({
|
|
125
|
+
"askClass": isAskClass,
|
|
126
|
+
"modeReleaseReason": isModeReleaseReason,
|
|
127
|
+
"askOrigin": isAskOrigin,
|
|
128
|
+
"persistedRuleMandate": isPersistedRuleMandate,
|
|
129
|
+
"deniedBy": isDeniedBy,
|
|
130
|
+
"settlementKind": isSettlementKind,
|
|
131
|
+
"settlementParty": isSettlementParty,
|
|
132
|
+
"classifierDenyCause": isClassifierDenyCause,
|
|
133
|
+
"permissionMode": isPermissionMode,
|
|
134
|
+
"autoModeFallbackCause": isAutoModeFallbackCause,
|
|
135
|
+
});
|
|
136
|
+
function retiredInOf(set, v) {
|
|
137
|
+
return RETIRED_MEMBERS[set].find((r) => r.word === v)?.retiredIn;
|
|
138
|
+
}
|
|
139
|
+
export function isRetiredMember(set, word) {
|
|
140
|
+
if (typeof set !== "string" || !Object.hasOwn(RETIRED_MEMBERS, set))
|
|
141
|
+
throw new TypeError(`isRetiredMember: ${JSON.stringify(String(set))} is not a closed set a gate record carries (${Object.keys(RETIRED_MEMBERS).join(", ")})`);
|
|
142
|
+
return retiredInOf(set, word) !== undefined;
|
|
143
|
+
}
|
|
144
|
+
export function releasePolicyDefects(g) {
|
|
145
|
+
const d = objectOf(objectOf(g)?.disposition);
|
|
146
|
+
const r = d?.kind === "allowed" ? objectOf(d.released) : undefined;
|
|
147
|
+
const askClass = r?.askClass;
|
|
148
|
+
const classes = r?.classes;
|
|
149
|
+
if (!isAskClass(askClass) || !Array.isArray(classes) || !classes.every(isAskClass))
|
|
150
|
+
return [];
|
|
151
|
+
if (!ASK_FACES.some((face) => releasableAskClassesFor(face).includes(askClass)))
|
|
152
|
+
return [`I7: released.askClass "${askClass}" is not a releasable class under this engine's table`];
|
|
153
|
+
const held = [askClass, ...classes];
|
|
154
|
+
return ASK_FACES.some((face) => held.every((c) => releasableAskClassesFor(face).includes(c))) ? [] : [`I7: released.classes ${JSON.stringify(classes)} is not a set one face of this engine's table releases`];
|
|
155
|
+
}
|
|
156
|
+
const DENY_ONLY_MEMBERS = Object.freeze(["deniedBy", "cause", "mode", "askClass", "classes"]);
|
|
157
|
+
function objectOf(v) {
|
|
158
|
+
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
|
|
159
|
+
}
|
|
160
|
+
function shapeOf(v) {
|
|
161
|
+
return v === null ? "null" : Array.isArray(v) ? "an array" : typeof v === "object" ? "an object" : typeof v;
|
|
162
|
+
}
|
|
163
|
+
function spelled(v) {
|
|
164
|
+
return v === null || (typeof v !== "object" && typeof v !== "function") ? `"${String(v)}"` : `(${shapeOf(v)})`;
|
|
165
|
+
}
|
|
166
|
+
function readingOf(opts) {
|
|
167
|
+
if (opts === undefined)
|
|
168
|
+
return "archive";
|
|
169
|
+
if (typeof opts !== "object" || opts === null)
|
|
170
|
+
throw new TypeError(`screenGateOutcome: the options are an object ({ reading?: "archive" | "producer" }), not ${opts === null ? "null" : typeof opts}`);
|
|
171
|
+
const reading = opts.reading;
|
|
172
|
+
if (reading === undefined || reading === "archive" || reading === "producer")
|
|
173
|
+
return reading ?? "archive";
|
|
174
|
+
throw new TypeError(`screenGateOutcome: reading ${typeof reading === "string" ? JSON.stringify(reading) : typeof reading} is not a reading (archive, producer)`);
|
|
175
|
+
}
|
|
176
|
+
export function screenGateOutcome(g, opts) {
|
|
177
|
+
const producer = readingOf(opts) === "producer";
|
|
178
|
+
const member = (set, v) => LIVE_MEMBER[set](v) || (!producer && retiredInOf(set, v) !== undefined);
|
|
179
|
+
const outside = (field, set, v) => {
|
|
180
|
+
const was = retiredInOf(set, v);
|
|
181
|
+
return `${field} ${spelled(v)} is outside the closed set${was !== undefined ? ` (retired in ${was})` : ""}`;
|
|
182
|
+
};
|
|
183
|
+
const classList = (v) => Array.isArray(v) && v.length > 0 && v.every((c) => member("askClass", c));
|
|
184
|
+
const notRecord = (field, v) => `${field} is not an object (${shapeOf(v)})`;
|
|
185
|
+
const record = objectOf(g);
|
|
186
|
+
if (record === undefined)
|
|
187
|
+
return [notRecord("the gate record", g)];
|
|
112
188
|
const defects = [];
|
|
113
|
-
const d =
|
|
114
|
-
if (d
|
|
115
|
-
defects.push(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
189
|
+
const d = objectOf(record.disposition);
|
|
190
|
+
if (d === undefined)
|
|
191
|
+
defects.push(notRecord("disposition", record.disposition));
|
|
192
|
+
else if (d.kind !== "allowed" && d.kind !== "denied")
|
|
193
|
+
defects.push(`disposition.kind ${spelled(d.kind)} is neither allowed nor denied`);
|
|
194
|
+
const denied = d?.kind === "denied" ? d : undefined;
|
|
195
|
+
const allowed = d?.kind === "allowed" ? d : undefined;
|
|
196
|
+
const layer = denied !== undefined && member("deniedBy", denied.deniedBy);
|
|
197
|
+
if (denied !== undefined && !layer)
|
|
198
|
+
defects.push(outside("disposition.deniedBy", "deniedBy", denied.deniedBy));
|
|
199
|
+
const settled = record.settlement !== undefined;
|
|
200
|
+
const s = objectOf(record.settlement);
|
|
201
|
+
if (settled && s === undefined)
|
|
202
|
+
defects.push(notRecord("settlement", record.settlement));
|
|
203
|
+
if (s !== undefined && !member("settlementKind", s.kind))
|
|
204
|
+
defects.push(outside("settlement.kind", "settlementKind", s.kind));
|
|
205
|
+
const party = objectOf(s?.who)?.party;
|
|
206
|
+
if (s !== undefined && !member("settlementParty", party))
|
|
207
|
+
defects.push(outside("settlement.who.party", "settlementParty", party));
|
|
208
|
+
if (record.origin !== undefined && !member("askOrigin", record.origin))
|
|
209
|
+
defects.push(outside("origin", "askOrigin", record.origin));
|
|
210
|
+
if (settled !== (record.origin !== undefined))
|
|
125
211
|
defects.push("I1: settlement and origin must be present together — they describe the same ask");
|
|
126
|
-
|
|
212
|
+
const kind = s !== undefined && isSettlementKind(s.kind) ? s.kind : undefined;
|
|
213
|
+
if (denied?.deniedBy === "ask_resolution" && (kind === undefined || !SETTLEMENT_IS_REFUSAL[kind]))
|
|
127
214
|
defects.push("I2: deniedBy ask_resolution requires a settlement whose kind is a refusal");
|
|
215
|
+
const approval = kind !== undefined && !SETTLEMENT_IS_REFUSAL[kind];
|
|
216
|
+
if (approval && denied !== undefined && layer && !(isDeniedBy(denied.deniedBy) && DENIED_BY_MAY_VETO[denied.deniedBy])) {
|
|
217
|
+
defects.push(`I3: a ${kind} settlement beside a deny must name a veto layer, not "${String(denied.deniedBy)}"`);
|
|
128
218
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (!isClassifierDenyCause(d.cause))
|
|
137
|
-
defects.push(`disposition.cause "${String(d.cause)}" is outside the closed set`);
|
|
138
|
-
else if (isDeniedBy(d.deniedBy) && !DENIED_BY_MAY_CARRY_CAUSE[d.deniedBy])
|
|
139
|
-
defects.push(`I5: a classifier cause cannot stand beside deniedBy "${d.deniedBy}"`);
|
|
219
|
+
if (allowed !== undefined && s !== undefined && !approval)
|
|
220
|
+
defects.push(`I4: an allowed disposition cannot carry a ${spelled(s.kind)} settlement`);
|
|
221
|
+
if (denied !== undefined && denied.cause !== undefined) {
|
|
222
|
+
if (!member("classifierDenyCause", denied.cause))
|
|
223
|
+
defects.push(outside("disposition.cause", "classifierDenyCause", denied.cause));
|
|
224
|
+
else if (layer && !(isDeniedBy(denied.deniedBy) && DENIED_BY_MAY_CARRY_CAUSE[denied.deniedBy]))
|
|
225
|
+
defects.push(`I5: a classifier cause cannot stand beside deniedBy "${String(denied.deniedBy)}"`);
|
|
140
226
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
227
|
+
for (const k of DENY_ONLY_MEMBERS)
|
|
228
|
+
if (allowed !== undefined && allowed[k] !== undefined)
|
|
229
|
+
defects.push(`disposition.${k} stands only on a denied disposition`);
|
|
230
|
+
if (denied !== undefined && denied.released !== undefined)
|
|
231
|
+
defects.push("I7: disposition.released stands only on an allowed disposition");
|
|
232
|
+
if (d !== undefined && d.released !== undefined) {
|
|
233
|
+
const r = objectOf(d.released);
|
|
234
|
+
if (r === undefined)
|
|
235
|
+
defects.push(notRecord("I7: disposition.released", d.released));
|
|
236
|
+
else {
|
|
237
|
+
if (settled)
|
|
238
|
+
defects.push("I7: a mode release carries no settlement — nobody answered the ask it released");
|
|
239
|
+
if (r.mode !== "bypassPermissions")
|
|
240
|
+
defects.push(`I7: a mode release names mode ${spelled(r.mode)} — only bypassPermissions releases`);
|
|
241
|
+
if (!member("askClass", r.askClass))
|
|
242
|
+
defects.push(outside("I7: released.askClass", "askClass", r.askClass));
|
|
243
|
+
if (!classList(r.classes))
|
|
244
|
+
defects.push("I7: released.classes must be a non-empty list of ask classes");
|
|
245
|
+
if (r.origin !== undefined && !member("askOrigin", r.origin))
|
|
246
|
+
defects.push(outside("I7: released.origin", "askOrigin", r.origin));
|
|
247
|
+
if (r.mandate !== undefined && !member("persistedRuleMandate", r.mandate))
|
|
248
|
+
defects.push(outside("I7: released.mandate", "persistedRuleMandate", r.mandate));
|
|
249
|
+
if (r.reason !== undefined && !member("modeReleaseReason", r.reason))
|
|
250
|
+
defects.push(outside("I7: released.reason", "modeReleaseReason", r.reason));
|
|
251
|
+
else if (r.reason !== undefined && Array.isArray(r.classes) && !r.classes.includes(r.reason))
|
|
252
|
+
defects.push(`I7: released.reason "${String(r.reason)}" is not a member of released.classes`);
|
|
253
|
+
}
|
|
157
254
|
}
|
|
158
|
-
if (
|
|
159
|
-
const modeRefusal =
|
|
160
|
-
if (modeRefusal !== (
|
|
255
|
+
if (denied !== undefined) {
|
|
256
|
+
const modeRefusal = denied.deniedBy === "mode";
|
|
257
|
+
if (modeRefusal !== (denied.mode !== undefined))
|
|
161
258
|
defects.push("I8: deniedBy mode and disposition.mode must be present together");
|
|
162
|
-
if (modeRefusal && (!
|
|
163
|
-
defects.push(`I8: a mode refusal names mode
|
|
164
|
-
|
|
165
|
-
|
|
259
|
+
if (modeRefusal && denied.mode !== undefined && (!member("permissionMode", denied.mode) || (denied.mode !== "bypassPermissions" && denied.mode !== "dontAsk"))) {
|
|
260
|
+
defects.push(`I8: a mode refusal names mode ${spelled(denied.mode)} — only bypassPermissions and dontAsk refuse`);
|
|
261
|
+
}
|
|
262
|
+
if (modeRefusal && (denied.askClass === undefined || denied.classes === undefined))
|
|
263
|
+
defects.push("I8: a mode refusal carries askClass and classes");
|
|
264
|
+
if (denied.askClass !== undefined && !member("askClass", denied.askClass))
|
|
265
|
+
defects.push(outside("disposition.askClass", "askClass", denied.askClass));
|
|
266
|
+
if (denied.classes !== undefined && !classList(denied.classes))
|
|
267
|
+
defects.push("disposition.classes must be a non-empty list of ask classes");
|
|
166
268
|
}
|
|
167
|
-
|
|
168
|
-
|
|
269
|
+
if (d !== undefined && d.classifier !== undefined) {
|
|
270
|
+
const attribution = objectOf(d.classifier);
|
|
169
271
|
const named = (v) => typeof v === "string" && v !== "";
|
|
170
|
-
if (
|
|
171
|
-
defects.push("I6: disposition.classifier
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
defects.push(
|
|
178
|
-
if (
|
|
179
|
-
|
|
272
|
+
if (attribution === undefined)
|
|
273
|
+
defects.push(notRecord("I6: disposition.classifier", d.classifier));
|
|
274
|
+
else {
|
|
275
|
+
if (!named(attribution.modelRequested) || !named(attribution.modelUsed))
|
|
276
|
+
defects.push("I6: disposition.classifier must name both the model the round was for and the model that answered");
|
|
277
|
+
const fb = objectOf(attribution.fallback);
|
|
278
|
+
if (attribution.fallback !== undefined && fb === undefined)
|
|
279
|
+
defects.push(notRecord("I6: disposition.classifier.fallback", attribution.fallback));
|
|
280
|
+
if (fb !== undefined) {
|
|
281
|
+
if (!named(fb.from) || !named(fb.to))
|
|
282
|
+
defects.push("I6: disposition.classifier.fallback must name the rung it left and the rung it arrived at");
|
|
283
|
+
else if (fb.to !== attribution.modelUsed)
|
|
284
|
+
defects.push(`I6: the fallback landed on ${spelled(fb.to)} but the record names ${spelled(attribution.modelUsed)} as the model that answered`);
|
|
285
|
+
if (!member("autoModeFallbackCause", fb.cause))
|
|
286
|
+
defects.push(outside("I6: fallback cause", "autoModeFallbackCause", fb.cause));
|
|
287
|
+
}
|
|
180
288
|
}
|
|
181
289
|
}
|
|
290
|
+
if (producer)
|
|
291
|
+
defects.push(...releasePolicyDefects(g));
|
|
182
292
|
return defects;
|
|
183
293
|
}
|
|
@@ -137,7 +137,7 @@ export type NoticeAudience = "user" | "operator";
|
|
|
137
137
|
* the union's catalog ({@link EngineNotice.code}). The groups below say only which family a row joins,
|
|
138
138
|
* so the catalog is not written a second time here and the two cannot drift.
|
|
139
139
|
*/
|
|
140
|
-
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.path_view_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", "permission.rule_row_dropped", "permission.allowed_tool_not_granted", "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", "route.tool_call_id_folded", "route.finish_reason_unknown", "route.stream_end_ignored", "route.extra_body_reserved_dropped", "context.tool_turn_repaired", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "task.tool_turn_incomplete", "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", "instructions.source_changed", "session.records_skipped", "memory.layer_locked_legacy", "server.model_unreachable"];
|
|
140
|
+
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.read_deny_swapped", "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.path_view_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", "permission.rule_row_dropped", "permission.allowed_tool_not_granted", "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", "route.tool_call_id_folded", "route.finish_reason_unknown", "route.stream_end_ignored", "route.extra_body_reserved_dropped", "context.tool_turn_repaired", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "task.tool_turn_incomplete", "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", "tool_result.media_refused", "instructions.source_changed", "session.records_skipped", "memory.layer_locked_legacy", "server.model_unreachable", "permission.entry_mode_clamped"];
|
|
141
141
|
/** A catalogued code (see {@link ENGINE_NOTICE_CODES}); {@link NOTICE_MINTER} says who mints it. NOT the type of
|
|
142
142
|
* `EngineNotice.code`, which stays `string` — a host forwarding its own notices through the same
|
|
143
143
|
* sink is a supported shape, and narrowing that field would break it. */
|
|
@@ -199,6 +199,7 @@ declare const NOTICE_AUDIENCE_TABLE: {
|
|
|
199
199
|
readonly "config.read_face_deployment_clamped": "operator";
|
|
200
200
|
readonly "config.write_face_swapped": "operator";
|
|
201
201
|
readonly "config.write_face_deployment_clamped": "operator";
|
|
202
|
+
readonly "config.read_deny_swapped": "operator";
|
|
202
203
|
readonly "config.tool_model_gate_removed": "operator";
|
|
203
204
|
readonly "config.tool_model_gate_unknown_class": "operator";
|
|
204
205
|
readonly "config.tool_model_gate_env_invalid": "operator";
|
|
@@ -241,10 +242,12 @@ declare const NOTICE_AUDIENCE_TABLE: {
|
|
|
241
242
|
readonly "skills.listing_truncated": "operator";
|
|
242
243
|
readonly "agents.listing_truncated": "operator";
|
|
243
244
|
readonly "tool_result.offload_put_failed": "operator";
|
|
245
|
+
readonly "tool_result.media_refused": "operator";
|
|
244
246
|
readonly "config.forward_sink_failed": "operator";
|
|
245
247
|
readonly "instructions.source_changed": "user";
|
|
246
248
|
readonly "session.records_skipped": "user";
|
|
247
249
|
readonly "server.model_unreachable": "user";
|
|
250
|
+
readonly "permission.entry_mode_clamped": "user";
|
|
248
251
|
readonly "memory.layer_locked_legacy": "operator";
|
|
249
252
|
};
|
|
250
253
|
/** Compile-time fence: every catalogued code has an audience row (and, via `satisfies`, only those). */
|
|
@@ -280,6 +283,7 @@ declare const NOTICE_MINTER_TABLE: {
|
|
|
280
283
|
readonly "config.read_face_deployment_clamped": "engine";
|
|
281
284
|
readonly "config.write_face_swapped": "engine";
|
|
282
285
|
readonly "config.write_face_deployment_clamped": "engine";
|
|
286
|
+
readonly "config.read_deny_swapped": "engine";
|
|
283
287
|
readonly "config.tool_model_gate_removed": "engine";
|
|
284
288
|
readonly "config.tool_model_gate_unknown_class": "engine";
|
|
285
289
|
readonly "config.tool_model_gate_env_invalid": "engine";
|
|
@@ -357,10 +361,12 @@ declare const NOTICE_MINTER_TABLE: {
|
|
|
357
361
|
readonly "skills.listing_truncated": "engine";
|
|
358
362
|
readonly "agents.listing_truncated": "engine";
|
|
359
363
|
readonly "tool_result.offload_put_failed": "engine";
|
|
364
|
+
readonly "tool_result.media_refused": "engine";
|
|
360
365
|
readonly "instructions.source_changed": "server";
|
|
361
366
|
readonly "session.records_skipped": "server";
|
|
362
367
|
readonly "memory.layer_locked_legacy": "server";
|
|
363
368
|
readonly "server.model_unreachable": "server";
|
|
369
|
+
readonly "permission.entry_mode_clamped": "server";
|
|
364
370
|
};
|
|
365
371
|
/** Compile-time fence: every catalogued code has a minter row (and, via `satisfies`, only those). */
|
|
366
372
|
export type MinterTableCoversEveryNoticeCode = AssertAllKeysHandled<Exclude<EngineNoticeCode, keyof typeof NOTICE_MINTER_TABLE>>;
|
|
@@ -128,6 +128,7 @@ export const ENGINE_NOTICE_CODES = Object.freeze([
|
|
|
128
128
|
"config.read_face_deployment_clamped",
|
|
129
129
|
"config.write_face_swapped",
|
|
130
130
|
"config.write_face_deployment_clamped",
|
|
131
|
+
"config.read_deny_swapped",
|
|
131
132
|
"config.tool_model_gate_removed",
|
|
132
133
|
"config.tool_model_gate_unknown_class",
|
|
133
134
|
"config.tool_model_gate_env_invalid",
|
|
@@ -205,10 +206,12 @@ export const ENGINE_NOTICE_CODES = Object.freeze([
|
|
|
205
206
|
"skills.listing_truncated",
|
|
206
207
|
"agents.listing_truncated",
|
|
207
208
|
"tool_result.offload_put_failed",
|
|
209
|
+
"tool_result.media_refused",
|
|
208
210
|
"instructions.source_changed",
|
|
209
211
|
"session.records_skipped",
|
|
210
212
|
"memory.layer_locked_legacy",
|
|
211
213
|
"server.model_unreachable",
|
|
214
|
+
"permission.entry_mode_clamped",
|
|
212
215
|
]);
|
|
213
216
|
const NOTICE_AUDIENCE_TABLE = {
|
|
214
217
|
"memory.session_polluted": "user",
|
|
@@ -254,6 +257,7 @@ const NOTICE_AUDIENCE_TABLE = {
|
|
|
254
257
|
"config.read_face_deployment_clamped": "operator",
|
|
255
258
|
"config.write_face_swapped": "operator",
|
|
256
259
|
"config.write_face_deployment_clamped": "operator",
|
|
260
|
+
"config.read_deny_swapped": "operator",
|
|
257
261
|
"config.tool_model_gate_removed": "operator",
|
|
258
262
|
"config.tool_model_gate_unknown_class": "operator",
|
|
259
263
|
"config.tool_model_gate_env_invalid": "operator",
|
|
@@ -296,10 +300,12 @@ const NOTICE_AUDIENCE_TABLE = {
|
|
|
296
300
|
"skills.listing_truncated": "operator",
|
|
297
301
|
"agents.listing_truncated": "operator",
|
|
298
302
|
"tool_result.offload_put_failed": "operator",
|
|
303
|
+
"tool_result.media_refused": "operator",
|
|
299
304
|
"config.forward_sink_failed": "operator",
|
|
300
305
|
"instructions.source_changed": "user",
|
|
301
306
|
"session.records_skipped": "user",
|
|
302
307
|
"server.model_unreachable": "user",
|
|
308
|
+
"permission.entry_mode_clamped": "user",
|
|
303
309
|
"memory.layer_locked_legacy": "operator",
|
|
304
310
|
};
|
|
305
311
|
export const NOTICE_AUDIENCE = NOTICE_AUDIENCE_TABLE;
|
|
@@ -316,6 +322,7 @@ const NOTICE_MINTER_TABLE = {
|
|
|
316
322
|
"config.read_face_deployment_clamped": "engine",
|
|
317
323
|
"config.write_face_swapped": "engine",
|
|
318
324
|
"config.write_face_deployment_clamped": "engine",
|
|
325
|
+
"config.read_deny_swapped": "engine",
|
|
319
326
|
"config.tool_model_gate_removed": "engine",
|
|
320
327
|
"config.tool_model_gate_unknown_class": "engine",
|
|
321
328
|
"config.tool_model_gate_env_invalid": "engine",
|
|
@@ -393,9 +400,11 @@ const NOTICE_MINTER_TABLE = {
|
|
|
393
400
|
"skills.listing_truncated": "engine",
|
|
394
401
|
"agents.listing_truncated": "engine",
|
|
395
402
|
"tool_result.offload_put_failed": "engine",
|
|
403
|
+
"tool_result.media_refused": "engine",
|
|
396
404
|
"instructions.source_changed": "server",
|
|
397
405
|
"session.records_skipped": "server",
|
|
398
406
|
"memory.layer_locked_legacy": "server",
|
|
399
407
|
"server.model_unreachable": "server",
|
|
408
|
+
"permission.entry_mode_clamped": "server",
|
|
400
409
|
};
|
|
401
410
|
export const NOTICE_MINTER = Object.freeze(NOTICE_MINTER_TABLE);
|
package/dist/core/hooks.d.ts
CHANGED
|
@@ -1131,7 +1131,7 @@ export interface PersistedRuleHit {
|
|
|
1131
1131
|
* design/252 G-2 — the answer a lane gives when it could not READ its store, or a partition of it (no `reason`; the CALL form is `reason`'s).
|
|
1132
1132
|
*
|
|
1133
1133
|
* The store form fails CLOSED: a deny or ask row inside what could not be read may forbid this call and an allow beside it
|
|
1134
|
-
* is no yes, so the decision becomes a real-approval ask (origin `rule_store_unavailable`, class `
|
|
1134
|
+
* is no yes, so the decision becomes a real-approval ask (origin `rule_store_unavailable`, class `rule_unreadable`:
|
|
1135
1135
|
* no automatic lane clears it, `bypassPermissions` keeps it for a person, headless it is refused) until a read succeeds
|
|
1136
1136
|
* again; a deny or ask row that WAS read still stands. The RECORD says `"unavailable"`, not `"no_match"`; the lane
|
|
1137
1137
|
* announces the skipped partition once per run.
|
|
@@ -1139,9 +1139,9 @@ export interface PersistedRuleHit {
|
|
|
1139
1139
|
export interface PersistedRuleUnreadable {
|
|
1140
1140
|
readonly unreadable: true;
|
|
1141
1141
|
/** WHY the lane could not read: absent for a store (or partition) read failure — the store form, a real-approval ask of
|
|
1142
|
-
* class `
|
|
1142
|
+
* class `rule_unreadable`; for a CALL the lane could not read against the person's standing deny/ask rows (an
|
|
1143
1143
|
* expansion where a rule reads a word, an unterminated quote, a syntax error) the lexer's reason — the call form, an allow
|
|
1144
|
-
* tightens to a real-approval ask of class
|
|
1144
|
+
* tightens to a real-approval ask of the same class. Both carry the origin `rule_store_unavailable`. */
|
|
1145
1145
|
readonly reason?: string;
|
|
1146
1146
|
}
|
|
1147
1147
|
/**
|
|
@@ -1457,7 +1457,7 @@ export interface ToolGateInput extends PermissionSeatInputs, SkillGateSeats, All
|
|
|
1457
1457
|
* small-model policy CLASSIFIER before any human/durable resolution:
|
|
1458
1458
|
* - verdict `allow` → the ask resolves to allow (`decisionReason:"classifier"`) — no suspend, no onAsk;
|
|
1459
1459
|
* - verdict `block` → deny (`decisionReason:"classifier"`, `source:"classifier"`; CC `automode-blocked`); `parse_error` too;
|
|
1460
|
-
* - `unavailable` → where `unavailable.
|
|
1460
|
+
* - `unavailable` → where `unavailable.route` names a person (`live_approver`, or `durable_park`) the ask goes on, its text saying why;
|
|
1461
1461
|
* else CC's refusal (`cause: "unavailable"`). Never counted by the denial limit (`classifierUnavailableArm`).
|
|
1462
1462
|
* SCOPE (declared semantic): this covers every surviving ask INCLUDING the deterministic egress/
|
|
1463
1463
|
* irreversibility safety tightens — auto mode is the deployment's explicit choice to let the
|
package/dist/core/hooks.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { decisionText, describeThrown, receiveHookResult, isEngineRefusal, unaskedSeatAnswer } from "./tool-policy.js";
|
|
2
2
|
import { asThrownError } from "./thrown-value.js";
|
|
3
3
|
import { createSettlementLedger, engineFailClosedExit, hookDenyExit } from "./runner/gate-exit.js";
|
|
4
4
|
import { traceHookCrash } from "./gate-crash-trace.js";
|
|
@@ -196,8 +196,9 @@ function screenPreToolUseResult(r) {
|
|
|
196
196
|
export function createPreToolUseConstraintPolicy(preToolUse, env, onCrash, timeoutMs) {
|
|
197
197
|
return brandPolicyAskClass({
|
|
198
198
|
check: async (req, signal) => {
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
const unasked = unaskedSeatAnswer(req, signal, { aborted: `the inherited PreToolUse screening of "${req.toolName}" was not consulted: the run or turn was already interrupted`, unreachable: `the inherited PreToolUse screening of "${req.toolName}" was not consulted: nobody can be asked about this call` }, "hook");
|
|
200
|
+
if (unasked !== undefined)
|
|
201
|
+
return unasked;
|
|
201
202
|
let r;
|
|
202
203
|
const boundMs = resolveHookTimeoutMs(timeoutMs, onCrash);
|
|
203
204
|
try {
|
|
@@ -54,6 +54,8 @@ export declare class McpConnectionPool {
|
|
|
54
54
|
/**
|
|
55
55
|
* The key a declaration leases under. `kind` is the dial's own single reading of the transport kind (the dial reads it once and threads it, so a
|
|
56
56
|
* transport object that answers differently to two readers cannot split the dial from its key); every other transport member is read here.
|
|
57
|
+
* The declaration's mount identity (`fingerprint`) is part of the key when declared: the revocation probe is asked with it, so two mounts the
|
|
58
|
+
* host tells apart never share one connection (an undeclared fingerprint leaves the key as it always was).
|
|
57
59
|
*/
|
|
58
60
|
static keyOf(spec: McpServerSpec, principal: string | undefined, kind: string | undefined): string;
|
|
59
61
|
/**
|
|
@@ -6,7 +6,8 @@ export class McpConnectionPool {
|
|
|
6
6
|
shut = false;
|
|
7
7
|
static keyOf(spec, principal, kind) {
|
|
8
8
|
const rest = Object.fromEntries(Object.keys(spec.transport).filter((k) => k !== "kind").map((k) => [k, Reflect.get(spec.transport, k)]));
|
|
9
|
-
|
|
9
|
+
const fingerprint = spec.fingerprint;
|
|
10
|
+
return canonicalize(fingerprint === undefined ? [spec.name, kind ?? null, rest, principal ?? null] : [spec.name, kind ?? null, rest, principal ?? null, fingerprint]);
|
|
10
11
|
}
|
|
11
12
|
async acquire(key, dial, opts) {
|
|
12
13
|
if (this.shut)
|
package/dist/core/mcp-failure.js
CHANGED
|
@@ -72,6 +72,7 @@ export function networkErrorCode(err, depth = 0) {
|
|
|
72
72
|
return networkErrorCode(err.cause, depth + 1);
|
|
73
73
|
}
|
|
74
74
|
const isNodeErrCode = (code) => code.startsWith("ERR_");
|
|
75
|
+
const isDeclarationFault = (code) => code === "ERR_INVALID_URL" || (typeof code === "string" && code.startsWith("config."));
|
|
75
76
|
export const FAILURE_CLASSIFIER = [
|
|
76
77
|
{
|
|
77
78
|
name: "sdk connection closed",
|
|
@@ -100,7 +101,7 @@ export const FAILURE_CLASSIFIER = [
|
|
|
100
101
|
},
|
|
101
102
|
{
|
|
102
103
|
name: "malformed declaration",
|
|
103
|
-
when: (err, at) => at.phase === "connect" && err instanceof Error && err.code
|
|
104
|
+
when: (err, at) => at.phase === "connect" && err instanceof Error && isDeclarationFault(err.code),
|
|
104
105
|
reads: () => ({ kind: "invalid_config" }),
|
|
105
106
|
},
|
|
106
107
|
{
|
|
@@ -33,8 +33,9 @@ export interface McpServerSpec {
|
|
|
33
33
|
* Per-task **end-user principal** header (design/62). When set, the Runner injects the running task's
|
|
34
34
|
* authenticated {@link TaskSpec.principal} as this header (e.g. `"X-Agent-Principal"`) on every call to
|
|
35
35
|
* this server's tools, so the MCP server can enforce per-user RBAC. Absent {@link TaskSpec.principal}
|
|
36
|
-
* ⇒ the header is **not** sent (the MCP server must default to deny/public, never admin).
|
|
37
|
-
*
|
|
36
|
+
* ⇒ the header is **not** sent (the MCP server must default to deny/public, never admin). A static `headers`
|
|
37
|
+
* entry under the same name in any case refuses this server at its dial (`config.protocol_header_collision`,
|
|
38
|
+
* header names being case-insensitive). The principal is Runner-held and the model/worker can neither read
|
|
38
39
|
* nor set it (it is not a tool argument) — see {@link TaskSpec.principal}.
|
|
39
40
|
*/
|
|
40
41
|
principalHeader?: string;
|
|
@@ -136,6 +137,24 @@ export interface McpServerSpec {
|
|
|
136
137
|
* plane and needs no such gate.
|
|
137
138
|
*/
|
|
138
139
|
contentOrigin?: ToolContentOrigin;
|
|
140
|
+
/**
|
|
141
|
+
* The host's identity of THIS MOUNT of the server — a non-empty string (≤ 128 characters) the host
|
|
142
|
+
* minted from the declaration; the engine computes nothing from it and reads nothing off it. When present
|
|
143
|
+
* it rides the revocation probe ({@link McpRevocationLedger.isRevoked}) as the second argument, so the
|
|
144
|
+
* host can tell one mount generation of a server from a re-declared one under the same name. Absent ⇒
|
|
145
|
+
* the probe is asked with the name alone. Any other value refuses this server at its dial
|
|
146
|
+
* (`config.mcp_fingerprint_invalid`).
|
|
147
|
+
*/
|
|
148
|
+
fingerprint?: string;
|
|
149
|
+
}
|
|
150
|
+
/** The identity of one mount of a declared server, as the host minted it ({@link McpServerSpec.fingerprint}). */
|
|
151
|
+
export interface McpMountIdentity {
|
|
152
|
+
readonly fingerprint: string;
|
|
153
|
+
}
|
|
154
|
+
/** The host's MCP revocation ledger ({@link RunnerDeps.mcpRevocations}) — declared once, read by every MCP dispatch outlet. */
|
|
155
|
+
export interface McpRevocationLedger {
|
|
156
|
+
/** Synchronous. `mount` is passed exactly when the server's declaration carries a `fingerprint`. */
|
|
157
|
+
isRevoked(serverName: string, mount?: McpMountIdentity): boolean;
|
|
139
158
|
}
|
|
140
159
|
/**
|
|
141
160
|
* Definition of one A2A (agent-to-agent protocol) PEER to talk to for the duration of one task.
|
|
@@ -157,14 +176,17 @@ export interface A2aServerSpec {
|
|
|
157
176
|
/** Explicit agent-card location, when the peer does not publish at a well-known path. Set ⇒ it is used
|
|
158
177
|
* ALONE (no well-known probing behind the operator's back). */
|
|
159
178
|
cardUrl?: string;
|
|
160
|
-
/** Static headers sent on every request to this peer (e.g. an auth bearer).
|
|
179
|
+
/** Static headers sent on every request to this peer (e.g. an auth bearer). They travel only to the
|
|
180
|
+
* declared origins (`url`, `cardUrl`): a card naming an endpoint on another origin refuses the peer
|
|
181
|
+
* (`config.protocol_endpoint_unbound`), and a redirect is not followed. */
|
|
161
182
|
headers?: Record<string, string>;
|
|
162
183
|
/**
|
|
163
184
|
* Per-task **end-user principal** header (design/62), same contract as the MCP side: when set, the
|
|
164
185
|
* Runner injects the running task's authenticated {@link TaskSpec.principal} under this header name on
|
|
165
186
|
* every call to this peer, so the peer can enforce per-user authorization. Absent principal ⇒ the
|
|
166
|
-
* header is not sent (the peer must then default to deny/public, never admin).
|
|
167
|
-
*
|
|
187
|
+
* header is not sent (the peer must then default to deny/public, never admin). A static entry in
|
|
188
|
+
* {@link headers} under the same name in any case refuses the peer (`config.protocol_header_collision`).
|
|
189
|
+
* The principal is Runner-held: the model can neither read nor set it.
|
|
168
190
|
*/
|
|
169
191
|
principalHeader?: string;
|
|
170
192
|
/** Optional allowlist of skill ids to expose (others are not mounted). */
|