@mcoda/codali 0.1.90 → 0.1.91

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 (105) hide show
  1. package/dist/cli/DatasetCommand.d.ts +38 -0
  2. package/dist/cli/DatasetCommand.d.ts.map +1 -0
  3. package/dist/cli/DatasetCommand.js +959 -0
  4. package/dist/cli/EvalCommand.d.ts +3 -0
  5. package/dist/cli/EvalCommand.d.ts.map +1 -1
  6. package/dist/cli/EvalCommand.js +55 -1
  7. package/dist/cli/ImprovementCommand.d.ts +97 -0
  8. package/dist/cli/ImprovementCommand.d.ts.map +1 -0
  9. package/dist/cli/ImprovementCommand.js +1879 -0
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +15 -0
  12. package/dist/dataset-cli.d.ts +3 -0
  13. package/dist/dataset-cli.d.ts.map +1 -0
  14. package/dist/dataset-cli.js +30 -0
  15. package/dist/eval/CodaliGatewayLiveHarness.d.ts +90 -1
  16. package/dist/eval/CodaliGatewayLiveHarness.d.ts.map +1 -1
  17. package/dist/eval/CodaliGatewayLiveHarness.js +443 -9
  18. package/dist/eval/GatewayDatasetEval.d.ts +70 -0
  19. package/dist/eval/GatewayDatasetEval.d.ts.map +1 -0
  20. package/dist/eval/GatewayDatasetEval.js +477 -0
  21. package/dist/eval/GatewayEvalSuite.d.ts +52 -0
  22. package/dist/eval/GatewayEvalSuite.d.ts.map +1 -1
  23. package/dist/eval/GatewayEvalSuite.js +135 -1
  24. package/dist/gateway/CodaliGateway.d.ts +4 -0
  25. package/dist/gateway/CodaliGateway.d.ts.map +1 -1
  26. package/dist/gateway/CodaliGateway.js +37 -1
  27. package/dist/gateway/CodaliGatewaySchemas.d.ts.map +1 -1
  28. package/dist/gateway/CodaliGatewaySchemas.js +0 -4
  29. package/dist/gateway/CodaliGatewayStore.d.ts.map +1 -1
  30. package/dist/gateway/CodaliGatewayStore.js +1 -0
  31. package/dist/gateway/GatewayPlanner.d.ts.map +1 -1
  32. package/dist/gateway/GatewayPlanner.js +355 -41
  33. package/dist/gateway/GatewayStateMachine.d.ts +1 -0
  34. package/dist/gateway/GatewayStateMachine.d.ts.map +1 -1
  35. package/dist/gateway/GatewayStateMachine.js +221 -5
  36. package/dist/improvement/CandidateReleaseBuilder.d.ts +201 -0
  37. package/dist/improvement/CandidateReleaseBuilder.d.ts.map +1 -0
  38. package/dist/improvement/CandidateReleaseBuilder.js +1175 -0
  39. package/dist/improvement/DatasetEligibilityGate.d.ts +75 -0
  40. package/dist/improvement/DatasetEligibilityGate.d.ts.map +1 -0
  41. package/dist/improvement/DatasetEligibilityGate.js +497 -0
  42. package/dist/improvement/DatasetExportManifestReader.d.ts +88 -0
  43. package/dist/improvement/DatasetExportManifestReader.d.ts.map +1 -0
  44. package/dist/improvement/DatasetExportManifestReader.js +333 -0
  45. package/dist/improvement/DocdexRetrievalCandidateBuilder.d.ts +192 -0
  46. package/dist/improvement/DocdexRetrievalCandidateBuilder.d.ts.map +1 -0
  47. package/dist/improvement/DocdexRetrievalCandidateBuilder.js +625 -0
  48. package/dist/improvement/EvalReplayCandidateBuilder.d.ts +173 -0
  49. package/dist/improvement/EvalReplayCandidateBuilder.d.ts.map +1 -0
  50. package/dist/improvement/EvalReplayCandidateBuilder.js +363 -0
  51. package/dist/improvement/FineTuneJobPlanner.d.ts +281 -0
  52. package/dist/improvement/FineTuneJobPlanner.d.ts.map +1 -0
  53. package/dist/improvement/FineTuneJobPlanner.js +823 -0
  54. package/dist/improvement/ImprovementEvalRunner.d.ts +50 -0
  55. package/dist/improvement/ImprovementEvalRunner.d.ts.map +1 -0
  56. package/dist/improvement/ImprovementEvalRunner.js +648 -0
  57. package/dist/improvement/ImprovementPolicy.d.ts +272 -0
  58. package/dist/improvement/ImprovementPolicy.d.ts.map +1 -0
  59. package/dist/improvement/ImprovementPolicy.js +1450 -0
  60. package/dist/improvement/ModelRouterCandidateBuilder.d.ts +186 -0
  61. package/dist/improvement/ModelRouterCandidateBuilder.d.ts.map +1 -0
  62. package/dist/improvement/ModelRouterCandidateBuilder.js +880 -0
  63. package/dist/improvement/OperatorInspector.d.ts +182 -0
  64. package/dist/improvement/OperatorInspector.d.ts.map +1 -0
  65. package/dist/improvement/OperatorInspector.js +604 -0
  66. package/dist/improvement/ProductionGovernance.d.ts +91 -0
  67. package/dist/improvement/ProductionGovernance.d.ts.map +1 -0
  68. package/dist/improvement/ProductionGovernance.js +329 -0
  69. package/dist/improvement/PromptSchemaToolMetadataCandidateBuilder.d.ts +152 -0
  70. package/dist/improvement/PromptSchemaToolMetadataCandidateBuilder.d.ts.map +1 -0
  71. package/dist/improvement/PromptSchemaToolMetadataCandidateBuilder.js +480 -0
  72. package/dist/improvement/PublishOrchestrator.d.ts +113 -0
  73. package/dist/improvement/PublishOrchestrator.d.ts.map +1 -0
  74. package/dist/improvement/PublishOrchestrator.js +590 -0
  75. package/dist/improvement/ReleaseOutcomeReporter.d.ts +133 -0
  76. package/dist/improvement/ReleaseOutcomeReporter.d.ts.map +1 -0
  77. package/dist/improvement/ReleaseOutcomeReporter.js +501 -0
  78. package/dist/improvement/StorageServiceImprovementClient.d.ts +71 -0
  79. package/dist/improvement/StorageServiceImprovementClient.d.ts.map +1 -0
  80. package/dist/improvement/StorageServiceImprovementClient.js +245 -0
  81. package/dist/index.d.ts +43 -0
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +21 -0
  84. package/dist/runtime/CodaliRuntime.d.ts +0 -2
  85. package/dist/runtime/CodaliRuntime.d.ts.map +1 -1
  86. package/dist/runtime/CodaliRuntime.js +0 -5
  87. package/dist/storage/CodaliDatasetPrivacyEngine.d.ts +138 -0
  88. package/dist/storage/CodaliDatasetPrivacyEngine.d.ts.map +1 -0
  89. package/dist/storage/CodaliDatasetPrivacyEngine.js +417 -0
  90. package/dist/storage/CodaliFeedbackReviewIngestion.d.ts +50 -0
  91. package/dist/storage/CodaliFeedbackReviewIngestion.d.ts.map +1 -0
  92. package/dist/storage/CodaliFeedbackReviewIngestion.js +141 -0
  93. package/dist/storage/CodaliStorageContracts.d.ts +337 -0
  94. package/dist/storage/CodaliStorageContracts.d.ts.map +1 -0
  95. package/dist/storage/CodaliStorageContracts.js +1555 -0
  96. package/dist/storage/DatasetExportJob.d.ts +51 -0
  97. package/dist/storage/DatasetExportJob.d.ts.map +1 -0
  98. package/dist/storage/DatasetExportJob.js +358 -0
  99. package/dist/storage/DatasetReviewQueue.d.ts +98 -0
  100. package/dist/storage/DatasetReviewQueue.d.ts.map +1 -0
  101. package/dist/storage/DatasetReviewQueue.js +426 -0
  102. package/dist/storage/GatewayDatasetStore.d.ts +233 -0
  103. package/dist/storage/GatewayDatasetStore.d.ts.map +1 -0
  104. package/dist/storage/GatewayDatasetStore.js +2070 -0
  105. package/package.json +5 -4
@@ -0,0 +1,2070 @@
1
+ import { createHash, createHmac, randomUUID } from "node:crypto";
2
+ import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+ import { redactCodaliGatewaySecrets, } from "../gateway/CodaliGatewayStore.js";
6
+ import { CODALI_STORAGE_CONTRACT_SCHEMA_VERSION, validateCodaliStorageDatasetRecord, } from "./CodaliStorageContracts.js";
7
+ export const GATEWAY_DATASET_SERVICE_SIGNATURE_VERSION = "codali.storage.hmac.v1";
8
+ export const DEFAULT_GATEWAY_DATASET_SERVICE_ENDPOINT = "/v1/gateway/batches";
9
+ export class GatewayDatasetStoreError extends Error {
10
+ constructor(code, message) {
11
+ super(`${code}: ${message}`);
12
+ this.name = "GatewayDatasetStoreError";
13
+ this.code = code;
14
+ }
15
+ }
16
+ export class GatewayDatasetServiceClientError extends Error {
17
+ constructor(input) {
18
+ super(`${input.code}: ${input.message}`);
19
+ this.name = "GatewayDatasetServiceClientError";
20
+ this.code = input.code;
21
+ this.status = input.status;
22
+ this.retryable = input.retryable ?? false;
23
+ }
24
+ }
25
+ const isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
26
+ const cloneJson = (value) => JSON.parse(JSON.stringify(value));
27
+ const sanitizeGatewayDatasetPayload = (value) => redactCodaliGatewaySecrets(value);
28
+ const sanitizeGatewayDatasetMetadata = (metadata) => {
29
+ if (!metadata)
30
+ return undefined;
31
+ const sanitized = sanitizeGatewayDatasetPayload(metadata);
32
+ return isRecord(sanitized) ? sanitized : undefined;
33
+ };
34
+ const normalizeCanonicalJson = (value) => {
35
+ if (value === undefined || value === null)
36
+ return null;
37
+ if (Array.isArray(value))
38
+ return value.map((item) => normalizeCanonicalJson(item));
39
+ if (isRecord(value)) {
40
+ return Object.keys(value)
41
+ .sort()
42
+ .reduce((output, key) => {
43
+ output[key] = normalizeCanonicalJson(value[key]);
44
+ return output;
45
+ }, {});
46
+ }
47
+ return value;
48
+ };
49
+ export const canonicalizeGatewayDatasetJson = (value) => JSON.stringify(normalizeCanonicalJson(value));
50
+ const sha256Hex = (value) => createHash("sha256").update(value).digest("hex");
51
+ export const hashGatewayDatasetRequestBody = (body) => sha256Hex(canonicalizeGatewayDatasetJson(body));
52
+ export const hashGatewayDatasetValue = (value) => `sha256:${hashGatewayDatasetRequestBody(value)}`;
53
+ const normalizeObjectPayloadBody = (payload, mimeType) => {
54
+ if (typeof payload === "string" && mimeType === "application/x-ndjson") {
55
+ return payload.endsWith("\n") ? payload : `${payload}\n`;
56
+ }
57
+ return `${canonicalizeGatewayDatasetJson(payload)}\n`;
58
+ };
59
+ const objectContentHash = (payload, mimeType) => sha256Hex(normalizeObjectPayloadBody(payload, mimeType));
60
+ const objectByteSize = (payload, mimeType) => Buffer.byteLength(normalizeObjectPayloadBody(payload, mimeType), "utf8");
61
+ const putGatewayDatasetObject = (objectStore, input) => objectStore.putObject({
62
+ ...input,
63
+ payload: sanitizeGatewayDatasetPayload(input.payload),
64
+ metadata: sanitizeGatewayDatasetMetadata(input.metadata),
65
+ });
66
+ export const createGatewayDatasetLocalOnlyPrivacy = (overrides = {}) => {
67
+ const { metadata, policyTags, ...rest } = overrides;
68
+ return {
69
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
70
+ classification: "internal",
71
+ containsPersonalData: false,
72
+ redactionStatus: "not_required",
73
+ uploadAllowed: false,
74
+ exportAllowed: false,
75
+ trainingAllowed: false,
76
+ policyTags: policyTags ?? ["local_only"],
77
+ metadata: {
78
+ collectionMode: "local_only",
79
+ ...(metadata ?? {}),
80
+ },
81
+ ...rest,
82
+ };
83
+ };
84
+ export const createGatewayDatasetLocalOnlyObjectPrivacyFlags = (overrides = {}) => ({
85
+ containsPersonalData: false,
86
+ containsSecrets: false,
87
+ containsTenantPrivateData: true,
88
+ containsSourceCode: false,
89
+ containsCustomerData: true,
90
+ trainingAllowed: false,
91
+ evalAllowed: true,
92
+ replayAllowed: true,
93
+ exportAllowed: false,
94
+ ...overrides,
95
+ });
96
+ export const gatewayDatasetScopeFromGatewayResult = (request, result, overrides = {}) => ({
97
+ tenantId: overrides.tenantId ?? request.tenant?.id ?? request.tenant?.slug ?? "local",
98
+ productId: overrides.productId ?? request.product?.name ?? "codali",
99
+ deploymentId: overrides.deploymentId ??
100
+ request.product?.version ??
101
+ request.product?.surface ??
102
+ "local",
103
+ runId: overrides.runId ?? result.runId,
104
+ });
105
+ export const buildGatewayDatasetIdempotencyKey = (scope, records) => `gateway-dataset:${hashGatewayDatasetRequestBody({
106
+ scope,
107
+ records: records.map((record) => ({
108
+ recordId: record.recordId,
109
+ datasetKind: record.datasetKind,
110
+ inputHash: record.inputRef.contentHash,
111
+ outputHash: record.outputRef?.contentHash,
112
+ evidenceHashes: record.evidenceRefs?.map((ref) => ref.contentHash) ?? [],
113
+ })),
114
+ })}`;
115
+ export const buildGatewayDatasetServiceSignatureHeaders = (input) => {
116
+ const bodyHash = hashGatewayDatasetRequestBody(input.body);
117
+ const canonicalPayload = [
118
+ GATEWAY_DATASET_SERVICE_SIGNATURE_VERSION,
119
+ input.scope.tenantId,
120
+ input.scope.productId,
121
+ input.scope.deploymentId,
122
+ input.scope.runId,
123
+ input.timestamp,
124
+ input.nonce,
125
+ bodyHash,
126
+ ].join("\n");
127
+ const signature = createHmac("sha256", input.hmacSecret)
128
+ .update(canonicalPayload)
129
+ .digest("hex");
130
+ return {
131
+ "x-codali-storage-timestamp": input.timestamp,
132
+ "x-codali-storage-nonce": input.nonce,
133
+ "x-codali-storage-body-sha256": bodyHash,
134
+ "x-codali-storage-signature": signature,
135
+ };
136
+ };
137
+ const validateDatasetRecordOrThrow = (record) => {
138
+ const validation = validateCodaliStorageDatasetRecord(record);
139
+ if (!validation.ok) {
140
+ throw new GatewayDatasetStoreError("GATEWAY_DATASET_RECORD_INVALID", validation.issues
141
+ .map((issue) => `${issue.path}:${issue.code}:${issue.message}`)
142
+ .join("; "));
143
+ }
144
+ return validation.value;
145
+ };
146
+ const validateDatasetRecords = (records) => records.map((record) => validateDatasetRecordOrThrow(record));
147
+ const buildObjectRef = (input) => {
148
+ const mimeType = input.mimeType ?? "application/json";
149
+ const bodyHash = objectContentHash(input.payload, mimeType);
150
+ const tenantHash = hashGatewayDatasetValue(input.scope.tenantId);
151
+ const ownerScope = {
152
+ tenantHash,
153
+ productId: input.scope.productId,
154
+ deploymentId: input.scope.deploymentId,
155
+ runId: input.scope.runId,
156
+ ownerType: input.ownerType,
157
+ ownerId: input.ownerId,
158
+ };
159
+ return {
160
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
161
+ refId: input.refId,
162
+ kind: input.kind ?? "payload",
163
+ uri: input.uri,
164
+ contentHash: `sha256:${bodyHash}`,
165
+ byteSize: objectByteSize(input.payload, mimeType),
166
+ mimeType,
167
+ privacyFlags: createGatewayDatasetLocalOnlyObjectPrivacyFlags(input.privacyFlags),
168
+ ownerScope,
169
+ ownerScopeHash: hashGatewayDatasetValue(ownerScope),
170
+ deletionGroupId: `gateway-dataset-${input.scope.runId}`,
171
+ retentionClass: input.retentionClass ?? "dataset",
172
+ mediaType: mimeType === "application/x-ndjson" ? "jsonl" : "json",
173
+ sizeBytes: objectByteSize(input.payload, mimeType),
174
+ sha256: bodyHash,
175
+ createdAt: (input.now ?? (() => new Date()))().toISOString(),
176
+ metadata: input.metadata,
177
+ };
178
+ };
179
+ export class InMemoryGatewayDatasetObjectStore {
180
+ constructor(options = {}) {
181
+ this.options = options;
182
+ this.objects = new Map();
183
+ }
184
+ async putObject(input) {
185
+ const refId = `${input.scope.runId}-${input.ownerType}-${randomUUID()}`;
186
+ const ref = buildObjectRef({
187
+ ...input,
188
+ refId,
189
+ uri: `memory://codali-dataset/objects/${refId}.json`,
190
+ now: this.options.now,
191
+ });
192
+ this.objects.set(ref.refId, { ref, payload: cloneJson(input.payload) });
193
+ return cloneJson(ref);
194
+ }
195
+ async readObject(ref) {
196
+ const object = this.objects.get(ref.refId);
197
+ return object ? cloneJson(object.payload) : undefined;
198
+ }
199
+ listObjects() {
200
+ return Array.from(this.objects.values()).map((object) => cloneJson(object.ref));
201
+ }
202
+ }
203
+ export const createInMemoryGatewayDatasetObjectStore = (options = {}) => new InMemoryGatewayDatasetObjectStore(options);
204
+ export class InMemoryGatewayDatasetStore {
205
+ constructor() {
206
+ this.records = new Map();
207
+ this.batches = [];
208
+ this.idempotency = new Map();
209
+ }
210
+ async collect(input) {
211
+ const records = validateDatasetRecords(input.records);
212
+ if (records.length === 0) {
213
+ return { accepted: true, status: "skipped", recordCount: 0 };
214
+ }
215
+ const idempotencyKey = input.idempotencyKey ?? buildGatewayDatasetIdempotencyKey(input.scope, records);
216
+ const replay = this.idempotency.get(idempotencyKey);
217
+ if (replay) {
218
+ return { ...cloneJson(replay), replayed: true };
219
+ }
220
+ for (const record of records) {
221
+ this.records.set(record.recordId, cloneJson(record));
222
+ }
223
+ const batchId = `memory-${hashGatewayDatasetRequestBody({
224
+ idempotencyKey,
225
+ recordIds: records.map((record) => record.recordId),
226
+ })}`;
227
+ this.batches.push({
228
+ idempotencyKey,
229
+ scope: cloneJson(input.scope),
230
+ recordIds: records.map((record) => record.recordId),
231
+ metadata: input.metadata ? cloneJson(input.metadata) : undefined,
232
+ });
233
+ const result = {
234
+ accepted: true,
235
+ status: "stored",
236
+ recordCount: records.length,
237
+ idempotencyKey,
238
+ batchId,
239
+ };
240
+ this.idempotency.set(idempotencyKey, cloneJson(result));
241
+ return result;
242
+ }
243
+ listRecords() {
244
+ return Array.from(this.records.values()).map((record) => cloneJson(record));
245
+ }
246
+ readRecord(recordId) {
247
+ const record = this.records.get(recordId);
248
+ return record ? cloneJson(record) : undefined;
249
+ }
250
+ listBatches() {
251
+ return cloneJson(this.batches);
252
+ }
253
+ }
254
+ export const createInMemoryGatewayDatasetStore = () => new InMemoryGatewayDatasetStore();
255
+ export class LocalJsonlGatewayDatasetObjectStore {
256
+ constructor(options) {
257
+ this.options = options;
258
+ }
259
+ async putObject(input) {
260
+ await mkdir(this.options.directory, { recursive: true });
261
+ const refId = `${input.scope.runId}-${input.ownerType}-${randomUUID()}`;
262
+ const filePath = path.join(this.options.directory, `${refId}.json`);
263
+ await writeFile(filePath, normalizeObjectPayloadBody(input.payload, input.mimeType), "utf8");
264
+ return buildObjectRef({
265
+ ...input,
266
+ refId,
267
+ uri: pathToFileURL(filePath).href,
268
+ now: this.options.now,
269
+ });
270
+ }
271
+ async readObject(ref) {
272
+ if (!ref.uri?.startsWith("file://"))
273
+ return undefined;
274
+ const raw = await readFile(fileURLToPath(ref.uri), "utf8");
275
+ try {
276
+ return JSON.parse(raw);
277
+ }
278
+ catch {
279
+ return raw;
280
+ }
281
+ }
282
+ }
283
+ export const createLocalJsonlGatewayDatasetObjectStore = (options) => new LocalJsonlGatewayDatasetObjectStore(options);
284
+ export class LocalJsonlGatewayDatasetStore {
285
+ constructor(options) {
286
+ this.options = options;
287
+ this.idempotency = new Map();
288
+ }
289
+ async collect(input) {
290
+ const records = validateDatasetRecords(input.records);
291
+ if (records.length === 0) {
292
+ return { accepted: true, status: "skipped", recordCount: 0 };
293
+ }
294
+ const idempotencyKey = input.idempotencyKey ?? buildGatewayDatasetIdempotencyKey(input.scope, records);
295
+ const replay = this.idempotency.get(idempotencyKey);
296
+ if (replay) {
297
+ return { ...cloneJson(replay), replayed: true };
298
+ }
299
+ await mkdir(this.options.directory, { recursive: true });
300
+ const recordsPath = path.join(this.options.directory, this.options.recordsFileName ?? "records.jsonl");
301
+ const body = {
302
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
303
+ collectedAt: (this.options.now ?? (() => new Date()))().toISOString(),
304
+ scope: input.scope,
305
+ idempotencyKey,
306
+ records,
307
+ metadata: input.metadata,
308
+ };
309
+ await appendFile(recordsPath, `${JSON.stringify(body)}\n`, "utf8");
310
+ const result = {
311
+ accepted: true,
312
+ status: "stored",
313
+ recordCount: records.length,
314
+ idempotencyKey,
315
+ batchId: `jsonl-${hashGatewayDatasetRequestBody(body)}`,
316
+ metadata: {
317
+ recordsPath,
318
+ },
319
+ };
320
+ this.idempotency.set(idempotencyKey, cloneJson(result));
321
+ return result;
322
+ }
323
+ }
324
+ export const createLocalJsonlGatewayDatasetStore = (options) => new LocalJsonlGatewayDatasetStore(options);
325
+ const chunkRecords = (records, size) => {
326
+ const chunks = [];
327
+ for (let index = 0; index < records.length; index += size) {
328
+ chunks.push(records.slice(index, index + size));
329
+ }
330
+ return chunks;
331
+ };
332
+ const positiveInteger = (value, fallback) => Number.isFinite(value) && value !== undefined && value > 0
333
+ ? Math.floor(value)
334
+ : fallback;
335
+ const sleep = (ms) => new Promise((resolve) => {
336
+ setTimeout(resolve, ms);
337
+ });
338
+ const endpointUrl = (baseUrl, endpointPath) => `${baseUrl.replace(/\/+$/, "")}/${endpointPath.replace(/^\/+/, "")}`;
339
+ const parseResponseBody = async (response) => {
340
+ const raw = await response.text();
341
+ if (!raw.trim())
342
+ return undefined;
343
+ try {
344
+ return JSON.parse(raw);
345
+ }
346
+ catch {
347
+ return raw;
348
+ }
349
+ };
350
+ const retryableStatus = (status) => status === 408 || status === 425 || status === 429 || status >= 500;
351
+ const errorMessage = (error) => error instanceof Error ? error.message : String(error);
352
+ export class GatewayDatasetServiceClient {
353
+ constructor(options) {
354
+ this.options = options;
355
+ const defaultFetch = globalThis.fetch?.bind(globalThis);
356
+ const fetchImpl = options.fetch ?? defaultFetch;
357
+ if (!fetchImpl) {
358
+ throw new GatewayDatasetServiceClientError({
359
+ code: "GATEWAY_DATASET_FETCH_UNAVAILABLE",
360
+ message: "No fetch implementation is available for GatewayDatasetServiceClient.",
361
+ });
362
+ }
363
+ this.fetchImpl = fetchImpl;
364
+ }
365
+ async collect(input) {
366
+ const records = validateDatasetRecords(input.records);
367
+ if (records.length === 0) {
368
+ return { accepted: true, status: "skipped", recordCount: 0 };
369
+ }
370
+ const idempotencyKey = input.idempotencyKey ?? buildGatewayDatasetIdempotencyKey(input.scope, records);
371
+ const batchSize = positiveInteger(this.options.batchSize, 25);
372
+ const batches = chunkRecords(records, batchSize);
373
+ const responses = [];
374
+ try {
375
+ for (let batchIndex = 0; batchIndex < batches.length; batchIndex += 1) {
376
+ const batchRecords = batches[batchIndex] ?? [];
377
+ const batchIdempotencyKey = batches.length === 1 ? idempotencyKey : `${idempotencyKey}:${batchIndex + 1}`;
378
+ responses.push(await this.writeBatch({
379
+ scope: input.scope,
380
+ records: batchRecords,
381
+ idempotencyKey: batchIdempotencyKey,
382
+ batchIndex,
383
+ batchCount: batches.length,
384
+ metadata: input.metadata,
385
+ }));
386
+ }
387
+ return {
388
+ accepted: true,
389
+ status: "stored",
390
+ recordCount: records.length,
391
+ idempotencyKey,
392
+ batchId: idempotencyKey,
393
+ metadata: {
394
+ batchCount: batches.length,
395
+ responses,
396
+ },
397
+ };
398
+ }
399
+ catch (error) {
400
+ if (this.options.fallbackStore) {
401
+ const fallback = await this.options.fallbackStore.collect({
402
+ ...input,
403
+ records,
404
+ idempotencyKey,
405
+ });
406
+ return {
407
+ ...fallback,
408
+ fallbackUsed: true,
409
+ errors: [...(fallback.errors ?? []), errorMessage(error)],
410
+ };
411
+ }
412
+ throw error;
413
+ }
414
+ }
415
+ async writeBatch(input) {
416
+ const endpoint = endpointUrl(this.options.baseUrl, this.options.endpointPath ?? DEFAULT_GATEWAY_DATASET_SERVICE_ENDPOINT);
417
+ const maxRetries = Math.max(0, Math.floor(this.options.maxRetries ?? 2));
418
+ const retryBaseMs = Math.max(0, Math.floor(this.options.retryBaseMs ?? 100));
419
+ let lastError;
420
+ for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
421
+ const body = {
422
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
423
+ scope: input.scope,
424
+ records: input.records,
425
+ metadata: {
426
+ ...(input.metadata ?? {}),
427
+ batchIndex: input.batchIndex,
428
+ batchCount: input.batchCount,
429
+ },
430
+ };
431
+ const timestamp = (this.options.now ?? (() => new Date()))().toISOString();
432
+ const nonce = this.options.nonceFactory?.() ?? randomUUID();
433
+ const bodyText = JSON.stringify(body);
434
+ const headers = {
435
+ authorization: `Bearer ${this.options.serviceToken}`,
436
+ "content-type": "application/json",
437
+ "x-codali-storage-tenant": input.scope.tenantId,
438
+ "x-codali-storage-product": input.scope.productId,
439
+ "x-codali-storage-deployment": input.scope.deploymentId,
440
+ "x-codali-storage-run": input.scope.runId,
441
+ "x-codali-storage-idempotency-key": input.idempotencyKey,
442
+ ...buildGatewayDatasetServiceSignatureHeaders({
443
+ scope: input.scope,
444
+ body,
445
+ hmacSecret: this.options.hmacSecret ?? this.options.serviceToken,
446
+ timestamp,
447
+ nonce,
448
+ }),
449
+ };
450
+ try {
451
+ const response = await this.fetchWithTimeout(endpoint, {
452
+ method: "POST",
453
+ headers,
454
+ body: bodyText,
455
+ });
456
+ const parsed = await parseResponseBody(response);
457
+ if (response.ok) {
458
+ return parsed;
459
+ }
460
+ const retryable = retryableStatus(response.status);
461
+ lastError = new GatewayDatasetServiceClientError({
462
+ code: "GATEWAY_DATASET_SERVICE_WRITE_FAILED",
463
+ message: `Storage service returned ${response.status}${response.statusText ? ` ${response.statusText}` : ""}.`,
464
+ status: response.status,
465
+ retryable,
466
+ });
467
+ if (!retryable || attempt >= maxRetries) {
468
+ throw lastError;
469
+ }
470
+ }
471
+ catch (error) {
472
+ const retryable = error instanceof GatewayDatasetServiceClientError ? error.retryable : true;
473
+ lastError = error;
474
+ if (!retryable || attempt >= maxRetries) {
475
+ throw error;
476
+ }
477
+ }
478
+ if (retryBaseMs > 0) {
479
+ await sleep(retryBaseMs * (attempt + 1));
480
+ }
481
+ }
482
+ throw lastError instanceof Error
483
+ ? lastError
484
+ : new GatewayDatasetServiceClientError({
485
+ code: "GATEWAY_DATASET_SERVICE_WRITE_FAILED",
486
+ message: "Storage service write failed without an error object.",
487
+ });
488
+ }
489
+ async fetchWithTimeout(url, request) {
490
+ const timeoutMs = Math.max(0, Math.floor(this.options.timeoutMs ?? 5000));
491
+ if (timeoutMs <= 0) {
492
+ return this.fetchImpl(url, request);
493
+ }
494
+ const controller = new AbortController();
495
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
496
+ try {
497
+ return await this.fetchImpl(url, { ...request, signal: controller.signal });
498
+ }
499
+ finally {
500
+ clearTimeout(timeout);
501
+ }
502
+ }
503
+ }
504
+ export const createGatewayDatasetServiceClient = (options) => new GatewayDatasetServiceClient(options);
505
+ const confidenceScore = (confidence) => {
506
+ if (confidence === "high")
507
+ return 0.95;
508
+ if (confidence === "medium")
509
+ return 0.7;
510
+ return 0.4;
511
+ };
512
+ const contextPackSummary = (contextPack) => contextPack
513
+ ? {
514
+ contextPackId: contextPack.id,
515
+ tokenEstimate: contextPack.tokenEstimate,
516
+ decisionFactCount: contextPack.decisionFacts.length,
517
+ contradictionCount: contextPack.contradictions.length,
518
+ missingInformationCount: contextPack.missingInformation.length,
519
+ }
520
+ : undefined;
521
+ const safeDatasetIdPart = (value) => {
522
+ const normalized = value
523
+ .trim()
524
+ .replace(/[^A-Za-z0-9._:-]+/g, "-")
525
+ .replace(/^-+|-+$/g, "")
526
+ .slice(0, 96);
527
+ return normalized || hashGatewayDatasetRequestBody(value).slice(0, 16);
528
+ };
529
+ const labelToken = (value) => value
530
+ .trim()
531
+ .toLowerCase()
532
+ .replace(/[^a-z0-9._:-]+/g, "_")
533
+ .replace(/^_+|_+$/g, "");
534
+ const uniqueLabels = (values) => {
535
+ const seen = new Set();
536
+ const output = [];
537
+ for (const value of values) {
538
+ if (!value)
539
+ continue;
540
+ const label = labelToken(value);
541
+ if (!label || seen.has(label))
542
+ continue;
543
+ seen.add(label);
544
+ output.push(label);
545
+ }
546
+ return output;
547
+ };
548
+ const uniqueStrings = (values) => {
549
+ const seen = new Set();
550
+ const output = [];
551
+ for (const value of values) {
552
+ if (!value || seen.has(value))
553
+ continue;
554
+ seen.add(value);
555
+ output.push(value);
556
+ }
557
+ return output;
558
+ };
559
+ const metadataNumber = (metadata, key) => {
560
+ const value = metadata?.[key];
561
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
562
+ };
563
+ const metadataRecord = (metadata, key) => {
564
+ const value = metadata?.[key];
565
+ return isRecord(value) ? value : undefined;
566
+ };
567
+ const modelCallRepairAttempts = (call) => metadataNumber(call.metadata, "repairAttempts")
568
+ ?? metadataNumber(call.metadata, "repair_attempts")
569
+ ?? 0;
570
+ const schemaFailureText = (value) => typeof value === "string" && /schema|json|parse|validation|invalid/i.test(value);
571
+ const isSchemaFailureModelCall = (call) => {
572
+ if (call.status === "repaired" || modelCallRepairAttempts(call) > 0)
573
+ return true;
574
+ if (schemaFailureText(call.errorCode) || schemaFailureText(call.errorMessage))
575
+ return true;
576
+ return call.status === "failed" && schemaFailureText(JSON.stringify(call.metadata ?? {}));
577
+ };
578
+ const modelRecordId = (runId, call, index) => `dataset-${runId}-model-${safeDatasetIdPart(call.id || `${index + 1}-${call.role}`)}`;
579
+ const schemaFailureRecordId = (runId, call, index) => `dataset-${runId}-schema-failure-${safeDatasetIdPart(call.id || `${index + 1}-${call.role}`)}`;
580
+ const normalizeStoredModelCalls = (trace, result) => {
581
+ if (trace?.modelCalls.length) {
582
+ return trace.modelCalls.map((call, index) => ({
583
+ ...call,
584
+ id: call.id || `model-${index + 1}`,
585
+ runId: call.runId || result.runId,
586
+ status: call.status,
587
+ }));
588
+ }
589
+ return result.trace.modelCalls.map((call, index) => ({
590
+ id: `trace-model-${index + 1}`,
591
+ runId: result.runId,
592
+ role: call.role,
593
+ status: call.status,
594
+ latencyMs: call.latencyMs,
595
+ agentSlug: call.agentSlug,
596
+ model: call.model,
597
+ provider: call.provider,
598
+ errorCode: call.errorCode,
599
+ metadata: {
600
+ tier: call.tier,
601
+ promptTokens: call.promptTokens,
602
+ completionTokens: call.completionTokens,
603
+ },
604
+ }));
605
+ };
606
+ const nextCorrectedModelCall = (call, index, calls) => {
607
+ if (call.status === "repaired")
608
+ return call;
609
+ return calls.slice(index + 1).find((candidate) => candidate.role === call.role
610
+ && (candidate.status === "success" || candidate.status === "repaired"));
611
+ };
612
+ const modelCallScore = (call) => {
613
+ if (call.status === "success")
614
+ return 0.55;
615
+ if (call.status === "repaired")
616
+ return 0.45;
617
+ if (call.status === "failed")
618
+ return 0.1;
619
+ return 0;
620
+ };
621
+ const arrayOfStrings = (value) => Array.isArray(value)
622
+ ? value.filter((item) => typeof item === "string")
623
+ : undefined;
624
+ const normalizeGoldTarget = (value) => {
625
+ if (!isRecord(value))
626
+ return undefined;
627
+ const kind = value.kind;
628
+ if (kind !== "accepted" && kind !== "corrected" && kind !== "reviewed")
629
+ return undefined;
630
+ return {
631
+ kind,
632
+ id: typeof value.id === "string" ? value.id : undefined,
633
+ input: value.input,
634
+ target: value.target ?? value.output,
635
+ sourceRecordId: typeof value.sourceRecordId === "string"
636
+ ? value.sourceRecordId
637
+ : typeof value.source_record_id === "string"
638
+ ? value.source_record_id
639
+ : undefined,
640
+ sourceModelCallId: typeof value.sourceModelCallId === "string"
641
+ ? value.sourceModelCallId
642
+ : typeof value.source_model_call_id === "string"
643
+ ? value.source_model_call_id
644
+ : undefined,
645
+ failedAttemptRecordId: typeof value.failedAttemptRecordId === "string"
646
+ ? value.failedAttemptRecordId
647
+ : typeof value.failed_attempt_record_id === "string"
648
+ ? value.failed_attempt_record_id
649
+ : undefined,
650
+ failedAttemptModelCallId: typeof value.failedAttemptModelCallId === "string"
651
+ ? value.failedAttemptModelCallId
652
+ : typeof value.failed_attempt_model_call_id === "string"
653
+ ? value.failed_attempt_model_call_id
654
+ : undefined,
655
+ reviewerId: typeof value.reviewerId === "string"
656
+ ? value.reviewerId
657
+ : typeof value.reviewer_id === "string"
658
+ ? value.reviewer_id
659
+ : undefined,
660
+ reasons: arrayOfStrings(value.reasons),
661
+ score: typeof value.score === "number" && Number.isFinite(value.score) ? value.score : undefined,
662
+ labels: arrayOfStrings(value.labels),
663
+ metadata: isRecord(value.metadata) ? value.metadata : undefined,
664
+ };
665
+ };
666
+ const goldTargetsFromMetadata = (metadata) => {
667
+ const raw = metadata?.goldTargets ?? metadata?.gold_targets;
668
+ if (!Array.isArray(raw))
669
+ return [];
670
+ return raw.flatMap((item) => {
671
+ const target = normalizeGoldTarget(item);
672
+ return target ? [target] : [];
673
+ });
674
+ };
675
+ const collectGatewayRunLabels = (result) => uniqueLabels([
676
+ "auto:gateway_run",
677
+ `auto:status:${result.status}`,
678
+ `auto:confidence:${result.confidence}`,
679
+ result.finalModel?.tier ? `auto:final_tier:${result.finalModel.tier}` : undefined,
680
+ ]);
681
+ const collectModelCallLabels = (call) => uniqueLabels([
682
+ "auto:model_call",
683
+ `auto:role:${call.role}`,
684
+ `auto:status:${call.status}`,
685
+ call.provider ? `auto:provider:${call.provider}` : undefined,
686
+ call.model ? "auto:model_present" : undefined,
687
+ modelCallRepairAttempts(call) > 0 ? "auto:repaired_after_schema_retry" : undefined,
688
+ ]);
689
+ const collectSchemaFailureLabels = (call) => uniqueLabels([
690
+ "auto:schema_failure",
691
+ "auto:needs_review",
692
+ `auto:role:${call.role}`,
693
+ `auto:status:${call.status}`,
694
+ ]);
695
+ const collectGoldTargetLabels = (target) => uniqueLabels([
696
+ "gold_target",
697
+ `gold:${target.kind}`,
698
+ ...(target.labels ?? []),
699
+ ]);
700
+ const toolNameTokens = (tool) => tool
701
+ .toLowerCase()
702
+ .split(/[^a-z0-9]+/g)
703
+ .filter(Boolean);
704
+ const hasToolToken = (tool, tokens) => {
705
+ const normalized = tool.toLowerCase();
706
+ const parts = toolNameTokens(tool);
707
+ return tokens.some((token) => parts.includes(token) ||
708
+ normalized === token ||
709
+ normalized.endsWith(`_${token}`));
710
+ };
711
+ const isDocdexTool = (tool) => tool.toLowerCase().startsWith("docdex_");
712
+ const isShellTool = (tool) => hasToolToken(tool, ["bash", "cmd", "exec", "execute", "shell", "terminal"]);
713
+ const isDestructiveTool = (tool) => hasToolToken(tool, [
714
+ "delete",
715
+ "destroy",
716
+ "drop",
717
+ "purge",
718
+ "remove",
719
+ "reset",
720
+ "rm",
721
+ "truncate",
722
+ "wipe",
723
+ ]);
724
+ const isWriteTool = (tool) => isDestructiveTool(tool) ||
725
+ hasToolToken(tool, [
726
+ "add",
727
+ "approve",
728
+ "assign",
729
+ "cancel",
730
+ "commit",
731
+ "create",
732
+ "dispatch",
733
+ "edit",
734
+ "merge",
735
+ "mutate",
736
+ "post",
737
+ "publish",
738
+ "push",
739
+ "send",
740
+ "submit",
741
+ "sync",
742
+ "transition",
743
+ "update",
744
+ "upload",
745
+ "write",
746
+ ]);
747
+ const policyEventTypeForTool = (tool) => {
748
+ if (isShellTool(tool))
749
+ return "shell_block";
750
+ if (isDestructiveTool(tool))
751
+ return "destructive_block";
752
+ if (isWriteTool(tool))
753
+ return "write_block";
754
+ return "denied_tool";
755
+ };
756
+ const readMetadataString = (metadata, key) => {
757
+ const value = metadata?.[key];
758
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
759
+ };
760
+ const readMetadataStringArray = (metadata, key) => arrayOfStrings(metadata?.[key]);
761
+ const taskById = (trace, taskId) => taskId ? trace?.tasks.find((task) => task.id === taskId) : undefined;
762
+ const toolDecisionFromCall = (call, index, trace) => {
763
+ const task = taskById(trace, call.taskId);
764
+ return {
765
+ id: call.id || `tool-call-${index + 1}`,
766
+ tool: call.tool,
767
+ taskId: call.taskId,
768
+ status: call.status,
769
+ args: call.args,
770
+ result: call.result,
771
+ latencyMs: call.latencyMs,
772
+ errorCode: call.errorCode,
773
+ errorMessage: call.errorMessage,
774
+ allowedTools: readMetadataStringArray(task?.metadata, "allowedTools"),
775
+ removedTools: readMetadataStringArray(task?.metadata, "removedTools"),
776
+ reason: call.status === "blocked" ? call.errorCode ?? "blocked" : undefined,
777
+ source: "tool_call",
778
+ metadata: call.metadata,
779
+ };
780
+ };
781
+ const blockedToolDecisionsFromTasks = (trace) => {
782
+ const decisions = [];
783
+ for (const task of trace?.tasks ?? []) {
784
+ const allowedTools = readMetadataStringArray(task.metadata, "allowedTools") ?? [];
785
+ const removedTools = readMetadataStringArray(task.metadata, "removedTools") ?? [];
786
+ const skippedReason = readMetadataString(task.metadata, "skippedReason");
787
+ for (const tool of removedTools) {
788
+ decisions.push({
789
+ id: `${task.id}-removed-${tool}`,
790
+ tool,
791
+ taskId: task.id,
792
+ status: "blocked",
793
+ allowedTools,
794
+ removedTools,
795
+ reason: "removed_by_policy",
796
+ source: "blocked_attempt",
797
+ metadata: task.metadata,
798
+ });
799
+ }
800
+ if (skippedReason && allowedTools.length > 0) {
801
+ for (const tool of allowedTools) {
802
+ decisions.push({
803
+ id: `${task.id}-skipped-${tool}-${skippedReason}`,
804
+ tool,
805
+ taskId: task.id,
806
+ status: "skipped",
807
+ allowedTools,
808
+ removedTools,
809
+ reason: skippedReason,
810
+ source: "blocked_attempt",
811
+ metadata: task.metadata,
812
+ });
813
+ }
814
+ }
815
+ }
816
+ return decisions;
817
+ };
818
+ const collectEvidenceItems = (trace, result) => {
819
+ const byId = new Map();
820
+ for (const item of [...(trace?.evidence ?? []), ...result.evidence]) {
821
+ byId.set(item.id, item);
822
+ }
823
+ return [...byId.values()];
824
+ };
825
+ const collectContextPacks = (trace, result) => {
826
+ const byId = new Map();
827
+ for (const pack of [trace?.contextPack, result.contextPack]) {
828
+ if (pack)
829
+ byId.set(pack.id, pack);
830
+ }
831
+ return [...byId.values()];
832
+ };
833
+ const extractReservedScopeKeys = (value, path = "$", matches = []) => {
834
+ if (Array.isArray(value)) {
835
+ value.forEach((item, index) => extractReservedScopeKeys(item, `${path}[${index}]`, matches));
836
+ return matches;
837
+ }
838
+ if (!isRecord(value))
839
+ return matches;
840
+ for (const [key, child] of Object.entries(value)) {
841
+ const childPath = `${path}.${key}`;
842
+ if (/^(docdex|repo|repoId|repo_id|repoRoot|repo_root|tenant|tenantId|tenant_id)$/i.test(key)) {
843
+ matches.push(childPath);
844
+ }
845
+ extractReservedScopeKeys(child, childPath, matches);
846
+ }
847
+ return matches;
848
+ };
849
+ const scopeOverrideEventType = (value) => {
850
+ const keys = extractReservedScopeKeys(value);
851
+ if (keys.some((key) => /docdex|repo/i.test(key)))
852
+ return "docdex_scope_override";
853
+ if (keys.some((key) => /tenant/i.test(key)))
854
+ return "tenant_scope_override";
855
+ return "scope_override";
856
+ };
857
+ const collectPolicyEvents = (input) => {
858
+ const events = [];
859
+ const addEvent = (event) => {
860
+ const key = [
861
+ event.eventType,
862
+ event.tool ?? "",
863
+ event.taskId ?? "",
864
+ event.reason,
865
+ event.errorCode ?? "",
866
+ ].join("|");
867
+ if (events.some((existing) => existing.id === event.id || [
868
+ existing.eventType,
869
+ existing.tool ?? "",
870
+ existing.taskId ?? "",
871
+ existing.reason,
872
+ existing.errorCode ?? "",
873
+ ].join("|") === key)) {
874
+ return;
875
+ }
876
+ events.push(event);
877
+ };
878
+ for (const tool of input.request.policy.deniedTools ?? []) {
879
+ addEvent({
880
+ id: `policy-denied-${tool}`,
881
+ eventType: "denied_tool",
882
+ reason: "policy_denied_tool",
883
+ tool,
884
+ });
885
+ }
886
+ for (const tool of input.request.policy.allowedTools) {
887
+ if (input.request.policy.allowShell === false && isShellTool(tool)) {
888
+ addEvent({
889
+ id: `policy-shell-${tool}`,
890
+ eventType: "shell_block",
891
+ reason: "shell_disabled",
892
+ tool,
893
+ });
894
+ }
895
+ else if (input.request.policy.allowDestructiveOperations === false &&
896
+ isDestructiveTool(tool)) {
897
+ addEvent({
898
+ id: `policy-destructive-${tool}`,
899
+ eventType: "destructive_block",
900
+ reason: "destructive_operations_disabled",
901
+ tool,
902
+ });
903
+ }
904
+ else if (input.request.policy.allowWrites === false && isWriteTool(tool)) {
905
+ addEvent({
906
+ id: `policy-write-${tool}`,
907
+ eventType: "write_block",
908
+ reason: "writes_disabled",
909
+ tool,
910
+ });
911
+ }
912
+ }
913
+ for (const task of input.trace?.tasks ?? []) {
914
+ const removedTools = readMetadataStringArray(task.metadata, "removedTools") ?? [];
915
+ const skippedReason = readMetadataString(task.metadata, "skippedReason");
916
+ for (const tool of removedTools) {
917
+ addEvent({
918
+ id: `task-${task.id}-removed-${tool}`,
919
+ eventType: policyEventTypeForTool(tool),
920
+ reason: "removed_from_worker_task",
921
+ tool,
922
+ taskId: task.id,
923
+ metadata: task.metadata,
924
+ });
925
+ }
926
+ if (skippedReason && /tool|budget|policy|scope|denied|blocked/i.test(skippedReason)) {
927
+ addEvent({
928
+ id: `task-${task.id}-skipped-${skippedReason}`,
929
+ eventType: skippedReason.includes("budget") ? "tool_budget_block" : "tool_block",
930
+ reason: skippedReason,
931
+ taskId: task.id,
932
+ status: task.status,
933
+ metadata: task.metadata,
934
+ });
935
+ }
936
+ }
937
+ for (const call of input.trace?.toolCalls ?? []) {
938
+ const errorCode = call.errorCode ?? "";
939
+ const eventType = /SCOPE_OVERRIDE/i.test(errorCode)
940
+ ? scopeOverrideEventType({ args: call.args, metadata: call.metadata })
941
+ : /DENIED/i.test(errorCode)
942
+ ? "denied_tool"
943
+ : /NOT_APPROVED|NOT_ALLOWED|BLOCK/i.test(errorCode) || call.status === "blocked"
944
+ ? policyEventTypeForTool(call.tool)
945
+ : undefined;
946
+ if (eventType) {
947
+ addEvent({
948
+ id: `tool-${call.id}-${eventType}`,
949
+ eventType,
950
+ reason: call.errorCode ?? call.status,
951
+ tool: call.tool,
952
+ taskId: call.taskId,
953
+ status: call.status,
954
+ errorCode: call.errorCode,
955
+ errorMessage: call.errorMessage,
956
+ metadata: call.metadata,
957
+ });
958
+ }
959
+ }
960
+ for (const warning of input.result.trace.warnings) {
961
+ if (!/denied|blocked|removed|scope|write|shell|destructive/i.test(warning))
962
+ continue;
963
+ addEvent({
964
+ id: `warning-${hashGatewayDatasetRequestBody(warning).slice(0, 16)}`,
965
+ eventType: /scope/i.test(warning) ? "scope_override" : "policy_warning",
966
+ reason: warning,
967
+ metadata: { source: "gateway_trace_warning" },
968
+ });
969
+ }
970
+ return events;
971
+ };
972
+ const collectToolDecisionLabels = (decision) => uniqueLabels([
973
+ "auto:tool_decision",
974
+ `auto:tool:${decision.tool}`,
975
+ `auto:status:${decision.status}`,
976
+ decision.source === "blocked_attempt" ? "auto:blocked_attempt" : undefined,
977
+ decision.reason ? `auto:reason:${decision.reason}` : undefined,
978
+ ]);
979
+ const collectRagRetrievalLabels = (call) => uniqueLabels([
980
+ "auto:rag_retrieval",
981
+ `auto:tool:${call.tool}`,
982
+ `auto:status:${call.status}`,
983
+ ]);
984
+ const collectEvidenceLabels = (item) => uniqueLabels([
985
+ "auto:evidence_item",
986
+ `auto:source:${item.sourceType}`,
987
+ item.usedTool ? `auto:tool:${item.usedTool}` : undefined,
988
+ item.freshness ? `auto:freshness:${item.freshness}` : undefined,
989
+ item.tenantScoped ? "auto:tenant_scoped" : undefined,
990
+ ]);
991
+ const collectContextPackLabels = (contextPack) => uniqueLabels([
992
+ "auto:context_pack",
993
+ contextPack.contradictions.length > 0 ? "auto:has_contradictions" : undefined,
994
+ contextPack.missingInformation.length > 0 ? "auto:missing_information" : undefined,
995
+ ]);
996
+ const collectArtifactLabels = (artifact) => uniqueLabels([
997
+ "auto:artifact",
998
+ `auto:artifact_type:${artifact.type}`,
999
+ /^image($|\/)|image/i.test(artifact.type) ? "auto:image_artifact" : undefined,
1000
+ artifact.uri || artifact.path ? "auto:object_ref_only" : undefined,
1001
+ ]);
1002
+ const collectPolicyEventLabels = (event) => uniqueLabels([
1003
+ "auto:policy_event",
1004
+ `auto:policy:${event.eventType}`,
1005
+ event.tool ? `auto:tool:${event.tool}` : undefined,
1006
+ ]);
1007
+ const artifactMetadataRefOnly = (value) => {
1008
+ if (!value)
1009
+ return undefined;
1010
+ const isRawArtifactPayloadKey = (key) => {
1011
+ const normalized = key.replace(/[-_\s]/g, "").toLowerCase();
1012
+ if (new Set([
1013
+ "base64",
1014
+ "binary",
1015
+ "blob",
1016
+ "body",
1017
+ "buffer",
1018
+ "bytes",
1019
+ "content",
1020
+ "data",
1021
+ "datauri",
1022
+ "dataurl",
1023
+ "imagedata",
1024
+ "payload",
1025
+ "raw",
1026
+ ]).has(normalized)) {
1027
+ return true;
1028
+ }
1029
+ return /^(?:image|base64|binary|blob|body|buffer|bytes|content|raw|file|payload|artifact|audio|video|media).*(?:base64|binary|blob|buffer|bytes|content|data|payload|raw)$/i
1030
+ .test(normalized);
1031
+ };
1032
+ const sanitizeArtifactValue = (entry) => {
1033
+ if (Array.isArray(entry))
1034
+ return entry.map(sanitizeArtifactValue);
1035
+ if (!isRecord(entry))
1036
+ return sanitizeGatewayDatasetPayload(entry);
1037
+ const output = {};
1038
+ for (const [key, child] of Object.entries(entry)) {
1039
+ output[key] = isRawArtifactPayloadKey(key)
1040
+ ? "[object-ref-only]"
1041
+ : sanitizeArtifactValue(child);
1042
+ }
1043
+ return output;
1044
+ };
1045
+ const sanitized = sanitizeArtifactValue(value);
1046
+ return isRecord(sanitized) ? sanitized : undefined;
1047
+ };
1048
+ export class GatewayDatasetCollector {
1049
+ async buildCollectInput(input) {
1050
+ const now = input.now ?? (() => new Date());
1051
+ const objectStore = input.objectStore ?? createInMemoryGatewayDatasetObjectStore({ now });
1052
+ const scope = gatewayDatasetScopeFromGatewayResult(input.request, input.result, input.scope);
1053
+ const trace = input.trace ?? (input.traceLoader ? await input.traceLoader() : undefined);
1054
+ const inputMetadata = sanitizeGatewayDatasetMetadata(input.metadata);
1055
+ const privacyMetadata = sanitizeGatewayDatasetMetadata(input.privacy?.metadata);
1056
+ const privacy = createGatewayDatasetLocalOnlyPrivacy({
1057
+ containsPersonalData: true,
1058
+ ...input.privacy,
1059
+ metadata: {
1060
+ containsPersonalDataAssumption: "unknown_assumed_true",
1061
+ source: "codali_gateway",
1062
+ collector: "gateway_dataset_collector",
1063
+ ...(privacyMetadata ?? {}),
1064
+ },
1065
+ });
1066
+ const privacyFlags = createGatewayDatasetLocalOnlyObjectPrivacyFlags({
1067
+ containsPersonalData: true,
1068
+ ...(input.privacyFlags ?? {}),
1069
+ });
1070
+ const modelCalls = normalizeStoredModelCalls(trace, input.result);
1071
+ const modelRecordIds = new Map();
1072
+ modelCalls.forEach((call, index) => {
1073
+ modelRecordIds.set(call.id, modelRecordId(input.result.runId, call, index));
1074
+ });
1075
+ const records = [
1076
+ await this.buildRunRecord({
1077
+ input,
1078
+ objectStore,
1079
+ scope,
1080
+ privacy,
1081
+ privacyFlags,
1082
+ now,
1083
+ trace,
1084
+ modelCalls,
1085
+ }),
1086
+ ];
1087
+ if (input.collectFinalAnswers !== false) {
1088
+ records.push(await this.buildFinalAnswerRecord({
1089
+ input,
1090
+ objectStore,
1091
+ scope,
1092
+ privacy,
1093
+ privacyFlags,
1094
+ now,
1095
+ }));
1096
+ }
1097
+ if (input.collectRagRetrievals !== false) {
1098
+ const ragCalls = (trace?.toolCalls ?? []).filter((call) => isDocdexTool(call.tool));
1099
+ for (let index = 0; index < ragCalls.length; index += 1) {
1100
+ const call = ragCalls[index];
1101
+ if (!call)
1102
+ continue;
1103
+ records.push(await this.buildRagRetrievalRecord({
1104
+ call,
1105
+ index,
1106
+ input,
1107
+ objectStore,
1108
+ scope,
1109
+ privacy,
1110
+ privacyFlags,
1111
+ now,
1112
+ }));
1113
+ }
1114
+ }
1115
+ if (input.collectToolDecisions !== false) {
1116
+ const decisions = [
1117
+ ...(trace?.toolCalls ?? []).map((call, index) => toolDecisionFromCall(call, index, trace)),
1118
+ ...blockedToolDecisionsFromTasks(trace),
1119
+ ];
1120
+ for (let index = 0; index < decisions.length; index += 1) {
1121
+ const decision = decisions[index];
1122
+ if (!decision)
1123
+ continue;
1124
+ records.push(await this.buildToolDecisionRecord({
1125
+ decision,
1126
+ index,
1127
+ input,
1128
+ objectStore,
1129
+ scope,
1130
+ privacy,
1131
+ privacyFlags,
1132
+ now,
1133
+ }));
1134
+ }
1135
+ }
1136
+ const evidenceItems = collectEvidenceItems(trace, input.result);
1137
+ if (input.collectEvidenceItems !== false) {
1138
+ for (let index = 0; index < evidenceItems.length; index += 1) {
1139
+ const evidenceItem = evidenceItems[index];
1140
+ if (!evidenceItem)
1141
+ continue;
1142
+ records.push(await this.buildEvidenceItemRecord({
1143
+ evidenceItem,
1144
+ index,
1145
+ input,
1146
+ objectStore,
1147
+ scope,
1148
+ privacy,
1149
+ privacyFlags,
1150
+ now,
1151
+ }));
1152
+ }
1153
+ }
1154
+ const contextPacks = collectContextPacks(trace, input.result);
1155
+ if (input.collectContextPacks !== false) {
1156
+ for (let index = 0; index < contextPacks.length; index += 1) {
1157
+ const contextPack = contextPacks[index];
1158
+ if (!contextPack)
1159
+ continue;
1160
+ records.push(await this.buildContextPackRecord({
1161
+ contextPack,
1162
+ index,
1163
+ input,
1164
+ objectStore,
1165
+ scope,
1166
+ privacy,
1167
+ privacyFlags,
1168
+ now,
1169
+ }));
1170
+ }
1171
+ }
1172
+ if (input.collectArtifacts !== false) {
1173
+ for (let index = 0; index < (trace?.artifacts.length ?? 0); index += 1) {
1174
+ const artifact = trace?.artifacts[index];
1175
+ if (!artifact)
1176
+ continue;
1177
+ records.push(await this.buildArtifactRecord({
1178
+ artifact,
1179
+ index,
1180
+ input,
1181
+ objectStore,
1182
+ scope,
1183
+ privacy,
1184
+ privacyFlags,
1185
+ now,
1186
+ }));
1187
+ }
1188
+ }
1189
+ const policyEvents = collectPolicyEvents({ request: input.request, result: input.result, trace });
1190
+ if (input.collectPolicyEvents !== false) {
1191
+ for (let index = 0; index < policyEvents.length; index += 1) {
1192
+ const event = policyEvents[index];
1193
+ if (!event)
1194
+ continue;
1195
+ records.push(await this.buildPolicyEventRecord({
1196
+ event,
1197
+ index,
1198
+ input,
1199
+ objectStore,
1200
+ scope,
1201
+ privacy,
1202
+ privacyFlags,
1203
+ now,
1204
+ }));
1205
+ }
1206
+ }
1207
+ if (input.collectModelCalls !== false) {
1208
+ for (let index = 0; index < modelCalls.length; index += 1) {
1209
+ const call = modelCalls[index];
1210
+ if (!call)
1211
+ continue;
1212
+ records.push(await this.buildModelCallRecord({
1213
+ call,
1214
+ index,
1215
+ input,
1216
+ objectStore,
1217
+ scope,
1218
+ privacy,
1219
+ privacyFlags,
1220
+ now,
1221
+ schemaFailureRecordId: isSchemaFailureModelCall(call)
1222
+ ? schemaFailureRecordId(input.result.runId, call, index)
1223
+ : undefined,
1224
+ }));
1225
+ }
1226
+ }
1227
+ if (input.collectSchemaFailures !== false) {
1228
+ for (let index = 0; index < modelCalls.length; index += 1) {
1229
+ const call = modelCalls[index];
1230
+ if (!call || !isSchemaFailureModelCall(call))
1231
+ continue;
1232
+ const corrected = nextCorrectedModelCall(call, index, modelCalls);
1233
+ records.push(await this.buildSchemaFailureRecord({
1234
+ call,
1235
+ index,
1236
+ corrected,
1237
+ input,
1238
+ objectStore,
1239
+ scope,
1240
+ privacy,
1241
+ privacyFlags,
1242
+ now,
1243
+ failedAttemptRecordId: modelRecordIds.get(call.id),
1244
+ correctedRecordId: corrected ? modelRecordIds.get(corrected.id) : undefined,
1245
+ }));
1246
+ }
1247
+ }
1248
+ const goldTargets = [
1249
+ ...(input.goldTargets ?? []),
1250
+ ...goldTargetsFromMetadata(input.result.metadata),
1251
+ ];
1252
+ if (input.collectGoldTargets !== false) {
1253
+ for (let index = 0; index < goldTargets.length; index += 1) {
1254
+ const target = goldTargets[index];
1255
+ if (!target)
1256
+ continue;
1257
+ records.push(await this.buildGoldTargetRecord({
1258
+ target,
1259
+ index,
1260
+ input,
1261
+ objectStore,
1262
+ scope,
1263
+ privacy,
1264
+ privacyFlags,
1265
+ now,
1266
+ modelRecordIds,
1267
+ }));
1268
+ }
1269
+ }
1270
+ const validatedRecords = validateDatasetRecords(records);
1271
+ return {
1272
+ scope,
1273
+ records: validatedRecords,
1274
+ idempotencyKey: input.idempotencyKey ?? buildGatewayDatasetIdempotencyKey(scope, validatedRecords),
1275
+ metadata: {
1276
+ source: "codali_gateway",
1277
+ collectionMode: "gateway_trace_collector",
1278
+ gatewayRunId: input.result.runId,
1279
+ traceAvailable: Boolean(trace),
1280
+ recordCounts: {
1281
+ total: validatedRecords.length,
1282
+ modelCalls: input.collectModelCalls === false ? 0 : modelCalls.length,
1283
+ ragRetrievals: input.collectRagRetrievals === false
1284
+ ? 0
1285
+ : validatedRecords.filter((record) => record.metadata?.exampleType === "rag_retrieval").length,
1286
+ toolDecisions: input.collectToolDecisions === false
1287
+ ? 0
1288
+ : validatedRecords.filter((record) => record.metadata?.exampleType === "tool_decision").length,
1289
+ evidenceItems: input.collectEvidenceItems === false ? 0 : evidenceItems.length,
1290
+ contextPacks: input.collectContextPacks === false ? 0 : contextPacks.length,
1291
+ finalAnswers: input.collectFinalAnswers === false
1292
+ ? 0
1293
+ : validatedRecords.filter((record) => record.metadata?.exampleType === "final_answer").length,
1294
+ artifacts: input.collectArtifacts === false
1295
+ ? 0
1296
+ : validatedRecords.filter((record) => record.metadata?.exampleType === "artifact").length,
1297
+ policyEvents: input.collectPolicyEvents === false
1298
+ ? 0
1299
+ : validatedRecords.filter((record) => record.metadata?.exampleType === "policy_event").length,
1300
+ schemaFailures: validatedRecords.filter((record) => record.metadata?.exampleType === "schema_failure").length,
1301
+ goldTargets: validatedRecords.filter((record) => record.metadata?.exampleType === "gold_target").length,
1302
+ },
1303
+ ...(inputMetadata ?? {}),
1304
+ },
1305
+ };
1306
+ }
1307
+ async buildRunRecord(input) {
1308
+ const recordId = `dataset-${input.input.result.runId}`;
1309
+ const run = input.trace?.run;
1310
+ const inputRef = await putGatewayDatasetObject(input.objectStore, {
1311
+ scope: input.scope,
1312
+ ownerType: "dataset_record",
1313
+ ownerId: recordId,
1314
+ kind: "dataset",
1315
+ payload: {
1316
+ requestId: input.input.request.id,
1317
+ query: input.input.request.query,
1318
+ mode: input.input.request.mode,
1319
+ conversationId: input.input.request.conversation?.id,
1320
+ traceRunId: run?.runId ?? input.input.result.runId,
1321
+ traceStatus: run?.status ?? input.input.result.status,
1322
+ taskCount: input.trace?.tasks.length ?? input.input.result.trace.iterations,
1323
+ modelCallCount: input.modelCalls.length,
1324
+ },
1325
+ privacyFlags: input.privacyFlags,
1326
+ metadata: { part: "gateway_run_input" },
1327
+ });
1328
+ const outputRef = await putGatewayDatasetObject(input.objectStore, {
1329
+ scope: input.scope,
1330
+ ownerType: "dataset_record",
1331
+ ownerId: recordId,
1332
+ kind: "dataset",
1333
+ payload: {
1334
+ status: input.input.result.status,
1335
+ answer: input.input.result.answer,
1336
+ confidence: input.input.result.confidence,
1337
+ sources: input.input.result.sources,
1338
+ warnings: input.input.result.trace.warnings,
1339
+ errors: input.input.result.trace.errors,
1340
+ finalModel: input.input.result.finalModel,
1341
+ telemetry: input.input.result.telemetry,
1342
+ },
1343
+ privacyFlags: input.privacyFlags,
1344
+ metadata: { part: "gateway_run_output" },
1345
+ });
1346
+ const evidenceRefs = input.input.result.evidence.length > 0
1347
+ ? [
1348
+ await putGatewayDatasetObject(input.objectStore, {
1349
+ scope: input.scope,
1350
+ ownerType: "dataset_record",
1351
+ ownerId: recordId,
1352
+ kind: "evidence",
1353
+ payload: {
1354
+ evidence: input.input.result.evidence,
1355
+ contextPack: contextPackSummary(input.input.result.contextPack),
1356
+ },
1357
+ privacyFlags: input.privacyFlags,
1358
+ metadata: { part: "gateway_run_evidence" },
1359
+ }),
1360
+ ]
1361
+ : undefined;
1362
+ return validateDatasetRecordOrThrow({
1363
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
1364
+ recordType: "dataset_record",
1365
+ recordId,
1366
+ datasetKind: input.input.datasetKind ?? "gateway_answer",
1367
+ createdAt: input.now().toISOString(),
1368
+ sourceGatewayRecordId: input.input.result.runId,
1369
+ inputRef,
1370
+ outputRef,
1371
+ evidenceRefs,
1372
+ quality: {
1373
+ score: confidenceScore(input.input.result.confidence),
1374
+ labels: collectGatewayRunLabels(input.input.result),
1375
+ reviewed: false,
1376
+ },
1377
+ privacy: input.privacy,
1378
+ metadata: {
1379
+ collectionMode: "gateway_trace_collector",
1380
+ exampleType: "gateway_run",
1381
+ gatewayStatus: input.input.result.status,
1382
+ finalModelTier: input.input.result.finalModel?.tier,
1383
+ finalModelAgentSlug: input.input.result.finalModel?.agentSlug,
1384
+ traceAvailable: Boolean(input.trace),
1385
+ modelCallCount: input.modelCalls.length,
1386
+ telemetry: input.input.result.telemetry,
1387
+ ...(sanitizeGatewayDatasetMetadata(input.input.metadata) ?? {}),
1388
+ },
1389
+ });
1390
+ }
1391
+ async buildFinalAnswerRecord(input) {
1392
+ const recordId = `dataset-${input.input.result.runId}-final-answer`;
1393
+ const inputRef = await putGatewayDatasetObject(input.objectStore, {
1394
+ scope: input.scope,
1395
+ ownerType: "final_answer_example",
1396
+ ownerId: recordId,
1397
+ kind: "dataset",
1398
+ payload: {
1399
+ requestId: input.input.request.id,
1400
+ query: input.input.request.query,
1401
+ responsePolicy: input.input.request.response,
1402
+ contextPack: contextPackSummary(input.input.result.contextPack),
1403
+ sourceEvidenceIds: input.input.result.sources.map((source) => source.evidenceId),
1404
+ finalModel: input.input.result.finalModel,
1405
+ },
1406
+ privacyFlags: input.privacyFlags,
1407
+ metadata: { part: "final_answer_input" },
1408
+ });
1409
+ const outputRef = await putGatewayDatasetObject(input.objectStore, {
1410
+ scope: input.scope,
1411
+ ownerType: "final_answer_example",
1412
+ ownerId: recordId,
1413
+ kind: "dataset",
1414
+ payload: {
1415
+ status: input.input.result.status,
1416
+ answer: input.input.result.answer,
1417
+ confidence: input.input.result.confidence,
1418
+ sources: input.input.result.sources,
1419
+ warnings: input.input.result.trace.warnings,
1420
+ errors: input.input.result.trace.errors,
1421
+ },
1422
+ privacyFlags: input.privacyFlags,
1423
+ metadata: { part: "final_answer_output" },
1424
+ });
1425
+ return validateDatasetRecordOrThrow({
1426
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
1427
+ recordType: "dataset_record",
1428
+ recordId,
1429
+ datasetKind: "gateway_answer",
1430
+ createdAt: input.now().toISOString(),
1431
+ sourceGatewayRecordId: input.input.result.runId,
1432
+ inputRef,
1433
+ outputRef,
1434
+ evidenceRefs: input.input.result.evidence.length > 0
1435
+ ? [
1436
+ await putGatewayDatasetObject(input.objectStore, {
1437
+ scope: input.scope,
1438
+ ownerType: "final_answer_example",
1439
+ ownerId: recordId,
1440
+ kind: "evidence",
1441
+ payload: { evidence: input.input.result.evidence },
1442
+ privacyFlags: input.privacyFlags,
1443
+ metadata: { part: "final_answer_evidence" },
1444
+ }),
1445
+ ]
1446
+ : undefined,
1447
+ quality: {
1448
+ score: confidenceScore(input.input.result.confidence),
1449
+ labels: uniqueLabels([
1450
+ "auto:final_answer",
1451
+ `auto:status:${input.input.result.status}`,
1452
+ `auto:confidence:${input.input.result.confidence}`,
1453
+ ]),
1454
+ reviewed: false,
1455
+ },
1456
+ privacy: input.privacy,
1457
+ metadata: {
1458
+ collectionMode: "gateway_trace_collector",
1459
+ exampleType: "final_answer",
1460
+ finalModelTier: input.input.result.finalModel?.tier,
1461
+ finalModelAgentSlug: input.input.result.finalModel?.agentSlug,
1462
+ sourceCount: input.input.result.sources.length,
1463
+ },
1464
+ });
1465
+ }
1466
+ async buildRagRetrievalRecord(input) {
1467
+ const recordId = `dataset-${input.input.result.runId}-rag-${safeDatasetIdPart(input.call.id || `${input.index + 1}-${input.call.tool}`)}`;
1468
+ const inputRef = await putGatewayDatasetObject(input.objectStore, {
1469
+ scope: input.scope,
1470
+ ownerType: "rag_retrieval_event",
1471
+ ownerId: recordId,
1472
+ kind: "dataset",
1473
+ payload: {
1474
+ toolCallId: input.call.id,
1475
+ taskId: input.call.taskId,
1476
+ tool: input.call.tool,
1477
+ args: input.call.args,
1478
+ },
1479
+ privacyFlags: input.privacyFlags,
1480
+ metadata: { part: "rag_retrieval_input", toolCallId: input.call.id },
1481
+ });
1482
+ const outputRef = await putGatewayDatasetObject(input.objectStore, {
1483
+ scope: input.scope,
1484
+ ownerType: "rag_retrieval_event",
1485
+ ownerId: recordId,
1486
+ kind: "dataset",
1487
+ payload: {
1488
+ toolCallId: input.call.id,
1489
+ status: input.call.status,
1490
+ result: input.call.result,
1491
+ errorCode: input.call.errorCode,
1492
+ errorMessage: input.call.errorMessage,
1493
+ latencyMs: input.call.latencyMs,
1494
+ },
1495
+ privacyFlags: input.privacyFlags,
1496
+ metadata: { part: "rag_retrieval_output", toolCallId: input.call.id },
1497
+ });
1498
+ return validateDatasetRecordOrThrow({
1499
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
1500
+ recordType: "dataset_record",
1501
+ recordId,
1502
+ datasetKind: "tool_trace",
1503
+ createdAt: input.now().toISOString(),
1504
+ sourceGatewayRecordId: input.input.result.runId,
1505
+ inputRef,
1506
+ outputRef,
1507
+ quality: {
1508
+ score: input.call.status === "success" ? 0.6 : 0.1,
1509
+ labels: collectRagRetrievalLabels(input.call),
1510
+ reviewed: false,
1511
+ },
1512
+ privacy: input.privacy,
1513
+ metadata: {
1514
+ collectionMode: "gateway_trace_collector",
1515
+ exampleType: "rag_retrieval",
1516
+ toolCallId: input.call.id,
1517
+ taskId: input.call.taskId,
1518
+ tool: input.call.tool,
1519
+ status: input.call.status,
1520
+ },
1521
+ });
1522
+ }
1523
+ async buildToolDecisionRecord(input) {
1524
+ const recordId = `dataset-${input.input.result.runId}-tool-decision-${safeDatasetIdPart(input.decision.id || `${input.index + 1}-${input.decision.tool}`)}`;
1525
+ const inputRef = await putGatewayDatasetObject(input.objectStore, {
1526
+ scope: input.scope,
1527
+ ownerType: "tool_decision_example",
1528
+ ownerId: recordId,
1529
+ kind: "dataset",
1530
+ payload: {
1531
+ tool: input.decision.tool,
1532
+ taskId: input.decision.taskId,
1533
+ args: input.decision.args,
1534
+ allowedTools: input.decision.allowedTools,
1535
+ removedTools: input.decision.removedTools,
1536
+ source: input.decision.source,
1537
+ },
1538
+ privacyFlags: input.privacyFlags,
1539
+ metadata: { part: "tool_decision_input", decisionId: input.decision.id },
1540
+ });
1541
+ const outputRef = await putGatewayDatasetObject(input.objectStore, {
1542
+ scope: input.scope,
1543
+ ownerType: "tool_decision_example",
1544
+ ownerId: recordId,
1545
+ kind: "dataset",
1546
+ payload: {
1547
+ status: input.decision.status,
1548
+ result: input.decision.result,
1549
+ errorCode: input.decision.errorCode,
1550
+ errorMessage: input.decision.errorMessage,
1551
+ reason: input.decision.reason,
1552
+ latencyMs: input.decision.latencyMs,
1553
+ },
1554
+ privacyFlags: input.privacyFlags,
1555
+ metadata: { part: "tool_decision_output", decisionId: input.decision.id },
1556
+ });
1557
+ return validateDatasetRecordOrThrow({
1558
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
1559
+ recordType: "dataset_record",
1560
+ recordId,
1561
+ datasetKind: "tool_trace",
1562
+ createdAt: input.now().toISOString(),
1563
+ sourceGatewayRecordId: input.input.result.runId,
1564
+ inputRef,
1565
+ outputRef,
1566
+ quality: {
1567
+ score: input.decision.status === "success" ? 0.55 : 0.05,
1568
+ labels: collectToolDecisionLabels(input.decision),
1569
+ reviewed: false,
1570
+ },
1571
+ privacy: input.privacy,
1572
+ metadata: {
1573
+ collectionMode: "gateway_trace_collector",
1574
+ exampleType: "tool_decision",
1575
+ decisionSource: input.decision.source,
1576
+ tool: input.decision.tool,
1577
+ taskId: input.decision.taskId,
1578
+ status: input.decision.status,
1579
+ reason: input.decision.reason,
1580
+ errorCode: input.decision.errorCode,
1581
+ ...(sanitizeGatewayDatasetMetadata(input.decision.metadata) ?? {}),
1582
+ },
1583
+ });
1584
+ }
1585
+ async buildEvidenceItemRecord(input) {
1586
+ const evidenceId = safeDatasetIdPart(input.evidenceItem.id || `evidence-${input.index + 1}`);
1587
+ const recordId = `dataset-${input.input.result.runId}-evidence-${evidenceId}`;
1588
+ const inputRef = await putGatewayDatasetObject(input.objectStore, {
1589
+ scope: input.scope,
1590
+ ownerType: "evidence_item_record",
1591
+ ownerId: recordId,
1592
+ kind: "evidence",
1593
+ payload: {
1594
+ evidenceId: input.evidenceItem.id,
1595
+ taskId: input.evidenceItem.taskId,
1596
+ stageId: input.evidenceItem.stageId,
1597
+ sourceType: input.evidenceItem.sourceType,
1598
+ sourceId: input.evidenceItem.sourceId,
1599
+ sourceUri: input.evidenceItem.sourceUri,
1600
+ sourceTitle: input.evidenceItem.sourceTitle,
1601
+ usedTool: input.evidenceItem.usedTool,
1602
+ tenantScoped: input.evidenceItem.tenantScoped,
1603
+ },
1604
+ privacyFlags: input.privacyFlags,
1605
+ metadata: { part: "evidence_item_input", evidenceId: input.evidenceItem.id },
1606
+ });
1607
+ const outputRef = await putGatewayDatasetObject(input.objectStore, {
1608
+ scope: input.scope,
1609
+ ownerType: "evidence_item_record",
1610
+ ownerId: recordId,
1611
+ kind: "evidence",
1612
+ payload: {
1613
+ claim: input.evidenceItem.claim,
1614
+ summary: input.evidenceItem.summary,
1615
+ rawExcerpt: input.evidenceItem.rawExcerpt,
1616
+ rawPayloadRef: input.evidenceItem.rawPayloadRef,
1617
+ confidence: input.evidenceItem.confidence,
1618
+ relevance: input.evidenceItem.relevance,
1619
+ freshness: input.evidenceItem.freshness,
1620
+ metadata: input.evidenceItem.metadata,
1621
+ },
1622
+ privacyFlags: input.privacyFlags,
1623
+ metadata: { part: "evidence_item_output", evidenceId: input.evidenceItem.id },
1624
+ });
1625
+ return validateDatasetRecordOrThrow({
1626
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
1627
+ recordType: "dataset_record",
1628
+ recordId,
1629
+ datasetKind: "evaluation",
1630
+ createdAt: input.now().toISOString(),
1631
+ sourceGatewayRecordId: input.input.result.runId,
1632
+ inputRef,
1633
+ outputRef,
1634
+ quality: {
1635
+ score: Math.max(0, Math.min(1, input.evidenceItem.confidence * input.evidenceItem.relevance)),
1636
+ labels: collectEvidenceLabels(input.evidenceItem),
1637
+ reviewed: false,
1638
+ },
1639
+ privacy: input.privacy,
1640
+ metadata: {
1641
+ collectionMode: "gateway_trace_collector",
1642
+ exampleType: "evidence_item",
1643
+ evidenceId: input.evidenceItem.id,
1644
+ sourceType: input.evidenceItem.sourceType,
1645
+ usedTool: input.evidenceItem.usedTool,
1646
+ tenantScoped: input.evidenceItem.tenantScoped,
1647
+ },
1648
+ });
1649
+ }
1650
+ async buildContextPackRecord(input) {
1651
+ const contextId = safeDatasetIdPart(input.contextPack.id || `context-${input.index + 1}`);
1652
+ const recordId = `dataset-${input.input.result.runId}-context-${contextId}`;
1653
+ const inputRef = await putGatewayDatasetObject(input.objectStore, {
1654
+ scope: input.scope,
1655
+ ownerType: "context_pack_record",
1656
+ ownerId: recordId,
1657
+ kind: "dataset",
1658
+ payload: {
1659
+ contextPackId: input.contextPack.id,
1660
+ originalQuery: input.contextPack.originalQuery,
1661
+ decisionFactIds: input.contextPack.decisionFacts.map((item) => item.id),
1662
+ selectedExcerpts: input.contextPack.selectedExcerpts,
1663
+ },
1664
+ privacyFlags: input.privacyFlags,
1665
+ metadata: { part: "context_pack_input", contextPackId: input.contextPack.id },
1666
+ });
1667
+ const outputRef = await putGatewayDatasetObject(input.objectStore, {
1668
+ scope: input.scope,
1669
+ ownerType: "context_pack_record",
1670
+ ownerId: recordId,
1671
+ kind: "dataset",
1672
+ payload: {
1673
+ tokenEstimate: input.contextPack.tokenEstimate,
1674
+ decisionFacts: input.contextPack.decisionFacts,
1675
+ contradictions: input.contextPack.contradictions,
1676
+ missingInformation: input.contextPack.missingInformation,
1677
+ toolSummary: input.contextPack.toolSummary,
1678
+ metadata: input.contextPack.metadata,
1679
+ },
1680
+ privacyFlags: input.privacyFlags,
1681
+ metadata: { part: "context_pack_output", contextPackId: input.contextPack.id },
1682
+ });
1683
+ return validateDatasetRecordOrThrow({
1684
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
1685
+ recordType: "dataset_record",
1686
+ recordId,
1687
+ datasetKind: "evaluation",
1688
+ createdAt: input.now().toISOString(),
1689
+ sourceGatewayRecordId: input.input.result.runId,
1690
+ inputRef,
1691
+ outputRef,
1692
+ quality: {
1693
+ score: input.contextPack.missingInformation.length === 0 ? 0.65 : 0.35,
1694
+ labels: collectContextPackLabels(input.contextPack),
1695
+ reviewed: false,
1696
+ },
1697
+ privacy: input.privacy,
1698
+ metadata: {
1699
+ collectionMode: "gateway_trace_collector",
1700
+ exampleType: "context_pack",
1701
+ contextPackId: input.contextPack.id,
1702
+ tokenEstimate: input.contextPack.tokenEstimate,
1703
+ decisionFactCount: input.contextPack.decisionFacts.length,
1704
+ contradictionCount: input.contextPack.contradictions.length,
1705
+ missingInformationCount: input.contextPack.missingInformation.length,
1706
+ },
1707
+ });
1708
+ }
1709
+ async buildArtifactRecord(input) {
1710
+ const artifactId = safeDatasetIdPart(input.artifact.id || `artifact-${input.index + 1}`);
1711
+ const recordId = `dataset-${input.input.result.runId}-artifact-${artifactId}`;
1712
+ const inputRef = await putGatewayDatasetObject(input.objectStore, {
1713
+ scope: input.scope,
1714
+ ownerType: "artifact_record",
1715
+ ownerId: recordId,
1716
+ kind: "artifact",
1717
+ payload: {
1718
+ artifactId: input.artifact.id,
1719
+ taskId: input.artifact.taskId,
1720
+ type: input.artifact.type,
1721
+ model: input.artifact.model,
1722
+ prompt: input.artifact.prompt,
1723
+ },
1724
+ privacyFlags: input.privacyFlags,
1725
+ metadata: { part: "artifact_input", artifactId: input.artifact.id },
1726
+ });
1727
+ const outputRef = await putGatewayDatasetObject(input.objectStore, {
1728
+ scope: input.scope,
1729
+ ownerType: "artifact_record",
1730
+ ownerId: recordId,
1731
+ kind: "artifact",
1732
+ payload: {
1733
+ artifactId: input.artifact.id,
1734
+ type: input.artifact.type,
1735
+ uri: input.artifact.uri,
1736
+ path: input.artifact.path,
1737
+ createdAt: input.artifact.createdAt,
1738
+ metadata: artifactMetadataRefOnly(input.artifact.metadata),
1739
+ },
1740
+ privacyFlags: input.privacyFlags,
1741
+ metadata: { part: "artifact_ref", artifactId: input.artifact.id, objectRefOnly: true },
1742
+ });
1743
+ return validateDatasetRecordOrThrow({
1744
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
1745
+ recordType: "dataset_record",
1746
+ recordId,
1747
+ datasetKind: "tool_trace",
1748
+ createdAt: input.now().toISOString(),
1749
+ sourceGatewayRecordId: input.input.result.runId,
1750
+ inputRef,
1751
+ outputRef,
1752
+ quality: {
1753
+ score: input.artifact.uri || input.artifact.path ? 0.5 : 0.15,
1754
+ labels: collectArtifactLabels(input.artifact),
1755
+ reviewed: false,
1756
+ },
1757
+ privacy: input.privacy,
1758
+ metadata: {
1759
+ collectionMode: "gateway_trace_collector",
1760
+ exampleType: "artifact",
1761
+ artifactId: input.artifact.id,
1762
+ artifactType: input.artifact.type,
1763
+ taskId: input.artifact.taskId,
1764
+ objectRefOnly: true,
1765
+ },
1766
+ });
1767
+ }
1768
+ async buildPolicyEventRecord(input) {
1769
+ const eventId = safeDatasetIdPart(input.event.id || `policy-${input.index + 1}`);
1770
+ const recordId = `dataset-${input.input.result.runId}-policy-${eventId}`;
1771
+ const inputRef = await putGatewayDatasetObject(input.objectStore, {
1772
+ scope: input.scope,
1773
+ ownerType: "policy_event_record",
1774
+ ownerId: recordId,
1775
+ kind: "dataset",
1776
+ payload: {
1777
+ eventType: input.event.eventType,
1778
+ tool: input.event.tool,
1779
+ taskId: input.event.taskId,
1780
+ status: input.event.status,
1781
+ },
1782
+ privacyFlags: input.privacyFlags,
1783
+ metadata: { part: "policy_event_input", policyEventId: input.event.id },
1784
+ });
1785
+ const outputRef = await putGatewayDatasetObject(input.objectStore, {
1786
+ scope: input.scope,
1787
+ ownerType: "policy_event_record",
1788
+ ownerId: recordId,
1789
+ kind: "dataset",
1790
+ payload: {
1791
+ reason: input.event.reason,
1792
+ errorCode: input.event.errorCode,
1793
+ errorMessage: input.event.errorMessage,
1794
+ metadata: input.event.metadata,
1795
+ },
1796
+ privacyFlags: input.privacyFlags,
1797
+ metadata: { part: "policy_event_output", policyEventId: input.event.id },
1798
+ });
1799
+ return validateDatasetRecordOrThrow({
1800
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
1801
+ recordType: "dataset_record",
1802
+ recordId,
1803
+ datasetKind: "tool_trace",
1804
+ createdAt: input.now().toISOString(),
1805
+ sourceGatewayRecordId: input.input.result.runId,
1806
+ inputRef,
1807
+ outputRef,
1808
+ quality: {
1809
+ score: 0,
1810
+ labels: collectPolicyEventLabels(input.event),
1811
+ reviewed: false,
1812
+ },
1813
+ privacy: input.privacy,
1814
+ metadata: {
1815
+ collectionMode: "gateway_trace_collector",
1816
+ exampleType: "policy_event",
1817
+ policyEventType: input.event.eventType,
1818
+ reason: input.event.reason,
1819
+ tool: input.event.tool,
1820
+ taskId: input.event.taskId,
1821
+ errorCode: input.event.errorCode,
1822
+ },
1823
+ });
1824
+ }
1825
+ async buildModelCallRecord(input) {
1826
+ const recordId = modelRecordId(input.input.result.runId, input.call, input.index);
1827
+ const inputRef = await putGatewayDatasetObject(input.objectStore, {
1828
+ scope: input.scope,
1829
+ ownerType: "model_stage_example",
1830
+ ownerId: recordId,
1831
+ kind: "dataset",
1832
+ payload: {
1833
+ modelCallId: input.call.id,
1834
+ role: input.call.role,
1835
+ taskId: input.call.taskId,
1836
+ agentSlug: input.call.agentSlug,
1837
+ model: input.call.model,
1838
+ provider: input.call.provider,
1839
+ input: input.call.input,
1840
+ },
1841
+ privacyFlags: input.privacyFlags,
1842
+ metadata: { part: "model_stage_input", modelCallId: input.call.id },
1843
+ });
1844
+ const outputRef = await putGatewayDatasetObject(input.objectStore, {
1845
+ scope: input.scope,
1846
+ ownerType: "model_stage_example",
1847
+ ownerId: recordId,
1848
+ kind: "dataset",
1849
+ payload: {
1850
+ modelCallId: input.call.id,
1851
+ status: input.call.status,
1852
+ output: input.call.output,
1853
+ errorCode: input.call.errorCode,
1854
+ errorMessage: input.call.errorMessage,
1855
+ latencyMs: input.call.latencyMs,
1856
+ usage: metadataRecord(input.call.metadata, "usage"),
1857
+ },
1858
+ privacyFlags: input.privacyFlags,
1859
+ metadata: { part: "model_stage_output", modelCallId: input.call.id },
1860
+ });
1861
+ return validateDatasetRecordOrThrow({
1862
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
1863
+ recordType: "dataset_record",
1864
+ recordId,
1865
+ datasetKind: "model_call",
1866
+ createdAt: input.now().toISOString(),
1867
+ sourceGatewayRecordId: input.input.result.runId,
1868
+ inputRef,
1869
+ outputRef,
1870
+ quality: {
1871
+ score: modelCallScore(input.call),
1872
+ labels: collectModelCallLabels(input.call),
1873
+ reviewed: false,
1874
+ },
1875
+ privacy: input.privacy,
1876
+ metadata: {
1877
+ collectionMode: "gateway_trace_collector",
1878
+ exampleType: "model_stage",
1879
+ modelCallId: input.call.id,
1880
+ taskId: input.call.taskId,
1881
+ role: input.call.role,
1882
+ status: input.call.status,
1883
+ repairAttempts: modelCallRepairAttempts(input.call),
1884
+ schemaFailureRecordId: input.schemaFailureRecordId,
1885
+ },
1886
+ });
1887
+ }
1888
+ async buildSchemaFailureRecord(input) {
1889
+ const recordId = schemaFailureRecordId(input.input.result.runId, input.call, input.index);
1890
+ const inputRef = await putGatewayDatasetObject(input.objectStore, {
1891
+ scope: input.scope,
1892
+ ownerType: "schema_failure_example",
1893
+ ownerId: recordId,
1894
+ kind: "dataset",
1895
+ payload: {
1896
+ modelCallId: input.call.id,
1897
+ role: input.call.role,
1898
+ taskId: input.call.taskId,
1899
+ failedInput: input.call.input,
1900
+ failedOutput: input.call.output,
1901
+ },
1902
+ privacyFlags: input.privacyFlags,
1903
+ metadata: { part: "schema_failure_input", modelCallId: input.call.id },
1904
+ });
1905
+ const outputRef = await putGatewayDatasetObject(input.objectStore, {
1906
+ scope: input.scope,
1907
+ ownerType: "schema_failure_example",
1908
+ ownerId: recordId,
1909
+ kind: "dataset",
1910
+ payload: {
1911
+ status: input.call.status,
1912
+ errorCode: input.call.errorCode,
1913
+ errorMessage: input.call.errorMessage,
1914
+ repairAttempts: modelCallRepairAttempts(input.call),
1915
+ correctedByModelCallId: input.corrected?.id,
1916
+ correctedOutput: input.corrected?.output,
1917
+ },
1918
+ privacyFlags: input.privacyFlags,
1919
+ metadata: { part: "schema_failure_output", modelCallId: input.call.id },
1920
+ });
1921
+ return validateDatasetRecordOrThrow({
1922
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
1923
+ recordType: "dataset_record",
1924
+ recordId,
1925
+ datasetKind: "evaluation",
1926
+ createdAt: input.now().toISOString(),
1927
+ sourceGatewayRecordId: input.input.result.runId,
1928
+ inputRef,
1929
+ outputRef,
1930
+ quality: {
1931
+ score: 0,
1932
+ labels: collectSchemaFailureLabels(input.call),
1933
+ reviewed: false,
1934
+ },
1935
+ privacy: input.privacy,
1936
+ metadata: {
1937
+ collectionMode: "gateway_trace_collector",
1938
+ exampleType: "schema_failure",
1939
+ failedAttemptModelCallId: input.call.id,
1940
+ failedAttemptRecordId: input.failedAttemptRecordId,
1941
+ correctedByModelCallId: input.corrected?.id,
1942
+ correctedByRecordId: input.correctedRecordId,
1943
+ correctedInSameCall: input.corrected?.id === input.call.id,
1944
+ },
1945
+ });
1946
+ }
1947
+ async buildGoldTargetRecord(input) {
1948
+ const targetId = safeDatasetIdPart(input.target.id ?? `${input.target.kind}-${input.index + 1}`);
1949
+ const recordId = `dataset-${input.input.result.runId}-gold-${targetId}`;
1950
+ const sourceModelRecordId = input.target.sourceModelCallId
1951
+ ? input.modelRecordIds.get(input.target.sourceModelCallId)
1952
+ : undefined;
1953
+ const failedAttemptRecordId = input.target.failedAttemptRecordId
1954
+ ?? (input.target.failedAttemptModelCallId
1955
+ ? input.modelRecordIds.get(input.target.failedAttemptModelCallId)
1956
+ : undefined);
1957
+ const inputRef = await putGatewayDatasetObject(input.objectStore, {
1958
+ scope: input.scope,
1959
+ ownerType: "gold_target",
1960
+ ownerId: recordId,
1961
+ kind: "dataset",
1962
+ payload: input.target.input ?? {
1963
+ query: input.input.request.query,
1964
+ sourceRecordId: input.target.sourceRecordId ?? sourceModelRecordId ?? `dataset-${input.input.result.runId}`,
1965
+ },
1966
+ privacyFlags: input.privacyFlags,
1967
+ metadata: { part: "gold_target_input", goldTargetKind: input.target.kind },
1968
+ });
1969
+ const outputRef = await putGatewayDatasetObject(input.objectStore, {
1970
+ scope: input.scope,
1971
+ ownerType: "gold_target",
1972
+ ownerId: recordId,
1973
+ kind: "dataset",
1974
+ payload: input.target.target ?? {
1975
+ answer: input.input.result.answer,
1976
+ status: input.input.result.status,
1977
+ },
1978
+ privacyFlags: input.privacyFlags,
1979
+ metadata: { part: "gold_target_output", goldTargetKind: input.target.kind },
1980
+ });
1981
+ return validateDatasetRecordOrThrow({
1982
+ schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
1983
+ recordType: "dataset_record",
1984
+ recordId,
1985
+ datasetKind: "curated_example",
1986
+ createdAt: input.now().toISOString(),
1987
+ sourceGatewayRecordId: input.input.result.runId,
1988
+ inputRef,
1989
+ outputRef,
1990
+ quality: {
1991
+ score: input.target.score ?? (input.target.kind === "accepted" ? 0.85 : 0.95),
1992
+ labels: collectGoldTargetLabels(input.target),
1993
+ reviewed: input.target.kind === "reviewed",
1994
+ },
1995
+ privacy: input.privacy,
1996
+ metadata: {
1997
+ collectionMode: "gateway_trace_collector",
1998
+ exampleType: "gold_target",
1999
+ goldTargetKind: input.target.kind,
2000
+ sourceRecordId: input.target.sourceRecordId,
2001
+ sourceModelCallId: input.target.sourceModelCallId,
2002
+ sourceModelRecordId,
2003
+ failedAttemptModelCallId: input.target.failedAttemptModelCallId,
2004
+ failedAttemptRecordId,
2005
+ reviewerId: input.target.reviewerId,
2006
+ reasons: input.target.reasons,
2007
+ deletionGroupId: outputRef.deletionGroupId,
2008
+ linkedDeletionGroupIds: uniqueStrings([
2009
+ inputRef.deletionGroupId,
2010
+ outputRef.deletionGroupId,
2011
+ ]),
2012
+ ...(input.target.metadata ?? {}),
2013
+ },
2014
+ });
2015
+ }
2016
+ }
2017
+ export const createGatewayDatasetCollector = () => new GatewayDatasetCollector();
2018
+ export const buildGatewayDatasetCollectInputFromGatewayResult = async (input) => {
2019
+ return new GatewayDatasetCollector().buildCollectInput(input);
2020
+ };
2021
+ export const collectGatewayDatasetResult = async (input) => {
2022
+ if (input.enabled === false) {
2023
+ return { accepted: true, status: "skipped", recordCount: 0 };
2024
+ }
2025
+ const collectInput = await buildGatewayDatasetCollectInputFromGatewayResult(input);
2026
+ try {
2027
+ return await input.store.collect(collectInput);
2028
+ }
2029
+ catch (error) {
2030
+ if (!input.fallbackStore)
2031
+ throw error;
2032
+ const fallback = await input.fallbackStore.collect(collectInput);
2033
+ return {
2034
+ ...fallback,
2035
+ fallbackUsed: true,
2036
+ errors: [...(fallback.errors ?? []), errorMessage(error)],
2037
+ };
2038
+ }
2039
+ };
2040
+ export const collectGatewayDatasetResultNonBlocking = (input) => {
2041
+ if (input.enabled === false) {
2042
+ return { accepted: true, status: "skipped", recordCount: 0 };
2043
+ }
2044
+ const queued = {
2045
+ accepted: true,
2046
+ status: "queued",
2047
+ recordCount: 1,
2048
+ idempotencyKey: input.idempotencyKey,
2049
+ };
2050
+ const run = async () => {
2051
+ try {
2052
+ const result = await collectGatewayDatasetResult(input);
2053
+ input.onResult?.(result);
2054
+ }
2055
+ catch (error) {
2056
+ input.onError?.(error);
2057
+ }
2058
+ };
2059
+ if (typeof queueMicrotask === "function") {
2060
+ queueMicrotask(() => {
2061
+ void run();
2062
+ });
2063
+ }
2064
+ else {
2065
+ setTimeout(() => {
2066
+ void run();
2067
+ }, 0);
2068
+ }
2069
+ return queued;
2070
+ };