@mcoda/codali 0.1.90 → 0.1.94

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
@@ -191,7 +191,7 @@ export const parseCodaliGatewayLiveInventory = (payload) => {
191
191
  return [];
192
192
  };
193
193
  const secretKeyPattern = /(api[_-]?key|authorization|bearer|password|secret|token|credential)/i;
194
- const safeTokenMetricKeyPattern = /^(cachedInputTokens|completionTokens|contextWindow|inputTokens|maxContextPackTokens|maxOutputTokens|maxTokens|outputTokens|promptTokens|tokenEstimate|tokensUsed|totalTokens|usageTokensTotal|usage_tokens_total|cached_input_tokens|completion_tokens|context_window|input_tokens|max_context_pack_tokens|max_output_tokens|max_tokens|output_tokens|prompt_tokens|token_estimate|tokens_used|total_tokens)$/i;
194
+ const safeTokenMetricKeyPattern = /^(cachedInputTokens|completionTokens|contextWindow|inputTokens|maxContextPackTokens|maxOutputTokens|maxTokens|outputTokens|outputTokensPerSecond|promptTokens|tokenEstimate|tokenUse|tokensPerSecond|tokensUsed|totalTokens|usageTokensTotal|usage_tokens_total|cached_input_tokens|completion_tokens|context_window|input_tokens|max_context_pack_tokens|max_output_tokens|max_tokens|output_tokens|output_tokens_per_second|prompt_tokens|token_estimate|token_use|tokens_per_second|tokens_used|total_tokens)$/i;
195
195
  const bearerPattern = /\bBearer\s+[A-Za-z0-9._~+/=-]+/gi;
196
196
  const apiKeyPattern = /\b(?:sk|pk|mswarm|mcoda)_[A-Za-z0-9_-]{12,}\b/gi;
197
197
  export const redactCodaliGatewayLiveValue = (value) => {
@@ -477,11 +477,21 @@ const normalizeArtifact = (value) => {
477
477
  const summarizeAgentRunMetadata = (metadata) => {
478
478
  const cli = asRecord(metadata?.cli);
479
479
  const usage = asRecord(metadata?.usage);
480
+ const localInference = asRecord(metadata?.localInference) ??
481
+ asRecord(metadata?.local_inference);
482
+ const queue = asRecord(metadata?.queue);
483
+ const throughput = asRecord(metadata?.throughput);
480
484
  const output = {};
481
485
  const mode = readString(metadata, ["mode"]);
482
486
  const adapterType = readString(metadata, ["adapterType", "adapter_type"]);
483
487
  const authMode = readString(metadata, ["authMode", "auth_mode"]);
484
488
  const cliVersion = readString(cli, ["version"]);
489
+ const costUsd = readNumber(metadata, [
490
+ "costUsd",
491
+ "cost_usd",
492
+ "estimatedCostUsd",
493
+ "estimated_cost_usd",
494
+ ]);
485
495
  if (mode)
486
496
  output.mode = mode;
487
497
  if (adapterType)
@@ -490,8 +500,17 @@ const summarizeAgentRunMetadata = (metadata) => {
490
500
  output.authMode = authMode;
491
501
  if (cliVersion)
492
502
  output.cli = { version: cliVersion };
503
+ if (costUsd !== undefined)
504
+ output.costUsd = costUsd;
493
505
  if (usage)
494
506
  output.usage = redactCodaliGatewayLiveValue(usage);
507
+ if (queue)
508
+ output.queue = redactCodaliGatewayLiveValue(queue);
509
+ if (throughput)
510
+ output.throughput = redactCodaliGatewayLiveValue(throughput);
511
+ if (localInference) {
512
+ output.localInference = redactCodaliGatewayLiveValue(localInference);
513
+ }
495
514
  return output;
496
515
  };
497
516
  const outputPreview = (text) => {
@@ -505,13 +524,26 @@ const classifyAgentRunCommandFailure = (result) => {
505
524
  normalized.includes("invalid model id") ||
506
525
  normalized.includes("model_not_found") ||
507
526
  normalized.includes("model not found");
508
- const status = knownCatalogMismatch
527
+ const upstreamUnavailable = normalized.includes("mswarm_error") ||
528
+ normalized.includes("upstream_error") ||
529
+ normalized.includes("self-hosted node is not currently reachable") ||
530
+ normalized.includes("self hosted node is not currently reachable") ||
531
+ normalized.includes("node is not currently reachable") ||
532
+ normalized.includes("connection_error") ||
533
+ normalized.includes("econnrefused") ||
534
+ normalized.includes("fetch failed");
535
+ const degradedFailureClass = knownCatalogMismatch
536
+ ? "agent_run_model_catalog_mismatch"
537
+ : upstreamUnavailable
538
+ ? "agent_run_upstream_unavailable"
539
+ : undefined;
540
+ const status = degradedFailureClass
509
541
  ? "degraded"
510
542
  : "failed";
511
543
  return {
512
544
  status,
513
- warnings: knownCatalogMismatch
514
- ? ["agent_run_model_catalog_mismatch"]
545
+ warnings: degradedFailureClass
546
+ ? [degradedFailureClass]
515
547
  : [],
516
548
  errors: [
517
549
  `agent_run_exit_${result.exitCode}`,
@@ -520,9 +552,7 @@ const classifyAgentRunCommandFailure = (result) => {
520
552
  metadata: {
521
553
  runner: "mcoda_agent_run",
522
554
  exitCode: result.exitCode,
523
- failureClass: knownCatalogMismatch
524
- ? "agent_run_model_catalog_mismatch"
525
- : "agent_run_command_failed",
555
+ failureClass: degradedFailureClass ?? "agent_run_command_failed",
526
556
  },
527
557
  };
528
558
  };
@@ -735,6 +765,333 @@ const summarizeHarness = (classification, scenarios) => {
735
765
  const assignmentForScenario = (classification, scenario) => classification.assignments[scenario.role] ??
736
766
  classification.resolution.assignments[ROLE_TO_RESOLVER_ROLE[scenario.role]];
737
767
  const redactScenario = (scenario) => redactCodaliGatewayLiveValue(scenario);
768
+ const normalizeShadowComparisonPolicy = (policy) => ({
769
+ enabled: policy?.enabled === true,
770
+ maxCandidatesPerScenario: Math.max(0, Math.min(5, Math.trunc(policy?.maxCandidatesPerScenario ?? 1))),
771
+ includePrimary: policy?.includePrimary !== false,
772
+ requireHealthy: policy?.requireHealthy !== false,
773
+ roles: policy?.roles?.length
774
+ ? unique(policy.roles)
775
+ : [...new Set(CODALI_GATEWAY_LIVE_SCENARIOS.map((scenario) => scenario.role))],
776
+ });
777
+ const buildRuntimeAgentInput = (candidate) => ({
778
+ slug: candidate.slug,
779
+ adapter: candidate.adapter,
780
+ provider: candidate.provider,
781
+ model: candidate.model,
782
+ baseUrl: candidate.baseUrl,
783
+ runnerKind: candidate.runnerKind,
784
+ supportsTools: candidate.supportsTools,
785
+ capabilities: candidate.capabilities,
786
+ contextWindow: candidate.contextWindow,
787
+ maxOutputTokens: candidate.maxOutputTokens,
788
+ });
789
+ const createComparisonAssignment = (params) => ({
790
+ role: params.resolverRole,
791
+ policy: params.primary?.policy ?? { tier: params.candidate.tier },
792
+ candidate: params.candidate,
793
+ agent: buildRuntimeAgentInput(params.candidate),
794
+ score: params.score ?? 0,
795
+ reasons: params.reasons ?? [],
796
+ });
797
+ const resolveShadowAssignmentsForScenario = (params) => {
798
+ if (!params.policy.enabled || params.policy.maxCandidatesPerScenario <= 0) {
799
+ return [];
800
+ }
801
+ if (!params.policy.roles.includes(params.scenario.role)) {
802
+ return [];
803
+ }
804
+ const resolverRole = ROLE_TO_RESOLVER_ROLE[params.scenario.role];
805
+ const primarySlug = params.primary?.candidate.slug;
806
+ return params.classification.resolution.diagnostics
807
+ .filter((diagnostic) => diagnostic.role === resolverRole &&
808
+ diagnostic.eligible &&
809
+ diagnostic.slug !== primarySlug &&
810
+ diagnostic.score !== undefined)
811
+ .map((diagnostic) => {
812
+ const candidate = params.classification.resolution.candidates.find((entry) => entry.slug === diagnostic.slug);
813
+ if (!candidate)
814
+ return undefined;
815
+ if (params.policy.requireHealthy && candidate.healthStatus !== "healthy") {
816
+ return undefined;
817
+ }
818
+ return createComparisonAssignment({
819
+ resolverRole,
820
+ primary: params.primary,
821
+ candidate,
822
+ score: diagnostic.score,
823
+ reasons: diagnostic.reasons,
824
+ });
825
+ })
826
+ .filter((assignment) => Boolean(assignment))
827
+ .sort((left, right) => {
828
+ const scoreDelta = right.score - left.score;
829
+ if (scoreDelta !== 0)
830
+ return scoreDelta;
831
+ return left.candidate.slug.localeCompare(right.candidate.slug);
832
+ })
833
+ .slice(0, params.policy.maxCandidatesPerScenario);
834
+ };
835
+ const metricRecords = (metadata) => {
836
+ const responseMetadata = asRecord(metadata?.responseMetadata);
837
+ const localInference = asRecord(metadata?.localInference) ??
838
+ asRecord(metadata?.local_inference) ??
839
+ asRecord(responseMetadata?.localInference) ??
840
+ asRecord(responseMetadata?.local_inference);
841
+ const usage = asRecord(metadata?.usage) ??
842
+ asRecord(responseMetadata?.usage) ??
843
+ asRecord(localInference?.usage);
844
+ const queue = asRecord(metadata?.queue) ??
845
+ asRecord(responseMetadata?.queue) ??
846
+ asRecord(localInference?.queue);
847
+ const throughput = asRecord(metadata?.throughput) ??
848
+ asRecord(responseMetadata?.throughput) ??
849
+ asRecord(localInference?.throughput);
850
+ return [metadata, responseMetadata, localInference, usage, queue, throughput]
851
+ .filter((record) => Boolean(record));
852
+ };
853
+ const firstMetricNumber = (records, keys) => {
854
+ for (const record of records) {
855
+ const value = readNumber(record, keys);
856
+ if (value !== undefined)
857
+ return value;
858
+ }
859
+ return undefined;
860
+ };
861
+ const firstMetricString = (records, keys) => {
862
+ for (const record of records) {
863
+ const value = readString(record, keys);
864
+ if (value)
865
+ return value;
866
+ }
867
+ return undefined;
868
+ };
869
+ const extractTokenUse = (records) => {
870
+ const inputTokens = firstMetricNumber(records, [
871
+ "inputTokens",
872
+ "input_tokens",
873
+ "promptTokens",
874
+ "prompt_tokens",
875
+ ]);
876
+ const outputTokens = firstMetricNumber(records, [
877
+ "outputTokens",
878
+ "output_tokens",
879
+ "completionTokens",
880
+ "completion_tokens",
881
+ ]);
882
+ const totalTokens = firstMetricNumber(records, ["totalTokens", "total_tokens", "tokensUsed", "tokens_used"]) ??
883
+ (inputTokens !== undefined || outputTokens !== undefined
884
+ ? (inputTokens ?? 0) + (outputTokens ?? 0)
885
+ : undefined);
886
+ if (inputTokens === undefined && outputTokens === undefined && totalTokens === undefined) {
887
+ return undefined;
888
+ }
889
+ return { inputTokens, outputTokens, totalTokens };
890
+ };
891
+ const extractQueueMetrics = (metadata) => {
892
+ const responseMetadata = asRecord(metadata?.responseMetadata);
893
+ const localInference = asRecord(metadata?.localInference) ??
894
+ asRecord(metadata?.local_inference) ??
895
+ asRecord(responseMetadata?.localInference) ??
896
+ asRecord(responseMetadata?.local_inference);
897
+ const queue = asRecord(metadata?.queue) ??
898
+ asRecord(responseMetadata?.queue) ??
899
+ asRecord(localInference?.queue);
900
+ const records = [queue, metadata, responseMetadata, localInference]
901
+ .filter((record) => Boolean(record));
902
+ const waitMs = firstMetricNumber(records, [
903
+ "waitMs",
904
+ "wait_ms",
905
+ "queueWaitMs",
906
+ "queue_wait_ms",
907
+ "queuedMs",
908
+ "queued_ms",
909
+ ]);
910
+ const depth = firstMetricNumber(records, ["depth", "queueDepth", "queue_depth"]);
911
+ const status = firstMetricString(records, ["status", "queueStatus", "queue_status"]);
912
+ if (waitMs === undefined && depth === undefined && status === undefined) {
913
+ return undefined;
914
+ }
915
+ return { waitMs, depth, status };
916
+ };
917
+ const extractThroughputMetrics = (params) => {
918
+ const responseMetadata = asRecord(params.metadata?.responseMetadata);
919
+ const localInference = asRecord(params.metadata?.localInference) ??
920
+ asRecord(params.metadata?.local_inference) ??
921
+ asRecord(responseMetadata?.localInference) ??
922
+ asRecord(responseMetadata?.local_inference);
923
+ const throughput = asRecord(params.metadata?.throughput) ??
924
+ asRecord(responseMetadata?.throughput) ??
925
+ asRecord(localInference?.throughput);
926
+ const records = [throughput, params.metadata, responseMetadata, localInference]
927
+ .filter((record) => Boolean(record));
928
+ const explicitTokensPerSecond = firstMetricNumber(records, [
929
+ "tokensPerSecond",
930
+ "tokens_per_second",
931
+ "outputTokensPerSecond",
932
+ "output_tokens_per_second",
933
+ ]);
934
+ const tokensPerSecond = explicitTokensPerSecond ??
935
+ (params.tokenUse?.outputTokens !== undefined && params.latencyMs && params.latencyMs > 0
936
+ ? params.tokenUse.outputTokens / (params.latencyMs / 1000)
937
+ : undefined);
938
+ const requestsPerMinute = firstMetricNumber(records, [
939
+ "requestsPerMinute",
940
+ "requests_per_minute",
941
+ "requestPerMinute",
942
+ "request_per_minute",
943
+ ]);
944
+ if (tokensPerSecond === undefined && requestsPerMinute === undefined) {
945
+ return undefined;
946
+ }
947
+ return { tokensPerSecond, requestsPerMinute };
948
+ };
949
+ const extractLocalInferenceMetrics = (metadata) => {
950
+ const responseMetadata = asRecord(metadata?.responseMetadata);
951
+ const localInference = asRecord(metadata?.localInference) ??
952
+ asRecord(metadata?.local_inference) ??
953
+ asRecord(responseMetadata?.localInference) ??
954
+ asRecord(responseMetadata?.local_inference);
955
+ return localInference
956
+ ? asRecord(redactCodaliGatewayLiveValue(localInference))
957
+ : undefined;
958
+ };
959
+ const qualityScore = (scenario) => {
960
+ if (scenario.status === "passed")
961
+ return 1;
962
+ if (scenario.status === "degraded")
963
+ return 0.5;
964
+ return 0;
965
+ };
966
+ const comparisonMetrics = (scenario, assignment) => {
967
+ const records = metricRecords(scenario.metadata);
968
+ const tokenUse = extractTokenUse(records);
969
+ const costUsd = firstMetricNumber(records, ["costUsd", "cost_usd", "estimatedCostUsd", "estimated_cost_usd"]) ??
970
+ (assignment?.candidate.costPerMillion !== undefined && tokenUse?.totalTokens !== undefined
971
+ ? (assignment.candidate.costPerMillion * tokenUse.totalTokens) / 1000000
972
+ : undefined);
973
+ const queue = extractQueueMetrics(scenario.metadata);
974
+ const throughput = extractThroughputMetrics({
975
+ metadata: scenario.metadata,
976
+ tokenUse,
977
+ latencyMs: scenario.latencyMs,
978
+ });
979
+ const failureStatus = scenario.status === "passed"
980
+ ? "none"
981
+ : scenario.status;
982
+ return {
983
+ quality: {
984
+ status: scenario.status,
985
+ score: qualityScore(scenario),
986
+ jsonValid: scenario.jsonValid,
987
+ toolCallCount: scenario.toolCallCount,
988
+ artifactPresent: scenario.artifact !== undefined,
989
+ finalAnswerSucceeded: scenario.finalAnswerStatus === "succeeded",
990
+ },
991
+ latencyMs: scenario.latencyMs,
992
+ costUsd,
993
+ tokenUse,
994
+ queue,
995
+ throughput,
996
+ failure: {
997
+ status: failureStatus,
998
+ reasons: failureStatus === "none"
999
+ ? []
1000
+ : unique([...scenario.errors, ...scenario.warnings]),
1001
+ },
1002
+ localInference: extractLocalInferenceMetrics(scenario.metadata),
1003
+ };
1004
+ };
1005
+ const comparisonRecord = (params) => {
1006
+ const candidate = params.assignment?.candidate;
1007
+ const agentSlug = candidate?.slug ?? params.result.agentSlug;
1008
+ const comparisonRole = params.primary ? "primary" : "shadow";
1009
+ return {
1010
+ id: `${params.scenario.id}:${comparisonRole}:${agentSlug ?? "unassigned"}`,
1011
+ scenarioId: params.scenario.id,
1012
+ role: params.scenario.role,
1013
+ resolverRole: ROLE_TO_RESOLVER_ROLE[params.scenario.role],
1014
+ comparisonRole,
1015
+ primary: params.primary,
1016
+ candidateRank: params.candidateRank,
1017
+ agentSlug,
1018
+ tier: candidate?.tier ?? params.result.tier,
1019
+ model: params.result.model ?? candidate?.model,
1020
+ adapter: params.result.adapter ?? candidate?.adapter,
1021
+ source: candidate?.source,
1022
+ healthStatus: candidate?.healthStatus,
1023
+ capabilities: candidate?.capabilities,
1024
+ resultStatus: params.result.status,
1025
+ selectedByPolicy: true,
1026
+ metrics: comparisonMetrics(params.result, params.assignment),
1027
+ warnings: params.result.warnings,
1028
+ errors: params.result.errors,
1029
+ metadata: params.result.metadata,
1030
+ };
1031
+ };
1032
+ const warningForRoleError = (role, error) => ({
1033
+ code: "GATEWAY_LIVE_ROLE_UNAVAILABLE",
1034
+ message: `No eligible inventory candidate found for live role ${role} (${error.role ?? ROLE_TO_RESOLVER_ROLE[role]}).`,
1035
+ reason: error.code,
1036
+ role,
1037
+ resolverRole: error.role,
1038
+ details: error.details,
1039
+ });
1040
+ const buildEnvironmentWarnings = (params) => {
1041
+ const warnings = [];
1042
+ if (params.discovery.status === "failed") {
1043
+ warnings.push({
1044
+ code: "GATEWAY_LIVE_INVENTORY_DISCOVERY_FAILED",
1045
+ message: "Agent inventory discovery failed before live model validation.",
1046
+ reason: params.discovery.errors.join("; ") || "inventory_command_failed",
1047
+ details: {
1048
+ command: params.discovery.command,
1049
+ args: params.discovery.args,
1050
+ },
1051
+ });
1052
+ }
1053
+ for (const [role, summary] of Object.entries(params.classification.roles)) {
1054
+ if (summary.status !== "assigned") {
1055
+ const matchingError = params.classification.errors.find((error) => error.role === summary.resolverRole);
1056
+ warnings.push(warningForRoleError(role, matchingError ?? {
1057
+ code: summary.errorCodes[0] ?? "GATEWAY_AGENT_ROLE_UNRESOLVED",
1058
+ message: `No eligible agent candidate found for role ${summary.resolverRole}.`,
1059
+ role: summary.resolverRole,
1060
+ }));
1061
+ continue;
1062
+ }
1063
+ const assignment = params.classification.assignments[role];
1064
+ const healthStatus = assignment?.candidate.healthStatus;
1065
+ if (assignment && healthStatus !== "healthy") {
1066
+ warnings.push({
1067
+ code: "GATEWAY_LIVE_AGENT_HEALTH_DEGRADED",
1068
+ message: `Assigned inventory candidate ${assignment.candidate.slug} for live role ${role} is ${healthStatus}.`,
1069
+ reason: `health_${healthStatus}`,
1070
+ role,
1071
+ resolverRole: summary.resolverRole,
1072
+ agentSlug: assignment.candidate.slug,
1073
+ healthStatus,
1074
+ });
1075
+ }
1076
+ }
1077
+ return warnings;
1078
+ };
1079
+ const buildScenarioEnvironmentWarnings = (scenarios) => scenarios.flatMap((scenario) => scenario.warnings
1080
+ .filter((warning) => warning === "agent_run_model_catalog_mismatch" ||
1081
+ warning === "agent_run_upstream_unavailable")
1082
+ .map((warning) => ({
1083
+ code: "GATEWAY_LIVE_AGENT_RUN_DEGRADED",
1084
+ message: `Live agent-run for scenario ${scenario.id} degraded due to an environment/runtime condition.`,
1085
+ reason: warning,
1086
+ role: scenario.role,
1087
+ resolverRole: ROLE_TO_RESOLVER_ROLE[scenario.role],
1088
+ agentSlug: scenario.agentSlug,
1089
+ details: {
1090
+ scenarioId: scenario.id,
1091
+ status: scenario.status,
1092
+ errors: scenario.errors,
1093
+ },
1094
+ })));
738
1095
  export const runCodaliGatewayLiveHarness = async (options = {}) => {
739
1096
  const startedMs = Date.now();
740
1097
  const startedAt = isoNow();
@@ -747,29 +1104,100 @@ export const runCodaliGatewayLiveHarness = async (options = {}) => {
747
1104
  allowImageWorker: options.allowImageWorker ?? true,
748
1105
  agentPolicy: options.agentPolicy,
749
1106
  });
1107
+ const environmentWarnings = buildEnvironmentWarnings({ discovery, classification });
1108
+ const shadowPolicy = normalizeShadowComparisonPolicy(options.shadowComparison);
1109
+ const shadowRecords = [];
1110
+ const shadowEnvironmentWarnings = [];
750
1111
  const scenarioRunner = options.scenarioRunner ?? createMcodaAgentRunScenarioRunner();
751
1112
  const scenarios = options.scenarios?.length
752
1113
  ? options.scenarios.map(scenarioById)
753
1114
  : CODALI_GATEWAY_LIVE_SCENARIOS;
754
1115
  const scenarioResults = [];
755
1116
  for (const scenario of scenarios) {
1117
+ const primaryAssignment = assignmentForScenario(classification, scenario);
756
1118
  const result = await scenarioRunner({
757
1119
  runId,
758
1120
  scenario,
759
- assignment: assignmentForScenario(classification, scenario),
1121
+ assignment: primaryAssignment,
760
1122
  classification,
761
1123
  commandRunner,
762
1124
  command: options.command ?? DEFAULT_MCODA_COMMAND,
763
1125
  timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
764
1126
  forceAgentRun: options.forceAgentRun === true,
765
1127
  });
766
- scenarioResults.push(redactScenario(result));
1128
+ const redactedResult = redactScenario(result);
1129
+ scenarioResults.push(redactedResult);
1130
+ if (!shadowPolicy.enabled) {
1131
+ continue;
1132
+ }
1133
+ if (shadowPolicy.includePrimary) {
1134
+ shadowRecords.push(comparisonRecord({
1135
+ scenario,
1136
+ result: redactedResult,
1137
+ assignment: primaryAssignment,
1138
+ primary: true,
1139
+ candidateRank: 0,
1140
+ }));
1141
+ }
1142
+ const shadowAssignments = resolveShadowAssignmentsForScenario({
1143
+ classification,
1144
+ scenario,
1145
+ primary: primaryAssignment,
1146
+ policy: shadowPolicy,
1147
+ });
1148
+ if (shadowAssignments.length === 0) {
1149
+ shadowEnvironmentWarnings.push({
1150
+ code: "GATEWAY_SHADOW_COMPARISON_CANDIDATE_UNAVAILABLE",
1151
+ message: `No eligible shadow comparison candidate found for live role ${scenario.role}.`,
1152
+ reason: shadowPolicy.requireHealthy
1153
+ ? "no_healthy_alternate_candidate"
1154
+ : "no_alternate_candidate",
1155
+ role: scenario.role,
1156
+ resolverRole: ROLE_TO_RESOLVER_ROLE[scenario.role],
1157
+ });
1158
+ }
1159
+ for (const [candidateIndex, shadowAssignment] of shadowAssignments.entries()) {
1160
+ const shadowResult = await scenarioRunner({
1161
+ runId,
1162
+ scenario,
1163
+ assignment: shadowAssignment,
1164
+ classification,
1165
+ commandRunner,
1166
+ command: options.command ?? DEFAULT_MCODA_COMMAND,
1167
+ timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
1168
+ forceAgentRun: options.forceAgentRun === true,
1169
+ });
1170
+ shadowRecords.push(comparisonRecord({
1171
+ scenario,
1172
+ result: redactScenario(shadowResult),
1173
+ assignment: shadowAssignment,
1174
+ primary: false,
1175
+ candidateRank: candidateIndex + 1,
1176
+ }));
1177
+ }
767
1178
  }
768
1179
  const endedAt = isoNow();
769
1180
  const summary = summarizeHarness(classification, scenarioResults);
1181
+ const scenarioEnvironmentWarnings = buildScenarioEnvironmentWarnings(scenarioResults);
1182
+ const allEnvironmentWarnings = [
1183
+ ...environmentWarnings,
1184
+ ...scenarioEnvironmentWarnings,
1185
+ ];
1186
+ const shadowComparison = {
1187
+ policy: shadowPolicy,
1188
+ status: !shadowPolicy.enabled
1189
+ ? "disabled"
1190
+ : shadowRecords.length > 0
1191
+ ? "compared"
1192
+ : "skipped",
1193
+ records: shadowRecords,
1194
+ environmentWarnings: shadowEnvironmentWarnings,
1195
+ };
770
1196
  const warnings = unique([
771
1197
  ...classification.warnings.map((warning) => warning.code),
772
1198
  ...scenarioResults.flatMap((scenario) => scenario.warnings),
1199
+ ...allEnvironmentWarnings.map((warning) => warning.code),
1200
+ ...shadowEnvironmentWarnings.map((warning) => warning.code),
773
1201
  ]);
774
1202
  const errors = unique([
775
1203
  ...discovery.errors,
@@ -787,6 +1215,8 @@ export const runCodaliGatewayLiveHarness = async (options = {}) => {
787
1215
  discovery,
788
1216
  classification: redactCodaliGatewayLiveValue(classification),
789
1217
  scenarios: scenarioResults,
1218
+ shadowComparison: redactCodaliGatewayLiveValue(shadowComparison),
1219
+ environmentWarnings: redactCodaliGatewayLiveValue(allEnvironmentWarnings),
790
1220
  summary,
791
1221
  warnings,
792
1222
  errors,
@@ -818,6 +1248,10 @@ export const formatCodaliGatewayLiveHarnessTextReport = (result) => {
818
1248
  `JSON-capable agents: ${result.summary.jsonValidAgents.length ? result.summary.jsonValidAgents.join(", ") : "none"}`,
819
1249
  `Large final synthesizer: ${result.summary.largeFinalSynthesizerOk ? "ok" : "missing or not proven"}`,
820
1250
  `Image artifact: ${result.summary.imageArtifactOk ? "ok" : "missing or not proven"}`,
1251
+ `Shadow comparison: ${result.shadowComparison.status} (${result.shadowComparison.records.length} records)`,
1252
+ result.environmentWarnings.length
1253
+ ? `Environment warnings: ${result.environmentWarnings.map((warning) => `${warning.code}:${warning.reason}`).join(", ")}`
1254
+ : "Environment warnings: none",
821
1255
  result.warnings.length ? `Warnings: ${result.warnings.join(", ")}` : "Warnings: none",
822
1256
  result.errors.length ? `Errors: ${result.errors.join(", ")}` : "Errors: none",
823
1257
  ].join("\n");
@@ -0,0 +1,70 @@
1
+ import { type CodaliStorageDatasetKind, type CodaliStorageExportKind, type CodaliStorageObjectRef } from "../storage/CodaliStorageContracts.js";
2
+ import { CODALI_DATASET_REPLAY_FIXTURE_SCHEMA_VERSION } from "../storage/DatasetExportJob.js";
3
+ import { type GatewayDatasetObjectStore } from "../storage/GatewayDatasetStore.js";
4
+ import type { CodaliGatewayEvalCase } from "./GatewayEvalSuite.js";
5
+ export declare const CODALI_GATEWAY_DATASET_EVAL_SCHEMA_VERSION: 1;
6
+ export type CodaliGatewayDatasetEvalStage = "classifier" | "planner" | "tool_router" | "rag_retrieval" | "evidence_extractor" | "verifier" | "context_pack" | "final_answer" | "schema_repair" | "policy_event";
7
+ export declare const CODALI_GATEWAY_DATASET_EVAL_STAGES: CodaliGatewayDatasetEvalStage[];
8
+ export declare const CODALI_GATEWAY_DATASET_EVAL_PROMPT_VERSIONS: Record<CodaliGatewayDatasetEvalStage, string>;
9
+ export interface CodaliGatewayDatasetReplayFixtureRecord {
10
+ recordId: string;
11
+ datasetKind: CodaliStorageDatasetKind;
12
+ sourceGatewayRecordId?: string;
13
+ inputRef: CodaliStorageObjectRef;
14
+ outputRef?: CodaliStorageObjectRef;
15
+ evidenceRefs?: CodaliStorageObjectRef[];
16
+ quality?: {
17
+ score?: number;
18
+ labels?: string[];
19
+ reviewed?: boolean;
20
+ };
21
+ metadata?: Record<string, unknown>;
22
+ }
23
+ export interface CodaliGatewayDatasetReplayFixture {
24
+ schemaVersion: typeof CODALI_DATASET_REPLAY_FIXTURE_SCHEMA_VERSION;
25
+ exportKind: CodaliStorageExportKind;
26
+ generatedAt: string;
27
+ records: CodaliGatewayDatasetReplayFixtureRecord[];
28
+ }
29
+ export interface CodaliGatewayDatasetEvalSkippedRecord {
30
+ recordId: string;
31
+ stage?: CodaliGatewayDatasetEvalStage;
32
+ reason: string;
33
+ }
34
+ export interface CodaliGatewayDatasetEvalImportLineage {
35
+ schemaVersion: typeof CODALI_GATEWAY_DATASET_EVAL_SCHEMA_VERSION;
36
+ source: "dataset_replay_fixture";
37
+ fixtureId?: string;
38
+ fixtureSchemaVersion: typeof CODALI_DATASET_REPLAY_FIXTURE_SCHEMA_VERSION;
39
+ exportKind: CodaliStorageExportKind;
40
+ generatedAt: string;
41
+ importedAt: string;
42
+ selectedRecordCount: number;
43
+ skippedRecordCount: number;
44
+ sourceRecordIds: string[];
45
+ sourceGatewayRecordIds: string[];
46
+ sourceObjectHashes: string[];
47
+ stageCounts: Record<CodaliGatewayDatasetEvalStage, number>;
48
+ skippedRecords: CodaliGatewayDatasetEvalSkippedRecord[];
49
+ }
50
+ export interface CodaliGatewayDatasetEvalImportResult {
51
+ cases: CodaliGatewayEvalCase[];
52
+ lineage: CodaliGatewayDatasetEvalImportLineage;
53
+ }
54
+ export interface CodaliGatewayDatasetReplayFixtureImportOptions {
55
+ fixture: unknown;
56
+ fixtureId?: string;
57
+ objectStore?: GatewayDatasetObjectStore;
58
+ stages?: CodaliGatewayDatasetEvalStage[];
59
+ limitPerStage?: number;
60
+ now?: () => Date;
61
+ }
62
+ export declare class CodaliGatewayDatasetEvalImportError extends Error {
63
+ readonly code: string;
64
+ constructor(code: string, message: string);
65
+ }
66
+ export declare const parseCodaliGatewayDatasetReplayFixture: (fixture: unknown) => CodaliGatewayDatasetReplayFixture;
67
+ export declare const importCodaliGatewayDatasetReplayFixture: (options: CodaliGatewayDatasetReplayFixtureImportOptions) => Promise<CodaliGatewayDatasetEvalImportResult>;
68
+ export declare const createDefaultCodaliGatewayDatasetReplayFixture: () => CodaliGatewayDatasetReplayFixture;
69
+ export declare const createDefaultCodaliGatewayDatasetEvalImport: () => Promise<CodaliGatewayDatasetEvalImportResult>;
70
+ //# sourceMappingURL=GatewayDatasetEval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GatewayDatasetEval.d.ts","sourceRoot":"","sources":["../../src/eval/GatewayDatasetEval.ts"],"names":[],"mappings":"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"}