@full-self-browsing/lattice 1.3.0 → 1.5.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 (108) hide show
  1. package/dist/agent-run-C6miAzwI.d.ts +45 -0
  2. package/dist/agent-run-C6miAzwI.d.ts.map +1 -0
  3. package/dist/agent-run-CgPVFl0Z.js +47 -0
  4. package/dist/agent-run-CgPVFl0Z.js.map +1 -0
  5. package/dist/agents.d.ts +5 -0
  6. package/dist/agents.js +6 -0
  7. package/dist/artifact-Bg6mJGnm.d.ts +125 -0
  8. package/dist/artifact-Bg6mJGnm.d.ts.map +1 -0
  9. package/dist/artifact-DOfpeXLb.js +140 -0
  10. package/dist/artifact-DOfpeXLb.js.map +1 -0
  11. package/dist/artifacts.d.ts +2 -0
  12. package/dist/artifacts.js +2 -0
  13. package/dist/audit.d.ts +3 -0
  14. package/dist/audit.js +4 -0
  15. package/dist/catalog-CAfYwB_-.js +91 -0
  16. package/dist/catalog-CAfYwB_-.js.map +1 -0
  17. package/dist/context-pack-Bz3GXmjv.js +99 -0
  18. package/dist/context-pack-Bz3GXmjv.js.map +1 -0
  19. package/dist/context.d.ts +2 -0
  20. package/dist/context.js +2 -0
  21. package/dist/contract-S3oJGlc9.d.ts +74 -0
  22. package/dist/contract-S3oJGlc9.d.ts.map +1 -0
  23. package/dist/core.d.ts +48 -0
  24. package/dist/core.d.ts.map +1 -0
  25. package/dist/core.js +95 -0
  26. package/dist/core.js.map +1 -0
  27. package/dist/errors-eEuEIx6X.js +407 -0
  28. package/dist/errors-eEuEIx6X.js.map +1 -0
  29. package/dist/eval.d.ts +2 -0
  30. package/dist/eval.js +2 -0
  31. package/dist/fingerprint-DodDbQKN.js +34 -0
  32. package/dist/fingerprint-DodDbQKN.js.map +1 -0
  33. package/dist/index-DpnHGHVL.d.ts +53 -0
  34. package/dist/index-DpnHGHVL.d.ts.map +1 -0
  35. package/dist/index.d.ts +78 -3234
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +365 -8434
  38. package/dist/index.js.map +1 -1
  39. package/dist/infer-DLqp5QIM.d.ts +96 -0
  40. package/dist/infer-DLqp5QIM.d.ts.map +1 -0
  41. package/dist/lineage-DBgoPWAZ.js +137 -0
  42. package/dist/lineage-DBgoPWAZ.js.map +1 -0
  43. package/dist/local-CXOGPJ1f.js +139 -0
  44. package/dist/local-CXOGPJ1f.js.map +1 -0
  45. package/dist/local-Dy--7peL.d.ts +10 -0
  46. package/dist/local-Dy--7peL.d.ts.map +1 -0
  47. package/dist/memory-CkQEW6m5.js +62 -0
  48. package/dist/memory-CkQEW6m5.js.map +1 -0
  49. package/dist/memory-DRig5EHV.d.ts +10 -0
  50. package/dist/memory-DRig5EHV.d.ts.map +1 -0
  51. package/dist/negotiate-ClD88hkc.js +10967 -0
  52. package/dist/negotiate-ClD88hkc.js.map +1 -0
  53. package/dist/otel-BgM4e55_.d.ts +421 -0
  54. package/dist/otel-BgM4e55_.d.ts.map +1 -0
  55. package/dist/permission-context-CUKMo79F.js +134 -0
  56. package/dist/permission-context-CUKMo79F.js.map +1 -0
  57. package/dist/plan-DFm8Llep.js +125 -0
  58. package/dist/plan-DFm8Llep.js.map +1 -0
  59. package/dist/preflight-DNHWuJ46.d.ts +64 -0
  60. package/dist/preflight-DNHWuJ46.d.ts.map +1 -0
  61. package/dist/provider-C2IfKsvz.d.ts +1178 -0
  62. package/dist/provider-C2IfKsvz.d.ts.map +1 -0
  63. package/dist/providers.d.ts +4 -0
  64. package/dist/providers.js +4 -0
  65. package/dist/rate-limit-group-nDsBJqSu.d.ts +235 -0
  66. package/dist/rate-limit-group-nDsBJqSu.d.ts.map +1 -0
  67. package/dist/receipt-FYouoPHv.js +205 -0
  68. package/dist/receipt-FYouoPHv.js.map +1 -0
  69. package/dist/replay-CtIhpLek.js +964 -0
  70. package/dist/replay-CtIhpLek.js.map +1 -0
  71. package/dist/result-DLEx2WvU.d.ts +38 -0
  72. package/dist/result-DLEx2WvU.d.ts.map +1 -0
  73. package/dist/router-DU4Z3pTd.js +314 -0
  74. package/dist/router-DU4Z3pTd.js.map +1 -0
  75. package/dist/router-Yo1-aDOv.d.ts +42 -0
  76. package/dist/router-Yo1-aDOv.d.ts.map +1 -0
  77. package/dist/routing.d.ts +6 -0
  78. package/dist/routing.js +4 -0
  79. package/dist/{run-crew-DDznbc3G.js → run-crew-B2fQLmgB.js} +16 -23
  80. package/dist/run-crew-B2fQLmgB.js.map +1 -0
  81. package/dist/run-crew-Bnve5dyI.d.ts +721 -0
  82. package/dist/run-crew-Bnve5dyI.d.ts.map +1 -0
  83. package/dist/{runtime-BTi8lr_O.js → runtime-Dxiet5YS.js} +100 -640
  84. package/dist/runtime-Dxiet5YS.js.map +1 -0
  85. package/dist/scaffolds-DKQrCRqh.d.ts +535 -0
  86. package/dist/scaffolds-DKQrCRqh.d.ts.map +1 -0
  87. package/dist/scaffolds-ekPIlBeU.js +3139 -0
  88. package/dist/scaffolds-ekPIlBeU.js.map +1 -0
  89. package/dist/schema-CNfa_VEy.d.ts +15 -0
  90. package/dist/schema-CNfa_VEy.d.ts.map +1 -0
  91. package/dist/storage-DJKmsaEI.d.ts +26 -0
  92. package/dist/storage-DJKmsaEI.d.ts.map +1 -0
  93. package/dist/storage.d.ts +10 -0
  94. package/dist/storage.d.ts.map +1 -0
  95. package/dist/storage.js +4 -0
  96. package/dist/tool-call-validation-BFoXkwbf.js +107 -0
  97. package/dist/tool-call-validation-BFoXkwbf.js.map +1 -0
  98. package/dist/tools-C4wHgGKQ.js +49 -0
  99. package/dist/tools-C4wHgGKQ.js.map +1 -0
  100. package/dist/tools.d.ts +46 -0
  101. package/dist/tools.d.ts.map +1 -0
  102. package/dist/tools.js +106 -0
  103. package/dist/tools.js.map +1 -0
  104. package/dist/validate-c7EL5uuH.js +224 -0
  105. package/dist/validate-c7EL5uuH.js.map +1 -0
  106. package/package.json +105 -6
  107. package/dist/run-crew-DDznbc3G.js.map +0 -1
  108. package/dist/runtime-BTi8lr_O.js.map +0 -1
@@ -1,142 +1,8 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-CiIaOW0V.js";
2
- import mime from "mime";
3
- import canonicalize from "canonicalize";
4
- //#region src/artifacts/metadata.ts
5
- const textEncoder$1 = new TextEncoder();
6
- function inferMediaType(value, options) {
7
- if (options.mediaType !== void 0) return options.mediaType;
8
- if (isBlobLike(value) && value.type !== "") return value.type;
9
- if (typeof value === "string") return mime.getType(value) ?? options.defaultMediaType;
10
- return options.defaultMediaType;
11
- }
12
- function measureArtifactValue(value, kind) {
13
- if (kind === "text" && typeof value === "string") return measureString(value);
14
- if (kind === "json") {
15
- const serialized = JSON.stringify(value);
16
- return serialized === void 0 ? void 0 : measureString(serialized);
17
- }
18
- if (isBlobLike(value)) return { bytes: value.size };
19
- }
20
- function measureString(value) {
21
- return {
22
- characters: value.length,
23
- bytes: textEncoder$1.encode(value).byteLength
24
- };
25
- }
26
- function isBlobLike(value) {
27
- return typeof Blob !== "undefined" && value instanceof Blob;
28
- }
29
- //#endregion
30
- //#region src/artifacts/artifact.ts
31
- const artifact = {
32
- text(value, options = {}) {
33
- return createArtifact("text", "inline", value, options, "text/plain");
34
- },
35
- json(value, options = {}) {
36
- return createArtifact("json", "inline", value, options, "application/json");
37
- },
38
- file(value, options = {}) {
39
- return createArtifact("file", "file", value, options);
40
- },
41
- image(value, options = {}) {
42
- return createArtifact("image", "file", value, options);
43
- },
44
- audio(value, options = {}) {
45
- return createArtifact("audio", "file", value, options);
46
- },
47
- document(value, options = {}) {
48
- return createArtifact("document", "file", value, options);
49
- },
50
- url(value, options = {}) {
51
- return createArtifact("url", "url", value.toString(), options);
52
- },
53
- toolResult(value, options) {
54
- return createArtifact("tool-result", "tool", value, {
55
- ...options,
56
- metadata: {
57
- ...options.metadata,
58
- toolName: options.toolName,
59
- ...options.callId !== void 0 ? { callId: options.callId } : {}
60
- }
61
- }, "application/json");
62
- },
63
- derive(input) {
64
- const { kind, source = "generated", value, parents, transform, ...options } = input;
65
- return createArtifact(kind, source, value, {
66
- ...options,
67
- lineage: {
68
- parents: parents.map(toArtifactRef),
69
- transform
70
- }
71
- }, defaultMediaTypeForKind(kind));
72
- }
73
- };
74
- function toArtifactRef(input) {
75
- return {
76
- id: input.id,
77
- kind: input.kind,
78
- source: input.source,
79
- privacy: input.privacy,
80
- ...input.mediaType !== void 0 ? { mediaType: input.mediaType } : {},
81
- ...input.label !== void 0 ? { label: input.label } : {},
82
- ...input.metadata !== void 0 ? { metadata: input.metadata } : {},
83
- ...input.size !== void 0 ? { size: input.size } : {},
84
- ...input.fingerprint !== void 0 ? { fingerprint: input.fingerprint } : {},
85
- ...input.storage !== void 0 ? { storage: input.storage } : {},
86
- ...input.lineage !== void 0 ? { lineage: input.lineage } : {}
87
- };
88
- }
89
- function isArtifactRef(value) {
90
- if (!isRecord(value)) return false;
91
- return typeof value.id === "string" && isArtifactKind(value.kind) && isArtifactSource(value.source) && isArtifactPrivacy(value.privacy);
92
- }
93
- function createArtifact(kind, source, value, options, defaultMediaType) {
94
- const mediaType = inferMediaType(value, {
95
- kind,
96
- ...options.mediaType !== void 0 ? { mediaType: options.mediaType } : {},
97
- ...defaultMediaType !== void 0 ? { defaultMediaType } : {}
98
- });
99
- const size = options.size ?? measureArtifactValue(value, kind);
100
- return {
101
- id: options.id ?? createArtifactId(kind),
102
- kind,
103
- source,
104
- value,
105
- privacy: options.privacy ?? "standard",
106
- ...mediaType !== void 0 ? { mediaType } : {},
107
- ...options.label !== void 0 ? { label: options.label } : {},
108
- ...options.metadata !== void 0 ? { metadata: options.metadata } : {},
109
- ...size !== void 0 ? { size } : {},
110
- ...options.fingerprint !== void 0 ? { fingerprint: options.fingerprint } : {},
111
- ...options.storage !== void 0 ? { storage: options.storage } : {},
112
- ...options.lineage !== void 0 ? { lineage: options.lineage } : {}
113
- };
114
- }
115
- function defaultMediaTypeForKind(kind) {
116
- switch (kind) {
117
- case "text": return "text/plain";
118
- case "json":
119
- case "tool-result": return "application/json";
120
- default: return;
121
- }
122
- }
123
- function isArtifactKind(value) {
124
- return value === "text" || value === "json" || value === "file" || value === "image" || value === "audio" || value === "video" || value === "document" || value === "url" || value === "tool-result";
125
- }
126
- function isArtifactSource(value) {
127
- return value === "inline" || value === "file" || value === "url" || value === "generated" || value === `provider-upload` || value === "tool";
128
- }
129
- function isArtifactPrivacy(value) {
130
- return value === "standard" || value === "sensitive" || value === "restricted";
131
- }
132
- function isRecord(value) {
133
- return typeof value === "object" && value !== null;
134
- }
135
- function createArtifactId(kind) {
136
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return `artifact:${kind}:${crypto.randomUUID()}`;
137
- return `artifact:${kind}:${Date.now()}:${Math.random().toString(16).slice(2)}`;
138
- }
139
- //#endregion
2
+ import { r as toArtifactRef } from "./artifact-DOfpeXLb.js";
3
+ import { t as createReceipt } from "./receipt-FYouoPHv.js";
4
+ import { i as standardSchemaToJsonSchema, n as validateOutputMapValues, o as parseToolUseEnvelope } from "./validate-c7EL5uuH.js";
5
+ import { r as runTool } from "./tools-C4wHgGKQ.js";
140
6
  //#region src/contract/bands.ts
141
7
  /**
142
8
  * Priority bands. Lower number = higher priority (runs first).
@@ -272,206 +138,6 @@ function createHookPipeline(options) {
272
138
  };
273
139
  }
274
140
  //#endregion
275
- //#region src/receipts/canonical.ts
276
- const encoder = new TextEncoder();
277
- /**
278
- * Convert costUsd (number | null) to its canonical string form.
279
- * RFC 8785 requires deterministic float-to-string; using JS Number→string
280
- * directly is unsafe across V8 versions (Grisu3 vs Dragonbox). We pin the
281
- * format by routing through Number.prototype.toString() for FINITE numbers
282
- * only, and treat NaN/Infinity as null. This matches "I-JSON only" from
283
- * 09-CONTEXT.md — receipts NEVER carry non-finite floats.
284
- */
285
- function stringifyCostUsd(costUsd) {
286
- if (costUsd === null) return null;
287
- if (!Number.isFinite(costUsd)) return null;
288
- return costUsd.toString();
289
- }
290
- /**
291
- * Convert a runtime Usage (number costUsd) to its canonical receipt form
292
- * (string costUsd). This is the single conversion site — canonical bytes
293
- * NEVER see a raw float in the cost field.
294
- */
295
- function usageToCanonical(usage) {
296
- return {
297
- promptTokens: usage.promptTokens,
298
- completionTokens: usage.completionTokens,
299
- costUsd: stringifyCostUsd(usage.costUsd)
300
- };
301
- }
302
- /**
303
- * Canonicalize a receipt body to JCS bytes (RFC 8785).
304
- *
305
- * INVARIANT: callers MUST pass an already-redacted body. The redactor in
306
- * redact.ts produces the input to this function — never the cleartext.
307
- * See 09-CONTEXT.md "Redact-Then-Sign Ordering (UNRETROFITTABLE)".
308
- *
309
- * Throws if canonicalize returns undefined (impossible for valid bodies
310
- * — surfaces a programmer error rather than silently producing zero
311
- * bytes that would later fail signature verification).
312
- */
313
- function canonicalizeReceiptBody(body) {
314
- const json = canonicalize(body);
315
- if (json === void 0) throw new Error("canonicalizeReceiptBody: canonicalize returned undefined; receipt body contained a non-canonicalizable value (function/symbol/undefined).");
316
- return encoder.encode(json);
317
- }
318
- //#endregion
319
- //#region src/receipts/envelope.ts
320
- const PAYLOAD_TYPE = "application/vnd.lattice.receipt+json";
321
- const textEncoder = new TextEncoder();
322
- function base64Encode(bytes) {
323
- return Buffer.from(bytes).toString("base64");
324
- }
325
- function base64Decode(value) {
326
- return new Uint8Array(Buffer.from(value, "base64"));
327
- }
328
- /**
329
- * DSSE v1.0 Pre-Authentication Encoding.
330
- *
331
- * Reference: https://github.com/secure-systems-lab/dsse/blob/v1.0.0/protocol.md
332
- *
333
- * PAE = UTF-8("DSSEv1 " + len(payloadType) + " " + payloadType
334
- * + " " + len(payload) + " " + payload)
335
- *
336
- * `payload` here is the BASE64-encoded string per DSSE v1.0 spec (NOT raw
337
- * canonical bytes). Both signing and verification MUST construct PAE the
338
- * same way; this module is the single source of truth.
339
- *
340
- * ASCII length is decimal (no zero-padding). e.g. length 1000 → "1000".
341
- */
342
- function buildPae(payloadType, payloadBase64) {
343
- const ascii = "DSSEv1 " + payloadType.length.toString() + " " + payloadType + " " + payloadBase64.length.toString() + " " + payloadBase64;
344
- return textEncoder.encode(ascii);
345
- }
346
- function encodeEnvelope(input) {
347
- return {
348
- payloadType: PAYLOAD_TYPE,
349
- payload: base64Encode(input.payloadBytes),
350
- signatures: input.signatures.map((entry) => ({
351
- keyid: entry.keyid,
352
- sig: base64Encode(entry.sig)
353
- }))
354
- };
355
- }
356
- function decodeEnvelope(envelope) {
357
- if (envelope.payloadType !== "application/vnd.lattice.receipt+json") throw new Error(`envelope payloadType mismatch: expected "${PAYLOAD_TYPE}" got "${envelope.payloadType}"`);
358
- return {
359
- payloadType: envelope.payloadType,
360
- payloadBytes: base64Decode(envelope.payload),
361
- signatures: envelope.signatures.map((entry) => ({
362
- keyid: entry.keyid,
363
- sig: base64Decode(entry.sig)
364
- }))
365
- };
366
- }
367
- //#endregion
368
- //#region src/receipts/redact.ts
369
- /**
370
- * Default redaction policy id for v1.1. Free-form string per
371
- * 09-CONTEXT.md — registry enforcement deferred to v1.2.
372
- */
373
- const DEFAULT_REDACTION_POLICY_ID = "lattice.default.v1";
374
- /**
375
- * Redact a receipt body BEFORE canonicalization (and BEFORE signing).
376
- *
377
- * The signed digest commits to canonicalize(redact(body)). NEVER the
378
- * other way around. See 09-CONTEXT.md "Redact-Then-Sign Ordering
379
- * (UNRETROFITTABLE)" and PITFALLS.md Pitfall #1.
380
- *
381
- * For v1.1 the default policy is minimal — the heavy lifting already
382
- * happened upstream:
383
- * - Tripwire evaluator emits {detector, substring} for no-pii (T-08-01).
384
- * - Provider responses are hashed into inputHashes/outputHash, never
385
- * embedded raw.
386
- * - Router reject messages do not contain PII by construction.
387
- *
388
- * This function therefore primarily:
389
- * 1. Materializes the redactions[] manifest declaring what WAS elided
390
- * upstream (so receipts are self-describing).
391
- * 2. Provides the extension point future policies will use.
392
- *
393
- * Returns a NEW body — never mutates the input.
394
- */
395
- function redactReceiptBody(body, policyId = DEFAULT_REDACTION_POLICY_ID) {
396
- const redactions = [];
397
- if (body.tripwireEvidence !== void 0 && body.tripwireEvidence.kind === "no-pii") redactions.push({
398
- path: "tripwireEvidence.observed",
399
- reason: "no-pii-detector-substring-only"
400
- });
401
- const sorted = [...redactions].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
402
- return {
403
- body: {
404
- ...body,
405
- redactionPolicyId: policyId,
406
- redactions: sorted
407
- },
408
- redactions: sorted
409
- };
410
- }
411
- //#endregion
412
- //#region src/receipts/receipt.ts
413
- /**
414
- * Build, redact, canonicalize, sign, and envelope a CapabilityReceipt.
415
- *
416
- * Ordering INVARIANT (09-CONTEXT.md, PITFALLS.md Pitfall #1):
417
- * redact -> canonicalize -> PAE -> sign -> encode
418
- *
419
- * The signed digest commits to canonicalize(redact(body)). The function
420
- * structure makes any other ordering impossible to write by accident —
421
- * canonicalizeReceiptBody is ONLY called on the output of redactReceiptBody.
422
- *
423
- * Defense in depth:
424
- * - body.kid is assigned from signer.kid, never from input (input has no
425
- * kid field). The signed body and the envelope keyid CANNOT disagree by
426
- * construction.
427
- * - signer.kid is also written to envelope.signatures[0].keyid, so the
428
- * verifier can cross-check (Step 7 of verifyReceipt).
429
- *
430
- * I-JSON guarantees: usage.costUsd is converted to string (or null) via
431
- * usageToCanonical. Receipts NEVER carry raw floats in the canonical form.
432
- */
433
- async function createReceipt(input, signer) {
434
- const policyId = input.redactionPolicyId ?? "lattice.default.v1";
435
- const receiptId = input.receiptId ?? crypto.randomUUID();
436
- const issuedAt = input.issuedAt ?? (/* @__PURE__ */ new Date()).toISOString();
437
- const { body } = redactReceiptBody({
438
- version: "lattice-receipt/v1.2",
439
- receiptId,
440
- runId: input.runId,
441
- issuedAt,
442
- kid: signer.kid,
443
- model: input.model,
444
- route: input.route,
445
- ...input.modelClass !== void 0 ? { modelClass: input.modelClass } : {},
446
- ...input.parentReceiptCid !== void 0 ? { parentReceiptCid: input.parentReceiptCid } : {},
447
- usage: usageToCanonical(input.usage),
448
- contractVerdict: input.contractVerdict,
449
- contractHash: input.contractHash,
450
- inputHashes: input.inputHashes,
451
- outputHash: input.outputHash,
452
- redactionPolicyId: policyId,
453
- redactions: [],
454
- ...input.noRouteReasons !== void 0 ? { noRouteReasons: input.noRouteReasons } : {},
455
- ...input.tripwireEvidence !== void 0 ? { tripwireEvidence: input.tripwireEvidence } : {},
456
- ...input.stepName !== void 0 ? { stepName: input.stepName } : {},
457
- ...input.stepIndex !== void 0 ? { stepIndex: input.stepIndex } : {},
458
- ...input.parentStepName !== void 0 ? { parentStepName: input.parentStepName } : {},
459
- ...input.previousStepName !== void 0 ? { previousStepName: input.previousStepName } : {},
460
- ...input.sessionId !== void 0 ? { sessionId: input.sessionId } : {},
461
- ...input.timestamp !== void 0 ? { timestamp: input.timestamp } : {}
462
- }, policyId);
463
- const payloadBytes = canonicalizeReceiptBody(body);
464
- const pae = buildPae(PAYLOAD_TYPE, base64Encode(payloadBytes));
465
- const sig = await signer.sign(pae);
466
- return encodeEnvelope({
467
- payloadBytes,
468
- signatures: [{
469
- keyid: signer.kid,
470
- sig
471
- }]
472
- });
473
- }
474
- //#endregion
475
141
  //#region src/contract/checkpoint.ts
476
142
  /**
477
143
  * The tracer event name Lattice's checkpoint hook emits per step transition.
@@ -588,34 +254,89 @@ function extractReceiptId(envelope) {
588
254
  }
589
255
  }
590
256
  //#endregion
591
- //#region src/agent/format-tools.ts
257
+ //#region src/runtime/survivability.ts
592
258
  /**
593
- * Convert a Standard Schema to a JSON Schema-shaped descriptor suitable for
594
- * inclusion in an LLM tool description. Standard Schema vendors can
595
- * optionally expose `toJSONSchema` on their schema objects; when absent,
596
- * we fall back to a minimal structural description that lists the schema
597
- * vendor + version + a placeholder. Models tolerate placeholder schemas in
598
- * practice because the tool description is supplementary — what matters
599
- * is the envelope contract (`{tool_call: {name, args}}`).
259
+ * Reference implementation of SurvivabilityAdapter<TState>. Records
260
+ * eviction events but does NOT persist; serialize / deserialize round-
261
+ * trip via JSON.stringify / JSON.parse. Analog to createFakeProvider
262
+ * in the providers/ module -- gives Lattice's vitest a complete shape-
263
+ * conformance target before the real (FSB-side) adapter ships in
264
+ * Plan 05-05.
265
+ *
266
+ * Per CONTEXT.md D-11 the noop adapter ships in Lattice (not FSB)
267
+ * because it covers the contract surface in Lattice's own test suite;
268
+ * FSB's real chrome.storage.session-backed adapter is glue layer.
600
269
  */
601
- function toolSchemaToJsonSchema(schema) {
602
- const standardSchema = schema["~standard"];
603
- if (typeof standardSchema === "object" && standardSchema !== null && "vendor" in standardSchema) {
604
- const vendor = standardSchema;
605
- const maybeToJson = schema.toJSONSchema;
606
- if (typeof maybeToJson === "function") try {
607
- return maybeToJson();
608
- } catch {}
609
- return {
610
- $comment: `standard-schema vendor: ${vendor.vendor}; toJSONSchema not available`,
611
- type: "object"
612
- };
613
- }
270
+ function createNoopSurvivabilityAdapter(options = {}) {
271
+ const id = options.id ?? "noop-survivability";
272
+ const defaultPolicy = options.policy ?? "SAFE";
273
+ const hooks = /* @__PURE__ */ new Set();
614
274
  return {
615
- $comment: "non-standard-schema input",
616
- type: "object"
275
+ kind: "survivability-adapter",
276
+ id,
277
+ serialize(state) {
278
+ return {
279
+ kind: "survivability-snapshot",
280
+ version: "lattice-survivability/v1",
281
+ payload: JSON.stringify(state ?? null),
282
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString()
283
+ };
284
+ },
285
+ deserialize(snapshot) {
286
+ return JSON.parse(snapshot.payload);
287
+ },
288
+ onEviction(hook) {
289
+ hooks.add(hook);
290
+ let unsubscribed = false;
291
+ return () => {
292
+ if (unsubscribed) return;
293
+ unsubscribed = true;
294
+ hooks.delete(hook);
295
+ };
296
+ },
297
+ async resume(_snapshot) {
298
+ return defaultPolicy;
299
+ }
617
300
  };
618
301
  }
302
+ //#endregion
303
+ //#region src/agent/format-tools.ts
304
+ /**
305
+ * formatToolsForProvider — Phase 19 (v1.2).
306
+ *
307
+ * The agent loop runs over the existing v1.1 + v1.2 `ProviderAdapter`
308
+ * interface unchanged (CONTEXT.md Q2). Adapters accept only a single
309
+ * `task: string` plus `outputs[]` — they have no native multi-turn or
310
+ * tool-use surface.
311
+ *
312
+ * This helper bridges that gap by encoding the running conversation +
313
+ * tool descriptions + a structured "respond with this envelope" instruction
314
+ * into the `task` string. The model is asked to either answer directly or
315
+ * emit a JSON envelope on a line by itself. `parseToolUse` extracts the
316
+ * envelope.
317
+ *
318
+ * The implementation works ACROSS all 7 logical providers (openai,
319
+ * openai-compat, anthropic, gemini, xai, openrouter, lm-studio) by virtue
320
+ * of being provider-agnostic: it uses the adapter's normalized text
321
+ * response (`ProviderRunResponse.rawOutputs`) and never touches the
322
+ * provider-specific request shape. Native tool_use (Anthropic Messages-API
323
+ * `tools[]`, OpenAI Chat-Completions `tools[]`, Gemini `function_declarations`)
324
+ * is DEFERRED to a follow-on milestone where the `ProviderAdapter` interface
325
+ * can be additively extended without breaking the INV-03 parity contract
326
+ * shipped in v1.2 Phase 17.
327
+ *
328
+ * Returned closure shape:
329
+ * {
330
+ * buildTask(conversation, system?) — encodes turns + tools + envelope
331
+ * instructions into a single string;
332
+ * parseToolUse(text) — extracts JSON tool-call envelopes
333
+ * from the response, returns null
334
+ * when the response is a final answer;
335
+ * describeForSystem() — returns the static tool-description
336
+ * block (for tracing / logging);
337
+ * }
338
+ */
339
+ const toolSchemaToJsonSchema = standardSchemaToJsonSchema;
619
340
  /**
620
341
  * Builds the prompt-reencoded tool-use protocol handle for any provider.
621
342
  *
@@ -689,281 +410,6 @@ function formatToolsForProvider(providerName, tools, options = {}) {
689
410
  mode: "prompt-reencoded"
690
411
  };
691
412
  }
692
- function parseToolUseEnvelope(responseText) {
693
- if (typeof responseText !== "string" || responseText.length === 0) return null;
694
- const candidates = extractJsonCandidates(responseText);
695
- for (const candidate of candidates) {
696
- const parsed = tryParseEnvelope(candidate);
697
- if (parsed !== null) return parsed;
698
- }
699
- return null;
700
- }
701
- /**
702
- * Extracts JSON-looking candidate substrings from a response text.
703
- *
704
- * Models routinely wrap JSON in markdown code fences (```json ... ```),
705
- * prepend explanatory prose ("I'll call the search tool: { ... }"), or
706
- * produce multiple JSON-shaped blobs. This extractor scans for plausible
707
- * candidates ordered by likelihood.
708
- */
709
- function extractJsonCandidates(text) {
710
- const candidates = [];
711
- const fenceRegex = /```(?:json)?\s*([\s\S]*?)```/g;
712
- let fenceMatch;
713
- while ((fenceMatch = fenceRegex.exec(text)) !== null) {
714
- const inner = fenceMatch[1];
715
- if (inner !== void 0) candidates.push(inner.trim());
716
- }
717
- const braceStart = text.indexOf("{");
718
- const braceEnd = text.lastIndexOf("}");
719
- if (braceStart !== -1 && braceEnd > braceStart) candidates.push(text.slice(braceStart, braceEnd + 1));
720
- candidates.push(text.trim());
721
- return candidates;
722
- }
723
- function tryParseEnvelope(jsonLike) {
724
- let parsed;
725
- try {
726
- parsed = JSON.parse(jsonLike);
727
- } catch {
728
- return null;
729
- }
730
- if (typeof parsed !== "object" || parsed === null) return null;
731
- const toolCalls = parsed["tool_calls"];
732
- if (!Array.isArray(toolCalls) || toolCalls.length === 0) return null;
733
- const requests = [];
734
- for (const call of toolCalls) {
735
- if (typeof call !== "object" || call === null) return null;
736
- const callRecord = call;
737
- const id = callRecord["id"];
738
- const name = callRecord["name"];
739
- const args = callRecord["args"];
740
- if (typeof id !== "string" || typeof name !== "string") return null;
741
- requests.push({
742
- id,
743
- name,
744
- args
745
- });
746
- }
747
- return requests;
748
- }
749
- //#endregion
750
- //#region src/outputs/validate.ts
751
- async function validateSchemaOutput(name, schema, value) {
752
- const result = schema["~standard"].validate(value);
753
- const validation = result instanceof Promise ? await result : result;
754
- if (validation.issues) return {
755
- ok: false,
756
- issue: {
757
- ["output"]: name,
758
- issues: validation.issues.map(normalizeIssue)
759
- }
760
- };
761
- return {
762
- ok: true,
763
- value: validation.value
764
- };
765
- }
766
- async function validateOutputMap(contracts, rawOutputs, plan) {
767
- const outputs = {};
768
- for (const [name, contract] of Object.entries(contracts)) {
769
- const value = rawOutputs[name];
770
- const issue = await validateOutput(name, contract, value);
771
- if (!issue.ok) return {
772
- ok: false,
773
- error: {
774
- kind: "validation",
775
- message: `Invalid output "${name}".`,
776
- ["output"]: name,
777
- issues: issue.issues
778
- },
779
- usage: {
780
- promptTokens: 0,
781
- completionTokens: 0,
782
- costUsd: null
783
- },
784
- raw: rawOutputs,
785
- partialOutputs: outputs,
786
- plan
787
- };
788
- outputs[name] = issue.value;
789
- }
790
- return {
791
- ok: true,
792
- outputs,
793
- artifacts: [],
794
- usage: {
795
- promptTokens: 0,
796
- completionTokens: 0,
797
- costUsd: null
798
- },
799
- plan
800
- };
801
- }
802
- async function validateOutput(name, contract, value) {
803
- if (contract === "text") {
804
- if (typeof value !== "string") return {
805
- ok: false,
806
- issues: [{ message: "Expected text output to be a string." }]
807
- };
808
- return {
809
- ok: true,
810
- value
811
- };
812
- }
813
- if (isStandardSchema(contract)) {
814
- const result = await validateSchemaOutput(name, contract, value);
815
- if (!result.ok) return {
816
- ok: false,
817
- issues: result.issue.issues
818
- };
819
- return {
820
- ok: true,
821
- value: result.value
822
- };
823
- }
824
- if (contract.kind === "citations") {
825
- if (!Array.isArray(value)) return {
826
- ok: false,
827
- issues: [{ message: "Expected citations output to be an array." }]
828
- };
829
- return {
830
- ok: true,
831
- value
832
- };
833
- }
834
- if (contract.kind === "artifacts") {
835
- if (!Array.isArray(value)) return {
836
- ok: false,
837
- issues: [{ message: "Expected artifacts output to be an array." }]
838
- };
839
- for (const item of value) {
840
- if (!isArtifactRef(item)) return {
841
- ok: false,
842
- issues: [{ message: "Expected artifacts output item to be an artifact ref." }]
843
- };
844
- if (contract.artifactKind !== void 0 && item.kind !== contract.artifactKind) return {
845
- ok: false,
846
- issues: [{ message: `Expected artifacts output item kind to be "${contract.artifactKind}".` }]
847
- };
848
- }
849
- return {
850
- ok: true,
851
- value: value.map(toArtifactRef)
852
- };
853
- }
854
- return {
855
- ok: false,
856
- issues: [{ message: "Unsupported output contract." }]
857
- };
858
- }
859
- function isStandardSchema(contract) {
860
- if (typeof contract !== "object" || contract === null) return false;
861
- return typeof contract["~standard"]?.validate === "function";
862
- }
863
- function normalizeIssue(issue) {
864
- const path = issue.path?.map(normalizePathSegment).filter((segment) => segment !== void 0);
865
- return {
866
- message: issue.message,
867
- ...path !== void 0 && path.length > 0 ? { path } : {}
868
- };
869
- }
870
- function normalizePathSegment(segment) {
871
- if (typeof segment === "string" || typeof segment === "number" || typeof segment === "symbol") return segment;
872
- return normalizePathKey(segment.key);
873
- }
874
- function normalizePathKey(key) {
875
- return key;
876
- }
877
- //#endregion
878
- //#region src/runtime/survivability.ts
879
- /**
880
- * Reference implementation of SurvivabilityAdapter<TState>. Records
881
- * eviction events but does NOT persist; serialize / deserialize round-
882
- * trip via JSON.stringify / JSON.parse. Analog to createFakeProvider
883
- * in the providers/ module -- gives Lattice's vitest a complete shape-
884
- * conformance target before the real (FSB-side) adapter ships in
885
- * Plan 05-05.
886
- *
887
- * Per CONTEXT.md D-11 the noop adapter ships in Lattice (not FSB)
888
- * because it covers the contract surface in Lattice's own test suite;
889
- * FSB's real chrome.storage.session-backed adapter is glue layer.
890
- */
891
- function createNoopSurvivabilityAdapter(options = {}) {
892
- const id = options.id ?? "noop-survivability";
893
- const defaultPolicy = options.policy ?? "SAFE";
894
- const hooks = /* @__PURE__ */ new Set();
895
- return {
896
- kind: "survivability-adapter",
897
- id,
898
- serialize(state) {
899
- return {
900
- kind: "survivability-snapshot",
901
- version: "lattice-survivability/v1",
902
- payload: JSON.stringify(state ?? null),
903
- capturedAt: (/* @__PURE__ */ new Date()).toISOString()
904
- };
905
- },
906
- deserialize(snapshot) {
907
- return JSON.parse(snapshot.payload);
908
- },
909
- onEviction(hook) {
910
- hooks.add(hook);
911
- let unsubscribed = false;
912
- return () => {
913
- if (unsubscribed) return;
914
- unsubscribed = true;
915
- hooks.delete(hook);
916
- };
917
- },
918
- async resume(_snapshot) {
919
- return defaultPolicy;
920
- }
921
- };
922
- }
923
- //#endregion
924
- //#region src/tools/tools.ts
925
- function defineTool(definition) {
926
- return {
927
- kind: "tool",
928
- ...definition
929
- };
930
- }
931
- async function runTool(tool, input, context = {}) {
932
- const validation = await validateSchemaOutput(tool.name, tool.inputSchema, input);
933
- if (!validation.ok) throw new Error(`Invalid input for tool "${tool.name}".`);
934
- const callId = createToolCallId();
935
- const output = await tool.execute(validation.value, context);
936
- return {
937
- callId,
938
- toolName: tool.name,
939
- artifact: artifact.toolResult(output, {
940
- id: `artifact:tool-result:${tool.name}:${callId}`,
941
- toolName: tool.name,
942
- callId
943
- })
944
- };
945
- }
946
- async function importMcpTools(client, toolNames) {
947
- const descriptors = await Promise.resolve(client.listTools?.() ?? []);
948
- const allowed = toolNames === void 0 ? void 0 : new Set(toolNames);
949
- return descriptors.filter((descriptor) => allowed === void 0 || allowed.has(descriptor.name)).map((descriptor) => defineTool({
950
- name: descriptor.name,
951
- ...descriptor.description !== void 0 ? { description: descriptor.description } : {},
952
- inputSchema: descriptor.inputSchema,
953
- execute: async (input) => client.callTool({
954
- name: descriptor.name,
955
- arguments: input
956
- })
957
- }));
958
- }
959
- function toolArtifactRef(result) {
960
- const { value: _value, ...ref } = result.artifact;
961
- return ref;
962
- }
963
- function createToolCallId() {
964
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
965
- return `${Date.now()}:${Math.random().toString(16).slice(2)}`;
966
- }
967
413
  //#endregion
968
414
  //#region src/agent/host.ts
969
415
  /**
@@ -1027,6 +473,7 @@ const ZERO_USAGE = {
1027
473
  completionTokens: 0,
1028
474
  costUsd: null
1029
475
  };
476
+ const DEFAULT_AGENT_OUTPUTS = { answer: "text" };
1030
477
  /**
1031
478
  * Resolves the runtime's behaviour for a single `ai.runAgent(intent)` call.
1032
479
  *
@@ -1072,6 +519,8 @@ async function runAgentInternal(intent, config = {}, internalOptions = {}) {
1072
519
  content: intent.task
1073
520
  }];
1074
521
  const handle = formatToolsForProvider(providerName, intent.tools);
522
+ const outputContracts = intent.outputs ?? DEFAULT_AGENT_OUTPUTS;
523
+ const outputNames = Object.keys(outputContracts);
1075
524
  const budget = intent.contract?.budget;
1076
525
  const maxIterations = budget?.maxIterations ?? Number.POSITIVE_INFINITY;
1077
526
  const maxWallTimeMs = budget?.maxWallTimeMs ?? Number.POSITIVE_INFINITY;
@@ -1165,7 +614,8 @@ async function runAgentInternal(intent, config = {}, internalOptions = {}) {
1165
614
  const providerRequest = {
1166
615
  task,
1167
616
  artifacts: [],
1168
- outputs: ["answer"],
617
+ outputs: outputNames,
618
+ outputContracts,
1169
619
  ...intent.policy !== void 0 ? { policy: intent.policy } : {}
1170
620
  };
1171
621
  response = host.transport !== void 0 ? await host.transport.call(provider, providerRequest) : await provider.execute(providerRequest);
@@ -1211,10 +661,20 @@ async function runAgentInternal(intent, config = {}, internalOptions = {}) {
1211
661
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1212
662
  previousStepName: `agent-iteration-${iterationIndex}-before`
1213
663
  });
664
+ const outputValidation = await validateOutputMapValues(outputContracts, response.rawOutputs);
665
+ if (!outputValidation.ok) return buildFailure({
666
+ kind: "validation",
667
+ reason: outputValidation.error.message,
668
+ cause: outputValidation.error,
669
+ iterations,
670
+ usage: cumulativeUsage
671
+ });
1214
672
  await host.storage?.clear();
673
+ const artifactRefs = response.artifactRefs !== void 0 ? response.artifactRefs.map(toArtifactRef) : [];
1215
674
  return {
1216
675
  kind: "success",
1217
- output: { answer: responseText },
676
+ output: outputValidation.outputs,
677
+ ...artifactRefs.length > 0 ? { artifacts: artifactRefs } : {},
1218
678
  usage: snapshotUsage(cumulativeUsage),
1219
679
  iterations: Object.freeze([...iterations])
1220
680
  };
@@ -1384,6 +844,6 @@ function stableHash(input) {
1384
844
  }
1385
845
  }
1386
846
  //#endregion
1387
- export { decodeEnvelope as C, artifact as D, createHookPipeline as E, toArtifactRef as O, buildPae as S, BAND as T, STEP_TRANSITION_EVENT_NAME as _, createNoopAgentHost as a, PAYLOAD_TYPE as b, runTool as c, validateOutputMap as d, validateSchemaOutput as f, DEFAULT_CHECKPOINT_BAND as g, toolSchemaToJsonSchema as h, AgentDeniedError as i, toolArtifactRef as l, parseToolUseEnvelope as m, runAgentInternal as n, defineTool as o, formatToolsForProvider as p, runtime_exports as r, importMcpTools as s, runAgent as t, createNoopSurvivabilityAdapter as u, createCheckpointHook as v, canonicalizeReceiptBody as w, base64Encode as x, createReceipt as y };
847
+ export { createNoopAgentHost as a, createNoopSurvivabilityAdapter as c, createCheckpointHook as d, BAND as f, AgentDeniedError as i, DEFAULT_CHECKPOINT_BAND as l, runAgentInternal as n, formatToolsForProvider as o, createHookPipeline as p, runtime_exports as r, toolSchemaToJsonSchema as s, runAgent as t, STEP_TRANSITION_EVENT_NAME as u };
1388
848
 
1389
- //# sourceMappingURL=runtime-BTi8lr_O.js.map
849
+ //# sourceMappingURL=runtime-Dxiet5YS.js.map