eval-quality 1.4.2 → 3.0.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.
Files changed (110) hide show
  1. package/README.md +6 -6
  2. package/corpus/dev/README.md +24 -13
  3. package/corpus/dev/compile-seal-example/brief.json +1 -1
  4. package/corpus/dev/compile-seal-example/contract.json +1 -1
  5. package/corpus/dev/contracts/absent-collection-locations.json +1 -1
  6. package/corpus/dev/contracts/absent-sibling-groups.json +1 -1
  7. package/corpus/dev/contracts/absent-success-indicator.json +1 -1
  8. package/corpus/dev/contracts/captured-read-back.json +1 -0
  9. package/corpus/dev/contracts/checklist-selection.json +1 -0
  10. package/corpus/dev/contracts/empty-channel-roles.json +1 -1
  11. package/corpus/dev/contracts/empty-collection-locations.json +1 -1
  12. package/corpus/dev/contracts/empty-request-shapes.json +1 -1
  13. package/corpus/dev/contracts/empty-sibling-groups.json +1 -1
  14. package/corpus/dev/contracts/fragment-selection.json +1 -1
  15. package/corpus/dev/contracts/no-collection-quantifier.json +1 -1
  16. package/corpus/dev/contracts/no-operation-inventory.json +1 -1
  17. package/corpus/dev/contracts/no-read-back-relation.json +1 -1
  18. package/corpus/dev/contracts/no-state-change-marker.json +1 -1
  19. package/corpus/dev/contracts/no-type-violating-step.json +1 -1
  20. package/corpus/dev/contracts/notes-tool-server.json +1 -0
  21. package/corpus/dev/contracts/per-key-split-oracles.json +1 -1
  22. package/corpus/dev/contracts/review-corpus.json +1 -1
  23. package/corpus/dev/contracts/satisfied-declarations.json +1 -1
  24. package/corpus/dev/contracts/single-required-response-key.json +1 -1
  25. package/corpus/dev/contracts/split-indicator-oracle.json +1 -1
  26. package/corpus/dev/contracts/unaddressed-parameter-sibling.json +1 -1
  27. package/corpus/dev/contracts/unnamed-reference-set.json +1 -1
  28. package/corpus/dev/contracts/wrong-cardinality-form.json +1 -1
  29. package/corpus/dev/index.json +1 -1
  30. package/dist/adapters/command-line-adapter.js +44 -6
  31. package/dist/adapters/index.d.ts +2 -0
  32. package/dist/adapters/index.js +1 -0
  33. package/dist/adapters/mcp-adapter.d.ts +32 -0
  34. package/dist/adapters/mcp-adapter.js +357 -0
  35. package/dist/adapters/mcp-target-policy.d.ts +39 -0
  36. package/dist/adapters/mcp-target-policy.js +30 -0
  37. package/dist/cli/render.d.ts +8 -3
  38. package/dist/cli/render.js +13 -6
  39. package/dist/cli/run.js +2 -1
  40. package/dist/core/compile/bindings.d.ts +8 -18
  41. package/dist/core/compile/bindings.js +13 -10
  42. package/dist/core/compile/compile.js +6 -1
  43. package/dist/core/compile/interface-inventory.d.ts +81 -3
  44. package/dist/core/compile/interface-inventory.js +134 -23
  45. package/dist/core/compile/reachability.d.ts +73 -1
  46. package/dist/core/compile/reachability.js +134 -19
  47. package/dist/core/compile/schema-version.d.ts +15 -2
  48. package/dist/core/compile/schema-version.js +11 -3
  49. package/dist/core/compile/sensitivity-witness.d.ts +22 -10
  50. package/dist/core/compile/sensitivity-witness.js +101 -13
  51. package/dist/core/coverage/operations.d.ts +1 -1
  52. package/dist/core/coverage/operations.js +1 -1
  53. package/dist/core/coverage/relevance.d.ts +3 -3
  54. package/dist/core/coverage/relevance.js +3 -3
  55. package/dist/core/declared-inputs.d.ts +21 -9
  56. package/dist/core/declared-inputs.js +51 -16
  57. package/dist/core/evaluate/evidence-resolution.d.ts +8 -7
  58. package/dist/core/evaluate/evidence-resolution.js +25 -18
  59. package/dist/core/preflight/plan.js +43 -6
  60. package/dist/core/preflight/projection.d.ts +10 -1
  61. package/dist/core/preflight/projection.js +9 -5
  62. package/dist/core/preflight/reduce.js +6 -2
  63. package/dist/core/preflight/witness-evidence.js +31 -9
  64. package/dist/core/schemas/artifact.d.ts +145 -49
  65. package/dist/core/schemas/defect-signature.d.ts +280 -23
  66. package/dist/core/schemas/defect-signature.js +65 -37
  67. package/dist/core/schemas/eval-contract.d.ts +33 -48
  68. package/dist/core/schemas/eval-contract.js +3 -3
  69. package/dist/core/schemas/interface.d.ts +193 -55
  70. package/dist/core/schemas/interface.js +82 -15
  71. package/dist/core/schemas/plan.d.ts +45 -1
  72. package/dist/core/schemas/plan.js +13 -2
  73. package/dist/core/schemas/pointer.d.ts +23 -9
  74. package/dist/core/schemas/pointer.js +25 -11
  75. package/dist/core/schemas/port-messages.d.ts +81 -0
  76. package/dist/core/schemas/port-messages.js +50 -3
  77. package/dist/core/schemas/primitives.d.ts +18 -0
  78. package/dist/core/schemas/primitives.js +29 -0
  79. package/dist/core/schemas/probe-policy.d.ts +41 -0
  80. package/dist/core/schemas/probe-policy.js +61 -1
  81. package/dist/core/schemas/probe.d.ts +156 -2
  82. package/dist/core/schemas/probe.js +45 -2
  83. package/dist/core/schemas/sealed-run-record.d.ts +11 -5
  84. package/dist/core/schemas/sealed-run-record.js +21 -9
  85. package/dist/core/schemas/sensitivity-witness.d.ts +31 -7
  86. package/dist/core/schemas/sensitivity-witness.js +32 -9
  87. package/dist/core/score/bindings.d.ts +1 -1
  88. package/dist/core/score/bindings.js +4 -4
  89. package/dist/core/score/qualification.d.ts +7 -5
  90. package/dist/core/score/qualification.js +92 -23
  91. package/dist/core/score/score.d.ts +1 -1
  92. package/dist/core/score/score.js +24 -1
  93. package/dist/core/seal/derived-reference.js +20 -17
  94. package/dist/core/seal/plan-index.d.ts +18 -9
  95. package/dist/core/seal/plan-index.js +79 -37
  96. package/dist/index.d.ts +1 -1
  97. package/dist/index.js +1 -1
  98. package/dist/ports/environment-probe-port.d.ts +50 -17
  99. package/dist/ports/environment-probe-port.js +26 -17
  100. package/dist/testing/conformance.d.ts +3 -2
  101. package/dist/testing/conformance.js +2 -1
  102. package/dist/testing/index.d.ts +3 -3
  103. package/dist/testing/index.js +1 -1
  104. package/dist/testing/probe-conformance.d.ts +90 -13
  105. package/dist/testing/probe-conformance.js +375 -160
  106. package/package.json +4 -2
  107. package/schemas/eval-contract.schema.json +571 -18
  108. package/schemas/probe.schema.json +152 -12
  109. package/schemas/rubric.schema.json +1 -1
  110. package/schemas/sealed-run-record.schema.json +24 -17
@@ -1,7 +1,7 @@
1
1
  /** permitted interfaces and the per-operation declaration inventory. */
2
2
  import { z } from 'zod';
3
3
  import { DescriptorPointer } from './pointer.js';
4
- import { Identifier, KeyedShapeDescriptor, KeyName, KeyTypeMap, } from './primitives.js';
4
+ import { Identifier, KeyedShapeDescriptor, KeyName, KeyTypeMap, ToolName, } from './primitives.js';
5
5
  import { SensitivityWitness } from './sensitivity-witness.js';
6
6
  /** AD-19's closed four-member set, declared per pointer the descriptor names. */
7
7
  export const ChannelRole = z.enum([
@@ -100,7 +100,7 @@ export const Operation = z
100
100
  })
101
101
  .meta({
102
102
  id: 'Operation',
103
- description: "AD-19's per-operation declaration inventory for an interface that speaks HTTP. Carried by the `api`, `web`, and `mcp` branches alike, so the export names it once instead of inlining three copies that could drift apart.",
103
+ description: "AD-19's per-operation declaration inventory for an interface that speaks HTTP. Carried by the `api` and `web` branches alike, so the export names it once instead of inlining two copies that could drift apart.",
104
104
  });
105
105
  /**
106
106
  * AD-35 applied to a command: the operation names a logical executable and the
@@ -183,6 +183,59 @@ export const CommandOperation = z.strictObject({
183
183
  volatilePointers: z.array(DescriptorPointer),
184
184
  sensitivityWitness: SensitivityWitness.nullable().describe("AD-10, mandatory per declared operation rather than per interface, on the api operation's own terms. `null` is legal only for an operation declaring no keys in any request channel, which for a command means no argument, no option, no environment variable, and no standard input. An input-bearing operation declaring `null` fails a strict compilation under `undeclared-mandatory-input`."),
185
185
  });
186
+ /**
187
+ * The tool-call counterpart of `RequestShape`, a one-key strict object for the
188
+ * reason that comment gives: a record over a channel enum demands every member
189
+ * at parse time, and the partial spelling that would relax it is the one the
190
+ * Consistency Conventions ban.
191
+ *
192
+ * One key rather than four. A tool call carries an arguments object and nothing
193
+ * else, so declaring `path`, `query`, and `header` beside it would put three
194
+ * channels on every contract that no call can ever send and no oracle can ever
195
+ * reach.
196
+ */
197
+ export const McpRequestShape = z.strictObject({
198
+ arguments: KeyedShapeDescriptor.describe("The tool's declared arguments, keyed by the names the server publishes for them. AD-18 applies as it does to a header: a declaration names an argument and its type and never carries a credential value."),
199
+ });
200
+ /**
201
+ * Which output channel the operation's one response descriptor describes.
202
+ *
203
+ * Tagged on `kind` with one member, following `CommandDescriptorChannel`. The
204
+ * second member is already known: a tool that returns prose rather than
205
+ * structured content is outside this version, and admitting it later adds a
206
+ * member, which AD-11 makes additive, where retyping a bare field would be
207
+ * breaking.
208
+ */
209
+ export const McpDescriptorChannel = z.discriminatedUnion('kind', [
210
+ z.strictObject({ kind: z.literal('structured-result') }),
211
+ ]);
212
+ /**
213
+ * A tool-call operation.
214
+ *
215
+ * `toolName` is the whole transport identity. Every MCP call shares the one
216
+ * transport identity `tools/call` and the tool name is what distinguishes two
217
+ * of them, so a method and a path template would render one signature for every
218
+ * tool a server publishes and collide under `duplicate-operation-signature`.
219
+ * AD-40 needs that identity readable by a probe seeder who has never opened the
220
+ * contract, and a published tool name is exactly that.
221
+ *
222
+ * It carries the same `ResponseDescriptor` an api operation carries, over the
223
+ * structured result its `descriptorChannel` nominates, so all fourteen AD-31
224
+ * predicates read one descriptor one dereference deep with no kind-specific
225
+ * arm.
226
+ */
227
+ export const McpOperation = z.strictObject({
228
+ operationId: Identifier,
229
+ toolName: ToolName,
230
+ stateChangeMarker: z
231
+ .boolean()
232
+ .describe('AD-19: whether the operation is intended to change state. AD-20 rule 7 relevance reads it, and AD-10 selects the sensitivity channel by it. Both values are legal and neither is a default.'),
233
+ requestShape: McpRequestShape,
234
+ descriptorChannel: McpDescriptorChannel,
235
+ responseDescriptor: ResponseDescriptor,
236
+ volatilePointers: z.array(DescriptorPointer),
237
+ sensitivityWitness: SensitivityWitness.nullable().describe("AD-10, mandatory per declared operation rather than per interface, on the api operation's own terms. `null` is legal only for an operation declaring no keys in its arguments channel. An input-bearing operation declaring `null` fails a strict compilation under `undeclared-mandatory-input`."),
238
+ });
186
239
  /**
187
240
  * AD-19's four interface kinds, exported once so nothing else respells them:
188
241
  * the sealed evaluator brief carries interface identity without the operation
@@ -193,12 +246,14 @@ export const CommandOperation = z.strictObject({
193
246
  export const INTERFACE_KINDS = ['api', 'web', 'cli', 'mcp'];
194
247
  export const InterfaceKind = z.enum(INTERFACE_KINDS);
195
248
  const LOGICAL_ID_DESCRIPTION = "AD-35: a logical identifier for the interface, never a URL, host, or port. Mapping it to a target is the caller's, outside the contract.";
196
- const OPERATIONS_DESCRIPTION = 'No uniqueness constraint: two operations colliding on their transport identity after parameter-name erasure is `duplicate-operation-signature`, a coded compile-time error, and a schema that deduped them would delete it.';
197
- // `web` and `mcp` carry the api operation shape unchanged. A two-member union
198
- // would make either one a parse failure, and a parse failure carries no AD-5
199
- // code, no artifact path, and no name for the kind that is unsupported, which
200
- // is the opposite of AD-10's "fails compilation honestly under
201
- // `unsupported-interface-kind`".
249
+ const OPERATIONS_DESCRIPTION = 'No uniqueness constraint: two operations colliding on their transport identity is `duplicate-operation-signature`, a coded compile-time error, and a schema that deduped them would delete it. What that identity is depends on the kind, and `compile/interface-inventory.ts` is what computes it.';
250
+ // `web` is the only kind left sharing the api operation shape, and the factory
251
+ // survives its second caller leaving because that is what keeps the two
252
+ // branches byte-identical. `web` still fails compilation under
253
+ // `unsupported-interface-kind`, which needs the kind to reach the compiler:
254
+ // a parse failure carries no AD-5 code, no artifact path, and no name for the
255
+ // kind that is unsupported, which is the opposite of AD-10's "fails
256
+ // compilation honestly".
202
257
  const apiShapedInterface = (kind) => z.strictObject({
203
258
  logicalId: Identifier.describe(LOGICAL_ID_DESCRIPTION),
204
259
  kind: z.literal(kind),
@@ -206,13 +261,18 @@ const apiShapedInterface = (kind) => z.strictObject({
206
261
  });
207
262
  /**
208
263
  * Discriminated on `kind`, so an operation shape cannot be smuggled onto the
209
- * wrong interface: a `cli` interface declaring `method` and an `api` interface
210
- * declaring `invocation` are both parse errors.
264
+ * wrong interface: a `cli` interface declaring `method`, an `api` interface
265
+ * declaring `invocation`, and an `mcp` interface declaring either are all parse
266
+ * errors.
211
267
  */
212
268
  export const PermittedInterface = z.discriminatedUnion('kind', [
213
269
  apiShapedInterface('api'),
214
270
  apiShapedInterface('web'),
215
- apiShapedInterface('mcp'),
271
+ z.strictObject({
272
+ logicalId: Identifier.describe(LOGICAL_ID_DESCRIPTION),
273
+ kind: z.literal('mcp'),
274
+ operations: z.array(McpOperation).describe(OPERATIONS_DESCRIPTION),
275
+ }),
216
276
  z.strictObject({
217
277
  logicalId: Identifier.describe(LOGICAL_ID_DESCRIPTION),
218
278
  kind: z.literal('cli'),
@@ -220,9 +280,16 @@ export const PermittedInterface = z.discriminatedUnion('kind', [
220
280
  }),
221
281
  ]);
222
282
  /**
223
- * One interface's operations widened to the element union. TypeScript will not
224
- * iterate `iface.operations` directly, because the union's branches give it a
225
- * union of array types whose `map` signatures do not unify; this is the one
226
- * place that widening is spelled, so no call site invents its own.
283
+ * One interface's operations widened to the element union, for the callers
284
+ * that read only fields all three shapes carry. `iface.operations.map(...)` on
285
+ * an un-narrowed interface does not compile, because the union's branches give
286
+ * it a union of array types whose `map` signatures do not unify, and this is
287
+ * the one place that widening is spelled so no call site invents its own.
288
+ *
289
+ * A caller that narrows on `iface.kind` first wants the opposite and should
290
+ * read `iface.operations` directly: each branch declares its own element type,
291
+ * and this function discards the kind-to-shape correlation that a later kind
292
+ * test cannot recover. `buildPlanIndex` (`core/seal/plan-index.ts`) is the site
293
+ * that sorts by kind, and it narrows first for exactly that reason.
227
294
  */
228
295
  export const operationsOf = (iface) => iface.operations;
@@ -186,10 +186,28 @@ export declare const CommandInputBinding: z.ZodObject<{
186
186
  principal: z.ZodString;
187
187
  }, z.core.$strict>]>>>;
188
188
  }, z.core.$strict>;
189
+ /** The same shape over the one channel a tool call accepts input on. */
190
+ export declare const McpInputBinding: z.ZodObject<{
191
+ arguments: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
192
+ literal: z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>;
193
+ }, z.core.$strict>, z.ZodObject<{
194
+ matcher: z.ZodEnum<{
195
+ any: "any";
196
+ "type-violating": "type-violating";
197
+ }>;
198
+ }, z.core.$strict>, z.ZodObject<{
199
+ captured: z.ZodString;
200
+ }, z.core.$strict>, z.ZodObject<{
201
+ principal: z.ZodString;
202
+ }, z.core.$strict>]>>>;
203
+ }, z.core.$strict>;
189
204
  /**
190
205
  * A plain union rather than a discriminated one, and the agreement between a
191
206
  * step's bound channels and its operation's kind is a compile-time check
192
- * rather than a schema refinement.
207
+ * rather than a schema refinement. That agreement is checked per bound channel,
208
+ * so a step binding every channel of the wrong kind's branch to `null` parses
209
+ * and passes: `null` means the step binds nothing there, and there is nothing
210
+ * left to disagree about.
193
211
  *
194
212
  * A step names an `operationId` and nothing else; the kind of the interface
195
213
  * declaring that operation lives in a different subtree of the same document.
@@ -297,6 +315,19 @@ export declare const InputBinding: z.ZodUnion<readonly [z.ZodObject<{
297
315
  }, z.core.$strict>, z.ZodObject<{
298
316
  principal: z.ZodString;
299
317
  }, z.core.$strict>]>>>;
318
+ }, z.core.$strict>, z.ZodObject<{
319
+ arguments: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
320
+ literal: z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>;
321
+ }, z.core.$strict>, z.ZodObject<{
322
+ matcher: z.ZodEnum<{
323
+ any: "any";
324
+ "type-violating": "type-violating";
325
+ }>;
326
+ }, z.core.$strict>, z.ZodObject<{
327
+ captured: z.ZodString;
328
+ }, z.core.$strict>, z.ZodObject<{
329
+ principal: z.ZodString;
330
+ }, z.core.$strict>]>>>;
300
331
  }, z.core.$strict>]>;
301
332
  export type InputBinding = z.infer<typeof InputBinding>;
302
333
  export type BindingChannel = z.infer<typeof BindingChannel>;
@@ -430,6 +461,19 @@ export declare const InteractionStep: z.ZodObject<{
430
461
  }, z.core.$strict>, z.ZodObject<{
431
462
  principal: z.ZodString;
432
463
  }, z.core.$strict>]>>>;
464
+ }, z.core.$strict>, z.ZodObject<{
465
+ arguments: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
466
+ literal: z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>;
467
+ }, z.core.$strict>, z.ZodObject<{
468
+ matcher: z.ZodEnum<{
469
+ any: "any";
470
+ "type-violating": "type-violating";
471
+ }>;
472
+ }, z.core.$strict>, z.ZodObject<{
473
+ captured: z.ZodString;
474
+ }, z.core.$strict>, z.ZodObject<{
475
+ principal: z.ZodString;
476
+ }, z.core.$strict>]>>>;
433
477
  }, z.core.$strict>]>;
434
478
  after: z.ZodNullable<z.ZodString>;
435
479
  cardinality: z.ZodEnum<{
@@ -93,10 +93,17 @@ export const CommandInputBinding = z.strictObject({
93
93
  environment: BindingChannel,
94
94
  stdin: BindingChannel,
95
95
  });
96
+ /** The same shape over the one channel a tool call accepts input on. */
97
+ export const McpInputBinding = z.strictObject({
98
+ arguments: BindingChannel,
99
+ });
96
100
  /**
97
101
  * A plain union rather than a discriminated one, and the agreement between a
98
102
  * step's bound channels and its operation's kind is a compile-time check
99
- * rather than a schema refinement.
103
+ * rather than a schema refinement. That agreement is checked per bound channel,
104
+ * so a step binding every channel of the wrong kind's branch to `null` parses
105
+ * and passes: `null` means the step binds nothing there, and there is nothing
106
+ * left to disagree about.
100
107
  *
101
108
  * A step names an `operationId` and nothing else; the kind of the interface
102
109
  * declaring that operation lives in a different subtree of the same document.
@@ -106,7 +113,11 @@ export const CommandInputBinding = z.strictObject({
106
113
  * already carries, which makes a disagreeing step a second inconsistency to
107
114
  * check rather than one fewer.
108
115
  */
109
- export const InputBinding = z.union([ApiInputBinding, CommandInputBinding]);
116
+ export const InputBinding = z.union([
117
+ ApiInputBinding,
118
+ CommandInputBinding,
119
+ McpInputBinding,
120
+ ]);
110
121
  /**
111
122
  * AD-39's declared selector cardinality (owed item 2): what a step means when
112
123
  * its selector matches more than one observation was not decided at the
@@ -45,25 +45,39 @@ export declare const TransportChannel: z.ZodEnum<{
45
45
  export declare const COMMAND_CHANNELS: readonly ['argument', 'option', 'environment', 'stdin'];
46
46
  export type CommandChannelName = (typeof COMMAND_CHANNELS)[number];
47
47
  /**
48
- * Every channel `call-inputs` may take as its next segment, both kinds
48
+ * The one channel a tool call accepts input on. Every MCP tool call carries an
49
+ * arguments object and nothing else, so the tuple has one member and the kind
50
+ * declares no dead channels the way the transport shape would have given it
51
+ * three.
52
+ *
53
+ * Its own name rather than a reuse of `argument`: a command's positional
54
+ * argument and a tool call's named arguments object are different shapes with
55
+ * different declaration rules, and one name for both would make an
56
+ * `undeclared-mandatory-input` message ambiguous about which it meant.
57
+ */
58
+ export declare const MCP_CHANNELS: readonly ['arguments'];
59
+ export type McpChannelName = (typeof MCP_CHANNELS)[number];
60
+ /**
61
+ * Every channel `call-inputs` may take as its next segment, all three kinds
49
62
  * together. A pointer is parsed with no contract in hand, so the grammar
50
- * admits all eight and the question of which four a given operation may use is
63
+ * admits all nine and the question of which a given operation may use is
51
64
  * answered by reachability, which has the operation.
52
65
  */
53
- export declare const INPUT_CHANNELS: readonly ["path", "query", "header", "body", "argument", "option", "environment", "stdin"];
66
+ export declare const INPUT_CHANNELS: readonly ["path", "query", "header", "body", "argument", "option", "environment", "stdin", "arguments"];
54
67
  export type InputChannelName = (typeof INPUT_CHANNELS)[number];
55
68
  export declare const TAIL_BEARING_CHANNELS: readonly ["response-body", "response-headers", "stdout", "stderr"];
56
69
  export declare const SCALAR_CHANNELS: readonly ["response-status", "exit-code"];
57
- export declare const TRANSPORT_ROOTED_CHANNEL: "call-inputs";
70
+ export declare const INPUT_ROOTED_CHANNEL: "call-inputs";
58
71
  export declare const IDENTIFIER_ROOTED_CHANNEL: "artifact";
59
72
  /**
60
- * The response-side channels each kind produces, and neither produces the
61
- * other's. Declared here rather than rebuilt from a description, so a channel
62
- * added to the vocabulary has to be assigned to a side and a test can assert
63
- * the two partition the response side exactly.
73
+ * The response-side channels an interface that speaks HTTP produces and the
74
+ * ones a command produces, and neither produces the other's. A tool call fills
75
+ * a strict subset of the first. Declared here rather than rebuilt from a
76
+ * description, so a channel added to the vocabulary has to be assigned to a
77
+ * side and a test can assert the two partition the response side exactly.
64
78
  *
65
79
  * `call-inputs` belongs to neither: it carries what was sent rather than what
66
- * came back, and both kinds have it.
80
+ * came back, and every kind has it.
67
81
  */
68
82
  export declare const API_RESPONSE_CHANNELS: readonly ["response-body", "response-headers", "response-status"];
69
83
  export declare const COMMAND_RESPONSE_CHANNELS: readonly ["stdout", "stderr", "exit-code", "artifact"];
@@ -30,7 +30,7 @@ export const EvidenceChannel = z.enum(EVIDENCE_CHANNELS).meta({
30
30
  export const TRANSPORT_CHANNELS = ['path', 'query', 'header', 'body'];
31
31
  export const TransportChannel = z.enum(TRANSPORT_CHANNELS).meta({
32
32
  id: 'TransportChannel',
33
- description: "AD-19's four transport channels. They are the segment `call-inputs` takes before its tail, and the four keys an observation's recorded call inputs are keyed by.",
33
+ description: "AD-19's four transport channels. They are four of the segments `call-inputs` takes before its tail, and four of the keys an observation's recorded call inputs are keyed by; `INPUT_CHANNELS` is the whole set.",
34
34
  });
35
35
  /**
36
36
  * The four channels a command-kind operation accepts input on. They sit beside
@@ -49,14 +49,27 @@ export const COMMAND_CHANNELS = [
49
49
  'stdin',
50
50
  ];
51
51
  /**
52
- * Every channel `call-inputs` may take as its next segment, both kinds
52
+ * The one channel a tool call accepts input on. Every MCP tool call carries an
53
+ * arguments object and nothing else, so the tuple has one member and the kind
54
+ * declares no dead channels the way the transport shape would have given it
55
+ * three.
56
+ *
57
+ * Its own name rather than a reuse of `argument`: a command's positional
58
+ * argument and a tool call's named arguments object are different shapes with
59
+ * different declaration rules, and one name for both would make an
60
+ * `undeclared-mandatory-input` message ambiguous about which it meant.
61
+ */
62
+ export const MCP_CHANNELS = ['arguments'];
63
+ /**
64
+ * Every channel `call-inputs` may take as its next segment, all three kinds
53
65
  * together. A pointer is parsed with no contract in hand, so the grammar
54
- * admits all eight and the question of which four a given operation may use is
66
+ * admits all nine and the question of which a given operation may use is
55
67
  * answered by reachability, which has the operation.
56
68
  */
57
69
  export const INPUT_CHANNELS = [
58
70
  ...TRANSPORT_CHANNELS,
59
71
  ...COMMAND_CHANNELS,
72
+ ...MCP_CHANNELS,
60
73
  ];
61
74
  // The four-way partition (tail-bearing, scalar, transport-rooted,
62
75
  // identifier-rooted) is spelled out and typed against the enum rather than
@@ -73,7 +86,7 @@ export const SCALAR_CHANNELS = [
73
86
  'response-status',
74
87
  'exit-code',
75
88
  ];
76
- export const TRANSPORT_ROOTED_CHANNEL = 'call-inputs';
89
+ export const INPUT_ROOTED_CHANNEL = 'call-inputs';
77
90
  // A fourth class rather than a fourth rule. AD-26's repair for `call-inputs`
78
91
  // is that "a channel that names one of several things needs a declared segment
79
92
  // to resolve against", and a written file is the same situation: an operation
@@ -85,13 +98,14 @@ export const TRANSPORT_ROOTED_CHANNEL = 'call-inputs';
85
98
  // rather than evidence that resolves absent.
86
99
  export const IDENTIFIER_ROOTED_CHANNEL = 'artifact';
87
100
  /**
88
- * The response-side channels each kind produces, and neither produces the
89
- * other's. Declared here rather than rebuilt from a description, so a channel
90
- * added to the vocabulary has to be assigned to a side and a test can assert
91
- * the two partition the response side exactly.
101
+ * The response-side channels an interface that speaks HTTP produces and the
102
+ * ones a command produces, and neither produces the other's. A tool call fills
103
+ * a strict subset of the first. Declared here rather than rebuilt from a
104
+ * description, so a channel added to the vocabulary has to be assigned to a
105
+ * side and a test can assert the two partition the response side exactly.
92
106
  *
93
107
  * `call-inputs` belongs to neither: it carries what was sent rather than what
94
- * came back, and both kinds have it.
108
+ * came back, and every kind has it.
95
109
  */
96
110
  export const API_RESPONSE_CHANNELS = [
97
111
  'response-body',
@@ -134,7 +148,7 @@ export const RESPONSE_SIDE_CHANNELS = [
134
148
  const TOKEN = '(?:[^/~]|~[01])*';
135
149
  const TAIL = `(?:/${TOKEN})*`;
136
150
  const alternation = (members) => members.join('|');
137
- export const INTERACTION_POINTER_PATTERN = new RegExp(`^/interactions/${IDENTIFIER_CHARSET_SOURCE}/(?:(?:${alternation(TAIL_BEARING_CHANNELS)})${TAIL}|(?:${alternation(SCALAR_CHANNELS)})|${TRANSPORT_ROOTED_CHANNEL}/(?:${alternation(INPUT_CHANNELS)})${TAIL}|${IDENTIFIER_ROOTED_CHANNEL}/${IDENTIFIER_CHARSET_SOURCE}${TAIL})$`);
151
+ export const INTERACTION_POINTER_PATTERN = new RegExp(`^/interactions/${IDENTIFIER_CHARSET_SOURCE}/(?:(?:${alternation(TAIL_BEARING_CHANNELS)})${TAIL}|(?:${alternation(SCALAR_CHANNELS)})|${INPUT_ROOTED_CHANNEL}/(?:${alternation(INPUT_CHANNELS)})${TAIL}|${IDENTIFIER_ROOTED_CHANNEL}/${IDENTIFIER_CHARSET_SOURCE}${TAIL})$`);
138
152
  export const BOUND_ELEMENT_POINTER_PATTERN = new RegExp(`^@(?:/${TOKEN})+$`);
139
153
  export const DESCRIPTOR_POINTER_PATTERN = new RegExp(`^(?:/${TOKEN})*$`);
140
154
  /**
@@ -144,7 +158,7 @@ export const DESCRIPTOR_POINTER_PATTERN = new RegExp(`^(?:/${TOKEN})*$`);
144
158
  export const InteractionPointer = z
145
159
  .string()
146
160
  .regex(INTERACTION_POINTER_PATTERN)
147
- .describe('AD-26 interaction-rooted pointer: "/interactions/{stepId}/" followed by one channel of the closed vocabulary. `call-inputs` takes one input channel as its next segment, one of the four transport channels or one of the four command channels; `artifact` takes the identifier of a file the operation declares it writes; `response-status` and `exit-code` take no tail. Syntax only: whether the step exists and whether the evidence is reachable are compile-time checks, not schema checks.');
161
+ .describe('AD-26 interaction-rooted pointer: "/interactions/{stepId}/" followed by one channel of the closed vocabulary. `call-inputs` takes one input channel as its next segment, one of the four transport channels, one of the four command channels, or the `arguments` channel a tool call accepts; `artifact` takes the identifier of a file the operation declares it writes; `response-status` and `exit-code` take no tail. Syntax only: whether the step exists and whether the evidence is reachable are compile-time checks, not schema checks.');
148
162
  /**
149
163
  * Spelling 2, bound-element relative. Consumer: a `{ pointer }` operand inside
150
164
  * a quantifier predicate. Never an evidence target, because AD-3 computes
@@ -97,6 +97,31 @@ export declare const CommandProbeRequest: z.ZodObject<{
97
97
  }, z.core.$strict>], "kind">;
98
98
  }, z.core.$strict>;
99
99
  }, z.core.$strict>;
100
+ /**
101
+ * A request to call one tool on an MCP server.
102
+ *
103
+ * AD-35 again: `toolName` is the name the server publishes for the tool, and
104
+ * which server that is comes from the adapter's mapping of `interfaceId`,
105
+ * outside the contract. No transport URL, no command, no process.
106
+ *
107
+ * One channel. A tool call carries an arguments object, which is what
108
+ * `McpRequestShape` declares, so the request carries the same one channel the
109
+ * operation could declare keys in.
110
+ *
111
+ * `arguments` holds declared JSON values and carries no credential, for the
112
+ * reason AD-18 gives: authorization material is the adapter's, supplied by the
113
+ * same mapping that authorizes the target.
114
+ */
115
+ export declare const McpProbeRequest: z.ZodObject<{
116
+ probeId: z.ZodString;
117
+ interfaceId: z.ZodString;
118
+ operationId: z.ZodString;
119
+ kind: z.ZodLiteral<"mcp">;
120
+ toolName: z.ZodString;
121
+ channels: z.ZodObject<{
122
+ arguments: z.ZodRecord<z.ZodString, z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>>;
123
+ }, z.core.$strict>;
124
+ }, z.core.$strict>;
100
125
  export declare const ProbeRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
101
126
  probeId: z.ZodString;
102
127
  interfaceId: z.ZodString;
@@ -144,6 +169,15 @@ export declare const ProbeRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
144
169
  kind: z.ZodLiteral<"absent">;
145
170
  }, z.core.$strict>], "kind">;
146
171
  }, z.core.$strict>;
172
+ }, z.core.$strict>, z.ZodObject<{
173
+ probeId: z.ZodString;
174
+ interfaceId: z.ZodString;
175
+ operationId: z.ZodString;
176
+ kind: z.ZodLiteral<"mcp">;
177
+ toolName: z.ZodString;
178
+ channels: z.ZodObject<{
179
+ arguments: z.ZodRecord<z.ZodString, z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>>;
180
+ }, z.core.$strict>;
147
181
  }, z.core.$strict>], "kind">;
148
182
  /**
149
183
  * What the adapter observed. Deliberately response content only: no elapsed
@@ -221,6 +255,36 @@ export declare const CommandProbeObservation: z.ZodObject<{
221
255
  kind: z.ZodLiteral<"absent">;
222
256
  }, z.core.$strict>], "kind">>;
223
257
  }, z.core.$strict>;
258
+ /**
259
+ * What the adapter observed of one tool call: the envelope's error flag and the
260
+ * structured result.
261
+ *
262
+ * `isError` is the tool's own report that the call did not go through, and it
263
+ * is an observation exactly as a 500 and a non-zero exit are. A JSON-RPC error
264
+ * answering `tools/call` lands here too, with the error object as `result`: the
265
+ * server answered, and a server refusing a tool the contract declares is
266
+ * precisely the defect an oracle should be able to assert on. Only a policy
267
+ * denial, a cap, an abort, or a failure to establish the session throws.
268
+ *
269
+ * `result` is the structured content the tool returned, which is the channel
270
+ * the operation's response descriptor describes.
271
+ */
272
+ export declare const McpProbeObservation: z.ZodObject<{
273
+ probeId: z.ZodString;
274
+ interfaceId: z.ZodString;
275
+ operationId: z.ZodString;
276
+ kind: z.ZodLiteral<"mcp">;
277
+ isError: z.ZodBoolean;
278
+ result: z.ZodDiscriminatedUnion<[z.ZodObject<{
279
+ kind: z.ZodLiteral<"json">;
280
+ value: z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>;
281
+ }, z.core.$strict>, z.ZodObject<{
282
+ kind: z.ZodLiteral<"text">;
283
+ value: z.ZodString;
284
+ }, z.core.$strict>, z.ZodObject<{
285
+ kind: z.ZodLiteral<"absent">;
286
+ }, z.core.$strict>], "kind">;
287
+ }, z.core.$strict>;
224
288
  export declare const ProbeObservation: z.ZodDiscriminatedUnion<[z.ZodObject<{
225
289
  probeId: z.ZodString;
226
290
  interfaceId: z.ZodString;
@@ -270,6 +334,21 @@ export declare const ProbeObservation: z.ZodDiscriminatedUnion<[z.ZodObject<{
270
334
  }, z.core.$strict>, z.ZodObject<{
271
335
  kind: z.ZodLiteral<"absent">;
272
336
  }, z.core.$strict>], "kind">>;
337
+ }, z.core.$strict>, z.ZodObject<{
338
+ probeId: z.ZodString;
339
+ interfaceId: z.ZodString;
340
+ operationId: z.ZodString;
341
+ kind: z.ZodLiteral<"mcp">;
342
+ isError: z.ZodBoolean;
343
+ result: z.ZodDiscriminatedUnion<[z.ZodObject<{
344
+ kind: z.ZodLiteral<"json">;
345
+ value: z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>;
346
+ }, z.core.$strict>, z.ZodObject<{
347
+ kind: z.ZodLiteral<"text">;
348
+ value: z.ZodString;
349
+ }, z.core.$strict>, z.ZodObject<{
350
+ kind: z.ZodLiteral<"absent">;
351
+ }, z.core.$strict>], "kind">;
273
352
  }, z.core.$strict>], "kind">;
274
353
  export type CorpusResolveRequest = z.infer<typeof CorpusResolveRequest>;
275
354
  export type CorpusResolveResponse = z.infer<typeof CorpusResolveResponse>;
@@ -281,7 +360,9 @@ export type FileWriteRequest = z.infer<typeof FileWriteRequest>;
281
360
  export type FileWriteResponse = z.infer<typeof FileWriteResponse>;
282
361
  export type ApiProbeRequest = z.infer<typeof ApiProbeRequest>;
283
362
  export type CommandProbeRequest = z.infer<typeof CommandProbeRequest>;
363
+ export type McpProbeRequest = z.infer<typeof McpProbeRequest>;
284
364
  export type ProbeRequest = z.infer<typeof ProbeRequest>;
285
365
  export type ApiProbeObservation = z.infer<typeof ApiProbeObservation>;
286
366
  export type CommandProbeObservation = z.infer<typeof CommandProbeObservation>;
367
+ export type McpProbeObservation = z.infer<typeof McpProbeObservation>;
287
368
  export type ProbeObservation = z.infer<typeof ProbeObservation>;
@@ -1,7 +1,7 @@
1
1
  /** the request and response shape of every AD-28 port method. */
2
2
  import { z } from 'zod';
3
3
  import { HttpMethod, PathTemplate } from './interface.js';
4
- import { Identifier, JsonValue, KeyName, Rfc3339Utc } from './primitives.js';
4
+ import { EnvironmentKeyName, Identifier, JsonValue, KeyName, Rfc3339Utc, ToolName, } from './primitives.js';
5
5
  import { ProbeObservedBody, ProbeRequestBody, ProbeRequestStdin, } from './probe-body.js';
6
6
  export { ProbeObservedBody, ProbeRequestBody } from './probe-body.js';
7
7
  // AD-8: the corpus port resolves an opaque reference to bytes from a
@@ -100,14 +100,38 @@ export const CommandProbeRequest = z.strictObject({
100
100
  argument: z.record(KeyName, JsonValue),
101
101
  option: z.record(KeyName, JsonValue),
102
102
  environment: z
103
- .record(KeyName, z.string())
104
- .describe('String-valued because an environment variable is a string to the process; the other channels carry the declared JSON value.'),
103
+ .record(EnvironmentKeyName, z.string())
104
+ .describe('String-valued because an environment variable is a string to the process; the other channels carry the declared JSON value. Keyed more narrowly than the other channels: this one becomes real variables on a real process, where `A=B` as a key would smuggle a second assignment past anyone reading the mapping.'),
105
105
  stdin: ProbeRequestStdin,
106
106
  }),
107
107
  });
108
+ /**
109
+ * A request to call one tool on an MCP server.
110
+ *
111
+ * AD-35 again: `toolName` is the name the server publishes for the tool, and
112
+ * which server that is comes from the adapter's mapping of `interfaceId`,
113
+ * outside the contract. No transport URL, no command, no process.
114
+ *
115
+ * One channel. A tool call carries an arguments object, which is what
116
+ * `McpRequestShape` declares, so the request carries the same one channel the
117
+ * operation could declare keys in.
118
+ *
119
+ * `arguments` holds declared JSON values and carries no credential, for the
120
+ * reason AD-18 gives: authorization material is the adapter's, supplied by the
121
+ * same mapping that authorizes the target.
122
+ */
123
+ export const McpProbeRequest = z.strictObject({
124
+ ...probeCorrelation,
125
+ kind: z.literal('mcp'),
126
+ toolName: ToolName.describe("The tool the adapter calls, in the server's own spelling (AD-35). Which server publishes it is the adapter's mapping of `interfaceId`, from configuration outside the contract."),
127
+ channels: z.strictObject({
128
+ arguments: z.record(KeyName, JsonValue),
129
+ }),
130
+ });
108
131
  export const ProbeRequest = z.discriminatedUnion('kind', [
109
132
  ApiProbeRequest,
110
133
  CommandProbeRequest,
134
+ McpProbeRequest,
111
135
  ]);
112
136
  /**
113
137
  * What the adapter observed. Deliberately response content only: no elapsed
@@ -151,7 +175,30 @@ export const CommandProbeObservation = z.strictObject({
151
175
  stderr: ProbeObservedBody,
152
176
  artifacts: z.record(Identifier, ProbeObservedBody),
153
177
  });
178
+ /**
179
+ * What the adapter observed of one tool call: the envelope's error flag and the
180
+ * structured result.
181
+ *
182
+ * `isError` is the tool's own report that the call did not go through, and it
183
+ * is an observation exactly as a 500 and a non-zero exit are. A JSON-RPC error
184
+ * answering `tools/call` lands here too, with the error object as `result`: the
185
+ * server answered, and a server refusing a tool the contract declares is
186
+ * precisely the defect an oracle should be able to assert on. Only a policy
187
+ * denial, a cap, an abort, or a failure to establish the session throws.
188
+ *
189
+ * `result` is the structured content the tool returned, which is the channel
190
+ * the operation's response descriptor describes.
191
+ */
192
+ export const McpProbeObservation = z.strictObject({
193
+ ...probeCorrelation,
194
+ kind: z.literal('mcp'),
195
+ isError: z
196
+ .boolean()
197
+ .describe("The MCP envelope's own error flag, true when the tool reported the call failed. Sealed evidence carries it as `responseStatus` 1 for true and 0 for false, since a tool call has no transport status of its own."),
198
+ result: ProbeObservedBody,
199
+ });
154
200
  export const ProbeObservation = z.discriminatedUnion('kind', [
155
201
  ApiProbeObservation,
156
202
  CommandProbeObservation,
203
+ McpProbeObservation,
157
204
  ]);
@@ -4,6 +4,8 @@ export declare const IDENTIFIER_PATTERN: RegExp;
4
4
  /** the charset spelling 1 embeds; exported so pointer.ts never re-spells it. */
5
5
  export declare const IDENTIFIER_CHARSET_SOURCE = "[a-z0-9]+(?:-[a-z0-9]+)*";
6
6
  export declare const Identifier: z.ZodString;
7
+ export declare const TOOL_NAME_PATTERN: RegExp;
8
+ export declare const ToolName: z.ZodString;
7
9
  export declare const BEHAVIOR_ID_PATTERN: RegExp;
8
10
  export declare const ORACLE_ID_PATTERN: RegExp;
9
11
  export declare const PROBE_ID_PATTERN: RegExp;
@@ -39,6 +41,22 @@ export declare const JsonTypeName: z.ZodEnum<{
39
41
  string: "string";
40
42
  }>;
41
43
  export declare const KeyName: z.ZodString;
44
+ /**
45
+ * An environment variable name, at the port boundary where one becomes a real
46
+ * variable on a real process. `KeyName` is plain text, and a plain-text key is
47
+ * a smuggling channel here: `A=B` reaches the child as a variable `A` whose
48
+ * value carries `B=` in front of the declared one, and a key holding a NUL
49
+ * fails the spawn itself. The charset is the portable one, which is what a
50
+ * caller reading a mapping's allowlist assumes it is reading.
51
+ *
52
+ * This keys the port message, which every request crosses, and the
53
+ * authorization allowlist, which only a caller who parses their own mapping
54
+ * crosses: nothing in this package parses `CommandTargetPolicy`. The contract
55
+ * side keeps `KeyName`, since AD-19's channel shapes are one grammar across
56
+ * every channel, so a contract declaring a malformed environment key compiles
57
+ * and fails at this boundary with `schema-parse-failure`.
58
+ */
59
+ export declare const EnvironmentKeyName: z.ZodString;
42
60
  export type JsonValue = string | number | boolean | null | JsonValue[] | {
43
61
  [key: string]: JsonValue;
44
62
  };