@cat-factory/orchestration 0.251.0 → 0.253.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 (29) hide show
  1. package/dist/modules/execution/DeployerStepController.d.ts.map +1 -1
  2. package/dist/modules/execution/DeployerStepController.js +12 -2
  3. package/dist/modules/execution/DeployerStepController.js.map +1 -1
  4. package/dist/modules/execution/DisposerStepController.d.ts.map +1 -1
  5. package/dist/modules/execution/DisposerStepController.js +15 -17
  6. package/dist/modules/execution/DisposerStepController.js.map +1 -1
  7. package/dist/modules/execution/prReport.environments.d.ts +8 -3
  8. package/dist/modules/execution/prReport.environments.d.ts.map +1 -1
  9. package/dist/modules/execution/prReport.environments.js +25 -40
  10. package/dist/modules/execution/prReport.environments.js.map +1 -1
  11. package/dist/modules/execution/runOutcome.boundary.d.ts.map +1 -1
  12. package/dist/modules/execution/runOutcome.boundary.js +29 -0
  13. package/dist/modules/execution/runOutcome.boundary.js.map +1 -1
  14. package/dist/modules/execution/toolServers.logic.d.ts +6 -3
  15. package/dist/modules/execution/toolServers.logic.d.ts.map +1 -1
  16. package/dist/modules/execution/toolServers.logic.js +6 -3
  17. package/dist/modules/execution/toolServers.logic.js.map +1 -1
  18. package/dist/validation/validateBinaryGenerators.d.ts +24 -0
  19. package/dist/validation/validateBinaryGenerators.d.ts.map +1 -0
  20. package/dist/validation/validateBinaryGenerators.js +138 -0
  21. package/dist/validation/validateBinaryGenerators.js.map +1 -0
  22. package/dist/validation/validateRegistrations.d.ts.map +1 -1
  23. package/dist/validation/validateRegistrations.js +5 -356
  24. package/dist/validation/validateRegistrations.js.map +1 -1
  25. package/dist/validation/validateToolServers.d.ts +24 -0
  26. package/dist/validation/validateToolServers.d.ts.map +1 -0
  27. package/dist/validation/validateToolServers.js +337 -0
  28. package/dist/validation/validateToolServers.js.map +1 -0
  29. package/package.json +11 -11
@@ -1 +1 @@
1
- {"version":3,"file":"toolServers.logic.js","sourceRoot":"","sources":["../../../src/modules/execution/toolServers.logic.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"toolServers.logic.js","sourceRoot":"","sources":["../../../src/modules/execution/toolServers.logic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { BinaryGeneratorRegistry } from '@cat-factory/kernel';
2
+ import type { RegistrationProblem } from './validateRegistrations.js';
3
+ /**
4
+ * Section 9 of `collectRegistrationProblems`: every generative binary integration a deployment
5
+ * registers must be a definition the platform can actually dispatch against.
6
+ *
7
+ * Its own module rather than more of `validateRegistrations.ts` because the section is a cohesive
8
+ * concern with a growing rule set, and its host had reached the file-size ratchet.
9
+ *
10
+ * Boot is the only place these can be caught. There is no write boundary that ever refused them
11
+ * (they are code), and every failure below is silent at run time in the same expensive way: a
12
+ * malformed definition or an unparseable contract becomes an integration the brief describes with
13
+ * no operations, a credential key that is not a valid environment-variable name is dropped by the
14
+ * harness's env validation and reappears as an unexplained 401 mid-run, and a cleartext endpoint
15
+ * puts that credential on the wire from inside the run container. Each of those costs a run to
16
+ * discover and names nothing that points back at the registration.
17
+ *
18
+ * A declared MEDIA TYPE that contradicts the declared modalities is an error too, not a warning:
19
+ * both halves drive selection (a step's content-type coverage is checked against `modalities`,
20
+ * while the brief tells the agent the `mediaTypes`), so an integration claiming `audio` while
21
+ * listing `image/png` will be picked for one job and asked to do the other.
22
+ */
23
+ export declare function checkBinaryGenerators(registry: BinaryGeneratorRegistry | undefined): RegistrationProblem[];
24
+ //# sourceMappingURL=validateBinaryGenerators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateBinaryGenerators.d.ts","sourceRoot":"","sources":["../../src/validation/validateBinaryGenerators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAelE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAErE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,uBAAuB,GAAG,SAAS,GAC5C,mBAAmB,EAAE,CAqBvB"}
@@ -0,0 +1,138 @@
1
+ import { describeFoundationalProblem, isAllowedMcpHttpUrl, validateFoundationalDefinition, } from '@cat-factory/kernel';
2
+ import { binaryCredentialInjectionName, binaryGeneratorDefinitionIssues, comparableCredentialInjectionName, modalitiesOfMediaType, } from '@cat-factory/contracts';
3
+ /**
4
+ * Section 9 of `collectRegistrationProblems`: every generative binary integration a deployment
5
+ * registers must be a definition the platform can actually dispatch against.
6
+ *
7
+ * Its own module rather than more of `validateRegistrations.ts` because the section is a cohesive
8
+ * concern with a growing rule set, and its host had reached the file-size ratchet.
9
+ *
10
+ * Boot is the only place these can be caught. There is no write boundary that ever refused them
11
+ * (they are code), and every failure below is silent at run time in the same expensive way: a
12
+ * malformed definition or an unparseable contract becomes an integration the brief describes with
13
+ * no operations, a credential key that is not a valid environment-variable name is dropped by the
14
+ * harness's env validation and reappears as an unexplained 401 mid-run, and a cleartext endpoint
15
+ * puts that credential on the wire from inside the run container. Each of those costs a run to
16
+ * discover and names nothing that points back at the registration.
17
+ *
18
+ * A declared MEDIA TYPE that contradicts the declared modalities is an error too, not a warning:
19
+ * both halves drive selection (a step's content-type coverage is checked against `modalities`,
20
+ * while the brief tells the agent the `mediaTypes`), so an integration claiming `audio` while
21
+ * listing `image/png` will be picked for one job and asked to do the other.
22
+ */
23
+ export function checkBinaryGenerators(registry) {
24
+ const problems = [];
25
+ if (!registry)
26
+ return problems;
27
+ const valid = [];
28
+ for (const definition of registry.all()) {
29
+ const issues = binaryGeneratorDefinitionIssues(definition);
30
+ if (issues.length > 0) {
31
+ problems.push({
32
+ severity: 'error',
33
+ code: 'binary_generator_invalid',
34
+ message: `Generative binary integration "${definition.id}" is not a valid definition: ${issues.join('; ')}`,
35
+ });
36
+ // The checks below read fields this parse just called malformed, so reporting them too
37
+ // would restate one fault as several.
38
+ continue;
39
+ }
40
+ valid.push(definition);
41
+ problems.push(...checkBinaryGeneratorDetails(definition));
42
+ }
43
+ problems.push(...checkInjectionNameCollisions(valid));
44
+ return problems;
45
+ }
46
+ /**
47
+ * The one check that spans DEFINITIONS: two integrations may not inject different values into one
48
+ * environment variable.
49
+ *
50
+ * Within a definition the schema already refuses a repeated injection name. Across definitions the
51
+ * same name is legitimate and common, because one vendor behind an image endpoint and a music
52
+ * endpoint is one account: what makes that case safe is that both look the value up under the SAME
53
+ * key, so whichever integration is resolved first sets the variable to exactly what the other
54
+ * wanted. Different keys behind one name is the opposite, and there is no arbitration that makes
55
+ * it right. Serving the first claimant sets the variable the second integration's brief tells the
56
+ * agent to read, so the agent authenticates one vendor with another's key; withholding it (what
57
+ * dispatch does, since a mothership node validates nothing) costs both integrations every run.
58
+ *
59
+ * Neither outcome is what the deployment meant, and the remedy is one `envName` on one definition,
60
+ * which is why this is an error at boot rather than a warning nobody acts on. Only definitions
61
+ * that PARSED are compared: a malformed one has already been reported, and reading its credentials
62
+ * here would restate that fault as a second, more confusing one.
63
+ */
64
+ function checkInjectionNameCollisions(definitions) {
65
+ // Grouped by the COMPARABLE (case-folded) name and reported under the spelling the deployment
66
+ // wrote, because `ACME_KEY` and `acme_key` are one variable wherever the environment is
67
+ // case-insensitive and two everywhere else: the pair collides on exactly the platform where the
68
+ // operator has the least chance of noticing.
69
+ const claims = new Map();
70
+ for (const definition of definitions) {
71
+ for (const credential of definition.credentials ?? []) {
72
+ const claim = claims.get(comparableCredentialInjectionName(credential)) ?? {
73
+ spelling: binaryCredentialInjectionName(credential),
74
+ byKey: new Map(),
75
+ };
76
+ claim.byKey.set(credential.key, [...(claim.byKey.get(credential.key) ?? []), definition.id]);
77
+ claims.set(comparableCredentialInjectionName(credential), claim);
78
+ }
79
+ }
80
+ const problems = [];
81
+ for (const [, { spelling: envName, byKey }] of claims) {
82
+ if (byKey.size < 2)
83
+ continue;
84
+ const described = [...byKey]
85
+ .map(([key, ids]) => `"${key}" (${ids.join(', ')})`)
86
+ .sort()
87
+ .join(' and ');
88
+ problems.push({
89
+ severity: 'error',
90
+ code: 'binary_generator_injection_name_collision',
91
+ message: `Generative binary integrations disagree about environment variable "${envName}": it is ` +
92
+ `declared for lookup keys ${described}. One variable cannot hold both values, so an agent ` +
93
+ `told to read it for one integration would authenticate with the other's credential. Give ` +
94
+ `one of them a distinct \`envName\`, or point both at the same lookup key if they really ` +
95
+ `share an account.`,
96
+ });
97
+ }
98
+ return problems;
99
+ }
100
+ /** The per-definition checks a valid PARSE cannot make: the endpoint, contracts, media types. */
101
+ function checkBinaryGeneratorDetails(definition) {
102
+ const problems = [];
103
+ const invalid = (code, message) => {
104
+ problems.push({ severity: 'error', code, message });
105
+ };
106
+ // The same rule an HTTP tool server's URL is held to, and for the same reason the helper
107
+ // states: a declared credential rides this request, so cleartext off loopback puts it on the
108
+ // wire. (The helper is MCP-named because that was its first caller; the rule is not.)
109
+ if (definition.endpoint && !isAllowedMcpHttpUrl(definition.endpoint)) {
110
+ invalid('insecure_binary_generator_endpoint', `Generative binary integration "${definition.id}" has endpoint "${definition.endpoint}". Its ` +
111
+ `credential is sent with every request, so the endpoint must be https (plain http is ` +
112
+ `accepted only on loopback).`);
113
+ }
114
+ for (const problem of validateFoundationalDefinition({ contracts: definition.contracts })) {
115
+ invalid('binary_generator_invalid', `Generative binary integration "${definition.id}": ${describeFoundationalProblem(problem)}`);
116
+ }
117
+ const declared = new Set(definition.modalities);
118
+ for (const mediaType of definition.mediaTypes ?? []) {
119
+ const consistent = modalitiesOfMediaType(mediaType);
120
+ // An UNRECOGNISED media type is not a fault: the platform's classifier is not a registry of
121
+ // every format that exists, and refusing one would make registering a new codec impossible.
122
+ // A recognised one that CONTRADICTS the declaration is, because both drive selection.
123
+ //
124
+ // Contradiction is an empty INTERSECTION, not an absent member, and for 3D that is the whole
125
+ // difference: a `.glb` is consistent with both `3d-model` and `3d-scene` because the container
126
+ // does not record which it holds, so requiring every member would refuse a scene generator
127
+ // for declaring the only format it can emit.
128
+ if (consistent.length > 0 && !consistent.some((modality) => declared.has(modality))) {
129
+ const names = consistent.join('/');
130
+ invalid('binary_generator_modality_mismatch', `Generative binary integration "${definition.id}" declares media type "${mediaType}" ` +
131
+ `(${names}) but lists none of those among its modalities ` +
132
+ `(${definition.modalities.join(', ')}). A step selecting it for ${names} would be ` +
133
+ `refused, and one selecting it for the listed modalities would be told it can emit this.`);
134
+ }
135
+ }
136
+ return problems;
137
+ }
138
+ //# sourceMappingURL=validateBinaryGenerators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateBinaryGenerators.js","sourceRoot":"","sources":["../../src/validation/validateBinaryGenerators.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"validateRegistrations.d.ts","sourceRoot":"","sources":["../../src/validation/validateRegistrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAE5D,OAAO,KAAK,EAEV,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,YAAY,EACZ,wBAAwB,EAGxB,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,qBAAqB,CAAA;AAgE5B,4FAA4F;AAC5F,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAA;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,iBAAiB,EAAE,iBAAiB,CAAA;IACpC;;;;OAIG;IACH,YAAY,EAAE,YAAY,CAAA;IAC1B;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,wBAAwB,CAAA;IACnD;;;;;;OAMG;IACH,2BAA2B,CAAC,EAAE,2BAA2B,CAAA;IACzD;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,uBAAuB,CAAA;IACjD;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAA;IAC/C;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,oBAAoB,CAAA;IACtC;;;;;;;OAOG;IACH,0BAA0B,CAAC,EAAE,0BAA0B,CAAA;CACxD;AAED,uFAAuF;AACvF,MAAM,WAAW,4BAA4B;IAC3C,+EAA+E;IAC/E,UAAU,EAAE,mBAAmB,CAAA;IAC/B,qGAAqG;IACrG,kBAAkB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACxC,qGAAqG;IACrG,kBAAkB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACxC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACrC;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAA;IAC/C;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAA;CAC5D;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,4BAA4B,GACjC,mBAAmB,EAAE,CA4FvB;AA+kCD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,CAuB9E;AAOD,yGAAyG;AACzG,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,CASlF;AAED,uFAAuF;AACvF,wBAAgB,gCAAgC,IAAI,IAAI,CAEvD"}
1
+ {"version":3,"file":"validateRegistrations.d.ts","sourceRoot":"","sources":["../../src/validation/validateRegistrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAG5D,OAAO,KAAK,EAEV,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,YAAY,EACZ,wBAAwB,EACxB,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,qBAAqB,CAAA;AAiD5B,4FAA4F;AAC5F,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAA;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,iBAAiB,EAAE,iBAAiB,CAAA;IACpC;;;;OAIG;IACH,YAAY,EAAE,YAAY,CAAA;IAC1B;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,wBAAwB,CAAA;IACnD;;;;;;OAMG;IACH,2BAA2B,CAAC,EAAE,2BAA2B,CAAA;IACzD;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,uBAAuB,CAAA;IACjD;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAA;IAC/C;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,oBAAoB,CAAA;IACtC;;;;;;;OAOG;IACH,0BAA0B,CAAC,EAAE,0BAA0B,CAAA;CACxD;AAED,uFAAuF;AACvF,MAAM,WAAW,4BAA4B;IAC3C,+EAA+E;IAC/E,UAAU,EAAE,mBAAmB,CAAA;IAC/B,qGAAqG;IACrG,kBAAkB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACxC,qGAAqG;IACrG,kBAAkB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACxC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACrC;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAA;IAC/C;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAA;CAC5D;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,4BAA4B,GACjC,mBAAmB,EAAE,CA4FvB;AA6sBD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,CAuB9E;AAOD,yGAAyG;AACzG,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,4BAA4B,GAAG,IAAI,CASlF;AAED,uFAAuF;AACvF,wBAAgB,gCAAgC,IAAI,IAAI,CAEvD"}
@@ -1,6 +1,8 @@
1
1
  import { INLINE_ENGINE_SYSTEM_PROMPTS, runsInContainer } from '@cat-factory/agents';
2
- import { CI_FIXER_AGENT_KIND, CONFLICT_RESOLVER_AGENT_KIND, FIXER_AGENT_KIND, ON_CALL_AGENT_KIND, MCP_OAUTH_DEFAULT_HEADER, MCP_SUPPORTED_HARNESSES, TOOL_SERVER_BUDGET, describeFoundationalProblem, isAllowedMcpHttpUrl, isValidMcpServerId, isValidMcpToolName, mcpServableHarnesses, seedPipelines, stubGateContext, toolServerDeclaredBytes, validateFoundationalDefinition, } from '@cat-factory/kernel';
3
- import { binaryGeneratorDefinitionIssues, descriptorConditionHasPredicate, duplicatedDescriptorSectionCaptions, isDeploymentScopedSource, foundationalServiceDefinitionIssues, isEnvVariableName, isNamespacedId, isReservedPlatformEnvKey, isToolchainEnvName, modalitiesOfMediaType, reservedEnvKeyMessage, toolchainEnvNameMessage, isValidResultViewId, RESULT_VIEW_ID_SET, } from '@cat-factory/contracts';
2
+ import { checkBinaryGenerators } from './validateBinaryGenerators.js';
3
+ import { CI_FIXER_AGENT_KIND, CONFLICT_RESOLVER_AGENT_KIND, FIXER_AGENT_KIND, ON_CALL_AGENT_KIND, describeFoundationalProblem, seedPipelines, stubGateContext, validateFoundationalDefinition, } from '@cat-factory/kernel';
4
+ import { descriptorConditionHasPredicate, duplicatedDescriptorSectionCaptions, isDeploymentScopedSource, foundationalServiceDefinitionIssues, isNamespacedId, isValidResultViewId, RESULT_VIEW_ID_SET, } from '@cat-factory/contracts';
5
+ import { checkKindToolServers } from './validateToolServers.js';
4
6
  // ---------------------------------------------------------------------------
5
7
  // Boot-time validation of the deployment's registered extensions (agent kinds, gates,
6
8
  // pipelines). A typo'd gate `helperKind`, an unknown `resultView`, or a pipeline naming a
@@ -101,7 +103,7 @@ export function collectRegistrationProblems(opts) {
101
103
  // 8. Deployment-registered FOUNDATIONAL SERVICES (only when a registry is supplied).
102
104
  problems.push(...checkFoundationalServices(opts));
103
105
  // 9. Deployment-registered GENERATIVE BINARY INTEGRATIONS (only when a registry is supplied).
104
- problems.push(...checkBinaryGenerators(opts));
106
+ problems.push(...checkBinaryGenerators(opts.registries.binaryGeneratorRegistry));
105
107
  // 10. Deployment-registered PROMPT FRAGMENTS (only when a registry is supplied).
106
108
  problems.push(...checkPromptFragments(opts));
107
109
  return problems;
@@ -170,81 +172,6 @@ function checkPromptFragments(opts) {
170
172
  }
171
173
  return problems;
172
174
  }
173
- /**
174
- * Section 9 of {@link collectRegistrationProblems}: every generative binary integration a
175
- * deployment registers must be a definition the platform can actually dispatch against.
176
- *
177
- * Boot is the only place these can be caught. There is no write boundary that ever refused them
178
- * (they are code), and every failure below is silent at run time in the same expensive way: a
179
- * malformed definition or an unparseable contract becomes an integration the brief describes with
180
- * no operations, a credential key that is not a valid environment-variable name is dropped by the
181
- * harness's env validation and reappears as an unexplained 401 mid-run, and a cleartext endpoint
182
- * puts that credential on the wire from inside the run container. Each of those costs a run to
183
- * discover and names nothing that points back at the registration.
184
- *
185
- * A declared MEDIA TYPE that contradicts the declared modalities is an error too, not a warning:
186
- * both halves drive selection (a step's content-type coverage is checked against `modalities`,
187
- * while the brief tells the agent the `mediaTypes`), so an integration claiming `audio` while
188
- * listing `image/png` will be picked for one job and asked to do the other.
189
- */
190
- function checkBinaryGenerators(opts) {
191
- const problems = [];
192
- if (!opts.registries.binaryGeneratorRegistry)
193
- return problems;
194
- for (const definition of opts.registries.binaryGeneratorRegistry.all()) {
195
- const issues = binaryGeneratorDefinitionIssues(definition);
196
- if (issues.length > 0) {
197
- problems.push({
198
- severity: 'error',
199
- code: 'binary_generator_invalid',
200
- message: `Generative binary integration "${definition.id}" is not a valid definition: ${issues.join('; ')}`,
201
- });
202
- // The checks below read fields this parse just called malformed, so reporting them too
203
- // would restate one fault as several.
204
- continue;
205
- }
206
- problems.push(...checkBinaryGeneratorDetails(definition));
207
- }
208
- return problems;
209
- }
210
- /** The per-definition checks a valid PARSE cannot make: the endpoint, contracts, media types. */
211
- function checkBinaryGeneratorDetails(definition) {
212
- const problems = [];
213
- const invalid = (code, message) => {
214
- problems.push({ severity: 'error', code, message });
215
- };
216
- // The same rule an HTTP tool server's URL is held to, and for the same reason the helper
217
- // states: a declared credential rides this request, so cleartext off loopback puts it on the
218
- // wire. (The helper is MCP-named because that was its first caller; the rule is not.)
219
- if (definition.endpoint && !isAllowedMcpHttpUrl(definition.endpoint)) {
220
- invalid('insecure_binary_generator_endpoint', `Generative binary integration "${definition.id}" has endpoint "${definition.endpoint}". Its ` +
221
- `credential is sent with every request, so the endpoint must be https (plain http is ` +
222
- `accepted only on loopback).`);
223
- }
224
- for (const problem of validateFoundationalDefinition({ contracts: definition.contracts })) {
225
- invalid('binary_generator_invalid', `Generative binary integration "${definition.id}": ${describeFoundationalProblem(problem)}`);
226
- }
227
- const declared = new Set(definition.modalities);
228
- for (const mediaType of definition.mediaTypes ?? []) {
229
- const consistent = modalitiesOfMediaType(mediaType);
230
- // An UNRECOGNISED media type is not a fault: the platform's classifier is not a registry of
231
- // every format that exists, and refusing one would make registering a new codec impossible.
232
- // A recognised one that CONTRADICTS the declaration is, because both drive selection.
233
- //
234
- // Contradiction is an empty INTERSECTION, not an absent member, and for 3D that is the whole
235
- // difference: a `.glb` is consistent with both `3d-model` and `3d-scene` because the container
236
- // does not record which it holds, so requiring every member would refuse a scene generator
237
- // for declaring the only format it can emit.
238
- if (consistent.length > 0 && !consistent.some((modality) => declared.has(modality))) {
239
- const names = consistent.join('/');
240
- invalid('binary_generator_modality_mismatch', `Generative binary integration "${definition.id}" declares media type "${mediaType}" ` +
241
- `(${names}) but lists none of those among its modalities ` +
242
- `(${definition.modalities.join(', ')}). A step selecting it for ${names} would be ` +
243
- `refused, and one selecting it for the listed modalities would be told it can emit this.`);
244
- }
245
- }
246
- return problems;
247
- }
248
175
  /**
249
176
  * Section 8 of {@link collectRegistrationProblems}: every foundational service a deployment
250
177
  * registers in code must be a definition the platform would have accepted over its own write
@@ -435,284 +362,6 @@ function checkKindSkills(kind, registry) {
435
362
  }
436
363
  return problems;
437
364
  }
438
- /**
439
- * A kind's declared TOOL SERVERS: the whole-list checks (unregistered ids, the per-dispatch budget,
440
- * the container surface), with each definition's own checks in
441
- * {@link checkToolServerDefinition}. "Declared for" includes assigned servers, see
442
- * {@link checkAgentCapabilities}.
443
- *
444
- * - an unregistered id is an ERROR, like an unregistered skill;
445
- * - past EITHER dimension of the per-dispatch budget is a WARNING, because the dispatch drops the
446
- * excess under `over_budget` rather than failing: the run still works, with fewer tools than the
447
- * deployment believes it wired, and boot is the only place the DECLARATIONS past the line can be
448
- * named. A warning also keeps the accretion case honest, a kind going over budget through
449
- * `assignToolServers` calls in several packages none of which is individually wrong. Both
450
- * dimensions are checked because the dispatch enforces both, and a byte-driven drop is the one
451
- * that surprises: a handful of servers with fat `env`/`args`/`headers` blocks is under the count
452
- * and over the payload;
453
- * - tool servers on a NON-container kind is a WARNING: an inline LLM call has no CLI to wire them
454
- * into, so they can never take effect. A warning rather than an error because a deployment may
455
- * deliberately declare them ahead of moving the kind onto a container surface.
456
- */
457
- function checkKindToolServers(kind, registry) {
458
- const problems = [];
459
- const tools = registry.toolServersFor(kind);
460
- for (const id of tools.unknown) {
461
- problems.push({
462
- severity: 'error',
463
- code: 'unknown_tool_server',
464
- message: `Agent kind "${kind}" declares tool server "${id}", which is not registered. Call ` +
465
- `registry.registerToolServer({ id: '${id}', … }) before registering the kind, or ` +
466
- `declare the server inline.`,
467
- });
468
- }
469
- for (const server of tools.servers)
470
- problems.push(...checkToolServerDefinition(kind, server));
471
- problems.push(...checkToolServerBudget(kind, tools.servers));
472
- if (tools.servers.length && !runsInContainer(kind, registry)) {
473
- problems.push({
474
- severity: 'warn',
475
- code: 'tool_servers_without_container',
476
- message: `Agent kind "${kind}" declares tool servers but does not run in a container — an ` +
477
- `inline LLM step has no agent CLI to wire them into, so they will never be available. ` +
478
- `Give the kind a container surface (agent.surface: 'container-explore' / ` +
479
- `'container-coding') or drop the tool servers.`,
480
- });
481
- }
482
- return problems;
483
- }
484
- /**
485
- * The per-dispatch budget, BOTH dimensions, as warnings: the dispatch drops the excess under
486
- * `over_budget` and the run works with fewer tools, so a registration fault belongs at boot rather
487
- * than in a refusal that takes the deployment down.
488
- *
489
- * The byte check measures {@link toolServerDeclaredBytes}, which is a FLOOR (a resolved credential
490
- * only adds to it), so a declaration already past the budget here is certainly past it at dispatch.
491
- * What boot deliberately does NOT claim is WHICH servers a run will lose: the dispatch keeps every
492
- * server that still fits, so once bytes are what bind the survivors are not a prefix of the
493
- * declaration. The count and the budget are what the author acts on, and the run itself names each
494
- * server it dropped.
495
- */
496
- function checkToolServerBudget(kind, servers) {
497
- const problems = [];
498
- if (servers.length > TOOL_SERVER_BUDGET.maxServers) {
499
- problems.push({
500
- severity: 'warn',
501
- code: 'too_many_tool_servers',
502
- message: `Agent kind "${kind}" has ${servers.length} tool servers declared for it, past the ` +
503
- `per-dispatch budget of ${TOOL_SERVER_BUDGET.maxServers}. A dispatch wires the first ` +
504
- `${TOOL_SERVER_BUDGET.maxServers} in declaration order and states the rest to the agent as ` +
505
- `unavailable (over_budget). Drop some, or split the work across kinds.`,
506
- });
507
- }
508
- const bytes = servers.reduce((total, server) => total + toolServerDeclaredBytes(server), 0);
509
- if (bytes > TOOL_SERVER_BUDGET.maxTotalBytes) {
510
- problems.push({
511
- severity: 'warn',
512
- code: 'tool_servers_over_byte_budget',
513
- message: `Agent kind "${kind}" declares tool servers whose transport config alone measures ${bytes} ` +
514
- `bytes, past the per-dispatch budget of ${TOOL_SERVER_BUDGET.maxTotalBytes}. A dispatch ` +
515
- `wires servers until that budget is spent and states the rest to the agent as unavailable ` +
516
- `(over_budget); resolved credentials only add to this figure, and which servers lose out ` +
517
- `depends on their sizes. Trim the env/args/headers blocks, or split the work across kinds.`,
518
- });
519
- }
520
- return problems;
521
- }
522
- /**
523
- * ONE tool server definition:
524
- *
525
- * - a malformed MCP server id is an ERROR, because it becomes both a tool-name fragment and a
526
- * Codex TOML table key, so the CLI fails on it far from the registration that caused it;
527
- * - an `allowedTools` entry that is not a valid tool NAME is an ERROR, the comma case above all:
528
- * the harness joins the whole list into one `--allowedTools` argument with commas, so an entry
529
- * carrying one splits into two patterns and the second matches nothing, while the prompt goes on
530
- * advertising the name verbatim. That is the "told about a tool it cannot call" failure the
531
- * unavailability vocabulary exists to prevent;
532
- * - a definition NO harness could ever serve is a WARNING, and it is the only check here a run
533
- * structurally cannot report: an `http` server narrowed to `harnesses: ['codex']` (whose client
534
- * is stdio-only), or anything narrowed to `['pi']` (which has no MCP client), is never dropped
535
- * FOR A REASON on any run. It simply never applies, so no prompt and no log line ever mentions
536
- * it. A warning rather than an error because the declaration is inert rather than dangerous;
537
- * - a cleartext `http://` endpoint off loopback is an ERROR: a resolved credential rides that
538
- * request as a header, and the harness refuses the same URL at the job boundary — so allowing
539
- * it here only moves the failure to a place with no registration to point at.
540
- */
541
- function checkToolServerDefinition(kind, server) {
542
- const problems = [];
543
- const on = `(on agent kind "${kind}")`;
544
- if (!isValidMcpServerId(server.id)) {
545
- problems.push({
546
- severity: 'error',
547
- code: 'invalid_tool_server_id',
548
- message: `Tool server "${server.id}" ${on} has an invalid id. It becomes part of the tool names ` +
549
- `the CLI exposes (mcp__<id>__<tool>) and a Codex config key, so it must match ` +
550
- `[a-z0-9][a-z0-9_-]*.`,
551
- });
552
- }
553
- for (const tool of server.allowedTools ?? []) {
554
- if (isValidMcpToolName(tool))
555
- continue;
556
- problems.push({
557
- severity: 'error',
558
- code: 'invalid_tool_server_tool_name',
559
- message: `Tool server "${server.id}" ${on} restricts allowedTools to "${tool}", which is not a ` +
560
- `single tool name (letters, digits, "_", "." and "-"). The harness joins the list into ` +
561
- `one --allowedTools argument with commas, so an entry with a comma or whitespace becomes ` +
562
- `patterns that match nothing while the prompt still advertises the name. List each tool ` +
563
- `as its own entry.`,
564
- });
565
- }
566
- const servable = mcpServableHarnesses(server);
567
- if (servable.length === 0) {
568
- problems.push({
569
- severity: 'warn',
570
- code: 'tool_server_unservable',
571
- message: `Tool server "${server.id}" ${on} declares transport "${server.transport.kind}" for ` +
572
- `harnesses [${(server.harnesses ?? MCP_SUPPORTED_HARNESSES).join(', ')}], and no harness ` +
573
- `can serve that combination, so the server never applies to any run and no prompt or log ` +
574
- `line will say why. Codex's MCP client is stdio-only and Pi has none at all. Widen the ` +
575
- `harnesses, change the transport, or drop the declaration.`,
576
- });
577
- }
578
- if (server.transport.kind === 'http' && !isAllowedMcpHttpUrl(server.transport.url)) {
579
- problems.push({
580
- severity: 'error',
581
- code: 'insecure_tool_server_url',
582
- message: `Tool server "${server.id}" ${on} has url "${server.transport.url}". An HTTP tool server ` +
583
- `carries its resolved credential in a request header, so the url must be https (plain ` +
584
- `http is accepted only on loopback).`,
585
- });
586
- }
587
- for (const secret of server.secretKeys ?? []) {
588
- problems.push(...checkToolServerSecret(kind, server, secret));
589
- }
590
- problems.push(...checkToolServerOAuth(kind, server));
591
- return problems;
592
- }
593
- /**
594
- * A tool server's OAUTH declaration, when it has one. Four rules, and each of them names a failure
595
- * that is otherwise invisible until a run or a button press:
596
- *
597
- * - `oauth` on a `stdio` server is an ERROR. A stdio server is a child process the CLI spawns;
598
- * there is no request to authorise, so the declaration is inert and reads as configured. The
599
- * dispatch drops the OAuth half silently for the mothership case, which is exactly why boot is
600
- * where the fault has to be named.
601
- * - a declared endpoint that fails the URL floor is an ERROR, and this is the sharpest one here:
602
- * an OAuth exchange carries the client secret and the tokens, so a cleartext endpoint puts both
603
- * on the wire. A DISCOVERED endpoint is held to the same rule at the moment it is read.
604
- * - a reserved `clientSecretKey` is an ERROR for the reason every capability credential is: the
605
- * declaration names both the key it wants and the token endpoint that key is posted to.
606
- * - a `secretKeys` entry naming the same header the access token rides is a WARNING. Both would
607
- * land in one header map and the granted token wins, so the static credential silently does
608
- * nothing — which reads, to whoever declared it, as the platform ignoring their credential.
609
- */
610
- function checkToolServerOAuth(kind, server) {
611
- const oauth = server.oauth;
612
- if (!oauth)
613
- return [];
614
- const problems = [];
615
- const on = `(on agent kind "${kind}")`;
616
- if (server.transport.kind !== 'http') {
617
- problems.push({
618
- severity: 'error',
619
- code: 'oauth_requires_http_transport',
620
- message: `Tool server "${server.id}" ${on} declares OAuth on a "${server.transport.kind}" ` +
621
- `transport. OAuth authenticates a REQUEST, and a stdio server is a child process the ` +
622
- `agent CLI spawns with no request to authorise — pass its credential through secretKeys ` +
623
- `instead.`,
624
- });
625
- }
626
- for (const [field, url] of [
627
- ['authorizationUrl', oauth.authorizationUrl],
628
- ['tokenUrl', oauth.tokenUrl],
629
- ]) {
630
- if (url === undefined || isAllowedMcpHttpUrl(url))
631
- continue;
632
- problems.push({
633
- severity: 'error',
634
- code: 'insecure_oauth_endpoint',
635
- message: `Tool server "${server.id}" ${on} declares OAuth ${field} "${url}". The exchange carries ` +
636
- `the client secret and the access token, so the endpoint must be https (plain http is ` +
637
- `accepted only on loopback).`,
638
- });
639
- }
640
- if (oauth.clientSecretKey !== undefined && isReservedPlatformEnvKey(oauth.clientSecretKey)) {
641
- problems.push({
642
- severity: 'error',
643
- code: 'reserved_credential_key',
644
- message: `Tool server "${server.id}" ${on} declares OAuth client secret ` +
645
- reservedEnvKeyMessage(oauth.clientSecretKey),
646
- });
647
- }
648
- const tokenHeader = (oauth.header ?? MCP_OAUTH_DEFAULT_HEADER).toLowerCase();
649
- for (const secret of server.secretKeys ?? []) {
650
- if (secret.header?.toLowerCase() !== tokenHeader)
651
- continue;
652
- problems.push({
653
- severity: 'warn',
654
- code: 'oauth_header_collision',
655
- message: `Tool server "${server.id}" ${on} declares credential "${secret.key}" on header ` +
656
- `"${secret.header}", which is also where its OAuth access token is sent. The granted ` +
657
- `token wins, so that credential reaches the server as nothing at all — send it under a ` +
658
- `different header, or drop it.`,
659
- });
660
- }
661
- return problems;
662
- }
663
- /**
664
- * ONE credential a tool server declares. The reserved-key check is the sharpest rule in this file:
665
- * a definition names both the key it wants and the endpoint that key is sent to, so
666
- * `{ key: 'ENCRYPTION_KEY', header: 'Authorization' }` is a registration that boots clean and ships
667
- * the deployment's master sealing key to a third party. The generative-integration half of the same
668
- * rule is enforced by its credential SCHEMA (there is no schema here — a tool server is a TypeScript
669
- * registration), and dispatch refuses both again for the mothership case.
670
- */
671
- function checkToolServerSecret(kind, server, secret) {
672
- const problems = [];
673
- const on = `(on agent kind "${kind}")`;
674
- if (isReservedPlatformEnvKey(secret.key)) {
675
- problems.push({
676
- severity: 'error',
677
- code: 'reserved_credential_key',
678
- message: `Tool server "${server.id}" ${on} declares credential ${reservedEnvKeyMessage(secret.key)}`,
679
- });
680
- }
681
- if (secret.envName === undefined)
682
- return problems;
683
- // The injection name is NOT held to the reserved floor (it reads nothing), so it carries its own
684
- // rule: a value set as `PATH` or `npm_config_registry` reconfigures the server's process instead
685
- // of authenticating a call. Dispatch drops one too, for the mothership case.
686
- if (!isEnvVariableName(secret.envName)) {
687
- problems.push({
688
- severity: 'error',
689
- code: 'invalid_credential_env_name',
690
- message: `Tool server "${server.id}" ${on} declares credential envName "${secret.envName}", which ` +
691
- `is not a valid environment variable name. It becomes a variable of the server's process, ` +
692
- `and the harness drops anything else.`,
693
- });
694
- }
695
- if (isToolchainEnvName(secret.envName)) {
696
- problems.push({
697
- severity: 'error',
698
- code: 'toolchain_credential_env_name',
699
- message: `Tool server "${server.id}" ${on} declares credential ${toolchainEnvNameMessage(secret.envName)}`,
700
- });
701
- }
702
- // An `http` server sends its value as a HEADER, so an injection name would be read by nothing.
703
- // A warning rather than an error: the declaration still works, it just says something that cannot
704
- // take effect, and failing boot over it would be out of proportion.
705
- if (server.transport.kind === 'http' && secret.header) {
706
- problems.push({
707
- severity: 'warn',
708
- code: 'unused_credential_env_name',
709
- message: `Tool server "${server.id}" ${on} declares credential envName "${secret.envName}" on a ` +
710
- `key that names a header. An http server's value is sent as that header, so the injection ` +
711
- `name is never used.`,
712
- });
713
- }
714
- return problems;
715
- }
716
365
  /**
717
366
  * Section 3 of {@link collectRegistrationProblems}: a coherence WARNING for a kind that declares
718
367
  * postOps but whose agent step is not structured output — those post-ops read `result.custom` and