@vostride/agent-qa-mcp 0.2.3 → 0.2.4
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/dist/index.cjs +44 -607
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -766
- package/dist/index.d.ts +59 -766
- package/dist/index.js +45 -605
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,21 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { McpServer, McpRequestContext, StandardSchemaWithJSON, CallToolResult, RequestMetaEnvelope, ResourceTemplate, ResourceMetadata, ReadResourceTemplateCallback, Variables, ReadResourceResult, ToolAnnotations } from '@modelcontextprotocol/server';
|
|
2
2
|
import { FunctionRegistry } from '@vostride/agent-qa-agent';
|
|
3
|
-
import { FunctionCaller, RiskActionDescriptor
|
|
3
|
+
import { FunctionCaller, RiskActionDescriptor } from '@vostride/agent-qa-schema';
|
|
4
4
|
import * as _modelcontextprotocol_node from '@modelcontextprotocol/node';
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* The tokenless dashboard live page for one run: `<base>/runs/<runId>/live` (D-08).
|
|
8
|
-
*
|
|
9
|
-
* TRAILING SLASHES ARE COLLAPSED rather than tolerated. An operator writing
|
|
10
|
-
* `baseUrl: http://dash.internal/` is writing the same base as one who did not, and a `//` in the
|
|
11
|
-
* middle of a path is a different path to some proxies — so the composition normalises instead of
|
|
12
|
-
* producing two addresses for one dashboard.
|
|
13
|
-
*
|
|
14
|
-
* THE RUN ID IS PERCENT-ENCODED, which is a no-op for every id this system mints (`runIdSchema`
|
|
15
|
-
* refuses any id the path layer would have to rewrite) and is written anyway: this function is
|
|
16
|
-
* exported, and a caller composing a link for an id that came from somewhere else should not be the
|
|
17
|
-
* one place a path segment escapes unencoded.
|
|
18
|
-
*/
|
|
19
6
|
declare function liveRunLinkOf(base: string, runId: string): string;
|
|
20
7
|
/**
|
|
21
8
|
* The human-readable line a carrier result carries when a base exists.
|
|
@@ -30,33 +17,7 @@ declare function liveRunLinkOf(base: string, runId: string): string;
|
|
|
30
17
|
* read as broken the moment the run ended, which is precisely when a person follows it.
|
|
31
18
|
*/
|
|
32
19
|
declare function liveLinkLine(url: string): string;
|
|
33
|
-
/**
|
|
34
|
-
* What a carrier result says when no dashboard base exists ANYWHERE (RESEARCH Open Question 3).
|
|
35
|
-
*
|
|
36
|
-
* It names the config knob an operator sets to point this host at a dashboard (the 387 D-10
|
|
37
|
-
* mechanism). It contains no URL — an absence that fabricated an address would be worse than no
|
|
38
|
-
* line at all.
|
|
39
|
-
*/
|
|
40
20
|
declare const LIVE_LINK_ABSENT_LINE = "no dashboard configured \u2014 set services.dashboard.baseUrl";
|
|
41
|
-
/**
|
|
42
|
-
* What a carrier result says when a dashboard base EXISTS but this result names no run to link to
|
|
43
|
-
* (2026-09-01, Phase 408 plan 10, WR-03 · T-408-22).
|
|
44
|
-
*
|
|
45
|
-
* A SECOND NAMED ABSENCE, BECAUSE THERE ARE TWO DISTINCT FACTS. `carrierRunIdOf`
|
|
46
|
-
* (`catalog.ts`) is DEFENSIVE ON BOTH ARMS by design and can honestly answer `undefined` — the
|
|
47
|
-
* `runIdFrom: 'output'` arm reads an op's own result, which is a `RunStartAcknowledgement` today
|
|
48
|
-
* and could be a fold's arm tomorrow, and the documented treatment of a shape it cannot read is a
|
|
49
|
-
* named absence rather than a crash. Until this constant existed, that arm printed
|
|
50
|
-
* `LIVE_LINK_ABSENT_LINE` — so a host that HAS `services.dashboard.baseUrl` set told the caller,
|
|
51
|
-
* and the model reading the result, to set a value already set and pointed at the wrong knob. That
|
|
52
|
-
* is a FALSE STATEMENT on a model-readable channel in a milestone whose law is honest absence, and
|
|
53
|
-
* a wrong sentence is worse than a vague one because a model will act on it.
|
|
54
|
-
*
|
|
55
|
-
* IT NAMES NO CONFIG KNOB, deliberately: there is nothing for the operator to set. The dashboard is
|
|
56
|
-
* configured; this particular result simply is not about a run. And it CONTAINS NO URL, the same
|
|
57
|
-
* property `LIVE_LINK_ABSENT_LINE` above is held to — an absence that fabricated an address would
|
|
58
|
-
* be the dishonesty this file exists to remove.
|
|
59
|
-
*/
|
|
60
21
|
declare const LIVE_LINK_NO_RUN_LINE = "no run to link to \u2014 this result names no run id";
|
|
61
22
|
/**
|
|
62
23
|
* How a HOST answers "which dashboard is this process pointed at".
|
|
@@ -73,56 +34,75 @@ declare const LIVE_LINK_NO_RUN_LINE = "no run to link to \u2014 this result name
|
|
|
73
34
|
interface LiveLinkSupplier {
|
|
74
35
|
readonly dashboardBaseUrl: () => string | undefined;
|
|
75
36
|
}
|
|
37
|
+
declare function liveLinkLineFor(base: string | undefined, runId: string | undefined): string;
|
|
38
|
+
|
|
39
|
+
declare const AGENT_QA_MCP_SERVER_VERSION = "2.0.0";
|
|
40
|
+
/** What a host must supply to build a server for one caller. */
|
|
41
|
+
interface AgentQaMcpServerDeps {
|
|
42
|
+
readonly registry: FunctionRegistry;
|
|
43
|
+
readonly caller: FunctionCaller;
|
|
44
|
+
readonly maskText: (text: string) => string;
|
|
45
|
+
readonly liveLink?: LiveLinkSupplier;
|
|
46
|
+
}
|
|
47
|
+
declare function buildAgentQaMcpServer(deps: AgentQaMcpServerDeps): McpServer;
|
|
48
|
+
|
|
49
|
+
declare const AGENT_QA_MCP_INSTRUCTIONS: string;
|
|
50
|
+
|
|
51
|
+
type AgentQaMcpDepsResolver = (ctx: McpRequestContext) => AgentQaMcpServerDeps;
|
|
52
|
+
declare function createAgentQaMcpNodeHandler(resolveDeps: AgentQaMcpDepsResolver): _modelcontextprotocol_node.NodeMcpRequestHandler;
|
|
53
|
+
|
|
54
|
+
/** A JSON-Schema document as it travels: an ordinary object, with nothing runnable on it. */
|
|
55
|
+
type JsonDocument = Record<string, unknown>;
|
|
76
56
|
/**
|
|
77
|
-
*
|
|
57
|
+
* Drop `$schema`, which the SDK's conversion stamps and the registry's projection does not.
|
|
78
58
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
59
|
+
* A DIALECT MARKER, not content: it says which JSON Schema draft the document speaks and describes
|
|
60
|
+
* nothing about the contract. Removing it is the one normalisation this file performs, and it is
|
|
61
|
+
* called out rather than buried because a normalisation that removed content would be the way to
|
|
62
|
+
* make a drift cell pass over real drift.
|
|
63
|
+
*/
|
|
64
|
+
declare function withoutDialect(document: unknown): JsonDocument;
|
|
65
|
+
/**
|
|
66
|
+
* Drop the `type: "object"` the SDK stamps onto a COMPOSITION root.
|
|
82
67
|
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
68
|
+
* The second and last normalisation, and it is as narrow as the first. `standardSchemaToJsonSchema`
|
|
69
|
+
* stamps `type: "object"` on a typeless root it can prove is object-shaped — a `oneOf` whose every
|
|
70
|
+
* member is `type: "object"`, which is exactly what a zod discriminated union emits. The SDK's own
|
|
71
|
+
* comment calls the stamp "redundant-but-valid", and a document is not changed by adding a
|
|
72
|
+
* constraint every one of its branches already imposes.
|
|
86
73
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* that arm answers `LIVE_LINK_NO_RUN_LINE`. Swapping these two lines back reinstates the false
|
|
92
|
-
* sentence a configured host used to receive.
|
|
74
|
+
* IT IS GATED ON THE COMPOSITION KEYWORD, so a genuine `type` disagreement still reds: the only
|
|
75
|
+
* case dropped is a root that carries `oneOf`/`anyOf` AND the stamp, which is the one case the SDK
|
|
76
|
+
* manufactures. An ungated `delete document.type` would have made the whole drift cell blind to a
|
|
77
|
+
* contract whose root type actually moved.
|
|
93
78
|
*/
|
|
94
|
-
declare function
|
|
79
|
+
declare function stripRedundantObjectStamp(document: JsonDocument): JsonDocument;
|
|
80
|
+
/**
|
|
81
|
+
* One union arm of a fold's composed contract, with the fold's discriminant taken back out.
|
|
82
|
+
*
|
|
83
|
+
* `required: []` is DROPPED rather than kept, and the case is real rather than defensive:
|
|
84
|
+
* `config.undo.propose` takes an all-optional input, so its own projection carries no `required`
|
|
85
|
+
* key at all while the extended arm carries `['action']` — and removing the discriminant from that
|
|
86
|
+
* leaves an empty array where the other side has nothing. JSON Schema reads an absent `required`
|
|
87
|
+
* and an empty one identically, so the two documents agree; keeping the empty array would have
|
|
88
|
+
* failed the cell on a difference that describes no difference.
|
|
89
|
+
*/
|
|
90
|
+
declare function armWithoutDiscriminant(arm: JsonDocument, discriminant: string): JsonDocument;
|
|
91
|
+
declare function foldArmFindings(listed: JsonDocument, discriminant: string, opId: string, own: JsonDocument): readonly string[];
|
|
92
|
+
declare function withoutLegacyResultWrap(document: unknown): JsonDocument;
|
|
95
93
|
|
|
96
94
|
/**
|
|
97
95
|
* What a tool DOES, coarsely — the axis annotations are derived along (`projection.ts`).
|
|
98
96
|
*
|
|
99
97
|
* `read` answers; `workflow` changes something in the call (a test file, a run, knowledge,
|
|
100
|
-
* configuration). `apply`
|
|
101
|
-
*
|
|
102
|
-
* mark's gold, evidence and fix in the call. Its credential check left with the MCP bearer
|
|
103
|
-
* credential (432-21, D-12), so `apply` is now a name for that one door and gates nothing.
|
|
104
|
-
*
|
|
105
|
-
* KEPT, DATED 2026-10-02 (432-42 census decision): the member is not renamed. It is a published MCP
|
|
106
|
-
* contract value (`mapping-bump-guard.test.ts` pins `kind: 'apply'` on `agent_qa_mark_run`, and the
|
|
107
|
-
* projection derives annotations from it); no D-decision renames a published name here, and the
|
|
108
|
-
* word now means "the mark door applies its fix in the call", which is true. It asks nothing.
|
|
98
|
+
* configuration). `apply` names the mark door, which records the mark and applies its fix in the
|
|
99
|
+
* call and asks nothing.
|
|
109
100
|
*/
|
|
110
101
|
type ToolProjectionKind = 'read' | 'workflow' | 'apply';
|
|
111
|
-
/**
|
|
112
|
-
* How a tool's arms compose — DERIVED from the entry, never stated a second time on it.
|
|
113
|
-
*
|
|
114
|
-
* `single` is a 1:1 projection. `union` is D-01's declared fold: several sibling ops behind one
|
|
115
|
-
* discriminated-union input, the arm selected by a literal. `pipeline` is D-02's one read door:
|
|
116
|
-
* two ops invoked in sequence, the first one's answer choosing whether the second runs at all.
|
|
117
|
-
*
|
|
118
|
-
* A third field on the entry would have been a third thing to keep in step with the first two, and
|
|
119
|
-
* the failure it would have hidden is a fold declared `single` — which registers a tool advertising
|
|
120
|
-
* one arm's contract while the guard reports two arms and agrees with itself.
|
|
121
|
-
*/
|
|
122
102
|
type ToolComposition = 'single' | 'union' | 'pipeline';
|
|
123
103
|
/** One declared tool. */
|
|
124
104
|
interface ToolProjection {
|
|
125
|
-
/** Curated `verb_noun` name per spec section 3.3
|
|
105
|
+
/** Curated `verb_noun` name per spec section 3.3. */
|
|
126
106
|
readonly name: string;
|
|
127
107
|
/** The registry op ids this tool projects. Length 1 = 1:1; length > 1 = a declared fold. */
|
|
128
108
|
readonly ops: readonly string[];
|
|
@@ -134,70 +114,14 @@ interface ToolProjection {
|
|
|
134
114
|
* than assumed from `ops.length > 1`.
|
|
135
115
|
*/
|
|
136
116
|
readonly discriminant?: string;
|
|
137
|
-
/**
|
|
138
|
-
* This tool CARRIES the live-view MCP App (2026-08-31, Phase 408 plan 01 · D-05 / MCPA-01).
|
|
139
|
-
*
|
|
140
|
-
* ITS PRESENCE IS THE SIGNAL, exactly as `discriminant`'s above is. A carrier is a tool whose
|
|
141
|
-
* declaration references the `ui://` App resource, so a host that negotiated the Apps extension
|
|
142
|
-
* can render the run's live view beside the result; a tool without this member references
|
|
143
|
-
* nothing, and `projection.ts` derives an ABSENT `_meta` for it rather than an empty one. Spelled
|
|
144
|
-
* per entry rather than inferred from a name, a kind or an op id, because the census that
|
|
145
|
-
* enumerates carriers reads THIS field — and a predicate over names would be a second, unwritten
|
|
146
|
-
* declaration of which doors reach a credential channel (the 399.1 lesson).
|
|
147
|
-
*
|
|
148
|
-
* `resource` names which `ui://` template the reference points at; one template exists today and
|
|
149
|
-
* the field is spelled anyway, so a second App is an added member here rather than a second
|
|
150
|
-
* meaning for a boolean.
|
|
151
|
-
*
|
|
152
|
-
* `runIdFrom` names where the CONCRETE runId for a per-call reference is found — on the call's
|
|
153
|
-
* input or in the op's own result. `agent_qa_start_run` answers a `RunStartAcknowledgement`
|
|
154
|
-
* carrying the id the run was given, so its reference is derivable only from the OUTPUT;
|
|
155
|
-
* `agent_qa_get_run_digest` is asked about a run by id, so its reference is derivable from the
|
|
156
|
-
* INPUT, which is what makes it the late-attach door (D-07).
|
|
157
|
-
*
|
|
158
|
-
* THE TASK-SHAPE QUESTION D-05 LEFT OPEN IS CLOSED, and closed by measurement rather than by
|
|
159
|
-
* preference: the task seam is dormant — "It registers nothing, advertises nothing and routes
|
|
160
|
-
* nothing" (task-seam.ts:21-23, and catalog.ts:34-36 states the matching absence) — so both
|
|
161
|
-
* carriers are plain tools answering immediate results, and there is no task completion or
|
|
162
|
-
* status poll for a reference to ride. (2026-08-31; the 2.0.0 core SDK has since grown the Tasks
|
|
163
|
-
* vocabulary, which makes the seam's own dated deferral stale at the SDK layer and changes
|
|
164
|
-
* nothing here: registering Tasks is not this phase's job.)
|
|
165
|
-
*/
|
|
166
117
|
readonly ui?: {
|
|
167
118
|
readonly resource: 'live-run';
|
|
168
119
|
readonly runIdFrom: 'input' | 'output';
|
|
169
120
|
};
|
|
170
|
-
/**
|
|
171
|
-
* 2026-09-28 (431 D-05): the result-size hint, advertised as `_meta[MAX_RESULT_SIZE_META_KEY]`.
|
|
172
|
-
* Present only on reads whose one-shot answer can outgrow a host's default result budget.
|
|
173
|
-
*/
|
|
174
121
|
readonly maxResultSizeChars?: number;
|
|
175
122
|
}
|
|
176
123
|
/** The composition, read off the entry. See {@link ToolComposition}. */
|
|
177
124
|
declare function compositionOf(projection: ToolProjection): ToolComposition;
|
|
178
|
-
/**
|
|
179
|
-
* The live input/output contracts of one projected op.
|
|
180
|
-
*
|
|
181
|
-
* WHY THIS TABLE EXISTS AT ALL, since it looks like a second declaration of something the registry
|
|
182
|
-
* already knows. It is not: `FunctionSpec` — the record holding the LIVE validators — deliberately
|
|
183
|
-
* never leaves the function layer's own directory (T-384-01 / T-384-14), and what a surface can see
|
|
184
|
-
* through `registry.list(caller)` is a `FunctionDeclaration` whose `input`/`output` are already
|
|
185
|
-
* INERT JSON-Schema projections. D-16 mechanic (a) needs the live Standard-Schema objects, so the
|
|
186
|
-
* projection names them here, imported from the ONE module the ops themselves import them from.
|
|
187
|
-
* Drift is therefore impossible in the only sense that matters: this is not a copy of the contract,
|
|
188
|
-
* it is the same object, so the schema advertised in `tools/list` and the schema `invokeFunction`
|
|
189
|
-
* validates with are one thing that cannot disagree.
|
|
190
|
-
*
|
|
191
|
-
* The alternative — `fromJsonSchema(declaration.output)` — was available and rejected: it is a
|
|
192
|
-
* SECOND conversion recipe beside the SDK's own, and the anti-pattern this phase's research names
|
|
193
|
-
* first is exactly two recipes for one conversion.
|
|
194
|
-
*
|
|
195
|
-
* The field type is the SDK's `StandardSchemaWithJSON` and NOT a zod type, which is the same
|
|
196
|
-
* decision seen from the other side: this package's runtime dependency allow-list is exactly the
|
|
197
|
-
* two protocol packages (D-20), so naming a validator library here — even in a type position, which
|
|
198
|
-
* the dependency lint reads no differently — would reopen the collapse the phase exists to make.
|
|
199
|
-
* A zod 4.4 schema satisfies the interface, which is the whole of what either side needs to know.
|
|
200
|
-
*/
|
|
201
125
|
interface ToolOpContracts {
|
|
202
126
|
readonly input: StandardSchemaWithJSON;
|
|
203
127
|
readonly output: StandardSchemaWithJSON;
|
|
@@ -209,307 +133,25 @@ interface ToolOpContracts {
|
|
|
209
133
|
* drift apart silently.
|
|
210
134
|
*/
|
|
211
135
|
declare const AGENT_QA_OP_CONTRACTS: Readonly<Record<string, ToolOpContracts>>;
|
|
212
|
-
/**
|
|
213
|
-
* THE CATALOGUE. Twenty tools, and the bump guard pins this exact list by name and by op set.
|
|
214
|
-
* (Sixteen until 2026-09-28, when 431 D-01 added the eight discovery reads; twenty-four until
|
|
215
|
-
* 2026-09-30, when 432-12 removed the four proposal tools.)
|
|
216
|
-
*
|
|
217
|
-
* The read side is D-02: one universal entity door plus the reads a workflow needs mid-flight and
|
|
218
|
-
* the 431 discovery reads. The write side changes things IN THE CALL (432 D-05/D-14): every
|
|
219
|
-
* `workflow` entry writes what it names and answers what it changed, and nothing waits for a second
|
|
220
|
-
* call. There is no apply tool, no `confirmed` input and no elicitation; the mark door is the one
|
|
221
|
-
* `apply` entry, and since 432-21 (D-12) it checks no credential.
|
|
222
|
-
*
|
|
223
|
-
* WHY THERE IS NO PROPOSAL TOOL AT ALL. The inbox's verbs declared a human-origin requirement, so a
|
|
224
|
-
* tool over either decision verb would have listed for every caller and refused every call; the
|
|
225
|
-
* bump guard stated the rule generally until 432-39 deleted the field. 432-12 unprojected the whole
|
|
226
|
-
* inbox and 432-17 deleted it, ops and all (re-spelled 2026-10-02, 432-42).
|
|
227
|
-
*/
|
|
228
136
|
declare const AGENT_QA_TOOL_PROJECTIONS: readonly ToolProjection[];
|
|
229
|
-
/**
|
|
230
|
-
* Tool name -> the contract that tool ADVERTISES, where it is not simply its one op's contract.
|
|
231
|
-
*
|
|
232
|
-
* Exactly two reasons an entry exists here, and no third:
|
|
233
|
-
*
|
|
234
|
-
* 1. A UNION FOLD validates with the discriminated union of its arms' own DTOs, composed in
|
|
235
|
-
* `@vostride/agent-qa-schema` (see `functions/tool-folds.ts` for why it is composed there and
|
|
236
|
-
* not here). Its output is the union of the arms' own outputs, collapsed when they are one.
|
|
237
|
-
* Its input is listed as one object by `projection.ts` (2026-10-20, 446-11).
|
|
238
|
-
* 2. THE PIPELINE advertises the FIRST op's contract on both sides: a caller hands `entities.resolve`
|
|
239
|
-
* the reference it has, and reads back the resolution union — a card, a small disambiguation
|
|
240
|
-
* set, or an honest miss. `entities.get` runs inside the resolved arm and contributes no shape
|
|
241
|
-
* of its own, which is why its contract is absent from the advertisement.
|
|
242
|
-
*
|
|
243
|
-
* 2026-10-01 (432-21, D-12): THE MARK DOOR'S ENTRY IS DELETED. It advertised the op's outcome OR the
|
|
244
|
-
* credential refusal (419 D-20); the refusal left with the MCP bearer credential, so the door
|
|
245
|
-
* advertises exactly its op's own contracts and needs no entry. (The D-11 apply envelope it once
|
|
246
|
-
* carried, `proposal_id` and `confirmed`, went with the apply tools in 432-12, D-05/D-14.)
|
|
247
|
-
*/
|
|
248
137
|
declare const AGENT_QA_COMPOSED_CONTRACTS: Readonly<Record<string, ToolOpContracts>>;
|
|
249
138
|
/** The contract a tool advertises: its composition when it has one, its one op's otherwise. */
|
|
250
139
|
declare function advertisedContracts(projection: ToolProjection): ToolOpContracts | undefined;
|
|
251
|
-
/**
|
|
252
|
-
* A tool whose advertised OUTPUT cannot be represented as JSON Schema, and therefore ships with no
|
|
253
|
-
* `outputSchema` at all (2026-08-16, D-16's explicit-degradation clause).
|
|
254
|
-
*
|
|
255
|
-
* MEASURED, NOT PREDICTED. `tools/list` converts every `outputSchema` in zod's OUTPUT mode
|
|
256
|
-
* (`mcp-DXXb3Vv3.mjs:1391`, uncaught), and a contract built over a branded id — `z.string()
|
|
257
|
-
* .transform(asRunId)` — makes that conversion throw `Transforms cannot be represented in JSON
|
|
258
|
-
* Schema`. The consequence is not a poorer listing for one tool: the throw takes the WHOLE
|
|
259
|
-
* `tools/list` response down for every caller who can see the tool. Phase 384 hit the same wall from
|
|
260
|
-
* the other side and answered it by projecting both sides in input mode (`declaration-projection.ts`).
|
|
261
|
-
*
|
|
262
|
-
* WHY OMISSION RATHER THAN A SECOND CONVERSION. Advertising the INPUT-mode document as the output
|
|
263
|
-
* would have worked and been a lie of exactly the kind D-16 forbids: a schema derived by a second
|
|
264
|
-
* recipe, no longer the object `invokeFunction` validates with, presented as though it were. What a
|
|
265
|
-
* caller gets instead is `content` plus `structuredContent` and no advertised shape — less, and
|
|
266
|
-
* true. `validateToolOutput` skips a tool with no `outputSchema`, so the result still flows.
|
|
267
|
-
*
|
|
268
|
-
* THE LIST IS TWO-SIDED. `projection.ts` PROBES the conversion for every tool: an undeclared
|
|
269
|
-
* failure throws, and a declared entry whose contract has since become convertible throws too. So
|
|
270
|
-
* this list cannot silently grow and cannot silently rot.
|
|
271
|
-
*
|
|
272
|
-
* 2026-09-28 (431-03): the way off this list is a canonical `OutputContract` from
|
|
273
|
-
* `@vostride/agent-qa-schema` — the op's own runtime schema as validator, with an explicit output
|
|
274
|
-
* document — never a narrowed stand-in authored here. `agent_qa_start_run` keeps its row.
|
|
275
|
-
*/
|
|
276
140
|
interface OutputSchemaDegradation {
|
|
277
141
|
readonly tool: string;
|
|
278
142
|
readonly opId: string;
|
|
279
143
|
/** The schema symbol whose transform the converter refused, for a reader chasing the cause. */
|
|
280
144
|
readonly schema: string;
|
|
281
|
-
/** The converter's own message, verbatim
|
|
145
|
+
/** The converter's own message, verbatim. */
|
|
282
146
|
readonly measured: string;
|
|
283
|
-
readonly adjudication: '397';
|
|
284
147
|
}
|
|
285
148
|
declare const DEGRADED_OUTPUT_CONTRACTS: readonly OutputSchemaDegradation[];
|
|
286
|
-
/**
|
|
287
|
-
* A registry op that this surface deliberately does NOT project, and why (2026-08-16 · D-03/D-15).
|
|
288
|
-
*
|
|
289
|
-
* THE DATED SCOPING NOTE IS THIS TABLE, not a comment beside it. 397's parity suite ranges over the
|
|
290
|
-
* projected set, and a suite that scoped itself to whatever happened to be in the mapping would be
|
|
291
|
-
* discovering an accident rather than honouring a decision. Every op the registry declares appears
|
|
292
|
-
* either above or here — the bump guard asserts that partition against the registry's own
|
|
293
|
-
* enumeration, so a new op cannot land undispositioned.
|
|
294
|
-
*
|
|
295
|
-
* `agent.ask` LEADS THE LIST because its exclusion is the one with a decision behind it (D-03).
|
|
296
|
-
* Projecting the agent's own turn entry over MCP is nested-agent delegation: a caller's tool call
|
|
297
|
-
* would spend this project's model tokens, take the lease the local host holds, and stream through a
|
|
298
|
-
* channel a single `tools/call` round trip has no shape for — four independent reasons, and no MCPS
|
|
299
|
-
* requirement asking for any of them. It is deferred as its own seam rather than left out by
|
|
300
|
-
* oversight, and the deferral is dated so a later reader can tell the two apart.
|
|
301
|
-
*
|
|
302
|
-
* The rest are D-02's line — this surface carries the reads a workflow needs MID-FLIGHT and not the
|
|
303
|
-
* whole catalogue — and the two ops that cannot serve an agent-origin caller at all.
|
|
304
|
-
*/
|
|
305
149
|
interface UnprojectedOp {
|
|
306
150
|
readonly opId: string;
|
|
307
151
|
readonly reason: string;
|
|
308
|
-
/**
|
|
309
|
-
* Who decided this absence is not a gap.
|
|
310
|
-
*
|
|
311
|
-
* 397 for every op that existed when 397's parity walk ruled the whole register (D-15). WIDENED
|
|
312
|
-
* to admit `'398'` on 2026-08-17 for `agent.explain`, and the widening is the honest move rather
|
|
313
|
-
* than a convenience: 397 could not have adjudicated an op that did not exist, and stamping this
|
|
314
|
-
* one `'397'` to keep a literal green would have made the field record a ruling nobody made — the
|
|
315
|
-
* one thing a provenance column is for. The vocabulary is a CLOSED set and the bump guard pins
|
|
316
|
-
* which entries carry which adjudicator by name, so it cannot widen again by habit.
|
|
317
|
-
*
|
|
318
|
-
* 405-05 adds `runs.artifact` under the SAME `'405'` stamp on the same day and by the same
|
|
319
|
-
* ruling; the vocabulary does not widen for it.
|
|
320
|
-
*
|
|
321
|
-
* WIDENED AGAIN on 2026-08-27 (405-01, D-03) for `runs.detail`, on exactly the 398 and 403
|
|
322
|
-
* reasoning and for no new one: 397 could not have ruled on an op registered eight phases
|
|
323
|
-
* later. The bump guard names which entry carries this adjudicator, in both directions, so an
|
|
324
|
-
* entry stamped `'405'` because it was easier reds there rather than joining a set that has
|
|
325
|
-
* quietly become "any phase number".
|
|
326
|
-
*
|
|
327
|
-
* WIDENED AGAIN on 2026-08-29 (407-01, D-05) for `runs.knowledge`, on exactly that reasoning and
|
|
328
|
-
* for no new one: 397 could not have ruled on an op registered ten phases later. Membership is
|
|
329
|
-
* still spelled by op id in the bump guard, in both directions.
|
|
330
|
-
*
|
|
331
|
-
* RE-STAMPED on 2026-08-31 (408 D-01): `'403'` LEAVES this vocabulary and `'408'` enters. This is
|
|
332
|
-
* not a widening and not a rename — it is the provenance moving with the ruling. `runs.liveurl`
|
|
333
|
-
* was the ONLY `'403'` entry, and its 403 stamp recorded a DEFERRAL ("Phase 408's question");
|
|
334
|
-
* 408 D-01 answered that question, so the phase that decided this absence is now 408 and saying
|
|
335
|
-
* `'403'` would credit 403 with a ruling it explicitly declined to make. Leaving `'403'` in a
|
|
336
|
-
* closed set with no member would also be exactly the empty literal the bump guard's
|
|
337
|
-
* non-vacuity check passes silently over, which is the hole that check exists to close.
|
|
338
|
-
*/
|
|
339
|
-
readonly adjudication: UnprojectedAdjudication;
|
|
340
152
|
}
|
|
341
|
-
/** The closed adjudicator vocabulary as a runtime tuple; the type above is derived from it. */
|
|
342
|
-
declare const UNPROJECTED_ADJUDICATIONS: readonly ["397", "398", "405", "408", "428"];
|
|
343
|
-
type UnprojectedAdjudication = (typeof UNPROJECTED_ADJUDICATIONS)[number];
|
|
344
153
|
declare const UNPROJECTED_OPS: readonly UnprojectedOp[];
|
|
345
154
|
|
|
346
|
-
/** Which of D-13's three closed answers a retired v1 name gets. */
|
|
347
|
-
type V1MigrationArm = 'renamed-to' | 'retired' | 'pending';
|
|
348
|
-
/** One retired v1 tool name and its ruled disposition. */
|
|
349
|
-
interface V1ToolMigrationEntry {
|
|
350
|
-
/** The v1 tool name, verbatim from the inventory's tool table. */
|
|
351
|
-
readonly v1Name: string;
|
|
352
|
-
readonly arm: V1MigrationArm;
|
|
353
|
-
/** The live v2 tool that carries this capability. Present ONLY on `renamed-to`. */
|
|
354
|
-
readonly v2Tool?: string;
|
|
355
|
-
/** The inventory's ruled reason, dated. Never empty. */
|
|
356
|
-
readonly note: string;
|
|
357
|
-
/** What to call instead. Present on `retired` and `pending`; a `renamed-to` row's route is its tool. */
|
|
358
|
-
readonly route?: string;
|
|
359
|
-
/** The milestone that owns the absence. Present ONLY on `pending`. */
|
|
360
|
-
readonly owner?: string;
|
|
361
|
-
}
|
|
362
|
-
/**
|
|
363
|
-
* THE MAP. One entry per v1 tool, in the inventory's original row order.
|
|
364
|
-
*
|
|
365
|
-
* The order is the inventory's rather than alphabetical, and the bump guard pins the SORTED set
|
|
366
|
-
* separately — order is a reading convenience here and a pinned fact nowhere, which is why nothing
|
|
367
|
-
* below depends on it.
|
|
368
|
-
*/
|
|
369
|
-
declare const V1_TOOL_MIGRATION_MAP: readonly V1ToolMigrationEntry[];
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* The version this server reports.
|
|
373
|
-
*
|
|
374
|
-
* A constant here and NOT the package version read off disk: reading `package.json` at runtime is a
|
|
375
|
-
* host capability this package does not have and must not acquire (its runtime dependency allow-list
|
|
376
|
-
* is the two protocol packages, D-20). Plan 05 decides whether the host should pass its own version
|
|
377
|
-
* in, which is the only place that fact honestly lives.
|
|
378
|
-
*/
|
|
379
|
-
declare const AGENT_QA_MCP_SERVER_VERSION = "2.0.0";
|
|
380
|
-
/** What a host must supply to build a server for one caller. */
|
|
381
|
-
interface AgentQaMcpServerDeps {
|
|
382
|
-
/** The composed function registry. Built by the HOST, never by this package (D-09). */
|
|
383
|
-
readonly registry: FunctionRegistry;
|
|
384
|
-
/**
|
|
385
|
-
* WHO this server answers as. Minted by the composition root and passed through untouched to
|
|
386
|
-
* `invokeFunction`. Since 432-20 (D-12) nothing here reads it to decide what to list or serve;
|
|
387
|
-
* its `surface` and `origin` reach only the receipts that already consume them. Since 432-21 no
|
|
388
|
-
* field of it is a credential this package reads.
|
|
389
|
-
*/
|
|
390
|
-
readonly caller: FunctionCaller;
|
|
391
|
-
/**
|
|
392
|
-
* How text leaving this process is masked (threat T-396-01).
|
|
393
|
-
*
|
|
394
|
-
* REQUIRED, not optional, and the reason is a measured one rather than a preference: the defect
|
|
395
|
-
* class this replaces (T-395-R5-10, closed in 395's final round) was a credential reaching an MCP
|
|
396
|
-
* caller verbatim through an error serialization. An optional masker is absent exactly where
|
|
397
|
-
* nobody thought to pass one, which is the same place the leak was found.
|
|
398
|
-
*/
|
|
399
|
-
readonly maskText: (text: string) => string;
|
|
400
|
-
/**
|
|
401
|
-
* Which dashboard this process is pointed at, for D-08's permanent link (408 plan 03).
|
|
402
|
-
*
|
|
403
|
-
* OPTIONAL, AND ITS ABSENCE IS THE DEGENERATE CASE RATHER THAN A SECOND BRANCH: a host that
|
|
404
|
-
* supplies nothing and a supplier that answers `undefined` are the same fact — no dashboard is
|
|
405
|
-
* configured and none is running here — and both take the same path to the same named-absence
|
|
406
|
-
* line. There is no arm below that asks which of the two happened, deliberately: a distinction
|
|
407
|
-
* nothing acts on is a distinction that eventually acquires a wrong action.
|
|
408
|
-
*
|
|
409
|
-
* A FACTORY, NOT A STRING, because on the co-hosted composition the bound origin does not exist
|
|
410
|
-
* until a port has been bound — `commands/dashboard.ts` composes this host before it listens.
|
|
411
|
-
*/
|
|
412
|
-
readonly liveLink?: LiveLinkSupplier;
|
|
413
|
-
}
|
|
414
|
-
/**
|
|
415
|
-
* Build the server this caller sees.
|
|
416
|
-
*
|
|
417
|
-
* The listing is read through `registry.list(deps.caller)` — the registry's own view of itself for
|
|
418
|
-
* this host — so `summary` and `risk` describe the composition in front of the caller rather than a
|
|
419
|
-
* table in this package that could describe a different one. The view is the same for every caller
|
|
420
|
-
* (432-20, D-12).
|
|
421
|
-
*/
|
|
422
|
-
declare function buildAgentQaMcpServer(deps: AgentQaMcpServerDeps): McpServer;
|
|
423
|
-
|
|
424
|
-
declare const AGENT_QA_MCP_INSTRUCTIONS: string;
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* How a host composes the server for one request.
|
|
428
|
-
*
|
|
429
|
-
* It takes the SDK's own construction context, so a per-request fact such as `era` arrives here
|
|
430
|
-
* rather than being read back out of a header inside a tool handler. What it returns is the same
|
|
431
|
-
* `deps` a stdio host composes, which is what keeps ONE `buildAgentQaMcpServer` shape across both
|
|
432
|
-
* transports. 2026-10-02 (432-42, D-12): nothing about the caller decides what it may see; the
|
|
433
|
-
* shipped host passes one fixed tokenless caller.
|
|
434
|
-
*/
|
|
435
|
-
type AgentQaMcpDepsResolver = (ctx: McpRequestContext) => AgentQaMcpServerDeps;
|
|
436
|
-
/**
|
|
437
|
-
* A Node request handler serving this catalog over stateless streamable HTTP.
|
|
438
|
-
*
|
|
439
|
-
* The factory is re-run per request, which is the SDK's own model and not an inefficiency to route
|
|
440
|
-
* around: a per-request instance is what makes a caller's tool set a property of that caller rather
|
|
441
|
-
* than of whoever connected first, and it is why absence stays structural on this transport too.
|
|
442
|
-
*
|
|
443
|
-
* IT TAKES A RESOLVER AND NOT A FIXED `deps` (plan 05). 2026-10-02 (432-42, D-12/D-13): the
|
|
444
|
-
* bearer check that once sat in front of this handler is deleted with MCP tokens (432-21); what
|
|
445
|
-
* stays in front of it is the host's Host and Origin guard on the loopback listener. The resolver
|
|
446
|
-
* receives the SDK context and builds the instance; it reads no header and verifies nothing.
|
|
447
|
-
*
|
|
448
|
-
* A host with one fixed caller passes `() => deps` — the static case is the degenerate resolver,
|
|
449
|
-
* not a second signature to keep in step.
|
|
450
|
-
*/
|
|
451
|
-
declare function createAgentQaMcpNodeHandler(resolveDeps: AgentQaMcpDepsResolver): _modelcontextprotocol_node.NodeMcpRequestHandler;
|
|
452
|
-
|
|
453
|
-
/** A JSON-Schema document as it travels: an ordinary object, with nothing runnable on it. */
|
|
454
|
-
type JsonDocument = Record<string, unknown>;
|
|
455
|
-
/**
|
|
456
|
-
* Drop `$schema`, which the SDK's conversion stamps and the registry's projection does not.
|
|
457
|
-
*
|
|
458
|
-
* A DIALECT MARKER, not content: it says which JSON Schema draft the document speaks and describes
|
|
459
|
-
* nothing about the contract. Removing it is the one normalisation this file performs, and it is
|
|
460
|
-
* called out rather than buried because a normalisation that removed content would be the way to
|
|
461
|
-
* make a drift cell pass over real drift.
|
|
462
|
-
*/
|
|
463
|
-
declare function withoutDialect(document: unknown): JsonDocument;
|
|
464
|
-
/**
|
|
465
|
-
* Drop the `type: "object"` the SDK stamps onto a COMPOSITION root.
|
|
466
|
-
*
|
|
467
|
-
* The second and last normalisation, and it is as narrow as the first. `standardSchemaToJsonSchema`
|
|
468
|
-
* stamps `type: "object"` on a typeless root it can prove is object-shaped — a `oneOf` whose every
|
|
469
|
-
* member is `type: "object"`, which is exactly what a zod discriminated union emits. The SDK's own
|
|
470
|
-
* comment calls the stamp "redundant-but-valid", and a document is not changed by adding a
|
|
471
|
-
* constraint every one of its branches already imposes.
|
|
472
|
-
*
|
|
473
|
-
* IT IS GATED ON THE COMPOSITION KEYWORD, so a genuine `type` disagreement still reds: the only
|
|
474
|
-
* case dropped is a root that carries `oneOf`/`anyOf` AND the stamp, which is the one case the SDK
|
|
475
|
-
* manufactures. An ungated `delete document.type` would have made the whole drift cell blind to a
|
|
476
|
-
* contract whose root type actually moved.
|
|
477
|
-
*/
|
|
478
|
-
declare function stripRedundantObjectStamp(document: JsonDocument): JsonDocument;
|
|
479
|
-
/**
|
|
480
|
-
* One union arm of a fold's composed contract, with the fold's discriminant taken back out.
|
|
481
|
-
*
|
|
482
|
-
* `required: []` is DROPPED rather than kept, and the case is real rather than defensive:
|
|
483
|
-
* `config.undo.propose` takes an all-optional input, so its own projection carries no `required`
|
|
484
|
-
* key at all while the extended arm carries `['action']` — and removing the discriminant from that
|
|
485
|
-
* leaves an empty array where the other side has nothing. JSON Schema reads an absent `required`
|
|
486
|
-
* and an empty one identically, so the two documents agree; keeping the empty array would have
|
|
487
|
-
* failed the cell on a difference that describes no difference.
|
|
488
|
-
*/
|
|
489
|
-
declare function armWithoutDiscriminant(arm: JsonDocument, discriminant: string): JsonDocument;
|
|
490
|
-
/**
|
|
491
|
-
* What a fold's one listed object leaves out of, or adds to, one arm's own contract.
|
|
492
|
-
*
|
|
493
|
-
* 2026-10-20 (446-11, D-25): a fold lists one object instead of its union, so an arm is no longer
|
|
494
|
-
* a member to compare whole. It is covered when the `discriminant` enum names it, every field it
|
|
495
|
-
* takes is listed with its own shape (the listing may add a description, never drop one), and the
|
|
496
|
-
* listing requires nothing the arm does not. That a call is still validated arm by arm is the
|
|
497
|
-
* C446-TOOL-SCHEMA counter-cell's claim, not this lens's.
|
|
498
|
-
*/
|
|
499
|
-
declare function foldArmFindings(listed: JsonDocument, discriminant: string, opId: string, own: JsonDocument): readonly string[];
|
|
500
|
-
/**
|
|
501
|
-
* Undo the SEP-2106 legacy `{ result }` wrap the SDK's 2025-11-25 codec puts around a non-object
|
|
502
|
-
* output root (2026-09-28, 431-03).
|
|
503
|
-
*
|
|
504
|
-
* A transport artifact, like the two stamps above: the 2026-07-28 codec advertises a list's array
|
|
505
|
-
* root as-is, while the 2025-11-25 codec projects it to `{ type: 'object', properties: { result },
|
|
506
|
-
* required: ['result'] }` and wraps `structuredContent` to match. Gated to exactly that document —
|
|
507
|
-
* nothing but those three keys (and a dialect marker), one property named `result`, and a wrapped
|
|
508
|
-
* root that is not itself an object — so an op whose real output is an object with a `result`
|
|
509
|
-
* field is left alone. Apply it BEFORE `withoutDialect`: the wrapped root keeps its own `$schema`.
|
|
510
|
-
*/
|
|
511
|
-
declare function withoutLegacyResultWrap(document: unknown): JsonDocument;
|
|
512
|
-
|
|
513
155
|
/**
|
|
514
156
|
* Mask once, and answer both channels from the one masked value.
|
|
515
157
|
*
|
|
@@ -520,35 +162,11 @@ declare function withoutLegacyResultWrap(document: unknown): JsonDocument;
|
|
|
520
162
|
*/
|
|
521
163
|
declare function maskedToolResult(output: unknown, maskText: (text: string) => string): CallToolResult;
|
|
522
164
|
|
|
523
|
-
/**
|
|
524
|
-
* The official MCP Apps extension id (spec revision 2026-01-26).
|
|
525
|
-
*
|
|
526
|
-
* RESTATED LOCALLY RATHER THAN IMPORTED, and the reason is a measured peer conflict:
|
|
527
|
-
* `@modelcontextprotocol/ext-apps` declares `peerDependencies: { '@modelcontextprotocol/sdk':
|
|
528
|
-
* '^1.29.0' }` — the v1 monolith — while this package ships the 2.0.0 split SDK, so importing its
|
|
529
|
-
* `/server` entry would drag a second SDK generation into production (Pitfall 3). The string is
|
|
530
|
-
* therefore ours, and Plan 06's census pins it against ext-apps' shipped schema so a typo'd
|
|
531
|
-
* reverse-DNS id cannot fail silently as "never negotiated".
|
|
532
|
-
*/
|
|
533
165
|
declare const MCP_APPS_EXTENSION_ID = "io.modelcontextprotocol/ui";
|
|
534
166
|
/** The media type an App resource is served as. Restated locally for the reason above. */
|
|
535
167
|
declare const APP_RESOURCE_MIME_TYPE = "text/html;profile=mcp-app";
|
|
536
168
|
/** The published App address. ONE template, per run — the runId is the only variable. */
|
|
537
169
|
declare const APP_RESOURCE_URI_TEMPLATE = "ui://agent-qa/live-run/{runId}";
|
|
538
|
-
/**
|
|
539
|
-
* The template EXPANDED for one concrete run (408 plan 03, D-05).
|
|
540
|
-
*
|
|
541
|
-
* WHY THIS IS A FUNCTION AND NOT A STRING EACH CALLER COMPOSES. `projection.ts` advertises the
|
|
542
|
-
* TEMPLATE on a carrier tool's `_meta`, and a carrier RESULT advertises the concrete address of the
|
|
543
|
-
* run it just named — two spellings of one address, in two files, that would be free to disagree
|
|
544
|
-
* about the scheme, the namespace or the segment order. Expanding through the template constant
|
|
545
|
-
* makes drift impossible: there is one place the address is written down and one rule for filling
|
|
546
|
-
* it in.
|
|
547
|
-
*
|
|
548
|
-
* ENCODED WITH `encodeURIComponent`, which is the exact inverse of the `decodeURIComponent` the one
|
|
549
|
-
* codec above applies — so `parseAppUri(appResourceUriFor(id)).runId === id` for every id, and an
|
|
550
|
-
* address this package composes is one this package can read back.
|
|
551
|
-
*/
|
|
552
170
|
declare function appResourceUriFor(runId: string): string;
|
|
553
171
|
/** The registered resource name. Prefixed like the tools, so one namespace reads as one server. */
|
|
554
172
|
declare const APP_RESOURCE_NAME = "agent_qa_live_run_app";
|
|
@@ -568,20 +186,6 @@ declare const APP_RESOURCE_NAME = "agent_qa_live_run_app";
|
|
|
568
186
|
declare function parseAppUri(href: string): {
|
|
569
187
|
readonly runId: string;
|
|
570
188
|
} | undefined;
|
|
571
|
-
/**
|
|
572
|
-
* The envelope member carrying the request's declared client capabilities.
|
|
573
|
-
*
|
|
574
|
-
* MEASURED, NOT GUESSED, and re-exported from the SDK rather than restated as a literal so a
|
|
575
|
-
* spelling drift is a compile error instead of a gate that silently answers "never negotiated".
|
|
576
|
-
* The measurement (2026-08-31, `src/__tests__/app-negotiation.test.ts`) drove a real
|
|
577
|
-
* `@modelcontextprotocol/client@2.0.0` against `createAgentQaMcpNodeHandler` over a loopback
|
|
578
|
-
* socket and read the resource-read context back out of the handler:
|
|
579
|
-
*
|
|
580
|
-
* ctx.mcpReq.envelope['io.modelcontextprotocol/clientCapabilities'].extensions['io.modelcontextprotocol/ui']
|
|
581
|
-
*
|
|
582
|
-
* That cell is the living record — it asserts the path against the wire, so an SDK that moves the
|
|
583
|
-
* member reds it rather than quietly turning this gate into a blanket refusal.
|
|
584
|
-
*/
|
|
585
189
|
declare const APP_NEGOTIATION_CLIENT_CAPABILITIES_KEY = "io.modelcontextprotocol/clientCapabilities";
|
|
586
190
|
/**
|
|
587
191
|
* The ONE member of the SDK's read context this module reads.
|
|
@@ -596,39 +200,7 @@ interface AppReadContext {
|
|
|
596
200
|
readonly envelope?: Partial<RequestMetaEnvelope>;
|
|
597
201
|
};
|
|
598
202
|
}
|
|
599
|
-
/**
|
|
600
|
-
* Did THIS request declare the MCP Apps extension?
|
|
601
|
-
*
|
|
602
|
-
* FAIL-CLOSED at every step: no context, no envelope, no capabilities member, no `extensions`
|
|
603
|
-
* record, or an `extensions` record without our id all answer `false`. The question is "is there
|
|
604
|
-
* evidence this caller negotiated the extension", and absence of evidence is not evidence — which
|
|
605
|
-
* matters most for the case the measurement surfaced, where a 2025-era caller carries no envelope
|
|
606
|
-
* at all and would otherwise be waved through by a predicate written as "not explicitly absent".
|
|
607
|
-
*
|
|
608
|
-
* WHAT FAILING CLOSED HERE DOES AND DOES NOT PROTECT (dated 2026-09-01, 408 gap round, road
|
|
609
|
-
* `demote` — the full ruling is in this file's header). The predicate is still fail-closed AS A
|
|
610
|
-
* PREDICATE, and every clause above is unchanged. What is CORRECTED is the reading that failing
|
|
611
|
-
* closed here protects the credential. It does not. The value read below is composed by the
|
|
612
|
-
* caller in its own per-request `_meta` and forwarded by the SDK unauthenticated
|
|
613
|
-
* (`_inputRequestCapabilityView` returns it verbatim; `checkInboundEnvelope` checks shape only),
|
|
614
|
-
* so a `false` answer means "this caller did not SAY it can render an App" — a host-capability
|
|
615
|
-
* fact — and never "this caller is not authorized to hold a live-view credential". What bounds
|
|
616
|
-
* the credential is `bindingStatus === 'bound'` and the local network controls D-13 keeps; the
|
|
617
|
-
* scope check that once sat beside it is deleted (432-20, D-12). See header clause 5.
|
|
618
|
-
*
|
|
619
|
-
* `RequestMetaEnvelope` is an intentionally EMPTY augmentable type in the shipped d.mts, so the
|
|
620
|
-
* envelope is read through one narrow cast at this single seam rather than by widening the SDK's
|
|
621
|
-
* types somewhere a second reader would have to find.
|
|
622
|
-
*/
|
|
623
203
|
declare function negotiatedAppsExtension(ctx: AppReadContext | undefined): boolean;
|
|
624
|
-
/**
|
|
625
|
-
* A read refused because the request never declared the Apps extension (clause 3 of the ruling
|
|
626
|
-
* in this file's header; D-09, T-408-01).
|
|
627
|
-
*
|
|
628
|
-
* IT NAMES THE EXTENSION AND CARRIES NOTHING ELSE. The message is authored prose plus a constant
|
|
629
|
-
* this repository owns, so there is nothing caller-supplied in it to mask and — the point of the
|
|
630
|
-
* whole arm — nothing minted in it to leak, because it is thrown before the first mint.
|
|
631
|
-
*/
|
|
632
204
|
declare class AppResourceNegotiationError extends Error {
|
|
633
205
|
constructor();
|
|
634
206
|
}
|
|
@@ -636,37 +208,9 @@ declare class AppResourceNegotiationError extends Error {
|
|
|
636
208
|
declare class AppResourceUriError extends Error {
|
|
637
209
|
constructor(detail: string);
|
|
638
210
|
}
|
|
639
|
-
/**
|
|
640
|
-
* A refusal from the App read that is NOT a run-liveness fact.
|
|
641
|
-
*
|
|
642
|
-
* Deliberately narrow: absence of a live session is rendered, never thrown (D-07), so anything that
|
|
643
|
-
* reaches this class is a genuine failure of the read — a missing scope, an unknown op, a broken
|
|
644
|
-
* composition — and it carries its message masked for the same reason `resources.ts`'s does.
|
|
645
|
-
*/
|
|
646
211
|
declare class AppResourceReadError extends Error {
|
|
647
212
|
constructor(message: string);
|
|
648
213
|
}
|
|
649
|
-
/**
|
|
650
|
-
* A document whose panes would span more than one origin (IN-02, T-408-26; added 2026-09-01).
|
|
651
|
-
*
|
|
652
|
-
* THE INVARIANT THIS SERVES USED TO BE AN ASSUMPTION. The `_meta.ui.csp` block below was derived
|
|
653
|
-
* from `mints[0]` alone, which is correct today only because `runs.liveurl` reads
|
|
654
|
-
* `liveView.origin()` once per invocation — a fact held in ANOTHER package, which nothing on that
|
|
655
|
-
* line checked. A composition that ever minted across origins would have shipped a frame allowance
|
|
656
|
-
* covering the first pane and blocking every other one: a blank inner frame, which is the exact
|
|
657
|
-
* failure this milestone exists to remove, arriving silently.
|
|
658
|
-
*
|
|
659
|
-
* WHY REFUSAL RATHER THAN A WIDER ALLOWANCE. Naming every origin in `frameDomains` would keep the
|
|
660
|
-
* document renderable, but it would also make the allowance a function of how many panes happened
|
|
661
|
-
* to be minted — precisely the "never broader than the document needs" property T-408-05 pins.
|
|
662
|
-
* This module's own reasoning is that the allow is as narrow as the document needs; a document that
|
|
663
|
-
* needs two origins is a composition this server does not know how to serve narrowly, and saying so
|
|
664
|
-
* is honest absence rather than a silent widening.
|
|
665
|
-
*
|
|
666
|
-
* ITS MESSAGE CARRIES NO CALLER TEXT AND NO CREDENTIAL. The origins are host-composed and
|
|
667
|
-
* `originOf` returns an origin only — never the path and never the fragment the token lives in — so
|
|
668
|
-
* there is nothing here to mask and nothing here to leak.
|
|
669
|
-
*/
|
|
670
214
|
declare class AppResourceOriginSpanError extends Error {
|
|
671
215
|
constructor(origins: readonly string[]);
|
|
672
216
|
}
|
|
@@ -676,19 +220,6 @@ interface AppResourceReadDeps {
|
|
|
676
220
|
readonly invoke: AppResourceInvoke;
|
|
677
221
|
/** How CALLER text leaving this handler is masked. Required — see the header's channel split. */
|
|
678
222
|
readonly maskText: (text: string) => string;
|
|
679
|
-
/**
|
|
680
|
-
* Which dashboard this process is pointed at, for D-08's permanent link (408 plan 03).
|
|
681
|
-
*
|
|
682
|
-
* OPTIONAL, and its absence is a real posture rather than a gap: a composition that knows of no
|
|
683
|
-
* dashboard renders the wrapper with NO anchor at all, which is what plan 01 shipped and what the
|
|
684
|
-
* honest-absence law requires — a link to nothing is the dishonesty this milestone removes.
|
|
685
|
-
*
|
|
686
|
-
* THIS IS THE ONE SLOT WHOSE ABSENCE COSTS A PERSON SOMETHING, which is why it is wired rather
|
|
687
|
-
* than left where plan 01 left it: the wrapper's `expired` and `unreachable-origin` states are
|
|
688
|
-
* D-11's answers for "the embed cannot show you this", and both of them are supposed to hand a
|
|
689
|
-
* person the browser instead. A wrapper reaching those states with no link is a dead end wearing
|
|
690
|
-
* an honest sentence.
|
|
691
|
-
*/
|
|
692
223
|
readonly liveLink?: LiveLinkSupplier;
|
|
693
224
|
}
|
|
694
225
|
/**
|
|
@@ -698,20 +229,6 @@ interface AppResourceReadDeps {
|
|
|
698
229
|
* observable independently of registration — the two halves fail in different directions.
|
|
699
230
|
*/
|
|
700
231
|
declare function createAppResourceReadHandler(deps: AppResourceReadDeps): (uri: URL, variables: Variables, ctx: AppReadContext) => Promise<ReadResourceResult>;
|
|
701
|
-
/**
|
|
702
|
-
* The slice of `McpServer` this module uses.
|
|
703
|
-
*
|
|
704
|
-
* The same narrow structural type `resources.ts` declares, spelled with the SDK's own
|
|
705
|
-
* `ResourceTemplate` / `ResourceMetadata` / `ReadResourceTemplateCallback` — so a cell can observe
|
|
706
|
-
* the registration without assembling a protocol server while a real `McpServer` still satisfies
|
|
707
|
-
* it. Read that module's note before narrowing any member here: a hand-narrowed config or a
|
|
708
|
-
* `readonly` result array makes `McpServer` unassignable and the compiler names the wrong cause.
|
|
709
|
-
*
|
|
710
|
-
* THE READ CALLBACK IS THE SDK'S OWN `ReadResourceTemplateCallback` AND NOT A LOCAL NARROWING, for
|
|
711
|
-
* the reason 408 plan 02 needed a third argument at all: the negotiation evidence arrives in the
|
|
712
|
-
* SDK's read CONTEXT, so a slice that spelled the callback `(uri) => …` would have declared the
|
|
713
|
-
* one parameter the gate reads out of existence.
|
|
714
|
-
*/
|
|
715
232
|
interface AppResourceRegistrar {
|
|
716
233
|
registerResource(name: string, template: ResourceTemplate, config: ResourceMetadata, read: ReadResourceTemplateCallback): unknown;
|
|
717
234
|
}
|
|
@@ -720,113 +237,23 @@ interface AppResourceDeps {
|
|
|
720
237
|
readonly registry: FunctionRegistry;
|
|
721
238
|
readonly caller: FunctionCaller;
|
|
722
239
|
readonly maskText: (text: string) => string;
|
|
723
|
-
/** D-08's base supplier, passed straight through to the read. See {@link AppResourceReadDeps}. */
|
|
724
240
|
readonly liveLink?: LiveLinkSupplier;
|
|
725
241
|
}
|
|
726
|
-
/**
|
|
727
|
-
* Register the App template when this composition can honestly serve it, and answer what it
|
|
728
|
-
* registered.
|
|
729
|
-
*
|
|
730
|
-
* Returns the registered template URIs rather than nothing, for `resources.ts`'s reason: "registered
|
|
731
|
-
* none" and "registered one" are the two halves of the claim, and a caller that had to inspect a
|
|
732
|
-
* server to tell them apart could only ever test one of them.
|
|
733
|
-
*
|
|
734
|
-
* TWO CONDITIONS, BOTH READ OFF THE REGISTRY'S OWN DECLARATIONS (D-02/D-03), AND NEITHER ABOUT THE
|
|
735
|
-
* CALLER (432-20, D-12 deleted the third, a `visibleToCaller` scope check):
|
|
736
|
-
*
|
|
737
|
-
* 1. Both ops this surface reads through — the mint and the roster read — are declared here. A
|
|
738
|
-
* composition that registered neither has no App to offer.
|
|
739
|
-
* 2. The mint is BOUND in this composition. This is D-03's floor made structural: the standalone
|
|
740
|
-
* `agent-qa mcp` process composes no `liveView` port, so its `runs.liveurl` is declared-and-
|
|
741
|
-
* unbound and every mint there would refuse by name. Registering an App on that host would
|
|
742
|
-
* advertise an address whose every read is honestly empty — so it registers nothing at all,
|
|
743
|
-
* and the link-out is that host's whole answer. On a co-hosted composition the same three
|
|
744
|
-
* lines register it, by composition rather than by configuration.
|
|
745
|
-
*
|
|
746
|
-
* A composition that fails any of them gets NO template and NO error — the same answer `catalog.ts`
|
|
747
|
-
* gives a tool whose op is missing, because refusing to build would take an entire MCP connection
|
|
748
|
-
* down over one unavailable capability.
|
|
749
|
-
*/
|
|
750
242
|
declare function registerAppResources(server: AppResourceRegistrar, deps: AppResourceDeps): readonly string[];
|
|
751
243
|
|
|
752
|
-
/**
|
|
753
|
-
* Every value `[data-app-state]` can take (D-11's closed set).
|
|
754
|
-
*
|
|
755
|
-
* Exported for the reason `LIVE_EMBED_STATES` is: the DOM cells range over this list, so a state
|
|
756
|
-
* added to the page without a cell is a visible diff rather than an unobserved arm.
|
|
757
|
-
*/
|
|
758
244
|
declare const APP_WRAPPER_STATES: readonly ["connecting", "live", "expired", "unreachable-origin", "run-ended"];
|
|
759
245
|
type AppWrapperState = (typeof APP_WRAPPER_STATES)[number];
|
|
760
246
|
/** What the page says while the inner frame has not painted yet. */
|
|
761
247
|
declare const APP_WRAPPER_CONNECTING_MESSAGE = "connecting to the live view\u2026";
|
|
762
|
-
/**
|
|
763
|
-
* The second line under `connecting`, and the WHOLE of what an initial render says on a host that
|
|
764
|
-
* never runs this document's script (WR-10, RESEARCH assumption A1; the cell is
|
|
765
|
-
* `app-wrapper-dom.test.ts` cell (7)).
|
|
766
|
-
*
|
|
767
|
-
* Named for the same reason every other absence here is named: a document that says only that it is
|
|
768
|
-
* connecting has made a promise its own script has to keep, and under a host CSP of
|
|
769
|
-
* `script-src 'none'` there is no script to keep it. The sentence therefore points at the ONE
|
|
770
|
-
* useful action available with no script at all — the permanent link rendered directly beneath it,
|
|
771
|
-
* which is now markup rather than something the script assigns. One useful action, no apology.
|
|
772
|
-
*/
|
|
773
248
|
declare const APP_WRAPPER_CONNECTING_HINT = "if the pane does not appear, open it in your browser";
|
|
774
249
|
/** What the page says once the inner frame has loaded on an origin this host can reach. */
|
|
775
250
|
declare const APP_WRAPPER_LIVE_MESSAGE = "live";
|
|
776
|
-
/**
|
|
777
|
-
* What an elapsed live link says (D-10).
|
|
778
|
-
*
|
|
779
|
-
* The wrapper's own sentence rather than the embed page's `link expired`, because the two are about
|
|
780
|
-
* different things: the embed says its socket was refused, and this says the link this App was
|
|
781
|
-
* rendered with has aged out.
|
|
782
|
-
*/
|
|
783
251
|
declare const APP_WRAPPER_EXPIRED_MESSAGE = "live link expired";
|
|
784
|
-
/**
|
|
785
|
-
* The second half of the expired state, and the WHOLE of D-10's refresh story (D-10).
|
|
786
|
-
*
|
|
787
|
-
* There is no re-mint inside this sandbox and there is never going to be one — a UI-initiated mint
|
|
788
|
-
* door was rejected precisely because its model-context hygiene is per-host behaviour nobody here
|
|
789
|
-
* controls. What replaces it is this sentence: the way to a fresh token is another watch call, one
|
|
790
|
-
* the person can simply ask for, and saying so is the difference between a dead end and a next
|
|
791
|
-
* move. Separate from the message above so the DOM can show both and a cell can assert each.
|
|
792
|
-
*/
|
|
793
252
|
declare const APP_WRAPPER_EXPIRED_HINT = "ask to watch again \u2014 a fresh watch mints a new link";
|
|
794
|
-
/**
|
|
795
|
-
* The REQUIRED unreachable-origin sentence (D-11), spelled EXACTLY as CONTEXT ratified it.
|
|
796
|
-
*
|
|
797
|
-
* A host running somewhere that cannot route to a loopback dashboard origin must say so and offer
|
|
798
|
-
* the browser, never show a blank inner frame. This string is a contract: the DOM cells assert
|
|
799
|
-
* against this constant, so the sentence and the assertion can never drift apart.
|
|
800
|
-
*/
|
|
801
253
|
declare const APP_WRAPPER_UNREACHABLE_MESSAGE = "this host can't reach the live view \u2014 open it in your browser";
|
|
802
|
-
/**
|
|
803
|
-
* What a run with no live session on this host says (D-07).
|
|
804
|
-
*
|
|
805
|
-
* Not an error and not a blank frame — and it points at the way to see the run anyway, which is
|
|
806
|
-
* the permanent link rendered directly beneath it. A run that has finished still HAPPENED, and the
|
|
807
|
-
* dashboard's own page is where its history lives.
|
|
808
|
-
*/
|
|
809
254
|
declare const APP_WRAPPER_RUN_ENDED_MESSAGE = "this run is not live \u2014 open it in your browser";
|
|
810
|
-
/** The label on the permanent link-out slot, when one is composed (D-08). */
|
|
811
255
|
declare const APP_WRAPPER_PERMANENT_LINK_LABEL = "open in your browser";
|
|
812
|
-
/**
|
|
813
|
-
* How long silence from the inner frame is allowed to last before it is named (D-11, T-408-08).
|
|
814
|
-
*
|
|
815
|
-
* Exported because the DOM cell must wait on the page's OWN bound rather than on a number a test
|
|
816
|
-
* file guessed. Ten seconds is chosen against the two shapes this arm exists for: a black-holed
|
|
817
|
-
* origin never answers at all, and a slow-but-reachable dashboard on loopback answers in
|
|
818
|
-
* milliseconds — there is no realistic case in between, so a longer bound would only make a person
|
|
819
|
-
* stare at a spinner for longer before reading the same sentence.
|
|
820
|
-
*/
|
|
821
256
|
declare const APP_WRAPPER_LOAD_TIMEOUT_MS = 10000;
|
|
822
|
-
/**
|
|
823
|
-
* The MCP Apps protocol version this wrapper announces in its `ui/initialize` request.
|
|
824
|
-
*
|
|
825
|
-
* MEASURED, not guessed (408-06 Task 1): `@modelcontextprotocol/ext-apps@1.7.5` carries exactly
|
|
826
|
-
* two protocol versions — `2025-11-21` and `2026-01-26` — and `2026-01-26` is the one its
|
|
827
|
-
* `AppBridge` treats as latest. `ext-apps-constants.test.ts` pins the spelling against the
|
|
828
|
-
* package's own shipped schema, so this literal cannot drift from the vocabulary in silence.
|
|
829
|
-
*/
|
|
830
257
|
declare const APP_WRAPPER_UI_PROTOCOL_VERSION = "2026-01-26";
|
|
831
258
|
/**
|
|
832
259
|
* What this document calls itself to a host (`ui/initialize`'s required `appInfo`).
|
|
@@ -837,13 +264,6 @@ declare const APP_WRAPPER_UI_PROTOCOL_VERSION = "2026-01-26";
|
|
|
837
264
|
declare const APP_WRAPPER_APP_NAME = "agent-qa-live-view";
|
|
838
265
|
/** The wrapper's own version, bumped only when the handshake it performs changes. */
|
|
839
266
|
declare const APP_WRAPPER_APP_VERSION = "1.0.0";
|
|
840
|
-
/**
|
|
841
|
-
* ONE minted view onto ONE member handle of the run.
|
|
842
|
-
*
|
|
843
|
-
* `liveUrl` carries its token in the FRAGMENT (403 D-04), so this value is the only place a live
|
|
844
|
-
* credential exists on this page — it is written into the frame's `src` and nowhere else: not into
|
|
845
|
-
* the document text, not into the picker's labels, not into any attribute a host serialises back.
|
|
846
|
-
*/
|
|
847
267
|
interface AppWrapperMint {
|
|
848
268
|
readonly handle: number;
|
|
849
269
|
readonly liveUrl: string;
|
|
@@ -852,40 +272,15 @@ interface AppWrapperMint {
|
|
|
852
272
|
}
|
|
853
273
|
interface AppWrapperInput {
|
|
854
274
|
readonly runId: string;
|
|
855
|
-
/** One mint per handle on the run's announced roster. Empty is the D-07 not-live case. */
|
|
856
275
|
readonly mints: readonly AppWrapperMint[];
|
|
857
|
-
/** The tokenless dashboard page
|
|
276
|
+
/** The tokenless dashboard page. `undefined` when no dashboard origin is composable. */
|
|
858
277
|
readonly permanentLink: string | undefined;
|
|
859
278
|
/** Whether this host holds a live session for the run at all. `false` renders `run-ended`. */
|
|
860
279
|
readonly runLive: boolean;
|
|
861
|
-
/**
|
|
862
|
-
* The instant this document is being built, in ms-epoch (D-10).
|
|
863
|
-
*
|
|
864
|
-
* A PARAMETER rather than a `Date.now()` call, for the reason every clock in this repository is a
|
|
865
|
-
* port: the expiry arm is the one thing this renderer decides about time, and a cell that cannot
|
|
866
|
-
* choose "now" could only ever assert the arm it happened to land in. Defaults to the wall clock,
|
|
867
|
-
* so the read handler passes nothing and gets the honest answer.
|
|
868
|
-
*/
|
|
869
280
|
readonly now?: number;
|
|
870
281
|
}
|
|
871
|
-
/**
|
|
872
|
-
* Render the App wrapper for one run.
|
|
873
|
-
*
|
|
874
|
-
* The whole document, every time — there is no partial render and no update path, because D-10's
|
|
875
|
-
* answer to a stale App is a FRESH read that mints again rather than a credential machine inside
|
|
876
|
-
* the sandbox.
|
|
877
|
-
*/
|
|
878
282
|
declare function renderAppWrapperPage(input: AppWrapperInput): string;
|
|
879
283
|
|
|
880
|
-
/**
|
|
881
|
-
* Everything the derivation reads about ONE op, assembled by `catalog.ts`.
|
|
882
|
-
*
|
|
883
|
-
* The two halves come from two places on purpose. `summary` and `risk` are read
|
|
884
|
-
* off the registry's own declaration for this host, so they describe the composition a caller is
|
|
885
|
-
* actually talking to; `input` and `output` are the live contracts named by the mapping table,
|
|
886
|
-
* because the registry deliberately publishes only inert projections of those (T-384-14). Nothing
|
|
887
|
-
* in this record is authored by this package — it is two views of the op, joined by its id.
|
|
888
|
-
*/
|
|
889
284
|
interface ProjectedOpSpec {
|
|
890
285
|
readonly id: string;
|
|
891
286
|
readonly summary: string;
|
|
@@ -905,46 +300,8 @@ interface RegisterToolConfig {
|
|
|
905
300
|
readonly inputSchema: StandardSchemaWithJSON;
|
|
906
301
|
readonly outputSchema?: StandardSchemaWithJSON;
|
|
907
302
|
readonly annotations: ToolAnnotations;
|
|
908
|
-
/**
|
|
909
|
-
* The SDK's own extension slot, carrying the live-view App reference for a CARRIER tool
|
|
910
|
-
* (2026-08-31, Phase 408 plan 01 · D-05/D-09 · MCPA-01).
|
|
911
|
-
*
|
|
912
|
-
* OPTIONAL, and its absence is a decision in the same sense `outputSchema`'s is: a tool that
|
|
913
|
-
* declares no `ui` member gets no `_meta` KEY at all rather than a `_meta` whose `ui` is
|
|
914
|
-
* `undefined`, because the two are different documents on the wire and only the first is honest
|
|
915
|
-
* about a tool that references no App.
|
|
916
|
-
*
|
|
917
|
-
* WHAT IT IS ON THE LISTING CHANNEL: inert metadata. Extensions are bilateral opt-in, so a host
|
|
918
|
-
* that never negotiated `io.modelcontextprotocol/ui` ignores this field by definition — it acts
|
|
919
|
-
* on nothing here, which is what makes "the App attaches only when the host negotiated" true of
|
|
920
|
-
* every channel that ACTS without needing a listing that varies per connection (SEP-2567 removed
|
|
921
|
-
* that, and the per-request factory context carries no client capabilities to key one on). The
|
|
922
|
-
* channels that act are gated where the capabilities actually arrive: in request handling.
|
|
923
|
-
*/
|
|
924
303
|
readonly _meta?: Record<string, unknown>;
|
|
925
304
|
}
|
|
926
|
-
/**
|
|
927
|
-
* Derive one tool's `registerTool` config.
|
|
928
|
-
*
|
|
929
|
-
* `advertised` is passed IN rather than looked up here so this module stays a derivation over
|
|
930
|
-
* values: `catalog.ts` owns the join between the mapping's data and this host's registry, and a
|
|
931
|
-
* second lookup inside the derivation would be a second chance for the two to disagree.
|
|
932
|
-
*
|
|
933
|
-
* `maskText` is a REQUIRED parameter and covers the DERIVED prose. The reason is the fresh wound
|
|
934
|
-
* this phase inherits: 395 closed a high-severity defect where a credential reached an MCP caller
|
|
935
|
-
* through a serialization nobody had thought of as an output channel. A description is composed
|
|
936
|
-
* from op summaries, and a summary is prose somebody may one day interpolate a resolved value into;
|
|
937
|
-
* routing it through the masker costs nothing on the ordinary path and closes the channel
|
|
938
|
-
* structurally rather than by remembering. Optionality is the failure mode here — an optional
|
|
939
|
-
* masker is a masker that is absent exactly where nobody looked — so the parameter has no default
|
|
940
|
-
* and the type refuses a call that omits it.
|
|
941
|
-
*
|
|
942
|
-
* TWO THINGS ARE DELIBERATELY NOT MASKED, and both would be damage rather than safety. The `title`
|
|
943
|
-
* is the curated tool NAME from the mapping table: a declared constant in this repository's own
|
|
944
|
-
* source, never data, and a masker that rewrote it would rename the tool a caller must call. The
|
|
945
|
-
* SCHEMAS are contracts rather than text — masking one would corrupt the very object
|
|
946
|
-
* `invokeFunction` validates against, and they carry field names and types, not values.
|
|
947
|
-
*/
|
|
948
305
|
declare function toRegisterToolConfig(projection: ToolProjection, specs: readonly ProjectedOpSpec[], advertised: ToolOpContracts, maskText: (text: string) => string): RegisterToolConfig;
|
|
949
306
|
/** A projection that cannot be derived honestly, named with the tool it is about. */
|
|
950
307
|
declare class McpProjectionError extends Error {
|
|
@@ -952,68 +309,4 @@ declare class McpProjectionError extends Error {
|
|
|
952
309
|
constructor(toolName: string, detail: string);
|
|
953
310
|
}
|
|
954
311
|
|
|
955
|
-
|
|
956
|
-
* The MCP task states this projection can produce.
|
|
957
|
-
*
|
|
958
|
-
* Four of the wire's five. The fifth, `input_required`, is deliberately absent from the table
|
|
959
|
-
* below and NOT from this union: it is a real state of the protocol that no agent-qa job status
|
|
960
|
-
* currently means, and a future status that did mean it should be able to say so by adding one row
|
|
961
|
-
* rather than by widening a type.
|
|
962
|
-
*/
|
|
963
|
-
type McpTaskState = 'working' | 'input_required' | 'completed' | 'failed' | 'cancelled';
|
|
964
|
-
/**
|
|
965
|
-
* The whole mapping, as data.
|
|
966
|
-
*
|
|
967
|
-
* `as const satisfies Record<JobStatus, McpTaskState>` is what makes this exhaustive AT COMPILE
|
|
968
|
-
* TIME: add an eighth member to `JobStatus` and this object stops satisfying the record, so the
|
|
969
|
-
* build fails at the one place that has to make a decision about it. The alternative — a `switch`
|
|
970
|
-
* with a `default` — is the same table with a hole in it, and the hole answers `working` for a
|
|
971
|
-
* status nobody has thought about yet, which is the wrong answer precisely when it matters.
|
|
972
|
-
*
|
|
973
|
-
* Exported because the table IS the reconciliation D-12 asks to be pinned, and a later phase
|
|
974
|
-
* registering the extension will read it rather than restate it.
|
|
975
|
-
*/
|
|
976
|
-
declare const JOB_STATUS_TO_MCP_TASK_STATE: {
|
|
977
|
-
readonly queued: "working";
|
|
978
|
-
readonly authored: "working";
|
|
979
|
-
readonly bundling: "working";
|
|
980
|
-
readonly committing: "working";
|
|
981
|
-
readonly done: "completed";
|
|
982
|
-
readonly failed: "failed";
|
|
983
|
-
readonly cancelled: "cancelled";
|
|
984
|
-
};
|
|
985
|
-
/**
|
|
986
|
-
* What one job looks like to a remote poller.
|
|
987
|
-
*
|
|
988
|
-
* Field names are the wire's (`taskId`, `statusMessage`, `lastUpdatedAt`) rather than the job
|
|
989
|
-
* record's, because this record's whole purpose is to be handed OUT: naming it after the source
|
|
990
|
-
* would push the rename onto the registration, which is the step this seam exists to make trivial.
|
|
991
|
-
* `lastUpdatedAt` carries `Job.updatedAt` verbatim; only the label differs.
|
|
992
|
-
*/
|
|
993
|
-
interface McpTaskProjection {
|
|
994
|
-
readonly taskId: string;
|
|
995
|
-
readonly status: McpTaskState;
|
|
996
|
-
/** The job's own one-line summary. The only prose that crosses. */
|
|
997
|
-
readonly statusMessage: string;
|
|
998
|
-
readonly createdAt: string;
|
|
999
|
-
readonly lastUpdatedAt: string;
|
|
1000
|
-
}
|
|
1001
|
-
/**
|
|
1002
|
-
* Project one job record into the task view.
|
|
1003
|
-
*
|
|
1004
|
-
* Total, pure, synchronous, and deliberately narrow. `attempts`, `kind`, `runRef` and
|
|
1005
|
-
* `conclusionKind` are NOT carried, and their absence is asserted by an exact-key cell rather than
|
|
1006
|
-
* left to be noticed: `attempts` is the retry discipline's private counter, and `conclusionKind` is
|
|
1007
|
-
* internal vocabulary (`knowledge_skipped_draft`, `insufficient_model_output`) that means nothing to
|
|
1008
|
-
* a caller outside this system. A projection that leaks the source record's shape is not a
|
|
1009
|
-
* projection; it is the coupling with an extra step.
|
|
1010
|
-
*
|
|
1011
|
-
* NOTHING IS MASKED HERE, and that is a statement rather than an oversight. This function is called
|
|
1012
|
-
* by nothing — it registers no handler and returns no result to any transport — so there is no
|
|
1013
|
-
* output channel to mask. Whoever eventually registers it inherits the masking obligation every
|
|
1014
|
-
* other handler in this package carries, because `statusMessage` is a job's own summary text and
|
|
1015
|
-
* summaries are prose somebody may one day interpolate a resolved value into.
|
|
1016
|
-
*/
|
|
1017
|
-
declare function projectJobAsTask(job: Job): McpTaskProjection;
|
|
1018
|
-
|
|
1019
|
-
export { AGENT_QA_COMPOSED_CONTRACTS, AGENT_QA_MCP_INSTRUCTIONS, AGENT_QA_MCP_SERVER_VERSION, AGENT_QA_OP_CONTRACTS, AGENT_QA_TOOL_PROJECTIONS, APP_NEGOTIATION_CLIENT_CAPABILITIES_KEY, APP_RESOURCE_MIME_TYPE, APP_RESOURCE_NAME, APP_RESOURCE_URI_TEMPLATE, APP_WRAPPER_APP_NAME, APP_WRAPPER_APP_VERSION, APP_WRAPPER_CONNECTING_HINT, APP_WRAPPER_CONNECTING_MESSAGE, APP_WRAPPER_EXPIRED_HINT, APP_WRAPPER_EXPIRED_MESSAGE, APP_WRAPPER_LIVE_MESSAGE, APP_WRAPPER_LOAD_TIMEOUT_MS, APP_WRAPPER_PERMANENT_LINK_LABEL, APP_WRAPPER_RUN_ENDED_MESSAGE, APP_WRAPPER_STATES, APP_WRAPPER_UI_PROTOCOL_VERSION, APP_WRAPPER_UNREACHABLE_MESSAGE, type AgentQaMcpDepsResolver, type AgentQaMcpServerDeps, type AppReadContext, type AppResourceDeps, type AppResourceInvoke, AppResourceNegotiationError, AppResourceOriginSpanError, type AppResourceReadDeps, AppResourceReadError, type AppResourceRegistrar, AppResourceUriError, type AppWrapperInput, type AppWrapperMint, type AppWrapperState, DEGRADED_OUTPUT_CONTRACTS, JOB_STATUS_TO_MCP_TASK_STATE, type JsonDocument, LIVE_LINK_ABSENT_LINE, LIVE_LINK_NO_RUN_LINE, type LiveLinkSupplier, MCP_APPS_EXTENSION_ID, McpProjectionError, type McpTaskProjection, type McpTaskState, type OutputSchemaDegradation, type ProjectedOpSpec, type RegisterToolConfig, type ToolComposition, type ToolOpContracts, type ToolProjection, type ToolProjectionKind, UNPROJECTED_OPS, type UnprojectedOp, type V1MigrationArm, type V1ToolMigrationEntry, V1_TOOL_MIGRATION_MAP, advertisedContracts, appResourceUriFor, armWithoutDiscriminant, buildAgentQaMcpServer, compositionOf, createAgentQaMcpNodeHandler, createAppResourceReadHandler, foldArmFindings, liveLinkLine, liveLinkLineFor, liveRunLinkOf, maskedToolResult, negotiatedAppsExtension, parseAppUri, projectJobAsTask, registerAppResources, renderAppWrapperPage, stripRedundantObjectStamp, toRegisterToolConfig, withoutDialect, withoutLegacyResultWrap };
|
|
312
|
+
export { AGENT_QA_COMPOSED_CONTRACTS, AGENT_QA_MCP_INSTRUCTIONS, AGENT_QA_MCP_SERVER_VERSION, AGENT_QA_OP_CONTRACTS, AGENT_QA_TOOL_PROJECTIONS, APP_NEGOTIATION_CLIENT_CAPABILITIES_KEY, APP_RESOURCE_MIME_TYPE, APP_RESOURCE_NAME, APP_RESOURCE_URI_TEMPLATE, APP_WRAPPER_APP_NAME, APP_WRAPPER_APP_VERSION, APP_WRAPPER_CONNECTING_HINT, APP_WRAPPER_CONNECTING_MESSAGE, APP_WRAPPER_EXPIRED_HINT, APP_WRAPPER_EXPIRED_MESSAGE, APP_WRAPPER_LIVE_MESSAGE, APP_WRAPPER_LOAD_TIMEOUT_MS, APP_WRAPPER_PERMANENT_LINK_LABEL, APP_WRAPPER_RUN_ENDED_MESSAGE, APP_WRAPPER_STATES, APP_WRAPPER_UI_PROTOCOL_VERSION, APP_WRAPPER_UNREACHABLE_MESSAGE, type AgentQaMcpDepsResolver, type AgentQaMcpServerDeps, type AppReadContext, type AppResourceDeps, type AppResourceInvoke, AppResourceNegotiationError, AppResourceOriginSpanError, type AppResourceReadDeps, AppResourceReadError, type AppResourceRegistrar, AppResourceUriError, type AppWrapperInput, type AppWrapperMint, type AppWrapperState, DEGRADED_OUTPUT_CONTRACTS, type JsonDocument, LIVE_LINK_ABSENT_LINE, LIVE_LINK_NO_RUN_LINE, type LiveLinkSupplier, MCP_APPS_EXTENSION_ID, McpProjectionError, type OutputSchemaDegradation, type ProjectedOpSpec, type RegisterToolConfig, type ToolComposition, type ToolOpContracts, type ToolProjection, type ToolProjectionKind, UNPROJECTED_OPS, type UnprojectedOp, advertisedContracts, appResourceUriFor, armWithoutDiscriminant, buildAgentQaMcpServer, compositionOf, createAgentQaMcpNodeHandler, createAppResourceReadHandler, foldArmFindings, liveLinkLine, liveLinkLineFor, liveRunLinkOf, maskedToolResult, negotiatedAppsExtension, parseAppUri, registerAppResources, renderAppWrapperPage, stripRedundantObjectStamp, toRegisterToolConfig, withoutDialect, withoutLegacyResultWrap };
|