@mcoda/codali 0.1.88 → 0.1.89

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 (68) hide show
  1. package/dist/cli/EvalCommand.d.ts +8 -0
  2. package/dist/cli/EvalCommand.d.ts.map +1 -1
  3. package/dist/cli/EvalCommand.js +93 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +1 -0
  6. package/dist/docdex/DocdexClient.d.ts +8 -1
  7. package/dist/docdex/DocdexClient.d.ts.map +1 -1
  8. package/dist/docdex/DocdexClient.js +126 -33
  9. package/dist/eval/CodaliGatewayLiveHarness.d.ts +169 -0
  10. package/dist/eval/CodaliGatewayLiveHarness.d.ts.map +1 -0
  11. package/dist/eval/CodaliGatewayLiveHarness.js +824 -0
  12. package/dist/eval/GatewayEvalSuite.d.ts +202 -0
  13. package/dist/eval/GatewayEvalSuite.d.ts.map +1 -0
  14. package/dist/eval/GatewayEvalSuite.js +673 -0
  15. package/dist/gateway/AgentTierResolver.d.ts +74 -0
  16. package/dist/gateway/AgentTierResolver.d.ts.map +1 -0
  17. package/dist/gateway/AgentTierResolver.js +576 -0
  18. package/dist/gateway/AppToolGatewayDispatcher.d.ts +88 -0
  19. package/dist/gateway/AppToolGatewayDispatcher.d.ts.map +1 -0
  20. package/dist/gateway/AppToolGatewayDispatcher.js +381 -0
  21. package/dist/gateway/CodaliGateway.d.ts +73 -0
  22. package/dist/gateway/CodaliGateway.d.ts.map +1 -0
  23. package/dist/gateway/CodaliGateway.js +824 -0
  24. package/dist/gateway/CodaliGatewaySchemas.d.ts +21 -0
  25. package/dist/gateway/CodaliGatewaySchemas.d.ts.map +1 -0
  26. package/dist/gateway/CodaliGatewaySchemas.js +874 -0
  27. package/dist/gateway/CodaliGatewayStore.d.ts +157 -0
  28. package/dist/gateway/CodaliGatewayStore.d.ts.map +1 -0
  29. package/dist/gateway/CodaliGatewayStore.js +206 -0
  30. package/dist/gateway/CodaliGatewayTypes.d.ts +336 -0
  31. package/dist/gateway/CodaliGatewayTypes.d.ts.map +1 -0
  32. package/dist/gateway/CodaliGatewayTypes.js +1 -0
  33. package/dist/gateway/ContextPackBuilder.d.ts +43 -0
  34. package/dist/gateway/ContextPackBuilder.d.ts.map +1 -0
  35. package/dist/gateway/ContextPackBuilder.js +317 -0
  36. package/dist/gateway/EvidenceNormalizer.d.ts +42 -0
  37. package/dist/gateway/EvidenceNormalizer.d.ts.map +1 -0
  38. package/dist/gateway/EvidenceNormalizer.js +488 -0
  39. package/dist/gateway/GatewayPlanner.d.ts +195 -0
  40. package/dist/gateway/GatewayPlanner.d.ts.map +1 -0
  41. package/dist/gateway/GatewayPlanner.js +379 -0
  42. package/dist/gateway/GatewayPolicyCompiler.d.ts +30 -0
  43. package/dist/gateway/GatewayPolicyCompiler.d.ts.map +1 -0
  44. package/dist/gateway/GatewayPolicyCompiler.js +114 -0
  45. package/dist/gateway/GatewaySecurityPolicy.d.ts +14 -0
  46. package/dist/gateway/GatewaySecurityPolicy.d.ts.map +1 -0
  47. package/dist/gateway/GatewaySecurityPolicy.js +350 -0
  48. package/dist/gateway/GatewayStateMachine.d.ts +165 -0
  49. package/dist/gateway/GatewayStateMachine.d.ts.map +1 -0
  50. package/dist/gateway/GatewayStateMachine.js +790 -0
  51. package/dist/gateway/GatewayTraceReplay.d.ts +120 -0
  52. package/dist/gateway/GatewayTraceReplay.d.ts.map +1 -0
  53. package/dist/gateway/GatewayTraceReplay.js +273 -0
  54. package/dist/gateway/ToolCapabilityCompiler.d.ts +50 -0
  55. package/dist/gateway/ToolCapabilityCompiler.d.ts.map +1 -0
  56. package/dist/gateway/ToolCapabilityCompiler.js +442 -0
  57. package/dist/index.d.ts +30 -0
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +15 -0
  60. package/dist/runtime/CodaliRuntime.d.ts +7 -0
  61. package/dist/runtime/CodaliRuntime.d.ts.map +1 -1
  62. package/dist/runtime/CodaliRuntime.js +98 -54
  63. package/dist/tools/ToolRegistry.d.ts.map +1 -1
  64. package/dist/tools/ToolRegistry.js +4 -0
  65. package/dist/tools/ToolTypes.d.ts +1 -1
  66. package/dist/tools/ToolTypes.d.ts.map +1 -1
  67. package/dist/tools/ToolTypes.js +5 -1
  68. package/package.json +3 -3
@@ -0,0 +1,874 @@
1
+ const GATEWAY_MODES = [
2
+ "fast",
3
+ "balanced",
4
+ "deep",
5
+ "cheap",
6
+ "image",
7
+ ];
8
+ const RESPONSE_FORMATS = [
9
+ "text",
10
+ "json",
11
+ "json_schema",
12
+ ];
13
+ const FRESHNESS_VALUES = [
14
+ "fresh",
15
+ "recent",
16
+ "stale",
17
+ "unknown",
18
+ ];
19
+ const DEFAULT_POLICY_BUDGETS = {
20
+ maxIterations: 3,
21
+ maxRuntimeMs: 90000,
22
+ maxToolCalls: 20,
23
+ maxModelCalls: 10,
24
+ maxEvidenceItems: 80,
25
+ maxImageArtifacts: 0,
26
+ maxContextPackTokens: 20000,
27
+ };
28
+ export const isCodaliGatewayValidationOk = (result) => result.ok;
29
+ export const validateCodaliGatewayRequest = (input) => {
30
+ const bag = { issues: [] };
31
+ const record = requireRecord(input, "$", bag);
32
+ if (!record) {
33
+ return fail(bag);
34
+ }
35
+ const query = readRequiredNonEmptyString(record, ["query"], "$.query", bag);
36
+ const policyValue = readAlias(record, ["policy"]);
37
+ const policy = validateCodaliGatewayPolicy(policyValue);
38
+ if (!policy.ok) {
39
+ addNestedIssues(bag, "$.policy", policy.issues);
40
+ }
41
+ const mode = readOptionalEnum(record, ["mode"], GATEWAY_MODES, "$.mode", bag, "balanced");
42
+ const response = normalizeResponsePolicy(readAlias(record, ["response"]), bag);
43
+ const agentPolicy = normalizeAgentTierPolicy(readAlias(record, ["agentPolicy", "agent_policy"]), bag);
44
+ if (!query || !policy.ok) {
45
+ return fail(bag);
46
+ }
47
+ const request = {
48
+ query,
49
+ mode,
50
+ policy: policy.value,
51
+ response,
52
+ agentPolicy,
53
+ };
54
+ copyOptionalString(record, request, "id", ["id"], "$.id", bag);
55
+ const product = normalizeStringObject(readAlias(record, ["product"]), ["name", "version", "surface"], "$.product", bag);
56
+ if (product) {
57
+ request.product = product;
58
+ }
59
+ const tenant = normalizeStringObject(readAlias(record, ["tenant"]), ["id", "slug", "realm"], "$.tenant", bag);
60
+ if (tenant) {
61
+ request.tenant = tenant;
62
+ }
63
+ const requester = normalizeStringObject(readAlias(record, ["requester"]), ["id", "email", "role", "locale"], "$.requester", bag);
64
+ if (requester) {
65
+ request.requester = requester;
66
+ }
67
+ const conversation = normalizeConversation(readAlias(record, ["conversation"]), bag);
68
+ if (conversation) {
69
+ request.conversation = conversation;
70
+ }
71
+ const docdex = readOptionalRecord(record, ["docdex"], "$.docdex", bag);
72
+ if (docdex) {
73
+ request.docdex = docdex;
74
+ }
75
+ const tools = readOptionalRecord(record, ["tools", "tool_manifest", "toolManifest"], "$.tools", bag);
76
+ if (tools) {
77
+ request.tools = tools;
78
+ }
79
+ const metadata = readOptionalRecord(record, ["metadata"], "$.metadata", bag);
80
+ if (metadata) {
81
+ request.metadata = metadata;
82
+ }
83
+ return bag.issues.length > 0 ? fail(bag) : ok(request);
84
+ };
85
+ export const validateCodaliGatewayPolicy = (input) => {
86
+ const bag = { issues: [] };
87
+ const record = requireRecord(input, "$", bag);
88
+ if (!record) {
89
+ return fail(bag);
90
+ }
91
+ const allowedTools = readOptionalStringArray(record, ["allowedTools", "allowed_tools"], "$.allowedTools", bag, []) ?? [];
92
+ const deniedTools = readOptionalStringArray(record, ["deniedTools", "denied_tools"], "$.deniedTools", bag, undefined);
93
+ const appVirtualTools = readOptionalStringArray(record, [
94
+ "appVirtualTools",
95
+ "app_virtual_tools",
96
+ "okacamVirtualTools",
97
+ "okacam_virtual_tools",
98
+ ], "$.appVirtualTools", bag, undefined);
99
+ requireFalse(record, ["allowWrites", "allow_writes"], "$.allowWrites", bag);
100
+ requireFalse(record, ["allowShell", "allow_shell"], "$.allowShell", bag);
101
+ requireFalse(record, ["allowDestructiveOperations", "allow_destructive_operations"], "$.allowDestructiveOperations", bag);
102
+ requireFalse(record, ["allowOutsideWorkspace", "allow_outside_workspace"], "$.allowOutsideWorkspace", bag);
103
+ const appToolContracts = readAppToolContracts(record, bag);
104
+ const appToolGateway = readAppToolGateway(record, bag);
105
+ const policy = {
106
+ allowedTools,
107
+ deniedTools,
108
+ appToolContracts,
109
+ appVirtualTools,
110
+ appToolGateway,
111
+ maxIterations: readPositiveInteger(record, ["maxIterations", "max_iterations"], "$.maxIterations", bag, DEFAULT_POLICY_BUDGETS.maxIterations),
112
+ maxRuntimeMs: readPositiveInteger(record, ["maxRuntimeMs", "max_runtime_ms"], "$.maxRuntimeMs", bag, DEFAULT_POLICY_BUDGETS.maxRuntimeMs),
113
+ maxToolCalls: readPositiveInteger(record, ["maxToolCalls", "max_tool_calls"], "$.maxToolCalls", bag, DEFAULT_POLICY_BUDGETS.maxToolCalls),
114
+ maxModelCalls: readPositiveInteger(record, ["maxModelCalls", "max_model_calls"], "$.maxModelCalls", bag, DEFAULT_POLICY_BUDGETS.maxModelCalls),
115
+ maxEvidenceItems: readPositiveInteger(record, ["maxEvidenceItems", "max_evidence_items"], "$.maxEvidenceItems", bag, DEFAULT_POLICY_BUDGETS.maxEvidenceItems),
116
+ maxImageArtifacts: readNonNegativeInteger(record, ["maxImageArtifacts", "max_image_artifacts"], "$.maxImageArtifacts", bag, DEFAULT_POLICY_BUDGETS.maxImageArtifacts),
117
+ maxContextPackTokens: readPositiveInteger(record, ["maxContextPackTokens", "max_context_pack_tokens"], "$.maxContextPackTokens", bag, DEFAULT_POLICY_BUDGETS.maxContextPackTokens),
118
+ allowWrites: false,
119
+ allowShell: false,
120
+ allowDestructiveOperations: false,
121
+ allowOutsideWorkspace: false,
122
+ requireFinalLargeModel: readOptionalBoolean(record, ["requireFinalLargeModel", "require_final_large_model"], "$.requireFinalLargeModel", bag, true) ?? true,
123
+ };
124
+ const allowDegradedFinalAnswer = readOptionalBoolean(record, ["allowDegradedFinalAnswer", "allow_degraded_final_answer"], "$.allowDegradedFinalAnswer", bag, undefined);
125
+ if (typeof allowDegradedFinalAnswer === "boolean") {
126
+ policy.allowDegradedFinalAnswer = allowDegradedFinalAnswer;
127
+ }
128
+ const allowImageWorker = readOptionalBoolean(record, ["allowImageWorker", "allow_image_worker"], "$.allowImageWorker", bag, undefined);
129
+ if (typeof allowImageWorker === "boolean") {
130
+ policy.allowImageWorker = allowImageWorker;
131
+ }
132
+ return bag.issues.length > 0 ? fail(bag) : ok(policy);
133
+ };
134
+ export const validateCodaliGatewayPlannerOutput = (input) => {
135
+ const bag = { issues: [] };
136
+ const record = requireRecord(input, "$", bag);
137
+ if (!record) {
138
+ return fail(bag);
139
+ }
140
+ const queryType = readRequiredNonEmptyString(record, ["queryType", "query_type"], "$.queryType", bag);
141
+ const subquestions = normalizeSubquestions(readAlias(record, ["subquestions", "sub_questions"]), "$.subquestions", bag);
142
+ const workerTasks = normalizeWorkerTasks(readAlias(record, ["workerTasks", "worker_tasks"]), "$.workerTasks", bag);
143
+ if (!queryType) {
144
+ return fail(bag);
145
+ }
146
+ const plannerOutput = {
147
+ queryType,
148
+ subquestions,
149
+ workerTasks,
150
+ };
151
+ copyOptionalString(record, plannerOutput, "runId", ["runId", "run_id"], "$.runId", bag);
152
+ copyOptionalString(record, plannerOutput, "summary", ["summary"], "$.summary", bag);
153
+ copyOptionalPositiveInteger(record, plannerOutput, "expectedEvidenceCount", ["expectedEvidenceCount", "expected_evidence_count"], "$.expectedEvidenceCount", bag);
154
+ copyOptionalPositiveInteger(record, plannerOutput, "maxIterations", ["maxIterations", "max_iterations"], "$.maxIterations", bag);
155
+ const requiresFinalLargeModel = readOptionalBoolean(record, ["requiresFinalLargeModel", "requires_final_large_model"], "$.requiresFinalLargeModel", bag, undefined);
156
+ if (typeof requiresFinalLargeModel === "boolean") {
157
+ plannerOutput.requiresFinalLargeModel = requiresFinalLargeModel;
158
+ }
159
+ const metadata = readOptionalRecord(record, ["metadata"], "$.metadata", bag);
160
+ if (metadata) {
161
+ plannerOutput.metadata = metadata;
162
+ }
163
+ return bag.issues.length > 0 ? fail(bag) : ok(plannerOutput);
164
+ };
165
+ export const validateCodaliGatewayWorkerTask = (input) => {
166
+ const bag = { issues: [] };
167
+ const task = normalizeWorkerTask(input, "$", bag);
168
+ if (!task) {
169
+ return fail(bag);
170
+ }
171
+ return bag.issues.length > 0 ? fail(bag) : ok(task);
172
+ };
173
+ export const validateCodaliEvidenceItem = (input) => {
174
+ const bag = { issues: [] };
175
+ const evidence = normalizeEvidenceItem(input, "$", bag);
176
+ if (!evidence) {
177
+ return fail(bag);
178
+ }
179
+ return bag.issues.length > 0 ? fail(bag) : ok(evidence);
180
+ };
181
+ export const validateCodaliGatewayVerifierOutput = (input) => {
182
+ const bag = { issues: [] };
183
+ const record = requireRecord(input, "$", bag);
184
+ if (!record) {
185
+ return fail(bag);
186
+ }
187
+ const passed = readRequiredBoolean(record, ["passed"], "$.passed", bag);
188
+ const confidence = readUnitNumber(record, ["confidence"], "$.confidence", bag);
189
+ const followUpTasks = normalizeWorkerTasks(readAlias(record, ["followUpTasks", "follow_up_tasks"]), "$.followUpTasks", bag, []);
190
+ if (typeof passed !== "boolean" || typeof confidence !== "number") {
191
+ return fail(bag);
192
+ }
193
+ const verifierOutput = {
194
+ passed,
195
+ confidence,
196
+ verifiedEvidenceIds: readOptionalStringArray(record, ["verifiedEvidenceIds", "verified_evidence_ids"], "$.verifiedEvidenceIds", bag, []) ?? [],
197
+ rejectedEvidenceIds: readOptionalStringArray(record, ["rejectedEvidenceIds", "rejected_evidence_ids"], "$.rejectedEvidenceIds", bag, []) ?? [],
198
+ issues: normalizeVerifierIssues(readAlias(record, ["issues"]), "$.issues", bag),
199
+ contradictions: normalizeContradictions(readAlias(record, ["contradictions"]), "$.contradictions", bag),
200
+ missingInformation: readOptionalStringArray(record, ["missingInformation", "missing_information"], "$.missingInformation", bag, []) ?? [],
201
+ followUpTasks,
202
+ };
203
+ const metadata = readOptionalRecord(record, ["metadata"], "$.metadata", bag);
204
+ if (metadata) {
205
+ verifierOutput.metadata = metadata;
206
+ }
207
+ return bag.issues.length > 0 ? fail(bag) : ok(verifierOutput);
208
+ };
209
+ export const validateCodaliContextPack = (input) => {
210
+ const bag = { issues: [] };
211
+ const record = requireRecord(input, "$", bag);
212
+ if (!record) {
213
+ return fail(bag);
214
+ }
215
+ const id = readRequiredNonEmptyString(record, ["id"], "$.id", bag);
216
+ const runId = readRequiredNonEmptyString(record, ["runId", "run_id"], "$.runId", bag);
217
+ const originalQuery = readRequiredNonEmptyString(record, ["originalQuery", "original_query"], "$.originalQuery", bag);
218
+ const tokenEstimate = readNonNegativeInteger(record, ["tokenEstimate", "token_estimate"], "$.tokenEstimate", bag, 0);
219
+ if (!id || !runId || !originalQuery) {
220
+ return fail(bag);
221
+ }
222
+ const contextPack = {
223
+ id,
224
+ runId,
225
+ originalQuery,
226
+ decisionFacts: normalizeEvidenceItems(readAlias(record, ["decisionFacts", "decision_facts"]), "$.decisionFacts", bag),
227
+ contradictions: normalizeContradictions(readAlias(record, ["contradictions"]), "$.contradictions", bag),
228
+ missingInformation: readOptionalStringArray(record, ["missingInformation", "missing_information"], "$.missingInformation", bag, []) ?? [],
229
+ selectedExcerpts: normalizeSelectedExcerpts(readAlias(record, ["selectedExcerpts", "selected_excerpts"]), "$.selectedExcerpts", bag),
230
+ toolSummary: normalizeToolSummary(readAlias(record, ["toolSummary", "tool_summary"]), "$.toolSummary", bag),
231
+ tokenEstimate,
232
+ };
233
+ const metadata = readOptionalRecord(record, ["metadata"], "$.metadata", bag);
234
+ if (metadata) {
235
+ contextPack.metadata = metadata;
236
+ }
237
+ return bag.issues.length > 0 ? fail(bag) : ok(contextPack);
238
+ };
239
+ export const validateGatewayRequest = validateCodaliGatewayRequest;
240
+ export const validateGatewayPolicy = validateCodaliGatewayPolicy;
241
+ export const validateGatewayPlannerOutput = validateCodaliGatewayPlannerOutput;
242
+ export const validateGatewayWorkerTask = validateCodaliGatewayWorkerTask;
243
+ export const validateGatewayEvidenceItem = validateCodaliEvidenceItem;
244
+ export const validateGatewayVerifierOutput = validateCodaliGatewayVerifierOutput;
245
+ export const validateGatewayContextPack = validateCodaliContextPack;
246
+ const ok = (value) => ({
247
+ ok: true,
248
+ value,
249
+ issues: [],
250
+ });
251
+ const fail = (bag) => ({
252
+ ok: false,
253
+ issues: bag.issues,
254
+ });
255
+ const addIssue = (bag, path, code, message, value) => {
256
+ bag.issues.push({ path, code, message, value });
257
+ };
258
+ const addNestedIssues = (bag, prefix, issues) => {
259
+ for (const issue of issues) {
260
+ const suffix = issue.path === "$" ? "" : issue.path.slice(1);
261
+ bag.issues.push({ ...issue, path: `${prefix}${suffix}` });
262
+ }
263
+ };
264
+ const isRecord = (input) => typeof input === "object" && input !== null && !Array.isArray(input);
265
+ const requireRecord = (input, path, bag) => {
266
+ if (!isRecord(input)) {
267
+ addIssue(bag, path, "expected_object", "Expected an object.", input);
268
+ return undefined;
269
+ }
270
+ return input;
271
+ };
272
+ const readAlias = (record, keys) => {
273
+ for (const key of keys) {
274
+ if (Object.prototype.hasOwnProperty.call(record, key)) {
275
+ return record[key];
276
+ }
277
+ }
278
+ return undefined;
279
+ };
280
+ const hasAlias = (record, keys) => keys.some((key) => Object.prototype.hasOwnProperty.call(record, key));
281
+ const readRequiredNonEmptyString = (record, keys, path, bag) => {
282
+ const value = readAlias(record, keys);
283
+ if (typeof value !== "string" || value.trim().length === 0) {
284
+ addIssue(bag, path, "expected_non_empty_string", "Expected a non-empty string.", value);
285
+ return undefined;
286
+ }
287
+ return value.trim();
288
+ };
289
+ const readOptionalString = (record, keys, path, bag) => {
290
+ const value = readAlias(record, keys);
291
+ if (typeof value === "undefined") {
292
+ return undefined;
293
+ }
294
+ if (typeof value !== "string") {
295
+ addIssue(bag, path, "expected_string", "Expected a string.", value);
296
+ return undefined;
297
+ }
298
+ return value;
299
+ };
300
+ const readRequiredBoolean = (record, keys, path, bag) => {
301
+ const value = readAlias(record, keys);
302
+ if (typeof value !== "boolean") {
303
+ addIssue(bag, path, "expected_boolean", "Expected a boolean.", value);
304
+ return undefined;
305
+ }
306
+ return value;
307
+ };
308
+ const readOptionalBoolean = (record, keys, path, bag, fallback) => {
309
+ const value = readAlias(record, keys);
310
+ if (typeof value === "undefined") {
311
+ return fallback;
312
+ }
313
+ if (typeof value !== "boolean") {
314
+ addIssue(bag, path, "expected_boolean", "Expected a boolean.", value);
315
+ return fallback;
316
+ }
317
+ return value;
318
+ };
319
+ const readPositiveInteger = (record, keys, path, bag, fallback) => {
320
+ const value = readAlias(record, keys);
321
+ if (typeof value === "undefined") {
322
+ return fallback;
323
+ }
324
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
325
+ addIssue(bag, path, "expected_positive_integer", "Expected a positive integer.", value);
326
+ return fallback;
327
+ }
328
+ return value;
329
+ };
330
+ const readNonNegativeInteger = (record, keys, path, bag, fallback) => {
331
+ const value = readAlias(record, keys);
332
+ if (typeof value === "undefined") {
333
+ return fallback;
334
+ }
335
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
336
+ addIssue(bag, path, "expected_non_negative_integer", "Expected a non-negative integer.", value);
337
+ return fallback;
338
+ }
339
+ return value;
340
+ };
341
+ const readUnitNumber = (record, keys, path, bag) => {
342
+ const value = readAlias(record, keys);
343
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) {
344
+ addIssue(bag, path, "expected_unit_number", "Expected a number between 0 and 1.", value);
345
+ return undefined;
346
+ }
347
+ return value;
348
+ };
349
+ const readOptionalEnum = (record, keys, values, path, bag, fallback) => {
350
+ const value = readAlias(record, keys);
351
+ if (typeof value === "undefined") {
352
+ return fallback;
353
+ }
354
+ if (typeof value !== "string" || !values.includes(value)) {
355
+ addIssue(bag, path, "expected_enum", `Expected one of: ${values.join(", ")}.`, value);
356
+ return fallback;
357
+ }
358
+ return value;
359
+ };
360
+ const readOptionalStringArray = (record, keys, path, bag, fallback) => {
361
+ const value = readAlias(record, keys);
362
+ if (typeof value === "undefined") {
363
+ return fallback;
364
+ }
365
+ if (!Array.isArray(value)) {
366
+ addIssue(bag, path, "expected_string_array", "Expected an array of strings.", value);
367
+ return fallback;
368
+ }
369
+ const strings = [];
370
+ for (let index = 0; index < value.length; index += 1) {
371
+ const item = value[index];
372
+ if (typeof item !== "string" || item.trim().length === 0) {
373
+ addIssue(bag, `${path}[${index}]`, "expected_non_empty_string", "Expected a non-empty string.", item);
374
+ continue;
375
+ }
376
+ strings.push(item.trim());
377
+ }
378
+ return strings;
379
+ };
380
+ const readOptionalRecord = (record, keys, path, bag) => {
381
+ const value = readAlias(record, keys);
382
+ if (typeof value === "undefined") {
383
+ return undefined;
384
+ }
385
+ if (!isRecord(value)) {
386
+ addIssue(bag, path, "expected_object", "Expected an object.", value);
387
+ return undefined;
388
+ }
389
+ return value;
390
+ };
391
+ const requireFalse = (record, keys, path, bag) => {
392
+ if (!hasAlias(record, keys)) {
393
+ return;
394
+ }
395
+ const value = readAlias(record, keys);
396
+ if (value !== false) {
397
+ addIssue(bag, path, "read_only_policy_required", "Initial Codali gateway policies must keep this permission false.", value);
398
+ }
399
+ };
400
+ const copyOptionalString = (record, target, targetKey, keys, path, bag) => {
401
+ const value = readOptionalString(record, keys, path, bag);
402
+ if (typeof value === "string") {
403
+ target[targetKey] = value;
404
+ }
405
+ };
406
+ const copyOptionalPositiveInteger = (record, target, targetKey, keys, path, bag) => {
407
+ if (!hasAlias(record, keys)) {
408
+ return;
409
+ }
410
+ target[targetKey] = readPositiveInteger(record, keys, path, bag, 1);
411
+ };
412
+ const normalizeStringObject = (input, keys, path, bag) => {
413
+ if (typeof input === "undefined") {
414
+ return undefined;
415
+ }
416
+ const record = requireRecord(input, path, bag);
417
+ if (!record) {
418
+ return undefined;
419
+ }
420
+ const normalized = {};
421
+ for (const key of keys) {
422
+ const value = readOptionalString(record, [key], `${path}.${key}`, bag);
423
+ if (typeof value === "string") {
424
+ normalized[key] = value;
425
+ }
426
+ }
427
+ return normalized;
428
+ };
429
+ const normalizeConversation = (input, bag) => {
430
+ if (typeof input === "undefined") {
431
+ return undefined;
432
+ }
433
+ const record = requireRecord(input, "$.conversation", bag);
434
+ if (!record) {
435
+ return undefined;
436
+ }
437
+ const conversation = {};
438
+ copyOptionalString(record, conversation, "id", ["id"], "$.conversation.id", bag);
439
+ const messagesValue = readAlias(record, ["messages"]);
440
+ if (typeof messagesValue !== "undefined") {
441
+ if (!Array.isArray(messagesValue)) {
442
+ addIssue(bag, "$.conversation.messages", "expected_message_array", "Expected an array of messages.", messagesValue);
443
+ }
444
+ else {
445
+ const messages = [];
446
+ for (let index = 0; index < messagesValue.length; index += 1) {
447
+ const message = normalizeConversationMessage(messagesValue[index], `$.conversation.messages[${index}]`, bag);
448
+ if (message) {
449
+ messages.push(message);
450
+ }
451
+ }
452
+ conversation.messages = messages;
453
+ }
454
+ }
455
+ return conversation;
456
+ };
457
+ const normalizeConversationMessage = (input, path, bag) => {
458
+ const record = requireRecord(input, path, bag);
459
+ if (!record) {
460
+ return undefined;
461
+ }
462
+ const role = readAlias(record, ["role"]);
463
+ if (role !== "system" && role !== "user" && role !== "assistant") {
464
+ addIssue(bag, `${path}.role`, "expected_message_role", "Expected message role system, user, or assistant.", role);
465
+ return undefined;
466
+ }
467
+ const content = readRequiredNonEmptyString(record, ["content"], `${path}.content`, bag);
468
+ if (!content) {
469
+ return undefined;
470
+ }
471
+ return { role, content };
472
+ };
473
+ const normalizeResponsePolicy = (input, bag) => {
474
+ if (typeof input === "undefined") {
475
+ return { format: "text", finalAnswerRequired: true };
476
+ }
477
+ const record = requireRecord(input, "$.response", bag);
478
+ if (!record) {
479
+ return { format: "text", finalAnswerRequired: true };
480
+ }
481
+ const response = {
482
+ format: readOptionalEnum(record, ["format"], RESPONSE_FORMATS, "$.response.format", bag, "text"),
483
+ finalAnswerRequired: readOptionalBoolean(record, ["finalAnswerRequired", "final_answer_required"], "$.response.finalAnswerRequired", bag, true),
484
+ };
485
+ const schema = readOptionalRecord(record, ["schema"], "$.response.schema", bag);
486
+ if (schema) {
487
+ response.schema = schema;
488
+ }
489
+ return response;
490
+ };
491
+ const normalizeAgentTierPolicy = (input, bag) => {
492
+ if (typeof input === "undefined") {
493
+ return { resolver: "mcoda_inventory" };
494
+ }
495
+ const record = requireRecord(input, "$.agentPolicy", bag);
496
+ if (!record) {
497
+ return { resolver: "mcoda_inventory" };
498
+ }
499
+ const resolver = readAlias(record, ["resolver"]);
500
+ if (typeof resolver !== "undefined" && resolver !== "mcoda_inventory") {
501
+ addIssue(bag, "$.agentPolicy.resolver", "unsupported_resolver", "Only mcoda_inventory is supported.", resolver);
502
+ }
503
+ const policy = {
504
+ resolver: "mcoda_inventory",
505
+ allowCloudFallback: readOptionalBoolean(record, ["allowCloudFallback", "allow_cloud_fallback"], "$.agentPolicy.allowCloudFallback", bag, undefined),
506
+ };
507
+ const rolesValue = readAlias(record, ["roles"]);
508
+ if (typeof rolesValue !== "undefined") {
509
+ const rolesRecord = requireRecord(rolesValue, "$.agentPolicy.roles", bag);
510
+ if (rolesRecord) {
511
+ const roles = {};
512
+ for (const [role, value] of Object.entries(rolesRecord)) {
513
+ const rolePolicy = normalizeAgentRolePolicy(value, `$.agentPolicy.roles.${role}`, bag);
514
+ if (rolePolicy) {
515
+ roles[role] = rolePolicy;
516
+ }
517
+ }
518
+ policy.roles = roles;
519
+ }
520
+ }
521
+ return policy;
522
+ };
523
+ const normalizeAgentRolePolicy = (input, path, bag) => {
524
+ const record = requireRecord(input, path, bag);
525
+ if (!record) {
526
+ return undefined;
527
+ }
528
+ const tier = readAlias(record, ["tier"]);
529
+ if (tier !== "small" && tier !== "medium" && tier !== "large" && tier !== "image") {
530
+ addIssue(bag, `${path}.tier`, "expected_model_tier", "Expected tier small, medium, large, or image.", tier);
531
+ return undefined;
532
+ }
533
+ const rolePolicy = { tier };
534
+ const capabilities = readOptionalStringArray(record, ["capabilities"], `${path}.capabilities`, bag, undefined);
535
+ if (capabilities) {
536
+ rolePolicy.capabilities = capabilities;
537
+ }
538
+ const preferredRunnerKinds = readOptionalStringArray(record, ["preferredRunnerKinds", "preferred_runner_kinds"], `${path}.preferredRunnerKinds`, bag, undefined);
539
+ if (preferredRunnerKinds) {
540
+ rolePolicy.preferredRunnerKinds = preferredRunnerKinds;
541
+ }
542
+ copyOptionalRoleBoolean(record, rolePolicy, "requiresTools", path, bag);
543
+ copyOptionalRoleBoolean(record, rolePolicy, "requiresJsonSchema", path, bag);
544
+ copyOptionalRolePositiveInteger(record, rolePolicy, "maxLatencyMs", path, bag);
545
+ copyOptionalRolePositiveInteger(record, rolePolicy, "minContextWindow", path, bag);
546
+ return rolePolicy;
547
+ };
548
+ const copyOptionalRoleBoolean = (record, target, key, path, bag) => {
549
+ const snakeKey = key === "requiresTools" ? "requires_tools" : "requires_json_schema";
550
+ const value = readOptionalBoolean(record, [key, snakeKey], `${path}.${key}`, bag, undefined);
551
+ if (typeof value === "boolean") {
552
+ target[key] = value;
553
+ }
554
+ };
555
+ const copyOptionalRolePositiveInteger = (record, target, key, path, bag) => {
556
+ const snakeKey = key === "maxLatencyMs" ? "max_latency_ms" : "min_context_window";
557
+ if (!hasAlias(record, [key, snakeKey])) {
558
+ return;
559
+ }
560
+ target[key] = readPositiveInteger(record, [key, snakeKey], `${path}.${key}`, bag, 1);
561
+ };
562
+ const readAppToolContracts = (record, bag) => {
563
+ const value = readAlias(record, [
564
+ "appToolContracts",
565
+ "app_tool_contracts",
566
+ "okacamToolContracts",
567
+ "okacam_tool_contracts",
568
+ ]);
569
+ if (typeof value === "undefined") {
570
+ return undefined;
571
+ }
572
+ if (!Array.isArray(value) && !isRecord(value)) {
573
+ addIssue(bag, "$.appToolContracts", "expected_tool_contracts", "Expected an object map or array of tool contracts.", value);
574
+ return undefined;
575
+ }
576
+ validateAppToolContractsReadOnly(value, bag);
577
+ return value;
578
+ };
579
+ const readAppToolGateway = (record, bag) => {
580
+ const value = readAlias(record, ["appToolGateway", "app_tool_gateway"]);
581
+ if (typeof value === "undefined") {
582
+ return undefined;
583
+ }
584
+ if (!isRecord(value)) {
585
+ addIssue(bag, "$.appToolGateway", "expected_tool_gateway", "Expected a gateway contract object.", value);
586
+ return undefined;
587
+ }
588
+ const readOnly = readAlias(value, ["readOnly", "read_only"]);
589
+ if (readOnly === false) {
590
+ addIssue(bag, "$.appToolGateway.readOnly", "read_only_gateway_required", "App tool gateway dispatch is read-only in this gateway phase.", readOnly);
591
+ }
592
+ return value;
593
+ };
594
+ const validateAppToolContractsReadOnly = (value, bag) => {
595
+ const contractEntries = Array.isArray(value)
596
+ ? value.map((contractValue, index) => [String(index), contractValue])
597
+ : Object.entries(value);
598
+ for (const [key, contractValue] of contractEntries) {
599
+ if (!isRecord(contractValue)) {
600
+ continue;
601
+ }
602
+ const readOnly = readAlias(contractValue, ["readOnly", "read_only"]);
603
+ if (readOnly === false) {
604
+ addIssue(bag, `$.appToolContracts[${key}].readOnly`, "read_only_tool_contract_required", "Runtime app tool contracts must be read-only in this gateway phase.", readOnly);
605
+ }
606
+ }
607
+ };
608
+ const normalizeSubquestions = (input, path, bag) => {
609
+ if (typeof input === "undefined") {
610
+ return [];
611
+ }
612
+ if (!Array.isArray(input)) {
613
+ addIssue(bag, path, "expected_subquestion_array", "Expected an array.", input);
614
+ return [];
615
+ }
616
+ const subquestions = [];
617
+ for (let index = 0; index < input.length; index += 1) {
618
+ const record = requireRecord(input[index], `${path}[${index}]`, bag);
619
+ if (!record) {
620
+ continue;
621
+ }
622
+ const id = readRequiredNonEmptyString(record, ["id"], `${path}[${index}].id`, bag);
623
+ const question = readRequiredNonEmptyString(record, ["question"], `${path}[${index}].question`, bag);
624
+ if (!id || !question) {
625
+ continue;
626
+ }
627
+ const subquestion = { id, question };
628
+ copyOptionalString(record, subquestion, "rationale", ["rationale"], `${path}[${index}].rationale`, bag);
629
+ copyOptionalPositiveInteger(record, subquestion, "priority", ["priority"], `${path}[${index}].priority`, bag);
630
+ subquestions.push(subquestion);
631
+ }
632
+ return subquestions;
633
+ };
634
+ const normalizeWorkerTasks = (input, path, bag, fallback = []) => {
635
+ if (typeof input === "undefined") {
636
+ return fallback;
637
+ }
638
+ if (!Array.isArray(input)) {
639
+ addIssue(bag, path, "expected_worker_task_array", "Expected an array.", input);
640
+ return fallback;
641
+ }
642
+ const tasks = [];
643
+ for (let index = 0; index < input.length; index += 1) {
644
+ const task = normalizeWorkerTask(input[index], `${path}[${index}]`, bag);
645
+ if (task) {
646
+ tasks.push(task);
647
+ }
648
+ }
649
+ return tasks;
650
+ };
651
+ const normalizeWorkerTask = (input, path, bag) => {
652
+ const record = requireRecord(input, path, bag);
653
+ if (!record) {
654
+ return undefined;
655
+ }
656
+ const id = readRequiredNonEmptyString(record, ["id"], `${path}.id`, bag);
657
+ const workerRole = readRequiredNonEmptyString(record, ["workerRole", "worker_role"], `${path}.workerRole`, bag);
658
+ const objective = readRequiredNonEmptyString(record, ["objective"], `${path}.objective`, bag);
659
+ const outputFormat = readRequiredNonEmptyString(record, ["outputFormat", "output_format"], `${path}.outputFormat`, bag);
660
+ if (!id || !workerRole || !objective || !outputFormat) {
661
+ return undefined;
662
+ }
663
+ const task = {
664
+ id,
665
+ workerRole,
666
+ objective,
667
+ toolsAllowed: readOptionalStringArray(record, ["toolsAllowed", "tools_allowed"], `${path}.toolsAllowed`, bag, []) ?? [],
668
+ outputFormat,
669
+ };
670
+ copyOptionalString(record, task, "query", ["query"], `${path}.query`, bag);
671
+ const expectedSources = readOptionalStringArray(record, ["expectedSources", "expected_sources"], `${path}.expectedSources`, bag, undefined);
672
+ if (expectedSources) {
673
+ task.expectedSources = expectedSources;
674
+ }
675
+ const constraints = readOptionalStringArray(record, ["constraints"], `${path}.constraints`, bag, undefined);
676
+ if (constraints) {
677
+ task.constraints = constraints;
678
+ }
679
+ copyOptionalPositiveInteger(record, task, "priority", ["priority"], `${path}.priority`, bag);
680
+ const metadata = readOptionalRecord(record, ["metadata"], `${path}.metadata`, bag);
681
+ if (metadata) {
682
+ task.metadata = metadata;
683
+ }
684
+ return task;
685
+ };
686
+ const normalizeEvidenceItems = (input, path, bag) => {
687
+ if (typeof input === "undefined") {
688
+ return [];
689
+ }
690
+ if (!Array.isArray(input)) {
691
+ addIssue(bag, path, "expected_evidence_array", "Expected an array.", input);
692
+ return [];
693
+ }
694
+ const evidence = [];
695
+ for (let index = 0; index < input.length; index += 1) {
696
+ const item = normalizeEvidenceItem(input[index], `${path}[${index}]`, bag);
697
+ if (item) {
698
+ evidence.push(item);
699
+ }
700
+ }
701
+ return evidence;
702
+ };
703
+ const normalizeEvidenceItem = (input, path, bag) => {
704
+ const record = requireRecord(input, path, bag);
705
+ if (!record) {
706
+ return undefined;
707
+ }
708
+ const id = readRequiredNonEmptyString(record, ["id"], `${path}.id`, bag);
709
+ const runId = readRequiredNonEmptyString(record, ["runId", "run_id"], `${path}.runId`, bag);
710
+ const claim = readRequiredNonEmptyString(record, ["claim"], `${path}.claim`, bag);
711
+ const sourceType = readRequiredNonEmptyString(record, ["sourceType", "source_type"], `${path}.sourceType`, bag);
712
+ const confidence = readUnitNumber(record, ["confidence"], `${path}.confidence`, bag);
713
+ const relevance = readUnitNumber(record, ["relevance"], `${path}.relevance`, bag);
714
+ const tenantScoped = readRequiredBoolean(record, ["tenantScoped", "tenant_scoped"], `${path}.tenantScoped`, bag);
715
+ if (!id ||
716
+ !runId ||
717
+ !claim ||
718
+ !sourceType ||
719
+ typeof confidence !== "number" ||
720
+ typeof relevance !== "number" ||
721
+ typeof tenantScoped !== "boolean") {
722
+ return undefined;
723
+ }
724
+ const item = {
725
+ id,
726
+ runId,
727
+ claim,
728
+ sourceType,
729
+ confidence,
730
+ relevance,
731
+ tenantScoped,
732
+ };
733
+ copyOptionalString(record, item, "taskId", ["taskId", "task_id"], `${path}.taskId`, bag);
734
+ copyOptionalString(record, item, "stageId", ["stageId", "stage_id"], `${path}.stageId`, bag);
735
+ copyOptionalString(record, item, "summary", ["summary"], `${path}.summary`, bag);
736
+ copyOptionalString(record, item, "sourceId", ["sourceId", "source_id"], `${path}.sourceId`, bag);
737
+ copyOptionalString(record, item, "sourceUri", ["sourceUri", "source_uri"], `${path}.sourceUri`, bag);
738
+ copyOptionalString(record, item, "sourceTitle", ["sourceTitle", "source_title"], `${path}.sourceTitle`, bag);
739
+ copyOptionalString(record, item, "sourceTimestamp", ["sourceTimestamp", "source_timestamp"], `${path}.sourceTimestamp`, bag);
740
+ copyOptionalString(record, item, "rawExcerpt", ["rawExcerpt", "raw_excerpt"], `${path}.rawExcerpt`, bag);
741
+ copyOptionalString(record, item, "rawPayloadRef", ["rawPayloadRef", "raw_payload_ref"], `${path}.rawPayloadRef`, bag);
742
+ const freshness = readAlias(record, ["freshness"]);
743
+ if (typeof freshness !== "undefined") {
744
+ if (typeof freshness === "string" && FRESHNESS_VALUES.includes(freshness)) {
745
+ item.freshness = freshness;
746
+ }
747
+ else {
748
+ addIssue(bag, `${path}.freshness`, "expected_freshness", `Expected one of: ${FRESHNESS_VALUES.join(", ")}.`, freshness);
749
+ }
750
+ }
751
+ copyOptionalString(record, item, "usedTool", ["usedTool", "used_tool"], `${path}.usedTool`, bag);
752
+ const metadata = readOptionalRecord(record, ["metadata"], `${path}.metadata`, bag);
753
+ if (metadata) {
754
+ item.metadata = metadata;
755
+ }
756
+ return item;
757
+ };
758
+ const normalizeVerifierIssues = (input, path, bag) => {
759
+ if (typeof input === "undefined") {
760
+ return [];
761
+ }
762
+ if (!Array.isArray(input)) {
763
+ addIssue(bag, path, "expected_issue_array", "Expected an array.", input);
764
+ return [];
765
+ }
766
+ const issues = [];
767
+ for (let index = 0; index < input.length; index += 1) {
768
+ const record = requireRecord(input[index], `${path}[${index}]`, bag);
769
+ if (!record) {
770
+ continue;
771
+ }
772
+ const code = readRequiredNonEmptyString(record, ["code"], `${path}[${index}].code`, bag);
773
+ const message = readRequiredNonEmptyString(record, ["message"], `${path}[${index}].message`, bag);
774
+ if (!code || !message) {
775
+ continue;
776
+ }
777
+ const issue = { code, message };
778
+ const severity = readAlias(record, ["severity"]);
779
+ if (severity === "info" || severity === "warning" || severity === "error") {
780
+ issue.severity = severity;
781
+ }
782
+ else if (typeof severity !== "undefined") {
783
+ addIssue(bag, `${path}[${index}].severity`, "expected_severity", "Expected severity info, warning, or error.", severity);
784
+ }
785
+ const evidenceIds = readOptionalStringArray(record, ["evidenceIds", "evidence_ids"], `${path}[${index}].evidenceIds`, bag, undefined);
786
+ if (evidenceIds) {
787
+ issue.evidenceIds = evidenceIds;
788
+ }
789
+ issues.push(issue);
790
+ }
791
+ return issues;
792
+ };
793
+ const normalizeContradictions = (input, path, bag) => {
794
+ if (typeof input === "undefined") {
795
+ return [];
796
+ }
797
+ if (!Array.isArray(input)) {
798
+ addIssue(bag, path, "expected_contradiction_array", "Expected an array.", input);
799
+ return [];
800
+ }
801
+ const contradictions = [];
802
+ for (let index = 0; index < input.length; index += 1) {
803
+ const record = requireRecord(input[index], `${path}[${index}]`, bag);
804
+ if (!record) {
805
+ continue;
806
+ }
807
+ const summary = readRequiredNonEmptyString(record, ["summary"], `${path}[${index}].summary`, bag);
808
+ if (!summary) {
809
+ continue;
810
+ }
811
+ contradictions.push({
812
+ summary,
813
+ evidenceIds: readOptionalStringArray(record, ["evidenceIds", "evidence_ids"], `${path}[${index}].evidenceIds`, bag, []) ?? [],
814
+ });
815
+ }
816
+ return contradictions;
817
+ };
818
+ const normalizeSelectedExcerpts = (input, path, bag) => {
819
+ if (typeof input === "undefined") {
820
+ return [];
821
+ }
822
+ if (!Array.isArray(input)) {
823
+ addIssue(bag, path, "expected_excerpt_array", "Expected an array.", input);
824
+ return [];
825
+ }
826
+ const excerpts = [];
827
+ for (let index = 0; index < input.length; index += 1) {
828
+ const record = requireRecord(input[index], `${path}[${index}]`, bag);
829
+ if (!record) {
830
+ continue;
831
+ }
832
+ const evidenceId = readRequiredNonEmptyString(record, ["evidenceId", "evidence_id"], `${path}[${index}].evidenceId`, bag);
833
+ const text = readRequiredNonEmptyString(record, ["text"], `${path}[${index}].text`, bag);
834
+ if (evidenceId && text) {
835
+ excerpts.push({ evidenceId, text });
836
+ }
837
+ }
838
+ return excerpts;
839
+ };
840
+ const normalizeToolSummary = (input, path, bag) => {
841
+ if (typeof input === "undefined") {
842
+ return [];
843
+ }
844
+ if (!Array.isArray(input)) {
845
+ addIssue(bag, path, "expected_tool_summary_array", "Expected an array.", input);
846
+ return [];
847
+ }
848
+ const summary = [];
849
+ for (let index = 0; index < input.length; index += 1) {
850
+ const record = requireRecord(input[index], `${path}[${index}]`, bag);
851
+ if (!record) {
852
+ continue;
853
+ }
854
+ const tool = readRequiredNonEmptyString(record, ["tool"], `${path}[${index}].tool`, bag);
855
+ const calls = readNonNegativeInteger(record, ["calls"], `${path}[${index}].calls`, bag, 0);
856
+ const statusesRecord = readOptionalRecord(record, ["statuses"], `${path}[${index}].statuses`, bag);
857
+ if (!tool) {
858
+ continue;
859
+ }
860
+ const statuses = {};
861
+ if (statusesRecord) {
862
+ for (const [status, count] of Object.entries(statusesRecord)) {
863
+ if (typeof count === "number" && Number.isInteger(count) && count >= 0) {
864
+ statuses[status] = count;
865
+ }
866
+ else {
867
+ addIssue(bag, `${path}[${index}].statuses.${status}`, "expected_non_negative_integer", "Expected a non-negative integer status count.", count);
868
+ }
869
+ }
870
+ }
871
+ summary.push({ tool, calls, statuses });
872
+ }
873
+ return summary;
874
+ };