@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
package/dist/core/mcp.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type McpInstructionsEntry, type PromptSectionSeat } from "./prompt-section-seat.js";
|
|
2
2
|
import { McpConnectionPool } from "./mcp-connection-pool.js";
|
|
3
3
|
import { type McpDelivered, type McpFailureKind, type McpServerLiveness } from "./mcp-failure.js";
|
|
4
4
|
import type { AgentTool } from "../internal/harness-types.js";
|
|
5
5
|
import type { ImageContent, TextContent } from "../internal/llm.js";
|
|
6
6
|
import { type McpImageResizer } from "./image-downsample.js";
|
|
7
7
|
import type { McpServerSpec, OnElicit, ToolEffect } from "./types.js";
|
|
8
|
+
import type { McpRevocationLedger } from "./mcp-server-spec.js";
|
|
8
9
|
import { type ReminderDisclosureCounts } from "./reminder-disclosure.js";
|
|
9
10
|
export { resolveMcpDeclaredResultSize } from "./mcp-tool-meta.js";
|
|
10
11
|
/**
|
|
@@ -100,13 +101,11 @@ export interface MaterializedMcp {
|
|
|
100
101
|
toolAxes: McpToolAxis[];
|
|
101
102
|
/**
|
|
102
103
|
* MCP server `instructions` (design/64 §17.3, CC `# MCP Server Instructions`): the optional guidance a
|
|
103
|
-
* connected server returns from `initialize
|
|
104
|
-
*
|
|
104
|
+
* connected server returns from `initialize`, after the host's disposition (`PromptProvider.section`,
|
|
105
|
+
* applied once at the dial: a dropped entry is absent, a replaced one carries `author: "host"`). The
|
|
106
|
+
* Runner injects these into the (stable) system prompt. One entry per server whose entry survived.
|
|
105
107
|
*/
|
|
106
|
-
serverInstructions:
|
|
107
|
-
server: string;
|
|
108
|
-
text: string;
|
|
109
|
-
}>;
|
|
108
|
+
serverInstructions: McpInstructionsEntry[];
|
|
110
109
|
/**
|
|
111
110
|
* G1 通告层续批 (CC `mcp_instructions_delta` parity) — the run-scoped instruction-delta ref the run
|
|
112
111
|
* loop drains at turn boundaries (`spec.attachments.mcpInstructions` opt-in; `newTools` consumption
|
|
@@ -121,15 +120,12 @@ export interface MaterializedMcp {
|
|
|
121
120
|
* CC deviation: prefix-cache-correct).
|
|
122
121
|
*/
|
|
123
122
|
instructionsDelta: {
|
|
124
|
-
pendingAdds:
|
|
125
|
-
server: string;
|
|
126
|
-
text: string;
|
|
127
|
-
}>;
|
|
123
|
+
pendingAdds: McpInstructionsEntry[];
|
|
128
124
|
pendingRemovals: string[];
|
|
129
125
|
};
|
|
130
126
|
/**
|
|
131
127
|
* The OPERATOR warning lane for this materialization (the Runner forwards each entry to
|
|
132
|
-
* `onError(phase:"mcp")`). Empty when every server connected and listed completely.
|
|
128
|
+
* `onError(phase:"mcp")`). Empty when every server connected and listed completely. Populations,
|
|
133
129
|
* distinguishable by the `code` each Error carries — a consumer must NOT read mere presence as "this
|
|
134
130
|
* server was skipped":
|
|
135
131
|
* - `mcp.server_unavailable` — a server that failed to connect / list its tools and was SKIPPED
|
|
@@ -138,7 +134,9 @@ export interface MaterializedMcp {
|
|
|
138
134
|
* - `mcp.listing_incomplete` — a server that DID connect but whose `tools/list` pagination walk did
|
|
139
135
|
* not reach the end ({@link McpListingIncomplete}): its tools past the stopping point are not
|
|
140
136
|
* mounted this task, which is a capability loss the operator must see rather than infer from a
|
|
141
|
-
* short tool list
|
|
137
|
+
* short tool list;
|
|
138
|
+
* - `prompt.section_answer_invalid` — the host's disposition of a CONNECTED server's instructions
|
|
139
|
+
* could not be read, so the entry was included as computed.
|
|
142
140
|
*/
|
|
143
141
|
warnings: Error[];
|
|
144
142
|
/**
|
|
@@ -471,28 +469,6 @@ export * from "./image-downsample.js";
|
|
|
471
469
|
* type, ≤10 object entries then `, ...`.
|
|
472
470
|
*/
|
|
473
471
|
export declare function inferCompactSchema(value: unknown, depth?: number): string;
|
|
474
|
-
/** The per-peer HTTP auth declaration every protocol spec carries in the same two fields — the static
|
|
475
|
-
* headers the deployment configured, and the header name the per-task principal rides on (design/62).
|
|
476
|
-
* Named rather than inlined because it is a cross-module signature: {@link McpServerSpec}'s http
|
|
477
|
-
* transport and A2A's peer spec both satisfy it, and the resolver below serves both. */
|
|
478
|
-
export interface ProtocolHttpAuth {
|
|
479
|
-
headers?: Record<string, string>;
|
|
480
|
-
principalHeader?: string;
|
|
481
|
-
}
|
|
482
|
-
/**
|
|
483
|
-
* Resolve the HTTP request headers for one remote PEER of `protocol`, injecting the per-task end-user
|
|
484
|
-
* principal (design/62). The principal is added **only** when the spec declares a `principalHeader` AND a
|
|
485
|
-
* `principal` is present; it is injected **last** so it overrides any same-named static header; an absent
|
|
486
|
-
* principal sends no such header (the remote must then default to deny/public, never admin). Pure +
|
|
487
|
-
* exported for testing and so a consumer can verify what its config sends.
|
|
488
|
-
*
|
|
489
|
-
* Shared across protocols (A2A reuses it verbatim), which is why the failure names its protocol from the
|
|
490
|
-
* ARGUMENT rather than from a hard-coded literal: a copy of this guard per protocol is exactly the
|
|
491
|
-
* "second mint" shape whose copies stop tracking each other. Callers must invoke it INSIDE their per-peer
|
|
492
|
-
* boundary — the throw is one peer's configuration defect, and the surrounding per-peer catch is what
|
|
493
|
-
* turns it into a skipped peer instead of a failed task.
|
|
494
|
-
*/
|
|
495
|
-
export declare function resolveProtocolHttpHeaders(protocol: ProtocolId, t: ProtocolHttpAuth, principal?: string): Record<string, string> | undefined;
|
|
496
472
|
/** `kind` is the dial's ONE read of `spec.transport.kind` (taken by the caller beside the dial and threaded
|
|
497
473
|
* here, to the idle bucket and to the failure classifier alike): a nested `transport` object is the
|
|
498
474
|
* caller's, and a getter that answered differently to two readers would otherwise connect over one
|
|
@@ -624,13 +600,12 @@ export declare function materializeMcpTools(specs: McpServerSpec[], principal?:
|
|
|
624
600
|
reminderDisclosure?: {
|
|
625
601
|
reminderMark?: string;
|
|
626
602
|
counts?: ReminderDisclosureCounts;
|
|
627
|
-
}, mcpRevocations?: {
|
|
628
|
-
isRevoked(serverName: string): boolean;
|
|
603
|
+
}, mcpRevocations?: McpRevocationLedger & {
|
|
629
604
|
onProbeFailure?: (error: unknown) => void;
|
|
630
605
|
}, poolOpts?: {
|
|
631
606
|
pool: McpConnectionPool;
|
|
632
607
|
ownsPool?: boolean;
|
|
633
|
-
}): Promise<MaterializedMcp>;
|
|
608
|
+
}, promptSections?: PromptSectionSeat): Promise<MaterializedMcp>;
|
|
634
609
|
/**
|
|
635
610
|
* Fold the caller's AUTHORITATIVE per-tool override (design F: caller = trust root) over the server-hint axis.
|
|
636
611
|
* Unlike server hints, the caller may RAISE or LOWER any axis: `effect` sets the repeat-safety class (lower to
|
package/dist/core/mcp.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { egressText } from "./untrusted-egress.js";
|
|
2
2
|
import { __resetEnvKnobAnnouncements } from "./env-knob-announce.js";
|
|
3
3
|
import { resolveEnvBudget } from "./env-budget.js";
|
|
4
4
|
import { MCP_NAMESPACE } from "./protocol-table.js";
|
|
5
|
+
import { resolveProtocolHttpHeaders } from "./protocol-http-auth.js";
|
|
6
|
+
import { disposeSectionEntry, sectionAnswerWarning } from "./prompt-section-seat.js";
|
|
5
7
|
import { findNamespacePrefixCollision, mintNamespacePrefix, mintNamespacedToolName, normalizeNameSegment } from "./protocol-naming.js";
|
|
6
8
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
7
9
|
import { McpConnectionPool } from "./mcp-connection-pool.js";
|
|
@@ -179,7 +181,7 @@ export function collapseMcpErrorPrefix(message) {
|
|
|
179
181
|
function sanitizeMcpErrorTextInPlace(err) {
|
|
180
182
|
if (!(err instanceof Error))
|
|
181
183
|
return;
|
|
182
|
-
const sanitized =
|
|
184
|
+
const sanitized = egressText("same-principal", err instanceof McpError ? collapseMcpErrorPrefix(err.message) : err.message);
|
|
183
185
|
if (sanitized !== err.message)
|
|
184
186
|
err.message = sanitized;
|
|
185
187
|
}
|
|
@@ -190,7 +192,7 @@ function namedMcpFailureText(err) {
|
|
|
190
192
|
}
|
|
191
193
|
function remoteErrorText(err, opts) {
|
|
192
194
|
const raw = thrownText(err);
|
|
193
|
-
return
|
|
195
|
+
return egressText("same-principal", opts?.collapse === true ? collapseMcpErrorPrefix(raw) : raw);
|
|
194
196
|
}
|
|
195
197
|
function writeEffectWarning(writeEffect) {
|
|
196
198
|
return writeEffect
|
|
@@ -372,14 +374,6 @@ export function inferCompactSchema(value, depth = 2) {
|
|
|
372
374
|
}
|
|
373
375
|
return typeof value;
|
|
374
376
|
}
|
|
375
|
-
export function resolveProtocolHttpHeaders(protocol, t, principal) {
|
|
376
|
-
if (t.principalHeader && principal !== undefined) {
|
|
377
|
-
if (/[\r\n]/.test(principal))
|
|
378
|
-
throw new Error(`${protocol}: principal must not contain CR/LF (header injection guard)`);
|
|
379
|
-
return { ...t.headers, [t.principalHeader]: principal };
|
|
380
|
-
}
|
|
381
|
-
return t.headers;
|
|
382
|
-
}
|
|
383
377
|
export const declaredEndpointFetch = (url, init) => fetch(url, { ...init, redirect: "manual" });
|
|
384
378
|
function buildTransport(spec, principal, kind) {
|
|
385
379
|
const t = spec.transport;
|
|
@@ -408,8 +402,10 @@ export async function mapContent(content, serverName, imageResizer) {
|
|
|
408
402
|
if (item.type === "text" && typeof item.text === "string") {
|
|
409
403
|
out.push({ type: "text", text: item.text });
|
|
410
404
|
}
|
|
411
|
-
else if (item.type === "image"
|
|
412
|
-
|
|
405
|
+
else if (item.type === "image") {
|
|
406
|
+
const data = typeof item.data === "string" ? item.data : "";
|
|
407
|
+
const mimeType = typeof item.mimeType === "string" ? item.mimeType : "";
|
|
408
|
+
out.push(data !== "" && mimeType !== "" ? await boundedImageBlock(data, mimeType, serverName, resizer) : { type: "image", data, mimeType });
|
|
413
409
|
}
|
|
414
410
|
else if (item.type === "audio" && typeof item.data === "string" && item.data) {
|
|
415
411
|
out.push(await blobToTextBlock(item.data, item.mimeType, serverName, `[Audio from ${serverName ?? "MCP server"}] `));
|
|
@@ -660,7 +656,7 @@ export function mcpToolSchemaProblem(schema) {
|
|
|
660
656
|
}
|
|
661
657
|
return undefined;
|
|
662
658
|
}
|
|
663
|
-
export async function materializeMcpTools(specs, principal, onElicit, imageResizer, reminderDisclosure, mcpRevocations, poolOpts) {
|
|
659
|
+
export async function materializeMcpTools(specs, principal, onElicit, imageResizer, reminderDisclosure, mcpRevocations, poolOpts, promptSections) {
|
|
664
660
|
const entries = snapshotMcpEntries(specs);
|
|
665
661
|
{
|
|
666
662
|
const collision = findNamespacePrefixCollision(MCP_NAMESPACE, entries.map((s) => s.name));
|
|
@@ -678,7 +674,8 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
678
674
|
if (mcpRevocations === undefined)
|
|
679
675
|
return false;
|
|
680
676
|
try {
|
|
681
|
-
const
|
|
677
|
+
const fingerprint = entries.find((e) => e.name === serverName)?.fingerprint;
|
|
678
|
+
const r = fingerprint === undefined ? mcpRevocations.isRevoked(serverName) : mcpRevocations.isRevoked(serverName, { fingerprint });
|
|
682
679
|
if (typeof r !== "boolean") {
|
|
683
680
|
throw new Error(`isRevoked returned ${r instanceof Promise ? "a Promise — the ledger seat is SYNCHRONOUS by contract" : `a non-boolean (${typeof r})`}`);
|
|
684
681
|
}
|
|
@@ -751,6 +748,8 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
751
748
|
droppedTools.push({ server: inlineUntrusted(spec.name, 160), ...d });
|
|
752
749
|
for (const a of s.schemaAdvisories)
|
|
753
750
|
raised.push(schemaAdvisoryWarning(spec.name, a.tool, a.reason));
|
|
751
|
+
if (s.instructionsDefect !== undefined)
|
|
752
|
+
raised.push(sectionAnswerWarning(s.instructionsDefect));
|
|
754
753
|
if (s.toolsCapabilityAbsent === true && spec.allowTools !== undefined && spec.allowTools.length > 0) {
|
|
755
754
|
raised.push(toolsCapabilityAbsentWarning(spec.name));
|
|
756
755
|
}
|
|
@@ -791,7 +790,7 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
791
790
|
hadPendingAdd = true;
|
|
792
791
|
}
|
|
793
792
|
if (after !== undefined) {
|
|
794
|
-
instructionsDelta.pendingAdds.push(
|
|
793
|
+
instructionsDelta.pendingAdds.push(after);
|
|
795
794
|
return;
|
|
796
795
|
}
|
|
797
796
|
if (connected)
|
|
@@ -799,7 +798,7 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
799
798
|
if (before !== undefined || owedRemoval || hadPendingAdd)
|
|
800
799
|
instructionsDelta.pendingRemovals.push(name);
|
|
801
800
|
};
|
|
802
|
-
const settled = await Promise.allSettled(entries.map((spec, i) => connectServer(spec, principal, onElicit, imageResizer, mcpDisclosure, isServerRevoked, dialKinds[i], poolOpts?.pool)));
|
|
801
|
+
const settled = await Promise.allSettled(entries.map((spec, i) => connectServer(spec, principal, onElicit, imageResizer, mcpDisclosure, isServerRevoked, dialKinds[i], poolOpts?.pool, { sections: promptSections })));
|
|
803
802
|
try {
|
|
804
803
|
for (let i = 0; i < entries.length; i++) {
|
|
805
804
|
const r = settled[i];
|
|
@@ -808,9 +807,8 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
808
807
|
const s = r.value;
|
|
809
808
|
tools.push(...s.tools);
|
|
810
809
|
toolAxes.push(...s.axes);
|
|
811
|
-
if (s.instructions)
|
|
812
|
-
serverInstructions.push(
|
|
813
|
-
}
|
|
810
|
+
if (s.instructions)
|
|
811
|
+
serverInstructions.push(s.instructions);
|
|
814
812
|
commitConnectedServer(spec, s, dialKinds[i]);
|
|
815
813
|
}
|
|
816
814
|
else {
|
|
@@ -857,7 +855,7 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
857
855
|
const dialKind = readDialKind(spec);
|
|
858
856
|
let connected;
|
|
859
857
|
try {
|
|
860
|
-
connected = await connectServer(spec, principal, onElicit, imageResizer, mcpDisclosure, isServerRevoked, dialKind, poolOpts?.pool, { fresh: true });
|
|
858
|
+
connected = await connectServer(spec, principal, onElicit, imageResizer, mcpDisclosure, isServerRevoked, dialKind, poolOpts?.pool, { fresh: true, sections: promptSections });
|
|
861
859
|
}
|
|
862
860
|
catch (err) {
|
|
863
861
|
const { kind, delivered, httpStatus } = classifyMcpFailure(err, { phase: "connect", transport: dialKind });
|
|
@@ -1523,6 +1521,10 @@ async function listToolsLenient(client, options) {
|
|
|
1523
1521
|
return { tools: walk.items, ...(walk.incomplete !== undefined ? { incomplete: walk.incomplete } : {}) };
|
|
1524
1522
|
}
|
|
1525
1523
|
async function connectServer(spec, principal, onElicit, imageResizer, reminderDisclosure, isServerRevoked, transportKind, pool, dialOpts) {
|
|
1524
|
+
const fp = spec.fingerprint;
|
|
1525
|
+
if (fp !== undefined && !(typeof fp === "string" && fp.length > 0 && fp.length <= 128)) {
|
|
1526
|
+
throw Object.assign(new Error(`MCP server "${inlineUntrusted(spec.name, 160)}": fingerprint must be a non-empty string of at most 128 characters (got ${typeof fp === "string" ? `${fp.length} characters` : typeof fp}) — not dialed`), { code: "config.mcp_fingerprint_invalid" });
|
|
1527
|
+
}
|
|
1526
1528
|
const elicitOn = spec.elicitation === true && onElicit !== undefined;
|
|
1527
1529
|
const health = { dead: false, pendingElicitations: 0, lastElicitationClosedAt: 0 };
|
|
1528
1530
|
const announce = {};
|
|
@@ -1588,7 +1590,9 @@ async function connectServer(spec, principal, onElicit, imageResizer, reminderDi
|
|
|
1588
1590
|
}
|
|
1589
1591
|
: undefined;
|
|
1590
1592
|
const rawInstructions = client.getInstructions();
|
|
1591
|
-
const
|
|
1593
|
+
const sanitized = rawInstructions && rawInstructions.trim() ? sanitizeUntrustedText(rawInstructions.trim()) : undefined;
|
|
1594
|
+
const disposed = sanitized === undefined ? undefined : disposeSectionEntry(dialOpts?.sections, { section: "core/discovery.mcp-instructions", server: spec.name, text: sanitized, mountedTools: serverTools.map((t) => t.name) });
|
|
1595
|
+
const instructions = disposed?.kept === undefined ? undefined : { server: spec.name, ...disposed.kept };
|
|
1592
1596
|
const info = client.getServerVersion();
|
|
1593
1597
|
const status = {
|
|
1594
1598
|
name: spec.name,
|
|
@@ -1611,6 +1615,7 @@ async function connectServer(spec, principal, onElicit, imageResizer, reminderDi
|
|
|
1611
1615
|
...(listed.toolsCapabilityAbsent === true ? { toolsCapabilityAbsent: true } : {}),
|
|
1612
1616
|
listedTools: listed.tools,
|
|
1613
1617
|
...(instructions ? { instructions } : {}),
|
|
1618
|
+
...(disposed?.defect !== undefined ? { instructionsDefect: disposed.defect } : {}),
|
|
1614
1619
|
...(listed.incomplete !== undefined ? { listingIncomplete: listed.incomplete } : {}),
|
|
1615
1620
|
...(resourceInfo && (resourceInfo.listAllowed || resourceInfo.readAllowed) ? { resourceServer: { server: spec.name, client, health, transportKind: idleKindOf(transportKind), ...resourceInfo } } : {}),
|
|
1616
1621
|
};
|
|
@@ -1761,7 +1766,7 @@ function intakeListedTools(listed, spec, client, health, imageResizer, reminderD
|
|
|
1761
1766
|
const errScLine = structuredContentErrorLine(res.structuredContent, parts.join("\n"));
|
|
1762
1767
|
if (errScLine !== undefined)
|
|
1763
1768
|
parts.push(errScLine);
|
|
1764
|
-
const body = truncateMcpErrorText(
|
|
1769
|
+
const body = truncateMcpErrorText(egressText("same-principal", parts.join("\n").trim()));
|
|
1765
1770
|
const msg = body
|
|
1766
1771
|
? `MCP tool ${inlineUntrusted(remoteName)} reported an error. The server's error content follows as external/untrusted data:\n${delimitUntrusted(`${spec.name} tool error`, body)}`
|
|
1767
1772
|
: `MCP tool ${inlineUntrusted(remoteName)} reported an error`;
|
|
@@ -24,6 +24,31 @@ export declare function mediaBudgetBytes(cap: number | undefined): number;
|
|
|
24
24
|
* (`0` and `Infinity` both mean "no budget"). A non-number, `NaN` or a negative is refused (`config.invalid_argument`),
|
|
25
25
|
* never read as "no budget". */
|
|
26
26
|
export declare function checkedMediaByteCap(value: unknown, seat: string): void;
|
|
27
|
+
/** A media block: an image or a document whose payload is a base64 string. */
|
|
28
|
+
export type MediaBlock = ImageContent | DocumentContent;
|
|
29
|
+
/** The media type a block claims — its `type`, read once, when that is a media type — or `undefined` for any other value. */
|
|
30
|
+
export declare function claimedMediaType(block: unknown): MediaBlock["type"] | undefined;
|
|
31
|
+
/** Whether a value is a media block: its `type` is a media type and its `data` a string. A block of a media type with no
|
|
32
|
+
* string `data` is not one — a {@link MediaDescriptor} is such a block. */
|
|
33
|
+
export declare function isMediaBlock(block: unknown): block is MediaBlock;
|
|
34
|
+
/** What media becomes where its bytes do not ride: one block's identity (its type, its media type when it can be one, its
|
|
35
|
+
* decoded size), or one summary of every block from there on. Never a string, never the bytes. */
|
|
36
|
+
export type MediaDescriptor = {
|
|
37
|
+
type: MediaBlock["type"];
|
|
38
|
+
mimeType?: string;
|
|
39
|
+
bytes: number;
|
|
40
|
+
truncated: true;
|
|
41
|
+
} | {
|
|
42
|
+
type: "omitted";
|
|
43
|
+
blocks: number;
|
|
44
|
+
bytes: number;
|
|
45
|
+
truncated: true;
|
|
46
|
+
};
|
|
47
|
+
/** A media block's descriptor. The media type is echoed only when it can be one (RFC 6838: type and subtype are at most
|
|
48
|
+
* 127 characters each); `bytes` is {@link imageBytes}. */
|
|
49
|
+
export declare function mediaBlockDescriptor(block: MediaBlock): MediaDescriptor;
|
|
50
|
+
/** The one summary of media blocks that leave together: how many, and their decoded size. */
|
|
51
|
+
export declare function omittedMediaDescriptor(blocks: readonly MediaBlock[]): MediaDescriptor;
|
|
27
52
|
/** Decoded byte size of a base64 media payload (4 base64 chars ≈ 3 bytes). Approximate — fine for a budget.
|
|
28
53
|
* PDF read: `document` blocks (base64 PDFs) count against the SAME aggregate budget — same class of inline
|
|
29
54
|
* media bytes, same unbounded-accumulation failure mode. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describeRejectedValue } from "./tool-search-setting.js";
|
|
2
|
+
import { readMember } from "./thrown-value.js";
|
|
2
3
|
export const AGGREGATE_MEDIA_BUDGET_BYTES = 30 * 1024 * 1024;
|
|
3
4
|
export function mediaBudgetBytes(cap) {
|
|
4
5
|
const limit = cap ?? AGGREGATE_MEDIA_BUDGET_BYTES;
|
|
@@ -12,6 +13,21 @@ export function checkedMediaByteCap(value, seat) {
|
|
|
12
13
|
throw e;
|
|
13
14
|
}
|
|
14
15
|
const MEDIA_STRIPPED_MARKER = "[media removed to fit the request media budget — re-read the file if you still need to see it]";
|
|
16
|
+
const MEDIA_BLOCK_TYPES = new Set(["image", "document"]);
|
|
17
|
+
export function claimedMediaType(block) {
|
|
18
|
+
const type = readMember(block, "type");
|
|
19
|
+
return MEDIA_BLOCK_TYPES.has(type) ? type : undefined;
|
|
20
|
+
}
|
|
21
|
+
const mediaTyped = (b) => claimedMediaType(b) !== undefined;
|
|
22
|
+
export function isMediaBlock(block) {
|
|
23
|
+
return mediaTyped(block) && typeof readMember(block, "data") === "string";
|
|
24
|
+
}
|
|
25
|
+
export function mediaBlockDescriptor(block) {
|
|
26
|
+
return { type: block.type, ...(typeof block.mimeType === "string" && block.mimeType.length <= 255 ? { mimeType: block.mimeType } : {}), bytes: imageBytes(block), truncated: true };
|
|
27
|
+
}
|
|
28
|
+
export function omittedMediaDescriptor(blocks) {
|
|
29
|
+
return { type: "omitted", blocks: blocks.length, bytes: blocks.reduce((n, b) => n + imageBytes(b), 0), truncated: true };
|
|
30
|
+
}
|
|
15
31
|
export function imageBytes(b) {
|
|
16
32
|
return typeof b.data === "string" ? Math.floor((b.data.length * 3) / 4) : 0;
|
|
17
33
|
}
|
|
@@ -28,7 +44,7 @@ export function capAggregateMediaBytes(messages, opts) {
|
|
|
28
44
|
const blocks = blocksOf(m);
|
|
29
45
|
if (blocks)
|
|
30
46
|
for (const b of blocks)
|
|
31
|
-
if (b
|
|
47
|
+
if (mediaTyped(b))
|
|
32
48
|
total += imageBytes(b);
|
|
33
49
|
}
|
|
34
50
|
if (total <= limitBytes)
|
|
@@ -43,7 +59,7 @@ export function capAggregateMediaBytes(messages, opts) {
|
|
|
43
59
|
continue;
|
|
44
60
|
let changed = false;
|
|
45
61
|
const next = blocks.map((b) => {
|
|
46
|
-
if (remaining > limitBytes && (b
|
|
62
|
+
if (remaining > limitBytes && mediaTyped(b)) {
|
|
47
63
|
const bytes = imageBytes(b);
|
|
48
64
|
remaining -= bytes;
|
|
49
65
|
removedBytes += bytes;
|
|
@@ -119,7 +119,7 @@ async function refuseUnlessWriteAllowed(judge, target, consequence) {
|
|
|
119
119
|
why = `the write judge could not answer${where !== undefined ? ` at ${where}` : ""}${text(answer.cause) !== undefined ? ` (${text(answer.cause)})` : ""}${text(answer.reason) !== undefined ? `: ${text(answer.reason)}` : ""}, so whether a standing rule forbids this write is unknown`;
|
|
120
120
|
else
|
|
121
121
|
why = "the write judge answered something outside its closed set of verdicts";
|
|
122
|
-
throw new ConsolidationRefusedError(CONSOLIDATION_WRITE_REFUSED_CODE, `memory consolidation refused for scope ${inlineUntrusted(target.scope, 80)}
|
|
122
|
+
throw new ConsolidationRefusedError(CONSOLIDATION_WRITE_REFUSED_CODE, `memory consolidation refused for scope ${inlineUntrusted(target.scope, 80)}: ${why}${consequence !== undefined ? ` — ${consequence}` : ""} (library ${inlineUntrusted(target.path, 200)})`);
|
|
123
123
|
}
|
|
124
124
|
const MAX_THROTTLE_WAITS_PER_SNAPSHOT = 4;
|
|
125
125
|
const MAX_BUSY_WAITS = 2;
|
|
@@ -19,6 +19,7 @@ import type { PermissionResult, ToolCallRequest, ToolPolicy } from "./tool-polic
|
|
|
19
19
|
import { type ToolRoster } from "./tool-roster.js";
|
|
20
20
|
import { type PermissionMode } from "./ask-origin.js";
|
|
21
21
|
import type { RuntimeCaps } from "./runtime-caps.js";
|
|
22
|
+
import type { RunnerDeps } from "./runner-deps.js";
|
|
22
23
|
import { recordedModeSeed } from "./checkpoint-store.js";
|
|
23
24
|
import type { TaskSpec } from "./task-spec.js";
|
|
24
25
|
/** WHY an effective mode is not the requested one: the deployment's deny bit (`RuntimeCaps.bypassPermissions` /
|
|
@@ -196,6 +197,20 @@ export interface PermissionModeArming {
|
|
|
196
197
|
* or a rejection from it is contained. */
|
|
197
198
|
readonly onArmFault?: (error: unknown) => void;
|
|
198
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* The auto-mode classifier FACE a leg arms from — ONE reading for the arming fact ({@link PermissionModeArming.autoFacePresent}),
|
|
202
|
+
* the arming itself and the face this leg hands its children: the leg's own deployment face (`RunnerDeps.autoMode`), else
|
|
203
|
+
* the face its ancestor armed from, riding the trusted chain (`InheritedGate.autoModeFace`). A delegated child whose own
|
|
204
|
+
* runner wires no face is classified from its ancestor's deployment face — each of a subagent's actions goes through the
|
|
205
|
+
* same decision order as the parent's — on the child's own transcript and model; the child's own face wins where it has one.
|
|
206
|
+
*/
|
|
207
|
+
export declare function autoModeFaceOf(deps: Pick<RunnerDeps, "autoMode">, inherited: InheritedAutoModeFace | undefined): RunnerDeps["autoMode"];
|
|
208
|
+
/** The chain's carriage of that face (`InheritedGate` extends it). */
|
|
209
|
+
export interface InheritedAutoModeFace {
|
|
210
|
+
/** The auto-mode face the spawning leg armed from — opaque half, never serialized; present only when that leg's deps (or its own
|
|
211
|
+
* chain) carried one. A child whose runner wires no face of its own arms its classifier from it ({@link autoModeFaceOf}). */
|
|
212
|
+
autoModeFace?: RunnerDeps["autoMode"];
|
|
213
|
+
}
|
|
199
214
|
/** A live ceiling: the parent leg's own controller (`current` tracks the parent's steers), or a recorded word. A ceiling
|
|
200
215
|
* that can start something on a read (a controller, the engine's leg ceiling) also answers `observed` — the same word,
|
|
201
216
|
* read without starting anything; one without it is read through `current` on an observation too. */
|
|
@@ -219,8 +234,11 @@ export declare class PermissionModeController {
|
|
|
219
234
|
private arming;
|
|
220
235
|
private readonly onFold;
|
|
221
236
|
private prePlan;
|
|
237
|
+
/** `onFold` hears every fold with the word the fold FOLDED — what a fold notice names: the leg's own request for the
|
|
238
|
+
* ceiling fold (the word the parent's mode outranks); for the deployment's folds the declared word (the request after
|
|
239
|
+
* the ceiling, or — a leg that requested nothing — the word it inherited). */
|
|
222
240
|
constructor(requestedBySpec: PermissionMode | undefined, ceiling: PermissionModeCeiling | undefined, opts?: {
|
|
223
|
-
onFold?: (receipt: PermissionModeReceipt) => void;
|
|
241
|
+
onFold?: (receipt: PermissionModeReceipt, folded: PermissionMode) => void;
|
|
224
242
|
prePlanMode?: PermissionMode;
|
|
225
243
|
});
|
|
226
244
|
/** The declared word the leg had when a steer (or the model's `EnterPlanMode`) last moved it into `plan`, while it is
|
|
@@ -249,6 +267,12 @@ export declare class PermissionModeController {
|
|
|
249
267
|
/** The three-member answer as it stands right now. */
|
|
250
268
|
receipt(): PermissionModeReceipt;
|
|
251
269
|
private receiptOf;
|
|
270
|
+
/** The receipt and, when it records a fold, the word that fold folded — one declaration read for both. */
|
|
271
|
+
private answerOf;
|
|
272
|
+
/** Hand a fold to the observer with the word it folded (an answer without a fold says nothing). */
|
|
273
|
+
private announce;
|
|
274
|
+
/** The construction-time announcement: a fold the leg STARTS with (a ceiling fold, before any arming is bound). */
|
|
275
|
+
announceStart(): PermissionModeReceipt;
|
|
252
276
|
/** The ceiling's source moved to another leg (the parent session's next leg); `before` is this leg's receipt under the
|
|
253
277
|
* previous one. A ceiling fold the move produced — or changed — is announced, as the construction-time fold is. */
|
|
254
278
|
ceilingRebound(before: PermissionModeReceipt): void;
|
package/dist/core/mode-policy.js
CHANGED
|
@@ -131,6 +131,9 @@ function screenAs(field, screen) {
|
|
|
131
131
|
throw e;
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
|
+
export function autoModeFaceOf(deps, inherited) {
|
|
135
|
+
return deps.autoMode ?? inherited?.autoModeFace;
|
|
136
|
+
}
|
|
134
137
|
export class PermissionModeController {
|
|
135
138
|
requestedBySpec;
|
|
136
139
|
ceiling;
|
|
@@ -183,6 +186,9 @@ export class PermissionModeController {
|
|
|
183
186
|
return this.receiptOf(false);
|
|
184
187
|
}
|
|
185
188
|
receiptOf(observe) {
|
|
189
|
+
return this.answerOf(observe).receipt;
|
|
190
|
+
}
|
|
191
|
+
answerOf(observe) {
|
|
186
192
|
const { effective: declared, clamped } = this.declaration(observe);
|
|
187
193
|
const requested = this.requested;
|
|
188
194
|
const base = requested !== undefined ? { requested } : {};
|
|
@@ -197,29 +203,39 @@ export class PermissionModeController {
|
|
|
197
203
|
? "operator_deny"
|
|
198
204
|
: undefined;
|
|
199
205
|
if (armingFold !== undefined)
|
|
200
|
-
return { ...base, effective: "default", foldedBy: armingFold };
|
|
206
|
+
return { receipt: { ...base, effective: "default", foldedBy: armingFold }, folded: declared };
|
|
201
207
|
if (clamped)
|
|
202
|
-
return { ...base, effective: declared, foldedBy: "inherited_ceiling" };
|
|
203
|
-
return { ...base, effective: declared };
|
|
208
|
+
return { receipt: { ...base, effective: declared, foldedBy: "inherited_ceiling" }, folded: requested ?? declared };
|
|
209
|
+
return { receipt: { ...base, effective: declared } };
|
|
210
|
+
}
|
|
211
|
+
announce(answer) {
|
|
212
|
+
if (answer.receipt.foldedBy !== undefined && answer.folded !== undefined)
|
|
213
|
+
this.onFold?.(answer.receipt, answer.folded);
|
|
214
|
+
}
|
|
215
|
+
announceStart() {
|
|
216
|
+
const answer = this.answerOf(false);
|
|
217
|
+
this.announce(answer);
|
|
218
|
+
return answer.receipt;
|
|
204
219
|
}
|
|
205
220
|
ceilingRebound(before) {
|
|
206
|
-
const after = this.
|
|
207
|
-
if (after.foldedBy === "inherited_ceiling" && (before.foldedBy !== "inherited_ceiling" || before.effective !== after.effective))
|
|
208
|
-
this.
|
|
221
|
+
const after = this.answerOf(false);
|
|
222
|
+
if (after.receipt.foldedBy === "inherited_ceiling" && (before.foldedBy !== "inherited_ceiling" || before.effective !== after.receipt.effective))
|
|
223
|
+
this.announce(after);
|
|
209
224
|
}
|
|
210
225
|
bindArming(arming) {
|
|
211
226
|
const before = this.receipt();
|
|
212
227
|
this.arming = arming;
|
|
213
|
-
const
|
|
214
|
-
if (
|
|
215
|
-
this.
|
|
216
|
-
return receipt;
|
|
228
|
+
const answer = this.answerOf(false);
|
|
229
|
+
if (answer.receipt.foldedBy !== before.foldedBy)
|
|
230
|
+
this.announce(answer);
|
|
231
|
+
return answer.receipt;
|
|
217
232
|
}
|
|
218
233
|
async steer(mode) {
|
|
219
234
|
const entry = { word: mode };
|
|
220
235
|
const before = this.declared;
|
|
221
236
|
this.steers.push(entry);
|
|
222
|
-
const
|
|
237
|
+
const answer = this.answerOf(false);
|
|
238
|
+
const receipt = answer.receipt;
|
|
223
239
|
const arming = this.arming;
|
|
224
240
|
if (receipt.effective === "auto" && arming?.armAuto !== undefined) {
|
|
225
241
|
this.armedForAuto = true;
|
|
@@ -243,8 +259,7 @@ export class PermissionModeController {
|
|
|
243
259
|
else if (before !== "plan")
|
|
244
260
|
this.prePlan = before;
|
|
245
261
|
}
|
|
246
|
-
|
|
247
|
-
this.onFold?.(receipt);
|
|
262
|
+
this.announce(answer);
|
|
248
263
|
return receipt;
|
|
249
264
|
}
|
|
250
265
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** How a refusal names the object being received. */
|
|
2
|
+
export interface OwnPropertyReceiptWords {
|
|
3
|
+
/** The object, e.g. `RunnerDeps`. */
|
|
4
|
+
readonly subject: string;
|
|
5
|
+
/** One of its properties, e.g. `seat`. */
|
|
6
|
+
readonly unit: string;
|
|
7
|
+
/** When the one read happens, e.g. `at new Runner`. */
|
|
8
|
+
readonly moment: string;
|
|
9
|
+
}
|
|
10
|
+
/** The receipt's coded refusal. */
|
|
11
|
+
export declare function receiptRefusal(message: string): Error & {
|
|
12
|
+
code: "config.invalid_argument";
|
|
13
|
+
};
|
|
14
|
+
/** The receipt's first look, before ANY reflection on the object (a door that must look at the caller's object before
|
|
15
|
+
* the receipt — a retired-name door reading its prototype chain — calls this first): a Proxy is refused. */
|
|
16
|
+
export declare function refuseProxy(given: object, words: OwnPropertyReceiptWords): void;
|
|
17
|
+
/** Read `given` by the receipt law (module note): the plain copy, every own member taken once from its captured descriptor. */
|
|
18
|
+
export declare function receiveOwnProperties<T extends object>(given: T, words: OwnPropertyReceiptWords): T;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { types } from "node:util";
|
|
2
|
+
export function receiptRefusal(message) {
|
|
3
|
+
const e = new Error(message);
|
|
4
|
+
e.code = "config.invalid_argument";
|
|
5
|
+
return e;
|
|
6
|
+
}
|
|
7
|
+
export function refuseProxy(given, words) {
|
|
8
|
+
if (types.isProxy(given))
|
|
9
|
+
throw receiptRefusal(`${words.subject} must be a plain object, not a Proxy: its ${words.unit}s are read once off its own descriptors, and a Proxy would run its traps in that read — refused rather than wired through it`);
|
|
10
|
+
}
|
|
11
|
+
export function receiveOwnProperties(given, words) {
|
|
12
|
+
refuseProxy(given, words);
|
|
13
|
+
const proto = Object.getPrototypeOf(given);
|
|
14
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
15
|
+
throw receiptRefusal(`${words.subject} must be a plain object (its prototype is neither Object.prototype nor null): every ${words.unit} is read once as an own property, and a ${words.unit} its prototype would supply is not read — refused rather than wired without it`);
|
|
16
|
+
}
|
|
17
|
+
const captured = Object.getOwnPropertyDescriptors(given);
|
|
18
|
+
const received = Object.create(Object.prototype);
|
|
19
|
+
for (const name of Object.getOwnPropertyNames(captured)) {
|
|
20
|
+
const d = captured[name];
|
|
21
|
+
let value;
|
|
22
|
+
if (Object.hasOwn(d, "value")) {
|
|
23
|
+
value = d.value;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
const get = Object.hasOwn(d, "get") ? d.get : undefined;
|
|
27
|
+
try {
|
|
28
|
+
value = get === undefined ? undefined : get.call(given);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
throw receiptRefusal(`${words.subject}.${name} threw while it was read ${words.moment} — a ${words.unit} is read once, here; refused rather than wired without it`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
Object.defineProperty(received, name, { value, enumerable: true, writable: true, configurable: true });
|
|
35
|
+
}
|
|
36
|
+
if (Object.getPrototypeOf(given) !== proto)
|
|
37
|
+
throw receiptRefusal(`${words.subject}'s prototype changed while its ${words.unit}s were read ${words.moment} — refused rather than wired from a moving object`);
|
|
38
|
+
return received;
|
|
39
|
+
}
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
* `Bash(git status:*)`) — the last two are strict-side divergences from upstream, registered as such.
|
|
74
74
|
*/
|
|
75
75
|
import { type DenyFace } from "../tools/fs/read-deny.js";
|
|
76
|
-
import type { AssertAllKeysHandled } from "./ask-origin.js";
|
|
76
|
+
import type { AssertAllKeysHandled, RuleOffersAbsence } from "./ask-origin.js";
|
|
77
77
|
import { type ToolCallLike } from "./tool-registry.js";
|
|
78
78
|
import { type PathDenialEvidence, type PathFamily, type PathIdentity } from "../tools/fs/safety.js";
|
|
79
79
|
import { type TargetViews } from "./path-rule-binding.js";
|
|
@@ -928,7 +928,13 @@ export declare function spelledScope<C extends {
|
|
|
928
928
|
* coverage (the eligibility predicate's behavior conjunct). The mixed-table walk that orders the three
|
|
929
929
|
* behaviors is {@link adjudicatePersistedRules}.
|
|
930
930
|
*/
|
|
931
|
-
export declare function findAdmittingRule(rules: readonly PersistedRule[], call:
|
|
931
|
+
export declare function findAdmittingRule(rules: readonly PersistedRule[], call: AdmissionCall): readonly PersistedRule[] | undefined;
|
|
932
|
+
/** Whose admission a list's allow rows answer: a GRANT (a person's recorded yes, a list that releases a call — the reference's
|
|
933
|
+
* per-segment form, where a read-only segment of a compound allows itself) or a NARROWING (a list outside of which nothing
|
|
934
|
+
* runs — every segment must be named by a row; nothing allows itself). The posture is the list's, never the call's. */
|
|
935
|
+
export type AdmissionPosture = "grant" | "narrowing";
|
|
936
|
+
/** The call {@link findAdmittingRule} / {@link commandCoverageOf} judge. */
|
|
937
|
+
export interface AdmissionCall {
|
|
932
938
|
tool: string;
|
|
933
939
|
command: string;
|
|
934
940
|
cwd: string | undefined;
|
|
@@ -938,7 +944,16 @@ export declare function findAdmittingRule(rules: readonly PersistedRule[], call:
|
|
|
938
944
|
* `./x` against the moved cursor, not the task root). Defaults to `cwd`; the scope/eligibility
|
|
939
945
|
* axis stays `cwd` either way. */
|
|
940
946
|
execCwd?: string;
|
|
941
|
-
}
|
|
947
|
+
/** {@link AdmissionPosture}; absent = `grant`. */
|
|
948
|
+
posture?: AdmissionPosture;
|
|
949
|
+
}
|
|
950
|
+
/** {@link findAdmittingRule}'s answer with its miss named: the rules that cover the command, or the first segment no row covers
|
|
951
|
+
* (`undefined` when the command cannot be read at all — the fail-closed floor). One walk; the miss is for a sentence only. */
|
|
952
|
+
export declare function commandCoverageOf(rules: readonly PersistedRule[], call: AdmissionCall): {
|
|
953
|
+
readonly covering: readonly PersistedRule[];
|
|
954
|
+
} | {
|
|
955
|
+
readonly uncovered: string | undefined;
|
|
956
|
+
};
|
|
942
957
|
/** The answer of {@link adjudicatePersistedRules}: which behavior decided, and the rule(s) it stands
|
|
943
958
|
* on — a one-element set for a deny/ask (the first table-order rule of the deciding behavior that
|
|
944
959
|
* reaches the call) and the allow direction's coverage SET ({@link findAdmittingRule}). */
|
|
@@ -1251,15 +1266,21 @@ export interface UncoveredSegmentDetail {
|
|
|
1251
1266
|
*/
|
|
1252
1267
|
export declare const UNCOVERED_SEGMENT_REASON_BASELINE: Readonly<Record<UncoveredSegmentDetail["reason"], string>>;
|
|
1253
1268
|
/**
|
|
1254
|
-
* design/382 §3.5-2 — the BASELINE sentence per card-level offer-ABSENCE reason (
|
|
1255
|
-
*
|
|
1256
|
-
*
|
|
1257
|
-
*
|
|
1258
|
-
*
|
|
1259
|
-
*
|
|
1260
|
-
* two reasons name their real outlets.
|
|
1269
|
+
* design/382 §3.5-2 — the BASELINE sentence per card-level offer-ABSENCE reason (`ruleOffersAbsence` on the ask and its
|
|
1270
|
+
* durable park twin; the closed set is {@link import("./ask-origin.js").RULE_OFFERS_ABSENCES}). Same reword-never-invert
|
|
1271
|
+
* contract as the table above. The load-bearing asymmetries: `mandated` and `allow_outranked` must NOT point at writing
|
|
1272
|
+
* rules — on a mandated ask no rule is consulted, an outranked allow shows another allow would be outranked the same way, and
|
|
1273
|
+
* directing a person to mint one that can never take effect is the #457④ anti-shape; the other two reasons name their real
|
|
1274
|
+
* outlets.
|
|
1261
1275
|
*/
|
|
1262
|
-
export declare const RULE_OFFERS_ABSENCE_BASELINE:
|
|
1276
|
+
export declare const RULE_OFFERS_ABSENCE_BASELINE: {
|
|
1277
|
+
readonly mandated: "This approval is required by the deployment's policy each time; a rule cannot waive it.";
|
|
1278
|
+
readonly allow_outranked: "An allow rule already matches this call, and this question outranks it — another allow rule would be outranked the same way; it is approved per use.";
|
|
1279
|
+
readonly ask_rule_matched: "An ask-each-time rule matches this call and keeps asking — a new allow rule changes nothing while it stands; to stop being asked, review the existing ask rules.";
|
|
1280
|
+
readonly lane_cannot_speak: "This card has no rule to offer for this exact command; it can be approved per use. A coverable form of what you trust can still get a rule — through a card drawn for that form, or settings.";
|
|
1281
|
+
};
|
|
1282
|
+
/** The fence over the baseline table: `never` while every word has a sentence. */
|
|
1283
|
+
export type OfferAbsenceBaselineCoversEveryWord = AssertAllKeysHandled<Exclude<RuleOffersAbsence, keyof typeof RULE_OFFERS_ABSENCE_BASELINE>>;
|
|
1263
1284
|
/**
|
|
1264
1285
|
* The offers presented on an approval card for `command` (design/375 §5.2).
|
|
1265
1286
|
*
|