@full-self-browsing/lattice 1.4.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.
- package/dist/agent-run-C6miAzwI.d.ts +45 -0
- package/dist/agent-run-C6miAzwI.d.ts.map +1 -0
- package/dist/agent-run-CgPVFl0Z.js +47 -0
- package/dist/agent-run-CgPVFl0Z.js.map +1 -0
- package/dist/agents.d.ts +5 -0
- package/dist/agents.js +6 -0
- package/dist/artifact-Bg6mJGnm.d.ts +125 -0
- package/dist/artifact-Bg6mJGnm.d.ts.map +1 -0
- package/dist/artifact-DOfpeXLb.js +140 -0
- package/dist/artifact-DOfpeXLb.js.map +1 -0
- package/dist/artifacts.d.ts +2 -0
- package/dist/artifacts.js +2 -0
- package/dist/audit.d.ts +3 -0
- package/dist/audit.js +4 -0
- package/dist/catalog-CAfYwB_-.js +91 -0
- package/dist/catalog-CAfYwB_-.js.map +1 -0
- package/dist/context-pack-Bz3GXmjv.js +99 -0
- package/dist/context-pack-Bz3GXmjv.js.map +1 -0
- package/dist/context.d.ts +2 -0
- package/dist/context.js +2 -0
- package/dist/contract-S3oJGlc9.d.ts +74 -0
- package/dist/contract-S3oJGlc9.d.ts.map +1 -0
- package/dist/core.d.ts +48 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +95 -0
- package/dist/core.js.map +1 -0
- package/dist/errors-eEuEIx6X.js +407 -0
- package/dist/errors-eEuEIx6X.js.map +1 -0
- package/dist/eval.d.ts +2 -0
- package/dist/eval.js +2 -0
- package/dist/fingerprint-DodDbQKN.js +34 -0
- package/dist/fingerprint-DodDbQKN.js.map +1 -0
- package/dist/index-DpnHGHVL.d.ts +53 -0
- package/dist/index-DpnHGHVL.d.ts.map +1 -0
- package/dist/index.d.ts +90 -3533
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -15968
- package/dist/index.js.map +1 -1
- package/dist/infer-DLqp5QIM.d.ts +96 -0
- package/dist/infer-DLqp5QIM.d.ts.map +1 -0
- package/dist/lineage-DBgoPWAZ.js +137 -0
- package/dist/lineage-DBgoPWAZ.js.map +1 -0
- package/dist/local-CXOGPJ1f.js +139 -0
- package/dist/local-CXOGPJ1f.js.map +1 -0
- package/dist/local-Dy--7peL.d.ts +10 -0
- package/dist/local-Dy--7peL.d.ts.map +1 -0
- package/dist/memory-CkQEW6m5.js +62 -0
- package/dist/memory-CkQEW6m5.js.map +1 -0
- package/dist/memory-DRig5EHV.d.ts +10 -0
- package/dist/memory-DRig5EHV.d.ts.map +1 -0
- package/dist/negotiate-ClD88hkc.js +10967 -0
- package/dist/negotiate-ClD88hkc.js.map +1 -0
- package/dist/otel-BgM4e55_.d.ts +421 -0
- package/dist/otel-BgM4e55_.d.ts.map +1 -0
- package/dist/permission-context-CUKMo79F.js +134 -0
- package/dist/permission-context-CUKMo79F.js.map +1 -0
- package/dist/plan-DFm8Llep.js +125 -0
- package/dist/plan-DFm8Llep.js.map +1 -0
- package/dist/preflight-DNHWuJ46.d.ts +64 -0
- package/dist/preflight-DNHWuJ46.d.ts.map +1 -0
- package/dist/provider-C2IfKsvz.d.ts +1178 -0
- package/dist/provider-C2IfKsvz.d.ts.map +1 -0
- package/dist/providers.d.ts +4 -0
- package/dist/providers.js +4 -0
- package/dist/rate-limit-group-nDsBJqSu.d.ts +235 -0
- package/dist/rate-limit-group-nDsBJqSu.d.ts.map +1 -0
- package/dist/receipt-FYouoPHv.js +205 -0
- package/dist/receipt-FYouoPHv.js.map +1 -0
- package/dist/replay-CtIhpLek.js +964 -0
- package/dist/replay-CtIhpLek.js.map +1 -0
- package/dist/result-DLEx2WvU.d.ts +38 -0
- package/dist/result-DLEx2WvU.d.ts.map +1 -0
- package/dist/router-DU4Z3pTd.js +314 -0
- package/dist/router-DU4Z3pTd.js.map +1 -0
- package/dist/router-Yo1-aDOv.d.ts +42 -0
- package/dist/router-Yo1-aDOv.d.ts.map +1 -0
- package/dist/routing.d.ts +6 -0
- package/dist/routing.js +4 -0
- package/dist/{run-crew-CKdBjh5P.js → run-crew-B2fQLmgB.js} +7 -136
- package/dist/run-crew-B2fQLmgB.js.map +1 -0
- package/dist/run-crew-Bnve5dyI.d.ts +721 -0
- package/dist/run-crew-Bnve5dyI.d.ts.map +1 -0
- package/dist/{runtime-D25ehzCj.js → runtime-Dxiet5YS.js} +98 -641
- package/dist/runtime-Dxiet5YS.js.map +1 -0
- package/dist/scaffolds-DKQrCRqh.d.ts +535 -0
- package/dist/scaffolds-DKQrCRqh.d.ts.map +1 -0
- package/dist/scaffolds-ekPIlBeU.js +3139 -0
- package/dist/scaffolds-ekPIlBeU.js.map +1 -0
- package/dist/schema-CNfa_VEy.d.ts +15 -0
- package/dist/schema-CNfa_VEy.d.ts.map +1 -0
- package/dist/storage-DJKmsaEI.d.ts +26 -0
- package/dist/storage-DJKmsaEI.d.ts.map +1 -0
- package/dist/storage.d.ts +10 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +4 -0
- package/dist/tool-call-validation-BFoXkwbf.js +107 -0
- package/dist/tool-call-validation-BFoXkwbf.js.map +1 -0
- package/dist/tools-C4wHgGKQ.js +49 -0
- package/dist/tools-C4wHgGKQ.js.map +1 -0
- package/dist/tools.d.ts +46 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +106 -0
- package/dist/tools.js.map +1 -0
- package/dist/validate-c7EL5uuH.js +224 -0
- package/dist/validate-c7EL5uuH.js.map +1 -0
- package/package.json +99 -2
- package/dist/run-crew-CKdBjh5P.js.map +0 -1
- package/dist/runtime-D25ehzCj.js.map +0 -1
|
@@ -0,0 +1,964 @@
|
|
|
1
|
+
import { r as toArtifactRef } from "./artifact-DOfpeXLb.js";
|
|
2
|
+
import { a as decodeEnvelope, i as buildPae, n as PAYLOAD_TYPE, o as canonicalizeReceiptBody, r as base64Encode, t as createReceipt } from "./receipt-FYouoPHv.js";
|
|
3
|
+
import { n as receiptCid, t as computeArtifactLineageMerkleRoot } from "./lineage-DBgoPWAZ.js";
|
|
4
|
+
import { r as withPlanStatus, t as createExecutionPlan } from "./plan-DFm8Llep.js";
|
|
5
|
+
import { t as fingerprintArtifactValue } from "./fingerprint-DodDbQKN.js";
|
|
6
|
+
import canonicalize from "canonicalize";
|
|
7
|
+
//#region src/observability/otel.ts
|
|
8
|
+
const DEFAULT_SPAN_NAME = "lattice.run";
|
|
9
|
+
const DEFAULT_LANGFUSE_BASE_URL = "https://cloud.langfuse.com";
|
|
10
|
+
const DEFAULT_PHOENIX_BASE_URL = "http://localhost:6006";
|
|
11
|
+
const OTEL_STATUS_OK = 1;
|
|
12
|
+
const OTEL_STATUS_ERROR = 2;
|
|
13
|
+
const SECRET_KEY_RE = /api[-_]?key|authorization|credentials?|headers?|password|secret|token/iu;
|
|
14
|
+
const CONTENT_KEY_RE = /artifact|body|content|input|inputs|message|messages|output|outputs|payload|prompt|rawOutputs|task|value/iu;
|
|
15
|
+
function createOtelRunEventSink(options) {
|
|
16
|
+
const spans = /* @__PURE__ */ new Map();
|
|
17
|
+
return async (event) => {
|
|
18
|
+
const span = getOrCreateRunSpan(event, options, spans);
|
|
19
|
+
const attributes = await createRunEventAttributes(event, options);
|
|
20
|
+
setSpanAttributes(span, attributes);
|
|
21
|
+
span.addEvent?.(`lattice.${event.kind}`, attributes);
|
|
22
|
+
if (event.kind === "capabilities.negotiation.fallback") {
|
|
23
|
+
span.setStatus?.({ code: OTEL_STATUS_OK });
|
|
24
|
+
span.end?.(eventTime(event));
|
|
25
|
+
spans.delete(event.runId);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (event.kind === "run.complete") {
|
|
29
|
+
span.setStatus?.({ code: OTEL_STATUS_OK });
|
|
30
|
+
span.end?.(eventTime(event));
|
|
31
|
+
spans.delete(event.runId);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (event.kind === "run.failed") {
|
|
35
|
+
const message = metadataString(event, "reason");
|
|
36
|
+
span.setStatus?.({
|
|
37
|
+
code: OTEL_STATUS_ERROR,
|
|
38
|
+
...message !== void 0 ? { message } : {}
|
|
39
|
+
});
|
|
40
|
+
if (message !== void 0) span.recordException?.(message);
|
|
41
|
+
span.end?.(eventTime(event));
|
|
42
|
+
spans.delete(event.runId);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function sanitizeRunEventAttributes(event, options = {}) {
|
|
47
|
+
const attributes = {
|
|
48
|
+
"lattice.event.kind": event.kind,
|
|
49
|
+
"lattice.run.id": event.runId
|
|
50
|
+
};
|
|
51
|
+
assignString(attributes, "lattice.plan.id", event.planId);
|
|
52
|
+
assignString(attributes, "lattice.stage.id", event.stageId);
|
|
53
|
+
assignString(attributes, "lattice.provider.id", event.providerId);
|
|
54
|
+
assignString(attributes, "lattice.model.id", event.modelId);
|
|
55
|
+
assignString(attributes, "lattice.artifact.id", event.artifactId);
|
|
56
|
+
assignString(attributes, "gen_ai.provider.name", event.providerId);
|
|
57
|
+
assignString(attributes, "gen_ai.request.model", event.modelId);
|
|
58
|
+
if (event.kind === "stream.start" || event.kind === "stream.complete" || event.kind === "stream.failed") attributes["gen_ai.request.stream"] = true;
|
|
59
|
+
const metadata = event.metadata ?? {};
|
|
60
|
+
assignString(attributes, "lattice.event.status", metadataString(event, "status"));
|
|
61
|
+
assignBoolean(attributes, "lattice.error.present", metadataString(event, "error") !== void 0 ? true : void 0);
|
|
62
|
+
assignString(attributes, "lattice.failure.reason", metadataString(event, "reason"));
|
|
63
|
+
assignString(attributes, "lattice.tripwire.invariant_id", asString(metadata.invariantId));
|
|
64
|
+
assignString(attributes, "lattice.artifact.source", asString(metadata.source));
|
|
65
|
+
assignString(attributes, "lattice.receipt.id", asString(metadata.receiptId));
|
|
66
|
+
assignBoolean(attributes, "lattice.receipt.mint_error.present", asString(metadata.mintError) !== void 0 ? true : void 0);
|
|
67
|
+
assignString(attributes, "lattice.route.selected_model", asString(metadata.selected));
|
|
68
|
+
assignNumber(attributes, "lattice.route.rejected.count", asNumber(metadata.rejected));
|
|
69
|
+
assignNumber(attributes, "lattice.route.fallback.count", asNumber(metadata.fallbacks));
|
|
70
|
+
assignBoolean(attributes, "lattice.provider.attempt.fallback", asBoolean(metadata.fallback));
|
|
71
|
+
assignNumber(attributes, "lattice.context.estimated_tokens", asNumber(metadata.estimatedTokens));
|
|
72
|
+
assignNumber(attributes, "lattice.context.included.count", asNumber(metadata.included));
|
|
73
|
+
assignNumber(attributes, "lattice.context.summarized.count", asNumber(metadata.summarized));
|
|
74
|
+
assignNumber(attributes, "lattice.context.omitted.count", asNumber(metadata.omitted));
|
|
75
|
+
assignString(attributes, "lattice.tool.name", asString(metadata.toolName));
|
|
76
|
+
assignString(attributes, "lattice.tool.call.id", asString(metadata.callId));
|
|
77
|
+
assignStringArray(attributes, "lattice.output.names", asStringArray(metadata.outputNames));
|
|
78
|
+
assignUsage(attributes, metadata);
|
|
79
|
+
assignGateway(attributes, metadata.gateway);
|
|
80
|
+
captureSafeMetadata(attributes, metadata, options);
|
|
81
|
+
return attributes;
|
|
82
|
+
}
|
|
83
|
+
async function createOtelReceiptAttributes(envelope) {
|
|
84
|
+
const attributes = {
|
|
85
|
+
"lattice.receipt.cid": await receiptCid(envelope),
|
|
86
|
+
"lattice.receipt.signature.count": envelope.signatures.length
|
|
87
|
+
};
|
|
88
|
+
assignString(attributes, "lattice.receipt.signature.keyid", envelope.signatures[0]?.keyid);
|
|
89
|
+
return attributes;
|
|
90
|
+
}
|
|
91
|
+
function createLangfuseOtlpConfig(options = {}) {
|
|
92
|
+
const authString = langfuseAuthString(options);
|
|
93
|
+
return {
|
|
94
|
+
endpoint: langfuseTraceEndpoint(options.baseUrl ?? DEFAULT_LANGFUSE_BASE_URL),
|
|
95
|
+
headers: {
|
|
96
|
+
...authString !== void 0 ? { Authorization: `Basic ${authString}` } : {},
|
|
97
|
+
"x-langfuse-ingestion-version": options.ingestionVersion ?? "4",
|
|
98
|
+
...options.headers ?? {}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function createPhoenixOtlpConfig(options = {}) {
|
|
103
|
+
return {
|
|
104
|
+
endpoint: options.endpoint ?? phoenixTraceEndpoint(options.baseUrl ?? DEFAULT_PHOENIX_BASE_URL),
|
|
105
|
+
headers: {
|
|
106
|
+
...options.apiKey !== void 0 ? { Authorization: `Bearer ${options.apiKey}` } : {},
|
|
107
|
+
...options.projectName !== void 0 ? { "x-project-name": options.projectName } : {},
|
|
108
|
+
...options.headers ?? {}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function getOrCreateRunSpan(event, options, spans) {
|
|
113
|
+
const existing = spans.get(event.runId);
|
|
114
|
+
if (existing !== void 0) return existing;
|
|
115
|
+
const span = options.tracer.startSpan(options.spanName ?? DEFAULT_SPAN_NAME, {
|
|
116
|
+
attributes: sanitizeRunEventAttributes(event, options),
|
|
117
|
+
startTime: eventTime(event)
|
|
118
|
+
});
|
|
119
|
+
spans.set(event.runId, span);
|
|
120
|
+
return span;
|
|
121
|
+
}
|
|
122
|
+
function setSpanAttributes(span, attributes) {
|
|
123
|
+
if (span.setAttributes !== void 0) {
|
|
124
|
+
span.setAttributes(attributes);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
for (const [key, value] of Object.entries(attributes)) span.setAttribute?.(key, value);
|
|
128
|
+
}
|
|
129
|
+
async function createRunEventAttributes(event, options) {
|
|
130
|
+
const attributes = sanitizeRunEventAttributes(event, options);
|
|
131
|
+
const envelope = findReceiptEnvelope(event.metadata);
|
|
132
|
+
if (envelope === void 0) return attributes;
|
|
133
|
+
try {
|
|
134
|
+
return {
|
|
135
|
+
...attributes,
|
|
136
|
+
...await createOtelReceiptAttributes(envelope)
|
|
137
|
+
};
|
|
138
|
+
} catch {
|
|
139
|
+
return attributes;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function eventTime(event) {
|
|
143
|
+
return new Date(event.timestamp);
|
|
144
|
+
}
|
|
145
|
+
function metadataString(event, key) {
|
|
146
|
+
return asString(event.metadata?.[key]);
|
|
147
|
+
}
|
|
148
|
+
function assignString(attributes, key, value) {
|
|
149
|
+
if (value !== void 0) attributes[key] = value;
|
|
150
|
+
}
|
|
151
|
+
function assignNumber(attributes, key, value) {
|
|
152
|
+
if (value !== void 0) attributes[key] = value;
|
|
153
|
+
}
|
|
154
|
+
function assignBoolean(attributes, key, value) {
|
|
155
|
+
if (value !== void 0) attributes[key] = value;
|
|
156
|
+
}
|
|
157
|
+
function assignStringArray(attributes, key, value) {
|
|
158
|
+
if (value !== void 0 && value.length > 0) attributes[key] = value;
|
|
159
|
+
}
|
|
160
|
+
function assignNumberArray(attributes, key, value) {
|
|
161
|
+
if (value !== void 0 && value.length > 0) attributes[key] = value;
|
|
162
|
+
}
|
|
163
|
+
function assignBooleanArray(attributes, key, value) {
|
|
164
|
+
if (value !== void 0 && value.length > 0) attributes[key] = value;
|
|
165
|
+
}
|
|
166
|
+
function asString(value) {
|
|
167
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
168
|
+
}
|
|
169
|
+
function asNumber(value) {
|
|
170
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
171
|
+
}
|
|
172
|
+
function asBoolean(value) {
|
|
173
|
+
return typeof value === "boolean" ? value : void 0;
|
|
174
|
+
}
|
|
175
|
+
function asStringArray(value) {
|
|
176
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
|
|
177
|
+
}
|
|
178
|
+
function asNumberArray(value) {
|
|
179
|
+
return Array.isArray(value) && value.every((item) => typeof item === "number" && Number.isFinite(item)) ? value : void 0;
|
|
180
|
+
}
|
|
181
|
+
function asBooleanArray(value) {
|
|
182
|
+
return Array.isArray(value) && value.every((item) => typeof item === "boolean") ? value : void 0;
|
|
183
|
+
}
|
|
184
|
+
function assignUsage(attributes, metadata) {
|
|
185
|
+
const usage = usageFrom(metadata.normalizedUsage) ?? usageFrom(metadata.usage) ?? usageFrom(metadata);
|
|
186
|
+
if (usage === void 0) return;
|
|
187
|
+
assignNumber(attributes, "gen_ai.usage.input_tokens", usage.inputTokens);
|
|
188
|
+
assignNumber(attributes, "gen_ai.usage.output_tokens", usage.outputTokens);
|
|
189
|
+
assignNumber(attributes, "llm.token_count.prompt", usage.inputTokens);
|
|
190
|
+
assignNumber(attributes, "llm.token_count.completion", usage.outputTokens);
|
|
191
|
+
assignNumber(attributes, "lattice.usage.cost_usd", usage.costUsd);
|
|
192
|
+
}
|
|
193
|
+
function usageFrom(value) {
|
|
194
|
+
if (!isRecord(value)) return;
|
|
195
|
+
const inputTokens = asNumber(value.promptTokens) ?? asNumber(value.inputTokens);
|
|
196
|
+
const outputTokens = asNumber(value.completionTokens) ?? asNumber(value.outputTokens);
|
|
197
|
+
const costUsd = asNumber(value.costUsd);
|
|
198
|
+
if (inputTokens === void 0 && outputTokens === void 0 && costUsd === void 0) return;
|
|
199
|
+
return {
|
|
200
|
+
...inputTokens !== void 0 ? { inputTokens } : {},
|
|
201
|
+
...outputTokens !== void 0 ? { outputTokens } : {},
|
|
202
|
+
...costUsd !== void 0 ? { costUsd } : {}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function assignGateway(attributes, value) {
|
|
206
|
+
if (!isRecord(value)) return;
|
|
207
|
+
assignBoolean(attributes, "lattice.gateway.used", asBoolean(value.used));
|
|
208
|
+
assignString(attributes, "lattice.gateway.provider.id", asString(value.providerId));
|
|
209
|
+
assignString(attributes, "lattice.gateway.selected_provider.id", asString(value.selectedProviderId));
|
|
210
|
+
assignString(attributes, "lattice.gateway.requested_model", asString(value.requestedModel));
|
|
211
|
+
assignString(attributes, "lattice.gateway.observed_model", asString(value.observedModel));
|
|
212
|
+
assignStringArray(attributes, "lattice.gateway.fallback_models", asStringArray(value.fallbackModels));
|
|
213
|
+
const requestedModel = asString(value.requestedModel);
|
|
214
|
+
const observedModel = asString(value.observedModel);
|
|
215
|
+
if (requestedModel !== void 0 && attributes["gen_ai.request.model"] === void 0) attributes["gen_ai.request.model"] = requestedModel;
|
|
216
|
+
assignString(attributes, "gen_ai.response.model", observedModel);
|
|
217
|
+
}
|
|
218
|
+
function captureSafeMetadata(attributes, metadata, options) {
|
|
219
|
+
if (options.contentCapture !== "metadata") return;
|
|
220
|
+
const knownKeys = new Set([
|
|
221
|
+
"callId",
|
|
222
|
+
"estimatedTokens",
|
|
223
|
+
"error",
|
|
224
|
+
"fallback",
|
|
225
|
+
"fallbacks",
|
|
226
|
+
"gateway",
|
|
227
|
+
"included",
|
|
228
|
+
"invariantId",
|
|
229
|
+
"mintError",
|
|
230
|
+
"normalizedUsage",
|
|
231
|
+
"omitted",
|
|
232
|
+
"outputNames",
|
|
233
|
+
"reason",
|
|
234
|
+
"receiptId",
|
|
235
|
+
"rejected",
|
|
236
|
+
"selected",
|
|
237
|
+
"source",
|
|
238
|
+
"status",
|
|
239
|
+
"summarized",
|
|
240
|
+
"toolName",
|
|
241
|
+
"usage"
|
|
242
|
+
]);
|
|
243
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
244
|
+
if (knownKeys.has(key) || isUnsafeMetadataKey(key)) continue;
|
|
245
|
+
const attrKey = `lattice.metadata.${safeAttributeKey(key)}`;
|
|
246
|
+
assignString(attributes, attrKey, asString(value));
|
|
247
|
+
assignNumber(attributes, attrKey, asNumber(value));
|
|
248
|
+
assignBoolean(attributes, attrKey, asBoolean(value));
|
|
249
|
+
assignStringArray(attributes, attrKey, asStringArray(value));
|
|
250
|
+
assignNumberArray(attributes, attrKey, asNumberArray(value));
|
|
251
|
+
assignBooleanArray(attributes, attrKey, asBooleanArray(value));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function isUnsafeMetadataKey(key) {
|
|
255
|
+
return SECRET_KEY_RE.test(key) || CONTENT_KEY_RE.test(key);
|
|
256
|
+
}
|
|
257
|
+
function safeAttributeKey(key) {
|
|
258
|
+
return key.replace(/[^a-zA-Z0-9_.-]+/gu, "_");
|
|
259
|
+
}
|
|
260
|
+
function findReceiptEnvelope(metadata) {
|
|
261
|
+
if (metadata === void 0) return;
|
|
262
|
+
return [
|
|
263
|
+
metadata.envelope,
|
|
264
|
+
metadata.receiptEnvelope,
|
|
265
|
+
metadata.receipt
|
|
266
|
+
].find(isReceiptEnvelope);
|
|
267
|
+
}
|
|
268
|
+
function isReceiptEnvelope(value) {
|
|
269
|
+
return isRecord(value) && value.payloadType === "application/vnd.lattice.receipt+json" && typeof value.payload === "string" && Array.isArray(value.signatures) && value.signatures.every((signature) => isRecord(signature) && typeof signature.keyid === "string" && typeof signature.sig === "string");
|
|
270
|
+
}
|
|
271
|
+
function isRecord(value) {
|
|
272
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
273
|
+
}
|
|
274
|
+
function langfuseAuthString(options) {
|
|
275
|
+
if (options.authString !== void 0) return options.authString;
|
|
276
|
+
if (options.publicKey === void 0 && options.secretKey === void 0) return;
|
|
277
|
+
if (options.publicKey === void 0 || options.secretKey === void 0) throw new Error("Langfuse OTLP auth requires both publicKey and secretKey.");
|
|
278
|
+
return base64Utf8(`${options.publicKey}:${options.secretKey}`);
|
|
279
|
+
}
|
|
280
|
+
function langfuseTraceEndpoint(baseUrl) {
|
|
281
|
+
const base = trimTrailingSlashes(baseUrl);
|
|
282
|
+
if (base.endsWith("/api/public/otel/v1/traces")) return base;
|
|
283
|
+
if (base.endsWith("/api/public/otel")) return `${base}/v1/traces`;
|
|
284
|
+
return `${base}/api/public/otel/v1/traces`;
|
|
285
|
+
}
|
|
286
|
+
function phoenixTraceEndpoint(baseUrl) {
|
|
287
|
+
const base = trimTrailingSlashes(baseUrl);
|
|
288
|
+
if (base.endsWith("/v1/traces")) return base;
|
|
289
|
+
return `${base}/v1/traces`;
|
|
290
|
+
}
|
|
291
|
+
function trimTrailingSlashes(value) {
|
|
292
|
+
return value.replace(/\/+$/u, "");
|
|
293
|
+
}
|
|
294
|
+
function base64Utf8(value) {
|
|
295
|
+
const bytes = new TextEncoder().encode(value);
|
|
296
|
+
let binary = "";
|
|
297
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
298
|
+
return btoa(binary);
|
|
299
|
+
}
|
|
300
|
+
//#endregion
|
|
301
|
+
//#region src/receipts/keyset.ts
|
|
302
|
+
/**
|
|
303
|
+
* In-memory KeySet factory.
|
|
304
|
+
*
|
|
305
|
+
* Verification flow (plan 09-03):
|
|
306
|
+
* - keySet.lookup(kid) returns undefined → VerifyError {kind: "key-not-found"}
|
|
307
|
+
* - entry.state === "revoked" → VerifyError {kind: "key-revoked"}
|
|
308
|
+
* - entry.state === "retired" → VerifyOk + keyState: "retired" (caller may warn)
|
|
309
|
+
* - entry.state === "active" → VerifyOk + keyState: "active"
|
|
310
|
+
*
|
|
311
|
+
* Duplicate kids: last write wins (deterministic — callers control entry order).
|
|
312
|
+
* Empty entries array is legal — every lookup returns undefined.
|
|
313
|
+
* Returned KeySet exposes only `lookup` — no enumeration.
|
|
314
|
+
*
|
|
315
|
+
* See 09-CONTEXT.md "Key Management (UNRETROFITTABLE)".
|
|
316
|
+
*/
|
|
317
|
+
function createMemoryKeySet(entries) {
|
|
318
|
+
const byKid = /* @__PURE__ */ new Map();
|
|
319
|
+
for (const entry of entries) byKid.set(entry.kid, entry);
|
|
320
|
+
return { lookup(kid) {
|
|
321
|
+
return byKid.get(kid);
|
|
322
|
+
} };
|
|
323
|
+
}
|
|
324
|
+
//#endregion
|
|
325
|
+
//#region src/receipts/remote-signer.ts
|
|
326
|
+
/**
|
|
327
|
+
* Adapt a remote signing service to Lattice's existing ReceiptSigner contract.
|
|
328
|
+
*
|
|
329
|
+
* The callback receives the exact DSSE PAE bytes that createReceipt signs.
|
|
330
|
+
* Cloud-specific request construction, hashing choices, credentials, retries,
|
|
331
|
+
* and audit logging stay outside core.
|
|
332
|
+
*/
|
|
333
|
+
function createRemoteReceiptSigner(options) {
|
|
334
|
+
return {
|
|
335
|
+
kid: options.kid,
|
|
336
|
+
publicKeyJwk: options.publicKeyJwk,
|
|
337
|
+
async sign(bytes) {
|
|
338
|
+
const result = await options.sign({
|
|
339
|
+
kid: options.kid,
|
|
340
|
+
publicKeyJwk: options.publicKeyJwk,
|
|
341
|
+
bytes: copyBytes(bytes),
|
|
342
|
+
payloadFormat: "dsse-pae",
|
|
343
|
+
algorithm: "Ed25519",
|
|
344
|
+
...options.provider !== void 0 ? { provider: options.provider } : {},
|
|
345
|
+
...options.keyRef !== void 0 ? { keyRef: options.keyRef } : {},
|
|
346
|
+
...options.metadata !== void 0 ? { metadata: options.metadata } : {}
|
|
347
|
+
});
|
|
348
|
+
return copyBytes(result instanceof Uint8Array ? result : result.signature);
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function copyBytes(bytes) {
|
|
353
|
+
const copy = new Uint8Array(bytes.byteLength);
|
|
354
|
+
copy.set(bytes);
|
|
355
|
+
return copy;
|
|
356
|
+
}
|
|
357
|
+
//#endregion
|
|
358
|
+
//#region src/receipts/sign.ts
|
|
359
|
+
const ALG = "Ed25519";
|
|
360
|
+
/**
|
|
361
|
+
* Copy a Uint8Array into a fresh ArrayBuffer. WebCrypto's BufferSource type
|
|
362
|
+
* (under exactOptionalPropertyTypes + strict TS) rejects `Uint8Array<ArrayBufferLike>`
|
|
363
|
+
* because the underlying buffer could be a SharedArrayBuffer. Matches the
|
|
364
|
+
* pattern used in storage/fingerprint.ts.
|
|
365
|
+
*/
|
|
366
|
+
function toArrayBuffer(bytes) {
|
|
367
|
+
const copy = new Uint8Array(bytes.byteLength);
|
|
368
|
+
copy.set(bytes);
|
|
369
|
+
return copy.buffer;
|
|
370
|
+
}
|
|
371
|
+
async function importEd25519PrivateKey(jwk) {
|
|
372
|
+
return crypto.subtle.importKey("jwk", jwk, ALG, true, ["sign"]);
|
|
373
|
+
}
|
|
374
|
+
async function importEd25519PublicKey(jwk) {
|
|
375
|
+
return crypto.subtle.importKey("jwk", jwk, ALG, true, ["verify"]);
|
|
376
|
+
}
|
|
377
|
+
async function generateEd25519KeyPairJwk() {
|
|
378
|
+
const pair = await crypto.subtle.generateKey(ALG, true, ["sign", "verify"]);
|
|
379
|
+
const [privateKeyJwk, publicKeyJwk] = await Promise.all([crypto.subtle.exportKey("jwk", pair.privateKey), crypto.subtle.exportKey("jwk", pair.publicKey)]);
|
|
380
|
+
return {
|
|
381
|
+
privateKeyJwk,
|
|
382
|
+
publicKeyJwk
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
async function verifyEd25519Signature(publicKeyJwk, message, signature) {
|
|
386
|
+
let key;
|
|
387
|
+
try {
|
|
388
|
+
key = await importEd25519PublicKey(publicKeyJwk);
|
|
389
|
+
} catch {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
try {
|
|
393
|
+
return await crypto.subtle.verify(ALG, key, toArrayBuffer(signature), toArrayBuffer(message));
|
|
394
|
+
} catch {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function createInMemorySigner(privateKeyJwk, options) {
|
|
399
|
+
let cachedKey;
|
|
400
|
+
const ensureKey = async () => {
|
|
401
|
+
if (cachedKey === void 0) cachedKey = await importEd25519PrivateKey(privateKeyJwk);
|
|
402
|
+
return cachedKey;
|
|
403
|
+
};
|
|
404
|
+
return {
|
|
405
|
+
kid: options.kid,
|
|
406
|
+
publicKeyJwk: options.publicKeyJwk,
|
|
407
|
+
async sign(bytes) {
|
|
408
|
+
const key = await ensureKey();
|
|
409
|
+
const sig = await crypto.subtle.sign(ALG, key, toArrayBuffer(bytes));
|
|
410
|
+
return new Uint8Array(sig);
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
//#endregion
|
|
415
|
+
//#region src/receipts/verify.ts
|
|
416
|
+
function fail$1(kind, message) {
|
|
417
|
+
return {
|
|
418
|
+
ok: false,
|
|
419
|
+
error: {
|
|
420
|
+
kind,
|
|
421
|
+
message
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
function bytesEqual(a, b) {
|
|
426
|
+
if (a.byteLength !== b.byteLength) return false;
|
|
427
|
+
for (let i = 0; i < a.byteLength; i += 1) if (a[i] !== b[i]) return false;
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Receipt body shape check. We trust the JSON parse — but we re-validate
|
|
432
|
+
* that the required fields exist with the right primitive types before
|
|
433
|
+
* canonicalizing again. Anything off -> version-mismatch (the body is
|
|
434
|
+
* structurally NOT a v1 receipt, even if it parses as JSON).
|
|
435
|
+
*/
|
|
436
|
+
function asReceiptBody(value) {
|
|
437
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
438
|
+
const v = value;
|
|
439
|
+
if (v.version !== void 0 && v.version !== "lattice-receipt/v1" && v.version !== "lattice-receipt/v1.1" && v.version !== "lattice-receipt/v1.2" && v.version !== "lattice-receipt/v1.3") return;
|
|
440
|
+
if (typeof v.receiptId !== "string") return void 0;
|
|
441
|
+
if (typeof v.runId !== "string") return void 0;
|
|
442
|
+
if (typeof v.issuedAt !== "string") return void 0;
|
|
443
|
+
if (typeof v.kid !== "string") return void 0;
|
|
444
|
+
if (typeof v.model !== "object" || v.model === null) return void 0;
|
|
445
|
+
if (typeof v.route !== "object" || v.route === null) return void 0;
|
|
446
|
+
if (typeof v.usage !== "object" || v.usage === null) return void 0;
|
|
447
|
+
if (typeof v.contractVerdict !== "string") return void 0;
|
|
448
|
+
if (!Array.isArray(v.inputHashes)) return void 0;
|
|
449
|
+
if (typeof v.redactionPolicyId !== "string") return void 0;
|
|
450
|
+
if (!Array.isArray(v.redactions)) return void 0;
|
|
451
|
+
return v;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Pure receipt verifier.
|
|
455
|
+
*
|
|
456
|
+
* Returns a typed VerifyResult — never throws across the verification
|
|
457
|
+
* boundary (PITFALLS.md security: "Verifier panics on malformed receipts
|
|
458
|
+
* -> DoS via crafted input"). All parsing failures become typed errors.
|
|
459
|
+
*
|
|
460
|
+
* Decision tree (first match wins):
|
|
461
|
+
* 1. decodeEnvelope throws OR signatures[] empty -> envelope-malformed
|
|
462
|
+
* 2. payload bytes are not valid JSON -> envelope-malformed
|
|
463
|
+
* 3. body shape check fails OR version unknown literal -> version-mismatch
|
|
464
|
+
* 4. body.version === undefined OR "lattice-receipt/v1"-> schema-version-too-low (CRYPTO-01)
|
|
465
|
+
* 5. keySet.lookup(keyid) === undefined -> key-not-found
|
|
466
|
+
* 6. entry.state === "revoked" -> key-revoked
|
|
467
|
+
* 7. re-canonicalized body != signed payloadBytes -> canonicalization-mismatch
|
|
468
|
+
* 8. Ed25519 verification of PAE fails -> signature-invalid
|
|
469
|
+
* 9. body.kid !== entry.kid (defense in depth) -> signature-invalid
|
|
470
|
+
* 10. otherwise -> ok + keyState
|
|
471
|
+
*/
|
|
472
|
+
async function verifyReceipt(envelope, keySet) {
|
|
473
|
+
let decoded;
|
|
474
|
+
try {
|
|
475
|
+
decoded = decodeEnvelope(envelope);
|
|
476
|
+
} catch (error) {
|
|
477
|
+
return fail$1("envelope-malformed", error instanceof Error ? error.message : String(error));
|
|
478
|
+
}
|
|
479
|
+
if (decoded.signatures.length === 0) return fail$1("envelope-malformed", "envelope has no signatures");
|
|
480
|
+
let parsed;
|
|
481
|
+
try {
|
|
482
|
+
parsed = JSON.parse(new TextDecoder().decode(decoded.payloadBytes));
|
|
483
|
+
} catch (error) {
|
|
484
|
+
return fail$1("envelope-malformed", `payload is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
485
|
+
}
|
|
486
|
+
const body = asReceiptBody(parsed);
|
|
487
|
+
if (body === void 0) return fail$1("version-mismatch", "receipt body is not a lattice-receipt/v1.1, lattice-receipt/v1.2, or lattice-receipt/v1.3 shape");
|
|
488
|
+
if (body.version === void 0 || body.version === "lattice-receipt/v1") return fail$1("schema-version-too-low", "Receipt body.version must be 'lattice-receipt/v1.1', 'lattice-receipt/v1.2', or 'lattice-receipt/v1.3' — v1 receipts are not accepted (CRYPTO-01).");
|
|
489
|
+
const firstSig = decoded.signatures[0];
|
|
490
|
+
const entry = keySet.lookup(firstSig.keyid);
|
|
491
|
+
if (entry === void 0) return fail$1("key-not-found", `keySet has no entry for kid "${firstSig.keyid}"`);
|
|
492
|
+
if (entry.state === "revoked") return fail$1("key-revoked", `key "${entry.kid}" is revoked`);
|
|
493
|
+
if (!bytesEqual(canonicalizeReceiptBody(body), decoded.payloadBytes)) return fail$1("canonicalization-mismatch", "re-canonicalized body does not match signed payload bytes");
|
|
494
|
+
const pae = buildPae(PAYLOAD_TYPE, base64Encode(decoded.payloadBytes));
|
|
495
|
+
if (!await verifyEd25519Signature(entry.publicKeyJwk, pae, firstSig.sig)) return fail$1("signature-invalid", "Ed25519 signature does not verify");
|
|
496
|
+
if (body.kid !== entry.kid) return fail$1("signature-invalid", `body.kid "${body.kid}" does not match envelope keyid "${entry.kid}"`);
|
|
497
|
+
return {
|
|
498
|
+
ok: true,
|
|
499
|
+
body,
|
|
500
|
+
keyState: entry.state
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
//#endregion
|
|
504
|
+
//#region src/version.ts
|
|
505
|
+
const latticeVersion = "1.5.0";
|
|
506
|
+
//#endregion
|
|
507
|
+
//#region src/audit/external-execution.ts
|
|
508
|
+
async function createExternalExecutionAudit(input, signer) {
|
|
509
|
+
const runId = input.runId ?? createId("external-run");
|
|
510
|
+
const receiptId = input.receiptId ?? createId("external-receipt");
|
|
511
|
+
const issuedAt = input.issuedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
512
|
+
const artifacts = input.artifacts ?? [];
|
|
513
|
+
const artifactRefs = artifacts.map(toArtifactRef);
|
|
514
|
+
const outputSpecs = input.outputSpecs ?? inferOutputSpecs(input.outputs);
|
|
515
|
+
const inputHashes = await hashInputArtifacts(artifacts);
|
|
516
|
+
const outputHash = input.outputs === void 0 ? null : await hashUnknown(input.outputs);
|
|
517
|
+
const contractHash = await hashCanonical(input.contract);
|
|
518
|
+
const lineageMerkleRoot = await computeArtifactLineageMerkleRoot(artifacts);
|
|
519
|
+
const rawRequestHash = input.rawRequest === void 0 ? void 0 : await hashUnknown(input.rawRequest) ?? void 0;
|
|
520
|
+
const rawResponseHash = input.rawResponse === void 0 ? void 0 : await hashUnknown(input.rawResponse) ?? void 0;
|
|
521
|
+
const contractVerdict = input.contractVerdict ?? "success";
|
|
522
|
+
const receipt = await createReceipt({
|
|
523
|
+
runId,
|
|
524
|
+
receiptId,
|
|
525
|
+
issuedAt,
|
|
526
|
+
model: input.model,
|
|
527
|
+
route: input.route,
|
|
528
|
+
...lineageMerkleRoot !== void 0 ? { lineageMerkleRoot } : {},
|
|
529
|
+
usage: input.usage,
|
|
530
|
+
contractVerdict,
|
|
531
|
+
contractHash,
|
|
532
|
+
inputHashes,
|
|
533
|
+
outputHash
|
|
534
|
+
}, signer);
|
|
535
|
+
return {
|
|
536
|
+
receipt,
|
|
537
|
+
sidecar: {
|
|
538
|
+
version: "lattice-sidecar/v1",
|
|
539
|
+
task: input.task,
|
|
540
|
+
outputs: outputSpecs,
|
|
541
|
+
policy: input.policy,
|
|
542
|
+
contract: input.contract,
|
|
543
|
+
...input.outputs !== void 0 ? { rawOutputs: input.outputs } : {},
|
|
544
|
+
externalExecution: {
|
|
545
|
+
kind: "external-execution",
|
|
546
|
+
model: input.model,
|
|
547
|
+
route: input.route,
|
|
548
|
+
usage: input.usage,
|
|
549
|
+
...input.rawRequest !== void 0 ? { rawRequest: input.rawRequest } : {},
|
|
550
|
+
...input.rawResponse !== void 0 ? { rawResponse: input.rawResponse } : {},
|
|
551
|
+
...rawRequestHash !== void 0 ? { rawRequestHash } : {},
|
|
552
|
+
...rawResponseHash !== void 0 ? { rawResponseHash } : {},
|
|
553
|
+
inputHashes,
|
|
554
|
+
outputHash,
|
|
555
|
+
...input.metadata !== void 0 ? { metadata: input.metadata } : {}
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
replayEnvelope: createExternalReplayEnvelope({
|
|
559
|
+
runId,
|
|
560
|
+
task: input.task,
|
|
561
|
+
artifacts: artifactRefs,
|
|
562
|
+
outputSpecs,
|
|
563
|
+
outputs: input.outputs,
|
|
564
|
+
catalogVersion: input.catalogVersion ?? "external",
|
|
565
|
+
route: input.route,
|
|
566
|
+
usage: input.usage,
|
|
567
|
+
receipt,
|
|
568
|
+
contract: input.contract,
|
|
569
|
+
contractVerdict,
|
|
570
|
+
metadata: input.metadata
|
|
571
|
+
}),
|
|
572
|
+
inputHashes,
|
|
573
|
+
outputHash
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
function createExternalReplayEnvelope(input) {
|
|
577
|
+
const isSuccess = input.contractVerdict === "success";
|
|
578
|
+
const attempt = {
|
|
579
|
+
providerId: input.route.providerId,
|
|
580
|
+
modelId: input.route.capabilityId,
|
|
581
|
+
status: isSuccess ? "succeeded" : "failed",
|
|
582
|
+
usage: usageRecord(input.usage),
|
|
583
|
+
metadata: { externalExecution: true },
|
|
584
|
+
...!isSuccess ? { error: input.contractVerdict } : {}
|
|
585
|
+
};
|
|
586
|
+
const plan = withPlanStatus(createExecutionPlan({
|
|
587
|
+
task: input.task,
|
|
588
|
+
artifacts: input.artifacts,
|
|
589
|
+
outputs: input.outputSpecs,
|
|
590
|
+
route: {
|
|
591
|
+
catalogVersion: input.catalogVersion,
|
|
592
|
+
selected: {
|
|
593
|
+
providerId: input.route.providerId,
|
|
594
|
+
modelId: input.route.capabilityId,
|
|
595
|
+
score: 0,
|
|
596
|
+
estimates: {
|
|
597
|
+
inputTokens: input.usage.promptTokens,
|
|
598
|
+
outputTokens: input.usage.completionTokens,
|
|
599
|
+
...input.usage.costUsd !== null ? { costUsd: input.usage.costUsd } : {}
|
|
600
|
+
},
|
|
601
|
+
inputModalities: [],
|
|
602
|
+
outputModalities: [],
|
|
603
|
+
fileTransport: []
|
|
604
|
+
},
|
|
605
|
+
candidates: [],
|
|
606
|
+
rejected: [],
|
|
607
|
+
fallbackChain: [],
|
|
608
|
+
noRouteReasons: []
|
|
609
|
+
},
|
|
610
|
+
metadata: {
|
|
611
|
+
externalExecution: true,
|
|
612
|
+
runId: input.runId,
|
|
613
|
+
contractVerdict: input.contractVerdict,
|
|
614
|
+
...input.metadata !== void 0 ? { external: input.metadata } : {}
|
|
615
|
+
}
|
|
616
|
+
}), isSuccess ? "completed" : "failed", {
|
|
617
|
+
stages: isSuccess ? markAllStagesCompleted : markExternalStagesFailed(input.contractVerdict),
|
|
618
|
+
attempts: [attempt]
|
|
619
|
+
});
|
|
620
|
+
return {
|
|
621
|
+
kind: "replay-envelope",
|
|
622
|
+
version: 1,
|
|
623
|
+
runtimeVersion: latticeVersion,
|
|
624
|
+
catalogVersion: input.catalogVersion,
|
|
625
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
626
|
+
plan,
|
|
627
|
+
artifacts: input.artifacts,
|
|
628
|
+
...isSuccess && input.outputs !== void 0 ? { outputs: input.outputs } : {},
|
|
629
|
+
warnings: [],
|
|
630
|
+
errors: isSuccess ? [] : [input.contractVerdict],
|
|
631
|
+
usage: usageRecord(input.usage),
|
|
632
|
+
events: [],
|
|
633
|
+
receipt: input.receipt,
|
|
634
|
+
contract: input.contract
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function markExternalStagesFailed(verdict) {
|
|
638
|
+
const failedStage = failedStageForVerdict(verdict);
|
|
639
|
+
let hasFailed = false;
|
|
640
|
+
return markAllStagesCompleted.map((stage) => {
|
|
641
|
+
if (stage.kind === failedStage) {
|
|
642
|
+
hasFailed = true;
|
|
643
|
+
return {
|
|
644
|
+
...stage,
|
|
645
|
+
status: "failed",
|
|
646
|
+
warnings: [...stage.warnings, verdict]
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
if (hasFailed) return {
|
|
650
|
+
...stage,
|
|
651
|
+
status: "skipped"
|
|
652
|
+
};
|
|
653
|
+
return stage;
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
function failedStageForVerdict(verdict) {
|
|
657
|
+
switch (verdict) {
|
|
658
|
+
case "validation-failed": return "validation";
|
|
659
|
+
case "tripwire-violated": return "tripwire";
|
|
660
|
+
case "no-contract-match": return "analysis";
|
|
661
|
+
case "execution-failed":
|
|
662
|
+
case "success": return "execution";
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
function usageRecord(usage) {
|
|
666
|
+
return {
|
|
667
|
+
inputTokens: usage.promptTokens,
|
|
668
|
+
outputTokens: usage.completionTokens,
|
|
669
|
+
totalTokens: usage.promptTokens + usage.completionTokens,
|
|
670
|
+
...usage.costUsd !== null ? { costUsd: usage.costUsd } : {}
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
function inferOutputSpecs(outputs) {
|
|
674
|
+
if (outputs === void 0) return {};
|
|
675
|
+
return Object.fromEntries(Object.keys(outputs).map((name) => [name, "text"]));
|
|
676
|
+
}
|
|
677
|
+
async function hashInputArtifacts(artifacts) {
|
|
678
|
+
const hashes = [];
|
|
679
|
+
for (const artifact of artifacts) hashes.push(artifact.fingerprint?.value ?? await hashUnknown(artifact.value) ?? "");
|
|
680
|
+
return hashes;
|
|
681
|
+
}
|
|
682
|
+
async function hashUnknown(value) {
|
|
683
|
+
return (await fingerprintArtifactValue(value))?.value ?? null;
|
|
684
|
+
}
|
|
685
|
+
async function hashCanonical(value) {
|
|
686
|
+
const canonical = canonicalize(value);
|
|
687
|
+
if (canonical === void 0) return null;
|
|
688
|
+
return hashUnknown(canonical);
|
|
689
|
+
}
|
|
690
|
+
function createId(prefix) {
|
|
691
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return `${prefix}:${crypto.randomUUID()}`;
|
|
692
|
+
return `${prefix}:${Date.now()}:${Math.random().toString(16).slice(2)}`;
|
|
693
|
+
}
|
|
694
|
+
const markAllStagesCompleted = [
|
|
695
|
+
{
|
|
696
|
+
id: "stage:analysis",
|
|
697
|
+
kind: "analysis",
|
|
698
|
+
status: "completed",
|
|
699
|
+
warnings: []
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
id: "stage:transforms",
|
|
703
|
+
kind: "transforms",
|
|
704
|
+
status: "completed",
|
|
705
|
+
warnings: []
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
id: "stage:context-packing",
|
|
709
|
+
kind: "context-packing",
|
|
710
|
+
status: "completed",
|
|
711
|
+
warnings: []
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
id: "stage:provider-packaging",
|
|
715
|
+
kind: "provider-packaging",
|
|
716
|
+
status: "completed",
|
|
717
|
+
warnings: []
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
id: "stage:tool-execution",
|
|
721
|
+
kind: "tool-execution",
|
|
722
|
+
status: "completed",
|
|
723
|
+
warnings: []
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
id: "stage:execution",
|
|
727
|
+
kind: "execution",
|
|
728
|
+
status: "completed",
|
|
729
|
+
warnings: []
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
id: "stage:validation",
|
|
733
|
+
kind: "validation",
|
|
734
|
+
status: "completed",
|
|
735
|
+
warnings: []
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
id: "stage:tripwire",
|
|
739
|
+
kind: "tripwire",
|
|
740
|
+
status: "completed",
|
|
741
|
+
warnings: []
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
id: "stage:persistence",
|
|
745
|
+
kind: "persistence",
|
|
746
|
+
status: "completed",
|
|
747
|
+
warnings: []
|
|
748
|
+
}
|
|
749
|
+
];
|
|
750
|
+
//#endregion
|
|
751
|
+
//#region src/replay/materialize.ts
|
|
752
|
+
function asMaterializationError(value) {
|
|
753
|
+
return typeof value === "object" && value !== null && typeof value.kind === "string" && typeof value.message === "string";
|
|
754
|
+
}
|
|
755
|
+
/** Throwable shape — `instanceof Error` is not required for typed unions, so
|
|
756
|
+
* the function just throws a plain object literal that matches the
|
|
757
|
+
* `MaterializationError` shape. Callers pattern-match on `err.kind`. */
|
|
758
|
+
function fail(kind, message) {
|
|
759
|
+
return {
|
|
760
|
+
kind,
|
|
761
|
+
message
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Pure async function that reconstructs a `ReplayEnvelope` from a receipt.
|
|
766
|
+
*
|
|
767
|
+
* Verify-FIRST ordering: `verifyReceipt` runs before `artifactLoader` is
|
|
768
|
+
* touched. Tampered receipts MUST NOT cause loader side effects.
|
|
769
|
+
*/
|
|
770
|
+
async function materializeReplayEnvelope(receipt, options) {
|
|
771
|
+
const verifyResult = await verifyReceipt(receipt, options.keySet);
|
|
772
|
+
if (!verifyResult.ok) throw fail(verifyResult.error.kind === "envelope-malformed" ? "envelope-malformed" : "verify-failed", verifyResult.error.message);
|
|
773
|
+
const body = verifyResult.body;
|
|
774
|
+
const loadedInputs = [];
|
|
775
|
+
for (const hash of body.inputHashes) {
|
|
776
|
+
if (hash === "") continue;
|
|
777
|
+
try {
|
|
778
|
+
const input = await options.artifactLoader(hash);
|
|
779
|
+
loadedInputs.push(input);
|
|
780
|
+
} catch (error) {
|
|
781
|
+
throw fail("artifact-load-failed", error instanceof Error ? error.message : asMaterializationError(error) ? error.message : String(error));
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
const artifactRefs = loadedInputs.map(toArtifactRef);
|
|
785
|
+
const outputsMap = options.outputs !== void 0 ? Object.fromEntries(Object.keys(options.outputs).map((k) => [k, "text"])) : {};
|
|
786
|
+
const plan = createExecutionPlan({
|
|
787
|
+
task: options.task ?? "",
|
|
788
|
+
artifacts: artifactRefs,
|
|
789
|
+
outputs: outputsMap,
|
|
790
|
+
route: {
|
|
791
|
+
catalogVersion: "materialized",
|
|
792
|
+
selected: {
|
|
793
|
+
providerId: body.route.providerId,
|
|
794
|
+
modelId: body.route.capabilityId,
|
|
795
|
+
score: 0,
|
|
796
|
+
estimates: {
|
|
797
|
+
inputTokens: 0,
|
|
798
|
+
outputTokens: 0
|
|
799
|
+
},
|
|
800
|
+
inputModalities: [],
|
|
801
|
+
outputModalities: [],
|
|
802
|
+
fileTransport: []
|
|
803
|
+
},
|
|
804
|
+
candidates: [],
|
|
805
|
+
rejected: [],
|
|
806
|
+
fallbackChain: [],
|
|
807
|
+
noRouteReasons: []
|
|
808
|
+
},
|
|
809
|
+
warnings: [],
|
|
810
|
+
metadata: {
|
|
811
|
+
materialized: true,
|
|
812
|
+
receiptId: body.receiptId,
|
|
813
|
+
runId: body.runId,
|
|
814
|
+
contractVerdict: body.contractVerdict,
|
|
815
|
+
...options.policy !== void 0 ? { policy: { ...options.policy } } : {}
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
const usage = {
|
|
819
|
+
inputTokens: body.usage.promptTokens,
|
|
820
|
+
outputTokens: body.usage.completionTokens,
|
|
821
|
+
...body.usage.costUsd !== null ? { costUsd: Number(body.usage.costUsd) } : {}
|
|
822
|
+
};
|
|
823
|
+
return {
|
|
824
|
+
kind: "replay-envelope",
|
|
825
|
+
version: 1,
|
|
826
|
+
runtimeVersion: latticeVersion,
|
|
827
|
+
catalogVersion: "materialized",
|
|
828
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
829
|
+
plan,
|
|
830
|
+
artifacts: artifactRefs,
|
|
831
|
+
...options.outputs !== void 0 ? { outputs: options.outputs } : {},
|
|
832
|
+
warnings: [],
|
|
833
|
+
errors: [],
|
|
834
|
+
usage,
|
|
835
|
+
events: [],
|
|
836
|
+
receipt,
|
|
837
|
+
...options.contract !== void 0 ? { contract: options.contract } : {}
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
//#endregion
|
|
841
|
+
//#region src/replay/replay.ts
|
|
842
|
+
function createReplayEnvelope(result) {
|
|
843
|
+
if (result.plan.kind !== "execution-plan") throw new Error("Replay envelopes require an execution plan.");
|
|
844
|
+
const usage = result.plan.attempts.at(-1)?.usage;
|
|
845
|
+
return {
|
|
846
|
+
kind: "replay-envelope",
|
|
847
|
+
version: 1,
|
|
848
|
+
runtimeVersion: latticeVersion,
|
|
849
|
+
catalogVersion: result.plan.route.catalogVersion,
|
|
850
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
851
|
+
plan: redactPlan(result.plan),
|
|
852
|
+
artifacts: result.ok ? result.artifacts : result.plan.artifactRefs,
|
|
853
|
+
...result.ok ? { outputs: result.outputs } : {},
|
|
854
|
+
warnings: result.plan.warnings,
|
|
855
|
+
errors: result.ok ? [] : [result.error.message],
|
|
856
|
+
...usage !== void 0 ? { usage } : {},
|
|
857
|
+
events: result.events ?? []
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
async function replayOffline(envelope) {
|
|
861
|
+
const replayedUsage = envelopeUsage(envelope);
|
|
862
|
+
if (envelope.outputs === void 0) return {
|
|
863
|
+
ok: false,
|
|
864
|
+
error: {
|
|
865
|
+
kind: "execution_unavailable",
|
|
866
|
+
message: "Replay envelope does not contain successful outputs."
|
|
867
|
+
},
|
|
868
|
+
usage: replayedUsage,
|
|
869
|
+
plan: envelope.plan,
|
|
870
|
+
events: envelope.events
|
|
871
|
+
};
|
|
872
|
+
return {
|
|
873
|
+
ok: true,
|
|
874
|
+
outputs: envelope.outputs,
|
|
875
|
+
artifacts: envelope.artifacts,
|
|
876
|
+
usage: replayedUsage,
|
|
877
|
+
plan: envelope.plan,
|
|
878
|
+
events: envelope.events
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
function envelopeUsage(envelope) {
|
|
882
|
+
if (envelope.usage === void 0) return {
|
|
883
|
+
promptTokens: 0,
|
|
884
|
+
completionTokens: 0,
|
|
885
|
+
costUsd: null
|
|
886
|
+
};
|
|
887
|
+
return {
|
|
888
|
+
promptTokens: envelope.usage.inputTokens ?? 0,
|
|
889
|
+
completionTokens: envelope.usage.outputTokens ?? 0,
|
|
890
|
+
costUsd: envelope.usage.costUsd ?? null
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
async function rerunLive(ai, envelope, intent) {
|
|
894
|
+
const result = await ai.run(intent);
|
|
895
|
+
if (result.plan.kind === "execution-plan") return {
|
|
896
|
+
...result,
|
|
897
|
+
plan: {
|
|
898
|
+
...result.plan,
|
|
899
|
+
warnings: [...result.plan.warnings, `Live rerun of ${envelope.plan.id}: provider behavior, model versions, cost, and latency may differ.`]
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
return result;
|
|
903
|
+
}
|
|
904
|
+
function redactReplayEnvelope(envelope) {
|
|
905
|
+
return {
|
|
906
|
+
...envelope,
|
|
907
|
+
plan: redactPlan(envelope.plan),
|
|
908
|
+
artifacts: envelope.artifacts.map(redactArtifactRef),
|
|
909
|
+
events: envelope.events.map((event) => {
|
|
910
|
+
const metadata = redactRecord(event.metadata);
|
|
911
|
+
return {
|
|
912
|
+
...event,
|
|
913
|
+
...metadata !== void 0 ? { metadata } : {}
|
|
914
|
+
};
|
|
915
|
+
})
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
function redactPlan(plan) {
|
|
919
|
+
return {
|
|
920
|
+
...plan,
|
|
921
|
+
task: redactText(plan.task),
|
|
922
|
+
artifactRefs: plan.artifactRefs.map(redactArtifactRef),
|
|
923
|
+
...plan.providerPackaging !== void 0 ? { providerPackaging: {
|
|
924
|
+
...plan.providerPackaging,
|
|
925
|
+
artifacts: plan.providerPackaging.artifacts.map((item) => ({
|
|
926
|
+
...item,
|
|
927
|
+
warnings: item.warnings.map(redactText)
|
|
928
|
+
})),
|
|
929
|
+
warnings: plan.providerPackaging.warnings.map(redactText)
|
|
930
|
+
} } : {},
|
|
931
|
+
warnings: plan.warnings.map(redactText)
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
function redactArtifactRef(ref) {
|
|
935
|
+
const redactedMetadata = redactRecord(ref.metadata);
|
|
936
|
+
return {
|
|
937
|
+
...ref,
|
|
938
|
+
...redactedMetadata !== void 0 ? { metadata: redactedMetadata } : {},
|
|
939
|
+
...ref.source === "url" ? { metadata: {
|
|
940
|
+
...redactedMetadata,
|
|
941
|
+
redactedSource: "url"
|
|
942
|
+
} } : {}
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
function redactRecord(record) {
|
|
946
|
+
if (record === void 0) return;
|
|
947
|
+
return Object.fromEntries(Object.entries(record).map(([key, value]) => [key, shouldRedactKey(key) ? "[redacted]" : redactValue(value)]));
|
|
948
|
+
}
|
|
949
|
+
function redactValue(value) {
|
|
950
|
+
if (typeof value === "string") return redactText(value);
|
|
951
|
+
if (Array.isArray(value)) return value.map(redactValue);
|
|
952
|
+
if (typeof value === "object" && value !== null) return redactRecord(value);
|
|
953
|
+
return value;
|
|
954
|
+
}
|
|
955
|
+
function redactText(value) {
|
|
956
|
+
return value.replace(/Bearer\s+[A-Za-z0-9._-]+/gu, "Bearer [redacted]").replace(/https?:\/\/[^\s)]+/gu, "[redacted-url]").replace(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/gu, "[redacted-email]");
|
|
957
|
+
}
|
|
958
|
+
function shouldRedactKey(key) {
|
|
959
|
+
return /api.?key|authorization|token|secret|password|credential|signed.?url|raw|body|transcript/iu.test(key);
|
|
960
|
+
}
|
|
961
|
+
//#endregion
|
|
962
|
+
export { createOtelRunEventSink as _, replayOffline as a, createExternalExecutionAudit as c, createInMemorySigner as d, generateEd25519KeyPairJwk as f, createOtelReceiptAttributes as g, createLangfuseOtlpConfig as h, redactReplayEnvelope as i, latticeVersion as l, createMemoryKeySet as m, redactArtifactRef as n, rerunLive as o, createRemoteReceiptSigner as p, redactPlan as r, materializeReplayEnvelope as s, createReplayEnvelope as t, verifyReceipt as u, createPhoenixOtlpConfig as v, sanitizeRunEventAttributes as y };
|
|
963
|
+
|
|
964
|
+
//# sourceMappingURL=replay-CtIhpLek.js.map
|