@hue-run/sdk 0.1.5 → 0.2.1

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 (56) hide show
  1. package/ENVIRONMENTS.md +182 -0
  2. package/EVALUATIONS.md +12 -0
  3. package/README.md +192 -18
  4. package/dist/ai-sdk.d.ts +9 -1
  5. package/dist/ai-sdk.js +34 -8
  6. package/dist/client.d.ts +121 -6
  7. package/dist/client.js +329 -56
  8. package/dist/config.d.ts +11 -2
  9. package/dist/config.js +36 -7
  10. package/dist/environment/client.d.ts +73 -0
  11. package/dist/environment/client.js +209 -0
  12. package/dist/environment/tools.d.ts +30 -0
  13. package/dist/environment/tools.js +24 -0
  14. package/dist/environment/types.d.ts +429 -0
  15. package/dist/environment/types.js +1 -0
  16. package/dist/environment.d.ts +5 -0
  17. package/dist/environment.js +2 -0
  18. package/dist/evals/attempt.d.ts +454 -0
  19. package/dist/evals/attempt.js +687 -0
  20. package/dist/evals/client.d.ts +99 -5
  21. package/dist/evals/client.js +136 -7
  22. package/dist/evals/environment-evidence.d.ts +6 -0
  23. package/dist/evals/environment-evidence.js +123 -0
  24. package/dist/evals/environment-json.d.ts +3 -0
  25. package/dist/evals/environment-json.js +76 -0
  26. package/dist/evals/json.d.ts +9 -1
  27. package/dist/evals/json.js +14 -6
  28. package/dist/evals/runner.d.ts +61 -2
  29. package/dist/evals/runner.js +71 -9
  30. package/dist/evals/scorer-publication.d.ts +2 -0
  31. package/dist/evals/scorer-publication.js +84 -0
  32. package/dist/evals/scorers.d.ts +11 -0
  33. package/dist/evals/scorers.js +56 -5
  34. package/dist/evals/simulation.d.ts +184 -0
  35. package/dist/evals/simulation.js +603 -0
  36. package/dist/evals/types.d.ts +304 -0
  37. package/dist/evals.d.ts +5 -1
  38. package/dist/evals.js +3 -1
  39. package/dist/experimental-telemetry.d.ts +8 -0
  40. package/dist/experimental-telemetry.js +13 -0
  41. package/dist/index.d.ts +3 -0
  42. package/dist/index.js +2 -0
  43. package/dist/managed.d.ts +51 -1
  44. package/dist/managed.js +11 -1
  45. package/dist/privacy.d.ts +2 -0
  46. package/dist/privacy.js +16 -1
  47. package/dist/receipt.d.ts +12 -1
  48. package/dist/receipt.js +10 -1
  49. package/dist/safety.d.ts +1 -2
  50. package/dist/snapshot.js +4 -0
  51. package/dist/transport.d.ts +41 -9
  52. package/dist/transport.js +80 -22
  53. package/dist/types.d.ts +144 -8
  54. package/dist/version.d.ts +2 -0
  55. package/dist/version.js +3 -0
  56. package/package.json +51 -15
@@ -0,0 +1,687 @@
1
+ import { createHash } from "node:crypto";
2
+ import { z } from "zod";
3
+ import { json, valueBounds } from "./json.js";
4
+ const canonicalUuid = z.uuid().transform((value) => value.toLowerCase());
5
+ const sha256Digest = z.string().regex(/^sha256:[0-9a-f]{64}$/);
6
+ const surfaceKey = z.enum(["google.gmail/mcp", "google.gmail/rest", "slack/mcp", "slack/web-api"]);
7
+ const mcpSurfaceKey = z.enum(["google.gmail/mcp", "slack/mcp"]);
8
+ const nativeSurfaceKey = z.enum(["google.gmail/rest", "slack/web-api"]);
9
+ const providerInstanceKey = z.string().regex(/^[a-z][a-z0-9_-]{0,63}$/);
10
+ const componentKey = z.enum(["agent", "prompt", "model", "tools", "approvals", "orchestration"]);
11
+ const componentKeys = componentKey.options;
12
+ const surfaceRegistrationId = z.string().regex(/^[a-z][a-z0-9._-]{0,127}$/);
13
+ const evidence = z.enum(["observed", "declared", "missing"]);
14
+ const digestEvidence = z
15
+ .strictObject({ digest: sha256Digest.nullable(), evidence })
16
+ .refine((value) => (value.evidence === "missing") === (value.digest === null), "Missing evidence must have a null digest; other evidence must have a digest");
17
+ const digestRequirement = z.strictObject({
18
+ digest: sha256Digest,
19
+ minimumEvidence: z.enum(["observed", "declared"]),
20
+ });
21
+ const missingEvidence = () => ({ digest: null, evidence: "missing" });
22
+ const missingComponents = () => Object.fromEntries(componentKeys.map((key) => [key, missingEvidence()]));
23
+ const components = z
24
+ .strictObject({
25
+ agent: digestEvidence.default(missingEvidence),
26
+ prompt: digestEvidence.default(missingEvidence),
27
+ model: digestEvidence.default(missingEvidence),
28
+ tools: digestEvidence.default(missingEvidence),
29
+ approvals: digestEvidence.default(missingEvidence),
30
+ orchestration: digestEvidence.default(missingEvidence),
31
+ })
32
+ .default(missingComponents);
33
+ const requirements = z.strictObject({
34
+ agent: digestRequirement,
35
+ prompt: digestRequirement,
36
+ model: digestRequirement,
37
+ tools: digestRequirement,
38
+ approvals: digestRequirement,
39
+ orchestration: digestRequirement,
40
+ });
41
+ function unique(items, key) {
42
+ return new Set(items.map(key)).size === items.length;
43
+ }
44
+ function sorted(items, key) {
45
+ return items.every((item, index) => index === 0 || key(items[index - 1]) < key(item));
46
+ }
47
+ const catalogIdentityV2 = { providerInstanceKey, surfaceKey: mcpSurfaceKey };
48
+ const helperIdentityV2 = { providerInstanceKey, surfaceKey: nativeSurfaceKey };
49
+ const actualHelperEvidence = z
50
+ .array(z
51
+ .strictObject({
52
+ ...helperIdentityV2,
53
+ digest: sha256Digest.nullable(),
54
+ evidence,
55
+ })
56
+ .refine((value) => (value.evidence === "missing") === (value.digest === null)))
57
+ .max(64)
58
+ .refine((items) => unique(items, (item) => `${item.providerInstanceKey}/${item.surfaceKey}`));
59
+ const expectedHelperEvidence = z
60
+ .array(z.strictObject({
61
+ ...helperIdentityV2,
62
+ digest: sha256Digest,
63
+ minimumEvidence: z.enum(["observed", "declared"]),
64
+ }))
65
+ .max(64)
66
+ .refine((items) => unique(items, (item) => `${item.providerInstanceKey}/${item.surfaceKey}`));
67
+ /** Runtime validator for the caller-observed V2 agent manifest supplied before target execution. */
68
+ export const actualAgentManifestV2 = z
69
+ .strictObject({
70
+ schemaVersion: z.literal(2).default(2),
71
+ components,
72
+ /** Effective model-visible order. Never sort this sequence. */
73
+ catalogs: z
74
+ .array(z
75
+ .strictObject({
76
+ ...catalogIdentityV2,
77
+ digest: sha256Digest.nullable(),
78
+ evidence,
79
+ })
80
+ .refine((value) => (value.evidence === "missing") === (value.digest === null)))
81
+ .max(64)
82
+ .refine((items) => unique(items, (item) => `${item.providerInstanceKey}/${item.surfaceKey}`))
83
+ .default(() => []),
84
+ helperConfigurations: actualHelperEvidence.default(() => []),
85
+ })
86
+ .default(() => ({
87
+ schemaVersion: 2,
88
+ components: missingComponents(),
89
+ catalogs: [],
90
+ helperConfigurations: [],
91
+ }));
92
+ /** Runtime validator for the immutable expected V2 agent manifest. */
93
+ export const expectedAgentManifestV2 = z.strictObject({
94
+ schemaVersion: z.literal(2),
95
+ components: requirements,
96
+ catalogs: z
97
+ .array(z.strictObject({
98
+ ...catalogIdentityV2,
99
+ digest: sha256Digest,
100
+ minimumEvidence: z.enum(["observed", "declared"]),
101
+ }))
102
+ .max(64)
103
+ .refine((items) => unique(items, (item) => `${item.providerInstanceKey}/${item.surfaceKey}`)),
104
+ helperConfigurations: expectedHelperEvidence,
105
+ });
106
+ const contractDigestsV2 = z
107
+ .array(z.strictObject({ surfaceKey, contractDigest: sha256Digest }))
108
+ .min(1)
109
+ .max(4)
110
+ .refine((items) => sorted(items, (item) => item.surfaceKey), "Contract digests must be sorted and unique by surface key");
111
+ const profilePinsV2 = z.strictObject({
112
+ profileId: z.string().regex(/^[a-z][a-z0-9._/-]{0,127}$/),
113
+ profileDigest: sha256Digest,
114
+ buildDigest: sha256Digest,
115
+ coverageDigest: sha256Digest,
116
+ contractDigests: contractDigestsV2,
117
+ });
118
+ const surfaceFieldsV2 = {
119
+ surfaceRegistrationId,
120
+ protocolVersion: z.string().regex(/^[a-zA-Z0-9._/+-]{1,64}$/),
121
+ contractDigest: sha256Digest,
122
+ runtimeRegistrationDigest: sha256Digest,
123
+ };
124
+ const mcpSurfaceV2 = z.strictObject({
125
+ ...surfaceFieldsV2,
126
+ surfaceKey: mcpSurfaceKey,
127
+ catalogDigest: sha256Digest,
128
+ helperConfigurationDigest: z.null(),
129
+ });
130
+ const nativeSurfaceV2 = z.strictObject({
131
+ ...surfaceFieldsV2,
132
+ surfaceKey: nativeSurfaceKey,
133
+ catalogDigest: z.null(),
134
+ helperConfigurationDigest: sha256Digest,
135
+ });
136
+ /** Runtime validator for one secret-free V2 surface binding. */
137
+ export const surfaceBindingV2 = z.discriminatedUnion("surfaceKey", [
138
+ mcpSurfaceV2,
139
+ nativeSurfaceV2,
140
+ ]);
141
+ const providerFieldsV2 = {
142
+ providerInstanceKey,
143
+ providerId: z.enum(["google.gmail", "slack"]),
144
+ syntheticPrincipalId: canonicalUuid,
145
+ scopes: z
146
+ .array(z.string().regex(/^[a-zA-Z0-9._:/-]{1,256}$/))
147
+ .max(64)
148
+ .refine((items) => sorted(items, (item) => item), "Scopes must be sorted and unique"),
149
+ profile: profilePinsV2,
150
+ workflowDigest: sha256Digest,
151
+ surfaces: z
152
+ .array(surfaceBindingV2)
153
+ .min(1)
154
+ .max(4)
155
+ .refine((items) => unique(items, (item) => item.surfaceKey), "Selected surfaces must be unique"),
156
+ };
157
+ const providerBaseV2 = z.strictObject(providerFieldsV2);
158
+ function validProviderV2(value) {
159
+ return (value.profile.contractDigests.every((item) => item.surfaceKey.startsWith(`${value.providerId}/`)) &&
160
+ value.surfaces.every((item) => item.surfaceKey.startsWith(`${value.providerId}/`) &&
161
+ value.profile.contractDigests.some((contract) => contract.surfaceKey === item.surfaceKey &&
162
+ contract.contractDigest === item.contractDigest)));
163
+ }
164
+ /** Runtime validator for one V2 provider dependency. */
165
+ export const dependencyProviderV2 = providerBaseV2.refine(validProviderV2, "Selected surface contracts must match the pinned profile and provider");
166
+ /** Runtime validator for the secret-free V2 dependency manifest. */
167
+ export const dependencyManifestV2 = z.strictObject({
168
+ schemaVersion: z.literal(2),
169
+ providers: z
170
+ .array(dependencyProviderV2)
171
+ .min(1)
172
+ .max(16)
173
+ .refine((items) => unique(items, (item) => item.providerInstanceKey), "Provider instances must be unique"),
174
+ });
175
+ function canonicalDigest(value) {
176
+ function canonical(input) {
177
+ if (input === null || typeof input === "string" || typeof input === "boolean")
178
+ return JSON.stringify(input);
179
+ if (typeof input === "number" && Number.isSafeInteger(input))
180
+ return JSON.stringify(input);
181
+ if (Array.isArray(input))
182
+ return `[${input.map(canonical).join(",")}]`;
183
+ if (typeof input === "object" && input !== null)
184
+ return `{${Object.keys(input)
185
+ .sort()
186
+ .map((key) => `${JSON.stringify(key)}:${canonical(input[key])}`)
187
+ .join(",")}}`;
188
+ throw new Error("Digest input must be bounded JSON values");
189
+ }
190
+ return `sha256:${createHash("sha256").update(canonical(value)).digest("hex")}`;
191
+ }
192
+ /** Computes the canonical digest shared by actual and expected V2 agent manifests. */
193
+ export function agentManifestDigestV2(manifest) {
194
+ return canonicalDigest({
195
+ schemaVersion: 2,
196
+ components: Object.fromEntries(componentKeys.map((key) => [key, manifest.components[key].digest])),
197
+ catalogs: manifest.catalogs.map(({ providerInstanceKey, surfaceKey: key, digest }) => ({
198
+ providerInstanceKey,
199
+ surfaceKey: key,
200
+ digest,
201
+ })),
202
+ helperConfigurations: manifest.helperConfigurations.map(({ providerInstanceKey, surfaceKey: key, digest }) => ({
203
+ providerInstanceKey,
204
+ surfaceKey: key,
205
+ digest,
206
+ })),
207
+ });
208
+ }
209
+ /** Runtime validator for an immutable V2 experiment baseline. */
210
+ export const attemptBaselineV2 = z
211
+ .strictObject({
212
+ schemaVersion: z.literal(2),
213
+ expectedAgentManifestId: canonicalUuid,
214
+ expectedAgentManifestDigest: sha256Digest,
215
+ expectedAgentManifest: expectedAgentManifestV2,
216
+ dependencyManifest: dependencyManifestV2,
217
+ })
218
+ .refine((value) => value.expectedAgentManifestDigest === agentManifestDigestV2(value.expectedAgentManifest), "Expected agent manifest digest does not match its immutable contents")
219
+ .refine((value) => {
220
+ const catalogs = value.expectedAgentManifest.catalogs;
221
+ const helpers = value.expectedAgentManifest.helperConfigurations;
222
+ const surfaces = value.dependencyManifest.providers.flatMap((provider) => provider.surfaces.map((surface) => ({
223
+ ...surface,
224
+ providerInstanceKey: provider.providerInstanceKey,
225
+ })));
226
+ if (catalogs.length + helpers.length !== surfaces.length)
227
+ return false;
228
+ return surfaces.every((surface) => surface.catalogDigest !== null
229
+ ? catalogs.some((entry) => entry.providerInstanceKey === surface.providerInstanceKey &&
230
+ entry.surfaceKey === surface.surfaceKey &&
231
+ entry.digest === surface.catalogDigest &&
232
+ entry.minimumEvidence === "observed")
233
+ : helpers.some((entry) => entry.providerInstanceKey === surface.providerInstanceKey &&
234
+ entry.surfaceKey === surface.surfaceKey &&
235
+ entry.digest === surface.helperConfigurationDigest));
236
+ }, "Selected MCP/native surfaces require exactly their matching catalog/helper evidence");
237
+ /** Internal parser reused by `runSimulation` before an execution exists. */
238
+ export const requestedAttemptProvidersV2 = z
239
+ .array(z.strictObject({
240
+ providerInstanceKey,
241
+ surfaceKeys: z
242
+ .array(surfaceKey)
243
+ .min(1)
244
+ .max(4)
245
+ .refine((items) => unique(items, (item) => item)),
246
+ }))
247
+ .min(1)
248
+ .max(16)
249
+ .refine((items) => unique(items, (item) => item.providerInstanceKey));
250
+ /** Runtime validator for the full V2 prepare input, including its route identity. */
251
+ export const prepareAttemptInputV2 = z.strictObject({
252
+ schemaVersion: z.literal(2),
253
+ idempotencyKey: canonicalUuid,
254
+ executionId: canonicalUuid,
255
+ environmentRunId: canonicalUuid,
256
+ expectedAgentManifestDigest: sha256Digest,
257
+ actualManifest: actualAgentManifestV2,
258
+ requestedProviders: requestedAttemptProvidersV2,
259
+ });
260
+ const findingMessagesV2 = {
261
+ baseline_missing: "The immutable attempt baseline is unavailable.",
262
+ baseline_assertion_mismatch: "The asserted expected agent manifest does not match the experiment baseline.",
263
+ manifest_mismatch: "The actual agent manifest does not match the experiment baseline.",
264
+ evidence_missing: "Required parity evidence is missing.",
265
+ evidence_insufficient: "The supplied parity evidence does not meet the required provenance level.",
266
+ helper_configuration_mismatch: "The native helper configuration or its order does not match the baseline.",
267
+ catalog_mismatch: "The effective provider catalog or its order does not match the baseline.",
268
+ provider_selection_mismatch: "The requested provider surfaces do not match the baseline.",
269
+ profile_unavailable: "The pinned provider profile is unavailable.",
270
+ profile_mismatch: "The resolved provider profile does not match the immutable pins.",
271
+ surface_unsupported: "A selected provider surface is not supported by this runtime.",
272
+ coverage_gap: "The pinned workflow is not covered by the provider profile.",
273
+ };
274
+ const findingCodeV2 = z.enum(Object.keys(findingMessagesV2));
275
+ /** Runtime validator for one V2 preflight finding. */
276
+ export const preflightFindingV2 = z
277
+ .strictObject({
278
+ code: findingCodeV2,
279
+ component: componentKey.nullable(),
280
+ providerInstanceKey: providerInstanceKey.nullable(),
281
+ surfaceKey: surfaceKey.nullable(),
282
+ message: z.string(),
283
+ })
284
+ .refine((value) => value.message === findingMessagesV2[value.code]);
285
+ /** Runtime validator for the V2 preflight report. */
286
+ export const preflightReportV2 = z
287
+ .strictObject({
288
+ schemaVersion: z.literal(2),
289
+ status: z.enum(["ready", "environment_incomplete"]),
290
+ evidenceSource: z.literal("caller_supplied"),
291
+ findings: z.array(preflightFindingV2).max(256),
292
+ })
293
+ .refine((value) => (value.status === "ready") === (value.findings.length === 0));
294
+ /** Runtime validator for stable, credential-free V2 parity evidence. */
295
+ export const parityEvidenceV2 = z.strictObject({
296
+ expectedAgentManifestId: canonicalUuid,
297
+ expectedAgentManifestDigest: sha256Digest,
298
+ actualAgentManifestDigest: sha256Digest,
299
+ actualManifest: actualAgentManifestV2,
300
+ dependencyManifestDigest: sha256Digest,
301
+ executionManifestDigest: sha256Digest,
302
+ evidenceSource: z.literal("caller_supplied"),
303
+ });
304
+ function isSafeHttpsEndpoint(value) {
305
+ if (!/^https:\/\//i.test(value) || /[\s\\?#]/u.test(value))
306
+ return false;
307
+ for (const character of value) {
308
+ const code = character.charCodeAt(0);
309
+ if (code <= 31 || (code >= 127 && code <= 159))
310
+ return false;
311
+ }
312
+ const afterScheme = value.slice(8);
313
+ const pathStart = afterScheme.indexOf("/");
314
+ const authority = pathStart === -1 ? afterScheme : afterScheme.slice(0, pathStart);
315
+ if (!authority || authority.includes("@"))
316
+ return false;
317
+ try {
318
+ const parsed = new URL(value);
319
+ return (parsed.protocol === "https:" &&
320
+ !!parsed.hostname &&
321
+ !parsed.username &&
322
+ !parsed.password &&
323
+ !parsed.search &&
324
+ !parsed.hash);
325
+ }
326
+ catch {
327
+ return false;
328
+ }
329
+ }
330
+ const endpoint = z.string().refine(isSafeHttpsEndpoint);
331
+ const credentials = { endpoint, bearer: z.string().regex(/^[A-Za-z0-9_.-]{32,2048}$/) };
332
+ const connectionSurfaceV2 = z.discriminatedUnion("surfaceKey", [
333
+ mcpSurfaceV2.extend(credentials),
334
+ nativeSurfaceV2.extend(credentials),
335
+ ]);
336
+ const connectionProviderV2 = z
337
+ .strictObject({
338
+ ...providerFieldsV2,
339
+ surfaces: z
340
+ .array(connectionSurfaceV2)
341
+ .min(1)
342
+ .max(4)
343
+ .refine((items) => unique(items, (item) => item.surfaceKey), "Selected surfaces must be unique"),
344
+ })
345
+ .refine(validProviderV2);
346
+ /** Runtime validator for the stable V2 attempt identity. */
347
+ export const attemptIdentityV2 = z.strictObject({
348
+ bindingId: canonicalUuid,
349
+ executionId: canonicalUuid,
350
+ environmentRunId: canonicalUuid,
351
+ });
352
+ function connectionDependenciesV2(bundle) {
353
+ return {
354
+ schemaVersion: 2,
355
+ providers: bundle.providers.map((provider) => ({
356
+ ...provider,
357
+ surfaces: provider.surfaces.map(({ surfaceRegistrationId: registrationId, surfaceKey: selectedSurfaceKey, protocolVersion, contractDigest, catalogDigest, helperConfigurationDigest, runtimeRegistrationDigest, }) => ({
358
+ surfaceRegistrationId: registrationId,
359
+ surfaceKey: selectedSurfaceKey,
360
+ protocolVersion,
361
+ contractDigest,
362
+ catalogDigest,
363
+ helperConfigurationDigest,
364
+ runtimeRegistrationDigest,
365
+ })),
366
+ })),
367
+ };
368
+ }
369
+ /** Binds actual agent evidence, secret-free dependencies and stable attempt identities. */
370
+ export function executionManifestDigestV2(actualManifest, dependencyManifest, identity) {
371
+ return canonicalDigest({
372
+ schemaVersion: 2,
373
+ actualManifest: actualAgentManifestV2.parse(actualManifest),
374
+ dependencyManifest: dependencyManifestV2.parse(dependencyManifest),
375
+ identity: attemptIdentityV2.parse(identity),
376
+ });
377
+ }
378
+ /** Runtime validator for a credential-bearing V2 connection bundle. */
379
+ export const attemptConnectionBundleV2 = z
380
+ .strictObject({
381
+ schemaVersion: z.literal(2),
382
+ bindingId: canonicalUuid,
383
+ executionId: canonicalUuid,
384
+ environmentRunId: canonicalUuid,
385
+ expiresAt: z.iso.datetime(),
386
+ credentialGeneration: z.number().int().min(0).max(2_147_483_647),
387
+ providers: z
388
+ .array(connectionProviderV2)
389
+ .min(1)
390
+ .max(16)
391
+ .refine((items) => unique(items, (item) => item.providerInstanceKey), "Provider instances must be unique"),
392
+ parity: parityEvidenceV2,
393
+ })
394
+ .refine((bundle) => {
395
+ const dependencies = dependencyManifestV2.safeParse(connectionDependenciesV2(bundle));
396
+ if (!dependencies.success)
397
+ return false;
398
+ return (bundle.parity.actualAgentManifestDigest ===
399
+ agentManifestDigestV2(bundle.parity.actualManifest) &&
400
+ bundle.parity.expectedAgentManifestDigest === bundle.parity.actualAgentManifestDigest &&
401
+ bundle.parity.dependencyManifestDigest === canonicalDigest(dependencies.data) &&
402
+ bundle.parity.executionManifestDigest ===
403
+ executionManifestDigestV2(bundle.parity.actualManifest, dependencies.data, {
404
+ bindingId: bundle.bindingId,
405
+ executionId: bundle.executionId,
406
+ environmentRunId: bundle.environmentRunId,
407
+ }));
408
+ }, "Bundle evidence must match its immutable contents and attempt identity");
409
+ /** Removes endpoints and bearers from a validated bundle, retaining its dependency identity. */
410
+ export function secretFreeBindingV2(bundle) {
411
+ return dependencyManifestV2.parse(connectionDependenciesV2(bundle));
412
+ }
413
+ /** Legacy context.mcp is a credential-bearing projection, never another source of binding truth. */
414
+ export function projectMcpConnectionV2(bundle, instanceKey) {
415
+ const surface = bundle.providers
416
+ .find((provider) => provider.providerInstanceKey === instanceKey)
417
+ ?.surfaces.find((candidate) => candidate.surfaceKey.endsWith("/mcp"));
418
+ return surface
419
+ ? { url: surface.endpoint, token: surface.bearer, expiresAt: bundle.expiresAt }
420
+ : null;
421
+ }
422
+ const actualAgentManifestV1 = z
423
+ .strictObject({
424
+ schemaVersion: z.literal(1).default(1),
425
+ components,
426
+ catalogs: z
427
+ .array(z
428
+ .strictObject({
429
+ providerInstanceKey,
430
+ surfaceKey,
431
+ digest: sha256Digest.nullable(),
432
+ evidence,
433
+ })
434
+ .refine((value) => (value.evidence === "missing") === (value.digest === null)))
435
+ .max(64)
436
+ .refine((items) => unique(items, (item) => `${item.providerInstanceKey}/${item.surfaceKey}`))
437
+ .default(() => []),
438
+ })
439
+ .default(() => ({ schemaVersion: 1, components: missingComponents(), catalogs: [] }));
440
+ const surfaceBindingV1 = z.strictObject({
441
+ surfaceRegistrationId,
442
+ surfaceKey,
443
+ protocolVersion: z.string().regex(/^[a-zA-Z0-9._/+-]{1,64}$/),
444
+ contractDigest: sha256Digest,
445
+ catalogDigest: sha256Digest,
446
+ });
447
+ const profilePinsV1 = z.strictObject({
448
+ profileId: z.string().regex(/^[a-z][a-z0-9._/-]{0,127}$/),
449
+ profileDigest: sha256Digest,
450
+ buildDigest: sha256Digest,
451
+ coverageDigest: sha256Digest,
452
+ contractDigests: z
453
+ .array(z.strictObject({ surfaceKey, contractDigest: sha256Digest }))
454
+ .min(1)
455
+ .max(4)
456
+ .refine((items) => sorted(items, (item) => item.surfaceKey)),
457
+ });
458
+ const dependencyProviderV1Base = z.strictObject({
459
+ providerInstanceKey,
460
+ providerId: z.enum(["google.gmail", "slack"]),
461
+ syntheticPrincipalId: canonicalUuid,
462
+ scopes: z
463
+ .array(z.string().regex(/^[a-zA-Z0-9._:/-]{1,256}$/))
464
+ .max(64)
465
+ .refine((items) => sorted(items, (item) => item)),
466
+ profile: profilePinsV1,
467
+ workflowDigest: sha256Digest,
468
+ surfaces: z
469
+ .array(surfaceBindingV1)
470
+ .min(1)
471
+ .max(4)
472
+ .refine((items) => unique(items, (item) => item.surfaceKey)),
473
+ });
474
+ const dependencyProviderV1 = dependencyProviderV1Base.refine((value) => value.profile.contractDigests.every((item) => item.surfaceKey.startsWith(`${value.providerId}/`)) &&
475
+ value.surfaces.every((item) => item.surfaceKey.startsWith(`${value.providerId}/`) &&
476
+ value.profile.contractDigests.some((contract) => contract.surfaceKey === item.surfaceKey &&
477
+ contract.contractDigest === item.contractDigest)));
478
+ const dependencyManifestV1 = z.strictObject({
479
+ schemaVersion: z.literal(1),
480
+ providers: z
481
+ .array(dependencyProviderV1)
482
+ .min(1)
483
+ .max(16)
484
+ .refine((items) => unique(items, (item) => item.providerInstanceKey)),
485
+ });
486
+ const findingMessagesV1 = {
487
+ baseline_missing: findingMessagesV2.baseline_missing,
488
+ baseline_assertion_mismatch: findingMessagesV2.baseline_assertion_mismatch,
489
+ manifest_mismatch: findingMessagesV2.manifest_mismatch,
490
+ evidence_missing: findingMessagesV2.evidence_missing,
491
+ evidence_insufficient: findingMessagesV2.evidence_insufficient,
492
+ catalog_mismatch: findingMessagesV2.catalog_mismatch,
493
+ provider_selection_mismatch: findingMessagesV2.provider_selection_mismatch,
494
+ profile_unavailable: findingMessagesV2.profile_unavailable,
495
+ profile_mismatch: findingMessagesV2.profile_mismatch,
496
+ surface_unsupported: findingMessagesV2.surface_unsupported,
497
+ coverage_gap: findingMessagesV2.coverage_gap,
498
+ };
499
+ const findingCodeV1 = z.enum(Object.keys(findingMessagesV1));
500
+ const preflightFindingV1 = z
501
+ .strictObject({
502
+ code: findingCodeV1,
503
+ component: componentKey.nullable(),
504
+ providerInstanceKey: providerInstanceKey.nullable(),
505
+ surfaceKey: surfaceKey.nullable(),
506
+ message: z.string(),
507
+ })
508
+ .refine((value) => value.message === findingMessagesV1[value.code]);
509
+ const preflightReportV1 = z
510
+ .strictObject({
511
+ schemaVersion: z.literal(1),
512
+ status: z.enum(["ready", "environment_incomplete"]),
513
+ evidenceSource: z.literal("caller_supplied"),
514
+ findings: z.array(preflightFindingV1).max(256),
515
+ })
516
+ .refine((value) => (value.status === "ready") === (value.findings.length === 0));
517
+ const bindingReadFields = {
518
+ bindingId: canonicalUuid,
519
+ executionId: canonicalUuid,
520
+ environmentRunId: canonicalUuid,
521
+ outcome: z.enum(["ready", "environment_incomplete"]),
522
+ credentialGeneration: z.number().int().min(0).max(2_147_483_647).nullable(),
523
+ expectedAgentManifestId: canonicalUuid.nullable(),
524
+ expectedAgentManifestDigest: sha256Digest.nullable(),
525
+ executionManifestDigest: sha256Digest.nullable(),
526
+ createdAt: z.iso.datetime(),
527
+ revokedAt: z.iso.datetime().nullable(),
528
+ };
529
+ const attemptBindingReadV1 = z
530
+ .strictObject({
531
+ schemaVersion: z.literal(1),
532
+ ...bindingReadFields,
533
+ actualManifest: actualAgentManifestV1,
534
+ dependencyManifest: dependencyManifestV1.nullable(),
535
+ preflightReport: preflightReportV1,
536
+ })
537
+ .refine((value) => value.outcome === value.preflightReport.status);
538
+ const attemptBindingReadV2 = z
539
+ .strictObject({
540
+ schemaVersion: z.literal(2),
541
+ ...bindingReadFields,
542
+ actualManifest: actualAgentManifestV2,
543
+ dependencyManifest: dependencyManifestV2.nullable(),
544
+ preflightReport: preflightReportV2,
545
+ })
546
+ .refine((value) => value.outcome === value.preflightReport.status);
547
+ /** Runtime validator for a coupled, secret-free V1 or V2 binding read. */
548
+ export const attemptBindingRead = z.union([
549
+ attemptBindingReadV1,
550
+ attemptBindingReadV2,
551
+ ]);
552
+ function parseGap(value) {
553
+ const source = z
554
+ .strictObject({
555
+ provider: z.string().min(1).max(128),
556
+ operation: z.string().min(1).max(256),
557
+ code: z.string().min(1).max(128),
558
+ args: z.record(z.string(), z.unknown()),
559
+ description: z.string().min(1).max(2000),
560
+ reportedAt: z.iso.datetime(),
561
+ reportedBy: z.strictObject({
562
+ kind: z.enum(["project_key", "user"]),
563
+ id: canonicalUuid,
564
+ }),
565
+ })
566
+ .parse(value);
567
+ return {
568
+ ...source,
569
+ args: json(source.args, { ...valueBounds, bytes: 16_000 }),
570
+ };
571
+ }
572
+ function requireFresh(bundle) {
573
+ if (Date.parse(bundle.expiresAt) <= Date.now())
574
+ throw new TypeError("Expired attempt connection");
575
+ return bundle;
576
+ }
577
+ export function validateAttemptConnectionBundleV2(value, options = {}) {
578
+ const bundle = attemptConnectionBundleV2.parse(value);
579
+ return options.requireFresh ? requireFresh(bundle) : bundle;
580
+ }
581
+ function selectedProviders(bundle) {
582
+ return bundle.providers.map((provider) => ({
583
+ providerInstanceKey: provider.providerInstanceKey,
584
+ surfaceKeys: provider.surfaces.map((surface) => surface.surfaceKey),
585
+ }));
586
+ }
587
+ export function parsePrepareAttemptResultV2(value, expected) {
588
+ if (value &&
589
+ typeof value === "object" &&
590
+ value.status === "environment_incomplete") {
591
+ const result = z
592
+ .strictObject({
593
+ status: z.literal("environment_incomplete"),
594
+ bindingId: canonicalUuid,
595
+ preflightReport: preflightReportV2,
596
+ gap: z.unknown(),
597
+ })
598
+ .parse(value);
599
+ if (result.preflightReport.status !== "environment_incomplete")
600
+ throw new TypeError("Invalid attempt preflight result");
601
+ const gap = parseGap(result.gap);
602
+ if (gap.provider === "hue.attempt") {
603
+ if (gap.operation !== "prepare_attempt" ||
604
+ gap.code !== "attempt_preflight_incomplete" ||
605
+ gap.description !== "Attempt preflight could not establish the required simulation parity.")
606
+ throw new TypeError("Invalid attempt coverage gap");
607
+ const args = z
608
+ .strictObject({ findingCodes: z.array(findingCodeV2).max(11) })
609
+ .refine((item) => unique(item.findingCodes, (code) => code))
610
+ .parse(gap.args);
611
+ const expectedCodes = [
612
+ ...new Set(result.preflightReport.findings.map((finding) => finding.code)),
613
+ ];
614
+ if (canonicalDigest(args.findingCodes) !== canonicalDigest(expectedCodes))
615
+ throw new TypeError("Invalid attempt finding evidence");
616
+ }
617
+ else if (!result.preflightReport.findings.some((finding) => finding.code === "coverage_gap")) {
618
+ throw new TypeError("Invalid preserved coverage gap");
619
+ }
620
+ return {
621
+ status: "environment_incomplete",
622
+ bindingId: result.bindingId,
623
+ preflightReport: { ...result.preflightReport, status: "environment_incomplete" },
624
+ gap,
625
+ };
626
+ }
627
+ const result = z
628
+ .strictObject({
629
+ status: z.literal("ready"),
630
+ preflightReport: preflightReportV2,
631
+ bundle: attemptConnectionBundleV2,
632
+ })
633
+ .parse(value);
634
+ if (result.preflightReport.status !== "ready")
635
+ throw new TypeError("Invalid attempt preflight result");
636
+ const bundle = requireFresh(result.bundle);
637
+ if (bundle.executionId !== expected.executionId ||
638
+ bundle.environmentRunId !== expected.environmentRunId ||
639
+ bundle.parity.expectedAgentManifestDigest !== expected.expectedAgentManifestDigest ||
640
+ canonicalDigest(bundle.parity.actualManifest) !== canonicalDigest(expected.actualManifest) ||
641
+ canonicalDigest(selectedProviders(bundle)) !== canonicalDigest(expected.requestedProviders))
642
+ throw new TypeError("Attempt connection does not match its request");
643
+ return {
644
+ status: "ready",
645
+ preflightReport: { ...result.preflightReport, status: "ready" },
646
+ bundle,
647
+ };
648
+ }
649
+ function stableBundleEvidence(bundle) {
650
+ return {
651
+ schemaVersion: bundle.schemaVersion,
652
+ bindingId: bundle.bindingId,
653
+ executionId: bundle.executionId,
654
+ environmentRunId: bundle.environmentRunId,
655
+ dependencyManifest: secretFreeBindingV2(bundle),
656
+ parity: bundle.parity,
657
+ };
658
+ }
659
+ export function parseRefreshedAttemptResultV2(value, previous) {
660
+ const trustedPrevious = attemptConnectionBundleV2.parse(previous);
661
+ const result = z
662
+ .strictObject({
663
+ status: z.literal("ready"),
664
+ preflightReport: preflightReportV2,
665
+ bundle: attemptConnectionBundleV2,
666
+ })
667
+ .parse(value);
668
+ const bundle = requireFresh(result.bundle);
669
+ if (result.preflightReport.status !== "ready" ||
670
+ bundle.credentialGeneration !== trustedPrevious.credentialGeneration + 1 ||
671
+ canonicalDigest(stableBundleEvidence(bundle)) !==
672
+ canonicalDigest(stableBundleEvidence(trustedPrevious)))
673
+ throw new TypeError("Refreshed attempt connection changed immutable evidence");
674
+ return {
675
+ status: "ready",
676
+ preflightReport: { ...result.preflightReport, status: "ready" },
677
+ bundle,
678
+ };
679
+ }
680
+ export function parseRevocationResult(value, expectedBindingId) {
681
+ const result = z
682
+ .strictObject({ bindingId: canonicalUuid, revokedAt: z.iso.datetime() })
683
+ .parse(value);
684
+ if (result.bindingId !== canonicalUuid.parse(expectedBindingId))
685
+ throw new TypeError("Revoked attempt identity changed");
686
+ return result;
687
+ }