@sema-agent/core 7.52.0 → 7.53.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 +194 -0
- package/README.md +3 -1
- package/dist/agents/cross-session-envelope.d.ts +8 -0
- package/dist/agents/cross-session-envelope.js +6 -0
- package/dist/agents/cross-session-judge.d.ts +21 -1
- package/dist/agents/cross-session-judge.js +8 -0
- package/dist/agents/cross-session-ref.d.ts +5 -0
- package/dist/agents/cross-session-ref.js +1 -0
- package/dist/agents/org-delivery-gate.d.ts +122 -0
- package/dist/agents/org-delivery-gate.js +69 -0
- package/dist/agents/peer-held-queue.d.ts +9 -4
- package/dist/agents/peer-held-queue.js +6 -5
- package/dist/agents/peer-session-drain.d.ts +35 -5
- package/dist/agents/peer-session-drain.js +271 -180
- package/dist/agents/read-inbox-tool.d.ts +7 -0
- package/dist/agents/read-inbox-tool.js +5 -1
- package/dist/agents/subagent.d.ts +3 -0
- package/dist/agents/subagent.js +99 -72
- package/dist/agents/tool-filter.d.ts +18 -0
- package/dist/agents/tool-filter.js +6 -0
- package/dist/brain/anthropic.d.ts +6 -1
- package/dist/brain/anthropic.js +4 -4
- package/dist/brain/model-presets.js +2 -1
- package/dist/brain/route-adjudicator.d.ts +6 -0
- package/dist/brain/routing.d.ts +24 -1
- package/dist/brain/routing.js +54 -19
- package/dist/brain/timeout.js +5 -4
- package/dist/core/a2a.d.ts +5 -2
- package/dist/core/a2a.js +30 -13
- package/dist/core/allow-entry.d.ts +2 -1
- package/dist/core/allow-entry.js +2 -2
- package/dist/core/auto-compaction.d.ts +57 -0
- package/dist/core/auto-compaction.js +28 -2
- package/dist/core/checkpoint-store.d.ts +29 -20
- package/dist/core/checkpoint-store.js +3 -3
- package/dist/core/compliance.js +3 -1
- package/dist/core/effective-path-target.d.ts +25 -6
- package/dist/core/effective-path-target.js +24 -2
- package/dist/core/engine-notice.d.ts +13 -2
- package/dist/core/gate-fold.js +3 -2
- package/dist/core/gate-lanes.js +11 -13
- package/dist/core/gate-outcome.d.ts +11 -6
- package/dist/core/governance-codes.d.ts +3 -1
- package/dist/core/governance-codes.js +3 -0
- package/dist/core/hands-band-screen.d.ts +7 -0
- package/dist/core/hands-band-screen.js +32 -0
- package/dist/core/hooks.d.ts +7 -5
- package/dist/core/hooks.js +5 -3
- package/dist/core/locked-config.d.ts +84 -32
- package/dist/core/locked-config.js +128 -42
- package/dist/core/mcp-probe.js +1 -1
- package/dist/core/mcp-server-spec.d.ts +88 -10
- package/dist/core/mcp-server-spec.js +183 -33
- package/dist/core/mcp.d.ts +21 -14
- package/dist/core/mcp.js +55 -51
- package/dist/core/memory-engine/engine.d.ts +14 -19
- package/dist/core/memory-engine/engine.js +7 -5
- package/dist/core/mode-policy.d.ts +3 -18
- package/dist/core/mode-policy.js +2 -16
- package/dist/core/mods/kernel/dispatch.js +13 -15
- package/dist/core/mods/kernel/events.d.ts +9 -6
- package/dist/core/mods/kernel/events.js +2 -2
- package/dist/core/mods/kernel/receive.d.ts +39 -10
- package/dist/core/mods/kernel/receive.js +119 -54
- package/dist/core/mods/kernel/site-rules.d.ts +80 -15
- package/dist/core/mods/kernel/site-rules.js +232 -40
- package/dist/core/mods/stations.d.ts +0 -6
- package/dist/core/mods/stations.js +0 -7
- package/dist/core/mounted-mcp.d.ts +57 -0
- package/dist/core/mounted-mcp.js +71 -2
- package/dist/core/own-property-receipt.d.ts +13 -0
- package/dist/core/own-property-receipt.js +3 -1
- package/dist/core/parent-spec-seat.d.ts +2 -2
- package/dist/core/permission-rule-model.d.ts +58 -9
- package/dist/core/permission-rule-model.js +81 -7
- package/dist/core/permission-rule-org.d.ts +163 -33
- package/dist/core/permission-rule-org.js +205 -71
- package/dist/core/permission-rules.d.ts +7 -1
- package/dist/core/permission-rules.js +37 -8
- package/dist/core/retention.js +7 -2
- package/dist/core/retired-keys.d.ts +8 -2
- package/dist/core/retired-keys.js +12 -1
- package/dist/core/roles.js +60 -10
- package/dist/core/rule-lane-reads.js +1 -1
- package/dist/core/runner/contracts.d.ts +1 -0
- package/dist/core/runner/gate-exit.js +4 -3
- package/dist/core/runner/mcp-redial.d.ts +52 -10
- package/dist/core/runner/mcp-redial.js +38 -9
- package/dist/core/runner/permission-rule-lanes.d.ts +5 -2
- package/dist/core/runner/permission-rule-lanes.js +22 -10
- package/dist/core/runner/person-path-rows.d.ts +3 -1
- package/dist/core/runner/person-path-rows.js +2 -2
- package/dist/core/runner/prepare-config-doors.d.ts +48 -2
- package/dist/core/runner/prepare-config-doors.js +161 -145
- package/dist/core/runner/prepare-defer-classify.js +4 -7
- package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
- package/dist/core/runner/prepare-delegation-surface.js +7 -5
- package/dist/core/runner/prepare-hands-readface.d.ts +1 -1
- package/dist/core/runner/prepare-hands-readface.js +2 -1
- package/dist/core/runner/prepare-inherited-gate.js +2 -2
- package/dist/core/runner/prepare-listings.d.ts +1 -1
- package/dist/core/runner/prepare-listings.js +4 -2
- package/dist/core/runner/prepare-memory-engine-session.d.ts +0 -6
- package/dist/core/runner/prepare-memory-engine-session.js +19 -6
- package/dist/core/runner/prepare-memory.d.ts +3 -2
- package/dist/core/runner/prepare-memory.js +6 -2
- package/dist/core/runner/prepare-offload-wrappers.d.ts +4 -4
- package/dist/core/runner/prepare-offload-wrappers.js +2 -1
- package/dist/core/runner/prepare-policy-chain.d.ts +1 -1
- package/dist/core/runner/prepare-policy-chain.js +12 -12
- package/dist/core/runner/prepare-project-context.d.ts +1 -1
- package/dist/core/runner/prepare-project-context.js +6 -4
- package/dist/core/runner/prepare-prompt-assembly.d.ts +1 -1
- package/dist/core/runner/prepare-prompt-assembly.js +20 -16
- package/dist/core/runner/prepare-prompt-inputs.d.ts +12 -6
- package/dist/core/runner/prepare-prompt-inputs.js +6 -3
- package/dist/core/runner/prepare-protocol-tools.d.ts +7 -5
- package/dist/core/runner/prepare-protocol-tools.js +19 -12
- package/dist/core/runner/prepare-question-face.d.ts +4 -2
- package/dist/core/runner/prepare-question-face.js +4 -3
- package/dist/core/runner/prepare-task.js +10 -10
- package/dist/core/runner/prepare-wiring-manifest.d.ts +8 -3
- package/dist/core/runner/prepare-wiring-manifest.js +8 -5
- package/dist/core/runner/resume-admission.d.ts +4 -1
- package/dist/core/runner/resume-admission.js +7 -5
- package/dist/core/runner/resume-internals-and-config.d.ts +3 -3
- package/dist/core/runner/resume-internals-and-config.js +3 -8
- package/dist/core/runner/retired-runner-deps.d.ts +5 -6
- package/dist/core/runner/retired-runner-deps.js +1 -4
- package/dist/core/runner/run-leg.js +42 -12
- package/dist/core/runner/run-recovery-lanes.js +5 -3
- package/dist/core/runner/run-terminal-adoption.js +6 -5
- package/dist/core/runner/run-turn-boundary.js +9 -3
- package/dist/core/runner/runtask.js +1 -1
- package/dist/core/runner/skills-seat.d.ts +15 -0
- package/dist/core/runner/skills-seat.js +57 -0
- package/dist/core/runner/sub-agent-deny-lists.d.ts +13 -11
- package/dist/core/runner/sub-agent-deny-lists.js +7 -5
- package/dist/core/runner/synthetic-tools.d.ts +5 -2
- package/dist/core/runner/synthetic-tools.js +2 -12
- package/dist/core/runner-deps.d.ts +29 -14
- package/dist/core/session-policy-store.d.ts +19 -0
- package/dist/core/session-policy-store.js +22 -2
- package/dist/core/store-contracts/mailbox-store-contract.js +13 -0
- package/dist/core/task-event.d.ts +38 -2
- package/dist/core/task-notification.js +2 -2
- package/dist/core/task-spec.d.ts +1 -1
- package/dist/core/tool-policy.d.ts +3 -0
- package/dist/core/tool-policy.js +10 -4
- package/dist/core/types.d.ts +2 -2
- package/dist/core/types.js +1 -1
- package/dist/core/untrusted-text.d.ts +16 -0
- package/dist/core/untrusted-text.js +7 -2
- package/dist/core/user-attachment.d.ts +13 -2
- package/dist/core/user-attachment.js +47 -41
- package/dist/core/wiring-manifest.d.ts +4 -2
- package/dist/engine/harness/agent-harness.js +2 -1
- package/dist/engine/harness/types.d.ts +3 -0
- package/dist/engine/loop/agent-loop.d.ts +12 -1
- package/dist/engine/loop/agent-loop.js +17 -5
- package/dist/engine/loop/types.d.ts +7 -1
- package/dist/index.d.ts +15 -9
- package/dist/index.js +12 -6
- package/dist/internal/harness.d.ts +1 -0
- package/dist/internal/harness.js +1 -0
- package/dist/orchestration/goal.d.ts +24 -1
- package/dist/orchestration/goal.js +3 -2
- package/dist/prompt-assembly/artifact-store.d.ts +44 -9
- package/dist/prompt-assembly/artifact-store.js +38 -12
- package/dist/prompt-assembly/artifact.d.ts +12 -11
- package/dist/prompt-assembly/artifact.js +52 -21
- package/dist/prompt-assembly/assemble.d.ts +2 -0
- package/dist/prompt-assembly/assemble.js +2 -1
- package/dist/prompt-assembly/catalog/index.js +2 -1
- package/dist/prompt-assembly/catalog/memory.d.ts +13 -0
- package/dist/prompt-assembly/catalog/memory.js +6 -0
- package/dist/prompt-assembly/catalog/schema.d.ts +16 -2
- package/dist/prompt-assembly/composer.d.ts +2 -0
- package/dist/prompt-assembly/composer.js +1 -1
- package/dist/prompt-assembly/overlay.d.ts +21 -11
- package/dist/prompt-assembly/overlay.js +55 -17
- package/dist/prompt-assembly/select.d.ts +21 -6
- package/dist/prompt-assembly/select.js +39 -2
- package/dist/prompts/default.d.ts +0 -11
- package/dist/prompts/default.js +0 -3
- package/dist/stores/file/permission-rule-store.d.ts +1 -1
- package/dist/stores/file/permission-rule-store.js +2 -1
- package/dist/tools/artifact/artifact-tool.js +4 -3
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +65 -5
- package/wire-changes/core@7.53.0.json +1946 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { McpServerSpec, RunnerDeps, TaskSpec } from "./types.js";
|
|
1
|
+
import type { A2aServerSpec, McpServerSpec, RunnerDeps, TaskSpec } from "./types.js";
|
|
2
|
+
import { type ToolAxesSurface } from "./mcp-server-spec.js";
|
|
2
3
|
import type { ToolPolicy } from "./tool-policy.js";
|
|
3
4
|
/** The closed set of administratively lockable configuration keys. Adding a member is a deliberate
|
|
4
5
|
* edit HERE (tsc forces the registry row), never a free-form string. */
|
|
@@ -34,7 +35,7 @@ export interface LockedConfig {
|
|
|
34
35
|
keys: readonly LockedKey[];
|
|
35
36
|
}
|
|
36
37
|
/** The preflight's resolved snapshot — the ONLY thing downstream task assembly reads for the
|
|
37
|
-
* guarded slots (never the raw `spec.mcp` / `spec.toolPolicy` fields). */
|
|
38
|
+
* guarded slots (never the raw `spec.mcp` / `spec.a2a` / `spec.toolPolicy` fields). */
|
|
38
39
|
export interface LockedPreflight {
|
|
39
40
|
/** The validated lock set (empty when the deployment declares none). */
|
|
40
41
|
lockedKeys: ReadonlySet<LockedKey>;
|
|
@@ -45,6 +46,10 @@ export interface LockedPreflight {
|
|
|
45
46
|
* `undefined` (core has no deployment-level MCP seat — a locked deployment mounts no
|
|
46
47
|
* task-supplied servers). */
|
|
47
48
|
mcp: TaskSpec["mcp"];
|
|
49
|
+
/** The task's A2A peers: a frozen SNAPSHOT of `spec.a2a` whose `toolAxes` were judged here by the same reader and with the same
|
|
50
|
+
* disposition as an MCP server's (a declaration that does not fit refused the whole prepare), each peer carrying the reader's plain
|
|
51
|
+
* copy; `undefined` when the spec declares no peer (absent or empty). Read once, here: the peers dialed are the peers judged. */
|
|
52
|
+
a2a: TaskSpec["a2a"];
|
|
48
53
|
/** Effective caller tool policy: the same `spec.toolPolicy ?? deps.toolPolicy` slot the gate has
|
|
49
54
|
* always enforced, resolved ONCE here. With `toolPolicy` locked, the deployment's own
|
|
50
55
|
* `deps.toolPolicy` is the whole answer (a spec-supplied policy has already been refused). */
|
|
@@ -56,12 +61,27 @@ export interface LockedPreflight {
|
|
|
56
61
|
* site). Unlocked: `spec.basePolicyForResumeEdit ?? deps.basePolicyForResumeEdit`. */
|
|
57
62
|
basePolicyForResumeEdit: ToolPolicy | undefined;
|
|
58
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* How one configuration door is entered: `seat` names the input that carries the value the door judges — the task's configuration or the
|
|
66
|
+
* deployment's deps. A prepare enters each door as it stands ({@link ENTER_AS_IS}: the door's own coded throw); a resume's screen before the
|
|
67
|
+
* claim enters the same door through the resume's refusal channel, which names the seat (`asResumeConfigRefusal`). One door, one function,
|
|
68
|
+
* two entrances: the seat is written once, where the door is called.
|
|
69
|
+
*/
|
|
70
|
+
export type ConfigDoorEntry = <T>(seat: "taskConfig" | "deps", door: () => T) => T;
|
|
71
|
+
export declare const ENTER_AS_IS: ConfigDoorEntry;
|
|
59
72
|
/**
|
|
60
73
|
* Validate a deployment's lock declaration against the closed registry. Fail-loud on any unknown
|
|
61
74
|
* key (`config.locked_registry`): a security control that doesn't parse must refuse to arm, not
|
|
62
75
|
* degrade to "that key silently unlocked". Exported for deployments that want the check at their
|
|
63
76
|
* own startup; the task-preparation preflight runs it on every leg regardless (a mis-declared lock
|
|
64
|
-
* then fails the first prepare instead of silently guarding nothing
|
|
77
|
+
* then fails the first prepare instead of silently guarding nothing — and, on a resume, the screen before
|
|
78
|
+
* the claim, which calls the same preflight).
|
|
79
|
+
*
|
|
80
|
+
* Total over the declaration: anything but `undefined` (no locks) or plain data whose `keys` is a list of registry names — `null`, a
|
|
81
|
+
* list, a primitive, a `keys` that is absent or not a list, a declaration that is not plain data (a Proxy, a `Set` of keys, a class
|
|
82
|
+
* instance, a getter that throws), an element that is not a string, a string that is not a registry name — is the same coded refusal
|
|
83
|
+
* naming `RunnerDeps.lockedConfig` and the member (an element by its index), never a TypeError and never "no locks". The declaration is
|
|
84
|
+
* read once, by the one declaration reading.
|
|
65
85
|
*/
|
|
66
86
|
export declare function resolveLockedKeys(config: LockedConfig | undefined): ReadonlySet<LockedKey>;
|
|
67
87
|
/**
|
|
@@ -74,23 +94,33 @@ export declare function resolveLockedKeys(config: LockedConfig | undefined): Rea
|
|
|
74
94
|
* read off a LIVE object is only true for as long as the object holds still, and the fail-open
|
|
75
95
|
* direction is the reachable one — so both boundaries consume data instead.
|
|
76
96
|
*
|
|
77
|
-
* The read discipline is the load-bearing part, and it is why
|
|
78
|
-
* "read the property, then copy the object": a getter answering ALTERNATING LEGAL values would
|
|
97
|
+
* The read discipline is the load-bearing part, and it is why the copy is made in the pass that reads
|
|
98
|
+
* rather than "read the property, then copy the object": a getter answering ALTERNATING LEGAL values would
|
|
79
99
|
* satisfy any number of re-reads (`"external"` to a validator, `"local"` to a mint site), and no
|
|
80
|
-
* vocabulary guard anywhere has purchase on a legal value. One
|
|
81
|
-
*
|
|
82
|
-
*
|
|
100
|
+
* vocabulary guard anywhere has purchase on a legal value. One pass evaluates every member EXACTLY ONCE
|
|
101
|
+
* and materializes the result as plain data; every later reader — validator, connect, refresh — then sees
|
|
102
|
+
* the same byte. Frozen so nothing downstream rewrites it.
|
|
83
103
|
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
104
|
+
* Plain data, stated: an entry IS plain data, to its whole depth, or it is refused whole. The entry is read
|
|
105
|
+
* by the one declaration reading ({@link receiveDeclaration} — the engine's one receipt of a caller's object,
|
|
106
|
+
* in its declaration mode): every own member read once off its captured descriptor, every level copied, every
|
|
107
|
+
* copied container frozen; an object the entry reaches through two members is read once, and both hold the one
|
|
108
|
+
* copy. So a server's `transport` with its `args` / `env` / `headers`, `allowTools`, `toolFaces`, `toolAxes`
|
|
109
|
+
* and a peer's `headers` and `allowSkills` are the copy's own, and a host that edits its own nested object after
|
|
110
|
+
* the door (a header value, an `allowSkills.push`) reaches nothing the engine holds: the dial, a refresh and a
|
|
111
|
+
* re-dial all work from this copy, on both surfaces. The axes are then judged on that copy by their own reader
|
|
112
|
+
* ({@link judgeEntryToolAxes}).
|
|
113
|
+
*
|
|
114
|
+
* There is no third outcome between "copied whole" and "refused": a value the copy cannot carry whole is a
|
|
115
|
+
* declaration the engine cannot read — a Proxy at any level, an object whose prototype is neither
|
|
116
|
+
* `Object.prototype` nor null (a class instance, a map, `process.env`, a record built on a prototype or in another
|
|
117
|
+
* realm, whose members a copy would drop or could not tell from its own), a function, a structure that contains
|
|
118
|
+
* itself, a getter that throws (whatever it throws), a nesting too deep to read, a list element that is not a
|
|
119
|
+
* string, a member named `__proto__`, an entry that is not an object. Each is refused as `config.invalid_argument`
|
|
120
|
+
* naming the declarer and the member where reading stopped (`toolFaces.write_file`, `allowTools[2]`), never as the
|
|
121
|
+
* raw error the host's code threw, and never by handing the host's object on: a member some of whose getters have
|
|
122
|
+
* already run would be read again downstream, and could answer differently. The door refuses the whole prepare and
|
|
123
|
+
* a library entrance the whole call, with this one sentence either way.
|
|
94
124
|
*
|
|
95
125
|
* Exported because the SECOND boundary needs the identical guarantee: `materializeMcpTools` is public
|
|
96
126
|
* and a library caller reaches it without passing any door, so it snapshots its own input through
|
|
@@ -98,16 +128,39 @@ export declare function resolveLockedKeys(config: LockedConfig | undefined): Rea
|
|
|
98
128
|
*/
|
|
99
129
|
export declare function snapshotMcpEntries(entries: readonly McpServerSpec[]): McpServerSpec[];
|
|
100
130
|
/**
|
|
101
|
-
* The
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
131
|
+
* The refusal of a declaration the engine cannot read (`config.invalid_argument`). `declarer` is the entry's own name as the engine captured
|
|
132
|
+
* it ({@link capturedName}) — absent when there was none to capture — so a consumer that holds the refused entry by a name (the A2A
|
|
133
|
+
* entrance's warning, its collision check) takes it from here and never reads the caller's object again.
|
|
134
|
+
*/
|
|
135
|
+
export type DeclarationRefusal = Error & {
|
|
136
|
+
code: "config.invalid_argument";
|
|
137
|
+
declarer?: string;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* The name a refused entry is held under by a consumer that holds it by a name (the A2A entrance): the refusal's own `declarer`, read as
|
|
141
|
+
* own data — never through anything the refusal's prototype carries — or `undefined` (the consumer names the entry by its place).
|
|
142
|
+
*/
|
|
143
|
+
export declare function refusedDeclarer(refusal: unknown): string | undefined;
|
|
144
|
+
/**
|
|
145
|
+
* One entry of EITHER surface that declares `toolAxes` (an MCP server, an A2A peer) as frozen plain data, read by the discipline
|
|
146
|
+
* {@link snapshotMcpEntries} states: the entry and everything in it read once by the one declaration reading and copied to its whole
|
|
147
|
+
* depth, or the entry refused ({@link DeclarationRefusal}), naming the member where reading stopped. The A2A peer's door and its
|
|
148
|
+
* materialization snapshot through this same function, so the two surfaces' declarations reach the one judge ({@link judgeEntryToolAxes})
|
|
149
|
+
* read the same way; `at` is the surface the entry sits on, which only decides what the refusal calls the declarer.
|
|
150
|
+
*/
|
|
151
|
+
export declare function snapshotDeclaredEntry<E extends McpServerSpec | A2aServerSpec>(entry: E, at: ToolAxesSurface): E;
|
|
152
|
+
/**
|
|
153
|
+
* The `toolAxes` question for ONE snapshot entry of either surface (`at`: an MCP server, an A2A peer) — asked by the preparation door (a
|
|
154
|
+
* refusal ends the prepare) and by each materialization entrance (a refusal fails that server's or that peer's dial), so there is one
|
|
155
|
+
* reading, one copy and one sentence whichever surface and whichever time it is asked. `ok: true` carries the entry with the reader's
|
|
156
|
+
* PLAIN COPY of the axes in place of the caller's object: what was judged is the very value the connect and every refresh read, not a
|
|
157
|
+
* live object that could answer differently later. The copy is frozen with the entry — a tool that shallow-copies a server
|
|
158
|
+
* (`mountedMcp()`) to customise its axes must not rewrite what the parent reads. `ok: false` carries the coded refusal
|
|
159
|
+
* (`config.invalid_argument`) naming the declarer, the tool (or skill) and the member, never a value.
|
|
107
160
|
*/
|
|
108
|
-
export declare function judgeEntryToolAxes(copy:
|
|
161
|
+
export declare function judgeEntryToolAxes<E extends McpServerSpec | A2aServerSpec>(copy: E, at: ToolAxesSurface): {
|
|
109
162
|
ok: true;
|
|
110
|
-
entry:
|
|
163
|
+
entry: E;
|
|
111
164
|
} | {
|
|
112
165
|
ok: false;
|
|
113
166
|
error: Error & {
|
|
@@ -118,14 +171,13 @@ export declare function judgeEntryToolAxes(copy: McpServerSpec): {
|
|
|
118
171
|
* The single preflight resolver: validate the lock declaration, refuse a spec that tries to occupy
|
|
119
172
|
* a locked slot (`config.locked_key`, two-state — the whole prepare is rejected, nothing is
|
|
120
173
|
* silently dropped or merged), and produce the resolved snapshot downstream assembly consumes.
|
|
121
|
-
* Runs BEFORE any side effect of task preparation
|
|
174
|
+
* Runs BEFORE any side effect of task preparation — and, on a resume, before the claim: the resume's
|
|
175
|
+
* screen calls this same function (`enter` naming each refusal's seat) and hands the leg the frozen
|
|
176
|
+
* entries it judged, so the leg's prepare judges engine-owned plain data and dials what was judged.
|
|
122
177
|
*/
|
|
123
|
-
export declare function preflightLockedConfig(spec: TaskSpec, deps: RunnerDeps): LockedPreflight;
|
|
178
|
+
export declare function preflightLockedConfig(spec: Pick<TaskSpec, "mcp" | "a2a" | "toolPolicy" | "basePolicyForResumeEdit">, deps: RunnerDeps, enter?: ConfigDoorEntry): LockedPreflight;
|
|
124
179
|
/**
|
|
125
180
|
* The two caller policies a prepare resolves to: the task's own seat over the deployment's, or the deployment's alone
|
|
126
|
-
* under a `toolPolicy` lock
|
|
181
|
+
* under a `toolPolicy` lock — one resolution, read by the preflight above (which a resume enters before its claim).
|
|
127
182
|
*/
|
|
128
183
|
export declare function callerPolicySeats(spec: Pick<TaskSpec, "toolPolicy" | "basePolicyForResumeEdit">, deps: Pick<RunnerDeps, "toolPolicy" | "basePolicyForResumeEdit">, toolPolicyLocked: boolean): Pick<LockedPreflight, "toolPolicy" | "basePolicyForResumeEdit">;
|
|
129
|
-
/** The retired-member door over the resolved caller policies: a member the engine no longer reads is refused by name
|
|
130
|
-
* (`config.deps_retired_key`), never dropped in silence. */
|
|
131
|
-
export declare function assertCallerPoliciesCurrent(policies: Pick<LockedPreflight, "toolPolicy" | "basePolicyForResumeEdit">): void;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { types } from "node:util";
|
|
2
|
+
import { DECLARATION_PATH_RULES, declarationFaultOf, declarerWord, readToolAxes, receiveDeclaration, toolAxesRefusal } from "./mcp-server-spec.js";
|
|
3
|
+
import { isPlainRecord, receive } from "./mods/kernel/receive.js";
|
|
4
|
+
import { CONFIG_RECEIPT_MODE } from "./own-property-receipt.js";
|
|
2
5
|
import { isToolContentOrigin, TOOL_CONTENT_ORIGINS } from "./memory-engine/content-origin.js";
|
|
3
6
|
import { inlineUntrusted } from "./untrusted-text.js";
|
|
4
7
|
import { assertNoRetiredToolPolicyKeys } from "./retired-keys.js";
|
|
@@ -8,6 +11,7 @@ export const LOCKED_KEY_REGISTRY = {
|
|
|
8
11
|
compliancePosture: { specFields: [] },
|
|
9
12
|
retentionPolicy: { specFields: [] },
|
|
10
13
|
};
|
|
14
|
+
export const ENTER_AS_IS = (_seat, door) => door();
|
|
11
15
|
function codedError(code, message) {
|
|
12
16
|
const e = new Error(message);
|
|
13
17
|
e.code = code;
|
|
@@ -17,21 +21,74 @@ export function resolveLockedKeys(config) {
|
|
|
17
21
|
if (config === undefined)
|
|
18
22
|
return new Set();
|
|
19
23
|
const keys = new Set();
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const listed = declaredLockKeys(config);
|
|
25
|
+
for (let index = 0; index < listed.length; index++) {
|
|
26
|
+
const key = listed[index];
|
|
27
|
+
if (!Object.hasOwn(LOCKED_KEY_REGISTRY, key)) {
|
|
28
|
+
throw lockDeclarationRefusal(`RunnerDeps.lockedConfig.keys[${index}]`, `is ${inlineUntrusted(JSON.stringify(key), 80)}, which is not a member of the locked-key registry (${Object.keys(LOCKED_KEY_REGISTRY).join(", ")})`);
|
|
25
29
|
}
|
|
26
30
|
keys.add(key);
|
|
27
31
|
}
|
|
28
32
|
return keys;
|
|
29
33
|
}
|
|
34
|
+
function declaredLockKeys(config) {
|
|
35
|
+
const received = receiveDeclaration(config);
|
|
36
|
+
if (!received.ok)
|
|
37
|
+
throw lockDeclarationRefusal(memberPath(received.fault.path, "RunnerDeps.lockedConfig"), `cannot be read: ${received.fault.problem}`);
|
|
38
|
+
const copy = received.value;
|
|
39
|
+
if (!isPlainRecord(copy))
|
|
40
|
+
throw lockDeclarationRefusal("RunnerDeps.lockedConfig", `is ${inlineUntrusted(describeDeclared(copy), 80)}, not an object with a \`keys\` list`);
|
|
41
|
+
const keys = Object.hasOwn(copy, "keys") ? copy["keys"] : undefined;
|
|
42
|
+
if (!Array.isArray(keys))
|
|
43
|
+
throw lockDeclarationRefusal("RunnerDeps.lockedConfig.keys", `is ${inlineUntrusted(describeDeclared(keys), 80)}, not a list of locked keys`);
|
|
44
|
+
return keys;
|
|
45
|
+
}
|
|
46
|
+
function lockDeclarationRefusal(member, problem) {
|
|
47
|
+
return codedError("config.locked_registry", `${member} ${problem}. A lock that does not parse must not half-arm — fix the deployment's lock declaration.`);
|
|
48
|
+
}
|
|
30
49
|
export function snapshotMcpEntries(entries) {
|
|
31
|
-
return entries.map((entry) =>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
50
|
+
return entries.map((entry) => snapshotDeclaredEntry(entry, "mcp"));
|
|
51
|
+
}
|
|
52
|
+
function memberPath(path, base = "") {
|
|
53
|
+
let rendered = base;
|
|
54
|
+
for (const key of path) {
|
|
55
|
+
const name = typeof key === "string" && /^\d+$/.test(key) ? JSON.stringify(key) : String(key);
|
|
56
|
+
rendered += typeof key === "number" ? `[${key}]` : rendered === "" ? name : `.${name}`;
|
|
57
|
+
if (rendered.length > 120 + base.length)
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
return rendered;
|
|
61
|
+
}
|
|
62
|
+
function unreadableDeclaration(at, declarer, member, problem) {
|
|
63
|
+
const refusal = codedError("config.invalid_argument", `${declarerWord(at)}${declarer === undefined ? "" : ` ${declarerNameOf(declarer)}`} declares ${inlineUntrusted(member, 80)} the engine cannot read: ${problem}. A declaration the engine cannot read is refused ` +
|
|
64
|
+
`rather than partly applied — what the caller wrote and what the engine runs on would otherwise differ without anyone being told.`);
|
|
65
|
+
if (declarer !== undefined)
|
|
66
|
+
Object.defineProperty(refusal, "declarer", { value: declarer, enumerable: true, writable: false, configurable: false });
|
|
67
|
+
return refusal;
|
|
68
|
+
}
|
|
69
|
+
function ownDataString(holder, key) {
|
|
70
|
+
const slot = Object.getOwnPropertyDescriptor(holder, key);
|
|
71
|
+
const value = slot !== undefined && Object.hasOwn(slot, "value") ? slot.value : undefined;
|
|
72
|
+
return typeof value === "string" ? value : undefined;
|
|
73
|
+
}
|
|
74
|
+
export function refusedDeclarer(refusal) {
|
|
75
|
+
return typeof refusal === "object" && refusal !== null && !types.isProxy(refusal) ? ownDataString(refusal, "declarer") : undefined;
|
|
76
|
+
}
|
|
77
|
+
function capturedName(entry) {
|
|
78
|
+
return typeof entry === "object" && entry !== null && !types.isProxy(entry) ? ownDataString(entry, "name") : undefined;
|
|
79
|
+
}
|
|
80
|
+
function entryFaultRefusal(at, declarer, path, problem) {
|
|
81
|
+
return unreadableDeclaration(at, declarer, path.length === 0 ? "an entry" : memberPath(path), problem);
|
|
82
|
+
}
|
|
83
|
+
export function snapshotDeclaredEntry(entry, at) {
|
|
84
|
+
const declared = entry;
|
|
85
|
+
const declarer = capturedName(declared);
|
|
86
|
+
const received = receiveDeclaration(declared, DECLARATION_PATH_RULES[at]);
|
|
87
|
+
if (!received.ok)
|
|
88
|
+
throw entryFaultRefusal(at, declarer, received.fault.path, received.fault.problem);
|
|
89
|
+
if (!isPlainRecord(received.value))
|
|
90
|
+
throw unreadableDeclaration(at, declarer, "an entry", `it is ${inlineUntrusted(describeDeclared(received.value), 80)}, not an object`);
|
|
91
|
+
return received.value;
|
|
35
92
|
}
|
|
36
93
|
function describeDeclared(value) {
|
|
37
94
|
try {
|
|
@@ -46,58 +103,91 @@ function describeDeclared(value) {
|
|
|
46
103
|
}
|
|
47
104
|
}
|
|
48
105
|
}
|
|
49
|
-
const
|
|
50
|
-
export function judgeEntryToolAxes(copy) {
|
|
106
|
+
const declarerNameOf = (name) => inlineUntrusted(typeof name === "string" ? JSON.stringify(name) : describeDeclared(name), 80);
|
|
107
|
+
export function judgeEntryToolAxes(copy, at) {
|
|
51
108
|
const { toolAxes: declaredAxes, ...rest } = copy;
|
|
52
|
-
const axes =
|
|
53
|
-
if (!axes.ok)
|
|
54
|
-
return {
|
|
55
|
-
ok: false,
|
|
56
|
-
error: codedError("config.invalid_argument", `MCP server ${serverNameOf(copy)} declares toolAxes the engine cannot read: ${inlineUntrusted(axes.problem, 240)}. A per-tool safety-axis declaration is the ` +
|
|
57
|
-
`caller's trust root for the approval gate, so it is refused rather than partly applied — dropping one key or defaulting one word ` +
|
|
58
|
-
`would leave a declaration that reads as in force while it is not.`),
|
|
59
|
-
};
|
|
60
|
-
}
|
|
109
|
+
const axes = readToolAxes(declaredAxes, at);
|
|
110
|
+
if (!axes.ok)
|
|
111
|
+
return { ok: false, error: toolAxesRefusal(at, declarerNameOf(copy.name), axes.problem) };
|
|
61
112
|
if (axes.toolAxes !== undefined) {
|
|
62
113
|
for (const perTool of Object.values(axes.toolAxes))
|
|
63
114
|
Object.freeze(perTool);
|
|
64
115
|
Object.freeze(axes.toolAxes);
|
|
65
116
|
}
|
|
66
|
-
return { ok: true, entry: Object.freeze(axes.toolAxes === undefined ? rest : { ...rest, toolAxes: axes.toolAxes }) };
|
|
117
|
+
return { ok: true, entry: Object.freeze((axes.toolAxes === undefined ? rest : { ...rest, toolAxes: axes.toolAxes })) };
|
|
118
|
+
}
|
|
119
|
+
function declaredEntriesOf(declared, at) {
|
|
120
|
+
const field = at;
|
|
121
|
+
const received = receive(declared, CONFIG_RECEIPT_MODE);
|
|
122
|
+
let problem;
|
|
123
|
+
if (received.ok) {
|
|
124
|
+
const list = received.value;
|
|
125
|
+
if (Array.isArray(list) && list !== declared)
|
|
126
|
+
return Array.from({ length: list.length }, (_, index) => list[index]);
|
|
127
|
+
problem = typeof list === "object" && list !== null ? "it is not an array" : `it is ${inlineUntrusted(describeDeclared(list), 80)}`;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const fault = declarationFaultOf(received.fault);
|
|
131
|
+
if (fault.path.length > 0)
|
|
132
|
+
throw unreadableDeclaration(at, undefined, "an entry", fault.problem);
|
|
133
|
+
problem = fault.problem;
|
|
134
|
+
}
|
|
135
|
+
throw codedError("config.invalid_argument", `TaskSpec.${field} must be a readable array of ${declarerWord(at)} declarations; ${problem}. A list the engine cannot read ` +
|
|
136
|
+
`is refused rather than read as "no entries" — the declared ${declarerWord(at)}s would otherwise be dropped without anyone being told.`);
|
|
67
137
|
}
|
|
68
|
-
function
|
|
69
|
-
|
|
138
|
+
function snapshotAndValidateA2aEntries(declared) {
|
|
139
|
+
const entries = declaredEntriesOf(declared, "a2a");
|
|
140
|
+
if (entries.length === 0)
|
|
141
|
+
return undefined;
|
|
142
|
+
return entries.map((entry) => {
|
|
143
|
+
const judged = judgeEntryToolAxes(snapshotDeclaredEntry(entry, "a2a"), "a2a");
|
|
144
|
+
if (!judged.ok)
|
|
145
|
+
throw judged.error;
|
|
146
|
+
return judged.entry;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function snapshotAndValidateMcpEntries(declared) {
|
|
150
|
+
return snapshotMcpEntries(declaredEntriesOf(declared, "mcp")).map((copy) => {
|
|
70
151
|
const declared = copy.contentOrigin;
|
|
71
152
|
if (declared !== undefined && !isToolContentOrigin(declared)) {
|
|
72
|
-
throw codedError("config.mcp_content_class", `MCP server ${
|
|
153
|
+
throw codedError("config.mcp_content_class", `MCP server ${declarerNameOf(copy.name)} declares contentOrigin ` +
|
|
73
154
|
`${inlineUntrusted(describeDeclared(declared), 80)}, which is not one of ` +
|
|
74
155
|
`${TOOL_CONTENT_ORIGINS.map((v) => `"${v}"`).join(" | ")}. The declaration decides whether this server's tools ` +
|
|
75
156
|
`mark the session's memory externally exposed — a value this engine cannot read is refused at the door rather ` +
|
|
76
157
|
`than folded to a class, because folding it would let a deployment believe a declaration is in force while every ` +
|
|
77
158
|
`call keeps writing a one-way durable mark.`);
|
|
78
159
|
}
|
|
79
|
-
const judged = judgeEntryToolAxes(copy);
|
|
160
|
+
const judged = judgeEntryToolAxes(copy, "mcp");
|
|
80
161
|
if (!judged.ok)
|
|
81
162
|
throw judged.error;
|
|
82
163
|
return judged.entry;
|
|
83
164
|
});
|
|
84
165
|
}
|
|
85
|
-
export function preflightLockedConfig(spec, deps) {
|
|
86
|
-
const lockedKeys = resolveLockedKeys(deps.lockedConfig);
|
|
87
|
-
|
|
88
|
-
for (const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
166
|
+
export function preflightLockedConfig(spec, deps, enter = ENTER_AS_IS) {
|
|
167
|
+
const lockedKeys = enter("deps", () => resolveLockedKeys(deps.lockedConfig));
|
|
168
|
+
enter("taskConfig", () => {
|
|
169
|
+
for (const key of lockedKeys) {
|
|
170
|
+
for (const field of LOCKED_KEY_REGISTRY[key].specFields) {
|
|
171
|
+
if (spec[field] !== undefined) {
|
|
172
|
+
throw codedError("config.locked_key", `TaskSpec.${field} is administratively locked by this deployment (locked key "${key}") — ` +
|
|
173
|
+
`the task-supplied value is refused wholesale (locks are config-time; there is no request-time ` +
|
|
174
|
+
`unlock). Remove the field from the task, or change the deployment's lock configuration.`);
|
|
175
|
+
}
|
|
93
176
|
}
|
|
94
177
|
}
|
|
95
|
-
}
|
|
96
|
-
const
|
|
97
|
-
|
|
178
|
+
});
|
|
179
|
+
const toolPolicyLocked = lockedKeys.has("toolPolicy");
|
|
180
|
+
const task = toolPolicyLocked ? {} : { toolPolicy: spec.toolPolicy, basePolicyForResumeEdit: spec.basePolicyForResumeEdit };
|
|
181
|
+
const policies = callerPolicySeats(task, deps, toolPolicyLocked);
|
|
182
|
+
enter(!toolPolicyLocked && task.toolPolicy != null ? "taskConfig" : "deps", () => assertNoRetiredToolPolicyKeys(policies.toolPolicy));
|
|
183
|
+
enter(!toolPolicyLocked && task.basePolicyForResumeEdit != null ? "taskConfig" : "deps", () => assertNoRetiredToolPolicyKeys(policies.basePolicyForResumeEdit));
|
|
184
|
+
const declaredServers = lockedKeys.has("mcp") ? undefined : spec.mcp;
|
|
185
|
+
const mcp = declaredServers === undefined ? undefined : enter("taskConfig", () => snapshotAndValidateMcpEntries(declaredServers));
|
|
186
|
+
const declaredPeers = spec.a2a;
|
|
98
187
|
return {
|
|
99
188
|
lockedKeys,
|
|
100
|
-
mcp
|
|
189
|
+
mcp,
|
|
190
|
+
a2a: declaredPeers === undefined ? undefined : enter("taskConfig", () => snapshotAndValidateA2aEntries(declaredPeers)),
|
|
101
191
|
...policies,
|
|
102
192
|
};
|
|
103
193
|
}
|
|
@@ -107,7 +197,3 @@ export function callerPolicySeats(spec, deps, toolPolicyLocked) {
|
|
|
107
197
|
basePolicyForResumeEdit: toolPolicyLocked ? deps.basePolicyForResumeEdit : (spec.basePolicyForResumeEdit ?? deps.basePolicyForResumeEdit),
|
|
108
198
|
};
|
|
109
199
|
}
|
|
110
|
-
export function assertCallerPoliciesCurrent(policies) {
|
|
111
|
-
assertNoRetiredToolPolicyKeys(policies.toolPolicy);
|
|
112
|
-
assertNoRetiredToolPolicyKeys(policies.basePolicyForResumeEdit);
|
|
113
|
-
}
|
package/dist/core/mcp-probe.js
CHANGED
|
@@ -128,5 +128,5 @@ export async function probeMcpServers(specs, opts = {}) {
|
|
|
128
128
|
fatal = { cause: abortCause(signal) };
|
|
129
129
|
if (fatal !== undefined)
|
|
130
130
|
throw fatal.cause;
|
|
131
|
-
return { entries: mcpManifestEntries(declared, statuses), servers };
|
|
131
|
+
return { entries: mcpManifestEntries(declared, statuses, servers.flatMap((s) => s?.tools ?? [])), servers };
|
|
132
132
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ReceiptFault } from "./mods/kernel/receive.js";
|
|
1
2
|
import type { ToolContentOrigin, ToolEffect } from "./tool-spec.js";
|
|
2
3
|
import type { ToolFamily, ToolPathTarget, ToolRenderHints, ToolRuleFace } from "./tool-face.js";
|
|
3
4
|
/** Definition of an MCP server to connect to for the duration of one task, then discard. */
|
|
@@ -61,7 +62,7 @@ export interface McpServerSpec {
|
|
|
61
62
|
* a read-only tool can return third-party text, and a deployment's own writer brings back nothing
|
|
62
63
|
* external — so vouching on one axis never quietly vouches on the other.
|
|
63
64
|
*
|
|
64
|
-
* THE SHAPE IS JUDGED, NOT TRUSTED: {@link
|
|
65
|
+
* THE SHAPE IS JUDGED, NOT TRUSTED: {@link readToolAxes} is the one reader of this declaration (an A2A peer's `toolAxes` is read by it too). The
|
|
65
66
|
* preparation door runs it on every entry, and a declaration it cannot read refuses the whole prepare
|
|
66
67
|
* (`config.invalid_argument`) — one bad tool refuses the entry, a bad key is never dropped and a bad word
|
|
67
68
|
* never defaults, because a caller's safety axis that silently reads as absent is a trust root that is not
|
|
@@ -167,12 +168,17 @@ export interface McpServerSpec {
|
|
|
167
168
|
}
|
|
168
169
|
type ToolAxes = NonNullable<McpServerSpec["toolAxes"]>;
|
|
169
170
|
/**
|
|
170
|
-
* {@link
|
|
171
|
+
* The surfaces that declare `toolAxes`, which {@link readToolAxes} names its reading by: an MCP server's keys are the server's
|
|
172
|
+
* remote TOOL names, an A2A peer's keys are the peer's remote SKILL ids.
|
|
173
|
+
*/
|
|
174
|
+
export type ToolAxesSurface = "mcp" | "a2a";
|
|
175
|
+
/**
|
|
176
|
+
* {@link readToolAxes}'s answer. `ok: true` carries the declaration as a PLAIN COPY the reader built from
|
|
171
177
|
* what it judged (never the caller's own object); `toolAxes` is absent when the entry declares nothing — no
|
|
172
178
|
* key at all, or only empty per-tool objects (which the engine reads the same as no declaration). `ok: false`
|
|
173
|
-
* carries one sentence naming the tool and the member that did not fit, never a value.
|
|
179
|
+
* carries one sentence naming the tool (or skill) and the member that did not fit, never a value.
|
|
174
180
|
*/
|
|
175
|
-
export type
|
|
181
|
+
export type ToolAxesRead = {
|
|
176
182
|
ok: true;
|
|
177
183
|
toolAxes?: ToolAxes;
|
|
178
184
|
} | {
|
|
@@ -180,18 +186,82 @@ export type McpToolAxesRead = {
|
|
|
180
186
|
problem: string;
|
|
181
187
|
};
|
|
182
188
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
|
|
189
|
+
* Where a declaration stops being one: the path from the declaration's root — a list index as a number, a member name as a string — and
|
|
190
|
+
* the clause that says what is there ("it contains itself; …"): a kind and a remedy, never a value the declarer wrote.
|
|
191
|
+
*/
|
|
192
|
+
export interface DeclarationFault {
|
|
193
|
+
readonly path: readonly (string | number)[];
|
|
194
|
+
readonly problem: string;
|
|
195
|
+
}
|
|
196
|
+
/** {@link receiveDeclaration}'s answer: the engine's own frozen copy, or where and why the declaration could not be received. */
|
|
197
|
+
export type DeclarationRead = {
|
|
198
|
+
readonly ok: true;
|
|
199
|
+
readonly value: unknown;
|
|
200
|
+
} | {
|
|
201
|
+
readonly ok: false;
|
|
202
|
+
readonly fault: DeclarationFault;
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* A receipt's refusal of a declaration (or of the list that carries declarations), said as a declaration refusal says it. The receipt's
|
|
206
|
+
* path does not say which kind of container each name came from, so a segment of digits alone is read as a list index.
|
|
207
|
+
*/
|
|
208
|
+
export declare function declarationFaultOf(fault: ReceiptFault): DeclarationFault;
|
|
209
|
+
/** What a declaration's own type fixes at one path, beyond being plain data: a string there, or a record of names to strings. */
|
|
210
|
+
export interface DeclarationPathRule {
|
|
211
|
+
readonly path: readonly string[];
|
|
212
|
+
readonly holds: "a string" | "names to strings";
|
|
213
|
+
/** The member must be there (a declaration with no value at the path is refused, as one with a value of another kind is). */
|
|
214
|
+
readonly required?: true;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Per surface, every path its declaration's type fixes beyond plain data — the declarer's `name` (a string, always there) and every record
|
|
218
|
+
* the type writes as `Record<string, string>`: an MCP server's `transport.env` (a stdio server's environment) and `transport.headers` (an
|
|
219
|
+
* HTTP server's headers); an A2A peer's `headers`. A path that is not its surface's (an A2A peer's `transport`) is not judged by another
|
|
220
|
+
* surface's row: it is a member the surface does not read.
|
|
221
|
+
*/
|
|
222
|
+
export declare const DECLARATION_PATH_RULES: Readonly<Record<ToolAxesSurface, readonly DeclarationPathRule[]>>;
|
|
223
|
+
/**
|
|
224
|
+
* The one reading of a declaration a caller hands the engine — an MCP server's or an A2A peer's entry (the snapshot), a `toolAxes`
|
|
225
|
+
* record ({@link readToolAxes}), a lock declaration: the engine's one receipt of a caller's object in its declaration mode, so the
|
|
226
|
+
* value is plain data held to the engine's one depth ({@link MODS_VALUE_DEPTH} levels below it) and every own member is read once, then, when the copy is a record, the declaration's own
|
|
227
|
+
* vocabulary over the copy (a list holds names; no member is named `__proto__`; each path `rules` names holds what its rule says — an
|
|
228
|
+
* entry's rules are its surface's {@link DECLARATION_PATH_RULES}). The copy is the answer — the caller's object is never handed on and
|
|
229
|
+
* never read again. A copy that is not a record is answered as it is, for the reader that expected a record to word.
|
|
230
|
+
*/
|
|
231
|
+
export declare function receiveDeclaration(declared: unknown, rules?: readonly DeclarationPathRule[]): DeclarationRead;
|
|
232
|
+
/**
|
|
233
|
+
* The one reader of a raw `toolAxes` declaration — {@link McpServerSpec.toolAxes} and {@link A2aServerSpec.toolAxes} are the same
|
|
234
|
+
* declaration, and the engine's preparation door, its materialization of an MCP server and of an A2A peer, and every host that
|
|
235
|
+
* assembles the field from JSON ask this same function. `at` names the surface the declaration sits on, because that decides what its
|
|
236
|
+
* keys are called in the sentence: `"mcp"` — the server's remote tools; `"a2a"` — the peer's remote skills. Absent ⇒ `{ ok: true }`
|
|
237
|
+
* (nothing declared). Anything else must be an object of per-tool objects whose members are exactly `effect` /
|
|
186
238
|
* `egress` / `irreversibility` with legal values; a declaration of any other shape, an empty tool name, an
|
|
187
239
|
* unknown member, a word outside the vocabulary or a non-boolean `egress` on ANY one tool refuses the whole
|
|
188
240
|
* declaration (`ok: false`) — the good tools are not kept and nothing is defaulted, since a caller axis is
|
|
189
241
|
* two-way (it can vouch a tool down as well as mark it up) and a silently lost half is a trust root that is
|
|
190
242
|
* not in force. The reader judges SHAPE only; whether the axes contradict each other (`egress` over a
|
|
191
|
-
* non-write effect) is the ingestion's own arithmetic. The
|
|
192
|
-
*
|
|
243
|
+
* non-write effect) is the ingestion's own arithmetic. The declaration is read once, as plain data, by {@link receiveDeclaration} — a
|
|
244
|
+
* Proxy, a map, a class instance, a record built on a prototype or in another realm, a getter that throws, a structure that contains
|
|
245
|
+
* itself, a tool named `__proto__` are refusals that say where they sit (never the thrown text), not a throw — and judged on that copy.
|
|
246
|
+
* `at` is the caller's own word, not the declarer's: one outside {@link ToolAxesSurface} throws `config.invalid_argument` naming the
|
|
247
|
+
* surfaces, whatever is declared.
|
|
248
|
+
*/
|
|
249
|
+
export declare function readToolAxes(declared: unknown, at: ToolAxesSurface): ToolAxesRead;
|
|
250
|
+
/** {@link readToolAxes} for an MCP server's declaration — the entry a host that mounts MCP servers asks. */
|
|
251
|
+
export declare function readMcpToolAxes(declared: unknown): ToolAxesRead;
|
|
252
|
+
/**
|
|
253
|
+
* The word a sentence about one declaration of the surface `at` names the declarer by — `MCP server`, `A2A peer`. The same row the
|
|
254
|
+
* `toolAxes` refusal below reads, so every sentence the engine writes about a declaration it cannot read calls the declarer one thing.
|
|
255
|
+
*/
|
|
256
|
+
export declare function declarerWord(at: ToolAxesSurface): string;
|
|
257
|
+
/**
|
|
258
|
+
* The refusal an entry's unreadable `toolAxes` becomes (`config.invalid_argument`), ONE sentence for every surface that declares them:
|
|
259
|
+
* the subject is the surface's declarer word and `name` (already quoted and bounded by the caller: `MCP server "db"`, `A2A peer "agent"`),
|
|
260
|
+
* `problem` is {@link readToolAxes}'s own sentence.
|
|
193
261
|
*/
|
|
194
|
-
export declare function
|
|
262
|
+
export declare function toolAxesRefusal(at: ToolAxesSurface, name: string, problem: string): Error & {
|
|
263
|
+
code: "config.invalid_argument";
|
|
264
|
+
};
|
|
195
265
|
/** The identity of one mount of a declared server, as the host minted it ({@link McpServerSpec.fingerprint}). */
|
|
196
266
|
export interface McpMountIdentity {
|
|
197
267
|
readonly fingerprint: string;
|
|
@@ -242,6 +312,14 @@ export interface A2aServerSpec {
|
|
|
242
312
|
* raise it — e.g. `{ egress: false, effect: "read" }` vouches that a skill only reads. Note that
|
|
243
313
|
* lowering `effect` WITHOUT clearing `egress` is a contradiction prepare-task refuses by design (an
|
|
244
314
|
* external write must be repeat-unsafe).
|
|
315
|
+
*
|
|
316
|
+
* THE SHAPE IS JUDGED, NOT TRUSTED, by the same reader as {@link McpServerSpec.toolAxes} ({@link readToolAxes} with `at: "a2a"`) and
|
|
317
|
+
* with the same disposition: a task's preparation door runs it on every peer of `TaskSpec.a2a`, and a declaration it cannot read
|
|
318
|
+
* refuses the whole prepare (`config.invalid_argument`, subject `A2A peer "<name>"`) before any peer is dialed — one bad skill refuses
|
|
319
|
+
* the task, a bad key is never dropped and a bad word never defaults. The materialization entrance (`materializeA2aTools`, the library
|
|
320
|
+
* entrance) asks it once per peer, when it reads that peer's declaration: there a declaration it cannot read fails THAT peer's dial
|
|
321
|
+
* (never dialed, a warning whose cause is the same refusal), its siblings unaffected; a refresh does not ask again, it dials the copy
|
|
322
|
+
* that was judged (a peer refused at the entrance is refused again by every refresh, from the stored refusal).
|
|
245
323
|
*/
|
|
246
324
|
toolAxes?: Record<string, {
|
|
247
325
|
effect?: ToolEffect;
|