@uluops/core 0.8.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +84 -25
  2. package/dist/ai/AIProvider.d.ts.map +1 -1
  3. package/dist/ai/AIProvider.js +28 -12
  4. package/dist/ai/AIProvider.js.map +1 -1
  5. package/dist/ai/ModelCatalog.js +2 -2
  6. package/dist/ai/ModelCatalog.js.map +1 -1
  7. package/dist/ai/ToolAdapter.d.ts.map +1 -1
  8. package/dist/ai/ToolAdapter.js +16 -27
  9. package/dist/ai/ToolAdapter.js.map +1 -1
  10. package/dist/ai/{ShellExecutor.d.ts.map → shellExecutor.d.ts.map} +1 -1
  11. package/dist/ai/{ShellExecutor.js → shellExecutor.js} +3 -2
  12. package/dist/ai/{ShellExecutor.js.map → shellExecutor.js.map} +1 -1
  13. package/dist/analysis/AnalysisSummaryExtractor.d.ts +135 -0
  14. package/dist/analysis/AnalysisSummaryExtractor.d.ts.map +1 -0
  15. package/dist/analysis/AnalysisSummaryExtractor.js +465 -0
  16. package/dist/analysis/AnalysisSummaryExtractor.js.map +1 -0
  17. package/dist/analysis/index.d.ts +2 -0
  18. package/dist/analysis/index.d.ts.map +1 -0
  19. package/dist/analysis/index.js +2 -0
  20. package/dist/analysis/index.js.map +1 -0
  21. package/dist/client/UluOpsClient.d.ts +12 -9
  22. package/dist/client/UluOpsClient.d.ts.map +1 -1
  23. package/dist/client/UluOpsClient.js +76 -47
  24. package/dist/client/UluOpsClient.js.map +1 -1
  25. package/dist/constants.d.ts +4 -3
  26. package/dist/constants.d.ts.map +1 -1
  27. package/dist/constants.js +13 -3
  28. package/dist/constants.js.map +1 -1
  29. package/dist/errors/index.d.ts +38 -8
  30. package/dist/errors/index.d.ts.map +1 -1
  31. package/dist/errors/index.js +52 -7
  32. package/dist/errors/index.js.map +1 -1
  33. package/dist/executor/AgentExecutor.d.ts +2 -0
  34. package/dist/executor/AgentExecutor.d.ts.map +1 -1
  35. package/dist/executor/AgentExecutor.js +33 -16
  36. package/dist/executor/AgentExecutor.js.map +1 -1
  37. package/dist/executor/CommandExecutor.d.ts +3 -1
  38. package/dist/executor/CommandExecutor.d.ts.map +1 -1
  39. package/dist/executor/CommandExecutor.js +17 -39
  40. package/dist/executor/CommandExecutor.js.map +1 -1
  41. package/dist/executor/PipelineExecutor.d.ts +12 -4
  42. package/dist/executor/PipelineExecutor.d.ts.map +1 -1
  43. package/dist/executor/PipelineExecutor.js +127 -23
  44. package/dist/executor/PipelineExecutor.js.map +1 -1
  45. package/dist/executor/ToolHandler.d.ts +5 -0
  46. package/dist/executor/ToolHandler.d.ts.map +1 -1
  47. package/dist/executor/ToolHandler.js +23 -14
  48. package/dist/executor/ToolHandler.js.map +1 -1
  49. package/dist/executor/WorkflowExecutor.d.ts +18 -2
  50. package/dist/executor/WorkflowExecutor.d.ts.map +1 -1
  51. package/dist/executor/WorkflowExecutor.js +107 -37
  52. package/dist/executor/WorkflowExecutor.js.map +1 -1
  53. package/dist/executor/classifyDecision.d.ts +2 -1
  54. package/dist/executor/classifyDecision.d.ts.map +1 -1
  55. package/dist/executor/classifyDecision.js +4 -2
  56. package/dist/executor/classifyDecision.js.map +1 -1
  57. package/dist/executor/mapCategory.d.ts +5 -0
  58. package/dist/executor/mapCategory.d.ts.map +1 -0
  59. package/dist/executor/mapCategory.js +5 -0
  60. package/dist/executor/mapCategory.js.map +1 -0
  61. package/dist/executor/preflight.d.ts.map +1 -1
  62. package/dist/executor/preflight.js +53 -12
  63. package/dist/executor/preflight.js.map +1 -1
  64. package/dist/index.d.ts +6 -5
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +5 -3
  67. package/dist/index.js.map +1 -1
  68. package/dist/parser/OutputExtractor.d.ts +7 -25
  69. package/dist/parser/OutputExtractor.d.ts.map +1 -1
  70. package/dist/parser/OutputExtractor.js +23 -528
  71. package/dist/parser/OutputExtractor.js.map +1 -1
  72. package/dist/parser/OutputNormalizer.d.ts +45 -0
  73. package/dist/parser/OutputNormalizer.d.ts.map +1 -0
  74. package/dist/parser/OutputNormalizer.js +572 -0
  75. package/dist/parser/OutputNormalizer.js.map +1 -0
  76. package/dist/parser/outputSchemas.d.ts +266 -36
  77. package/dist/parser/outputSchemas.d.ts.map +1 -1
  78. package/dist/parser/outputSchemas.js +109 -0
  79. package/dist/parser/outputSchemas.js.map +1 -1
  80. package/dist/registry/RegistryClient.d.ts +9 -7
  81. package/dist/registry/RegistryClient.d.ts.map +1 -1
  82. package/dist/registry/RegistryClient.js +82 -51
  83. package/dist/registry/RegistryClient.js.map +1 -1
  84. package/dist/submission/SubmissionClient.d.ts +81 -0
  85. package/dist/submission/SubmissionClient.d.ts.map +1 -0
  86. package/dist/submission/SubmissionClient.js +266 -0
  87. package/dist/submission/SubmissionClient.js.map +1 -0
  88. package/dist/types/agent.d.ts +13 -1
  89. package/dist/types/agent.d.ts.map +1 -1
  90. package/dist/types/command.d.ts +1 -1
  91. package/dist/types/command.d.ts.map +1 -1
  92. package/dist/types/config.d.ts +21 -7
  93. package/dist/types/config.d.ts.map +1 -1
  94. package/dist/types/execution.d.ts +7 -0
  95. package/dist/types/execution.d.ts.map +1 -1
  96. package/dist/types/index.d.ts +1 -1
  97. package/dist/types/pipeline.d.ts +18 -6
  98. package/dist/types/pipeline.d.ts.map +1 -1
  99. package/dist/types/registry.d.ts +11 -4
  100. package/dist/types/registry.d.ts.map +1 -1
  101. package/dist/types/submission.d.ts +116 -0
  102. package/dist/types/submission.d.ts.map +1 -0
  103. package/dist/types/submission.js +2 -0
  104. package/dist/types/submission.js.map +1 -0
  105. package/dist/types/validation.d.ts +4 -0
  106. package/dist/types/validation.d.ts.map +1 -1
  107. package/dist/types/workflow.d.ts +7 -1
  108. package/dist/types/workflow.d.ts.map +1 -1
  109. package/dist/utils/aggregateScores.d.ts +26 -0
  110. package/dist/utils/aggregateScores.d.ts.map +1 -0
  111. package/dist/utils/aggregateScores.js +45 -0
  112. package/dist/utils/aggregateScores.js.map +1 -0
  113. package/dist/utils/parseRef.d.ts.map +1 -1
  114. package/dist/utils/parseRef.js +3 -1
  115. package/dist/utils/parseRef.js.map +1 -1
  116. package/dist/utils/topoSort.d.ts.map +1 -1
  117. package/dist/utils/topoSort.js +5 -2
  118. package/dist/utils/topoSort.js.map +1 -1
  119. package/dist/validation/ValidationClient.d.ts +30 -0
  120. package/dist/validation/ValidationClient.d.ts.map +1 -1
  121. package/dist/validation/ValidationClient.js +106 -18
  122. package/dist/validation/ValidationClient.js.map +1 -1
  123. package/package.json +6 -6
  124. package/dist/ai/index.d.ts +0 -6
  125. package/dist/ai/index.d.ts.map +0 -1
  126. package/dist/ai/index.js +0 -4
  127. package/dist/ai/index.js.map +0 -1
  128. package/dist/registry/index.d.ts +0 -2
  129. package/dist/registry/index.d.ts.map +0 -1
  130. package/dist/registry/index.js +0 -2
  131. package/dist/registry/index.js.map +0 -1
  132. package/dist/validation/index.d.ts +0 -2
  133. package/dist/validation/index.d.ts.map +0 -1
  134. package/dist/validation/index.js +0 -2
  135. package/dist/validation/index.js.map +0 -1
  136. /package/dist/ai/{ShellExecutor.d.ts → shellExecutor.d.ts} +0 -0
@@ -1,12 +1,17 @@
1
1
  import type { AgentType } from '../types/execution.js';
2
2
  import type { ParsedOutput, ExtractionOptions, ExtractionResult } from '../types/parser.js';
3
3
  /**
4
- * Extracts structured output from LLM responses using a 3-strategy fallback:
5
- * 1. JSON code fence (highest confidence)
4
+ * Extracts structured output from LLM responses using a 4-strategy fallback:
5
+ * 0. AI SDK structured output (highest confidence — schema-validated by the SDK)
6
+ * 1. JSON code fence
6
7
  * 2. Inline JSON detection
7
8
  * 3. Structured text pattern matching (lowest confidence)
9
+ *
10
+ * Normalization of parsed JSON into the unified ParsedOutput type is delegated
11
+ * to OutputNormalizer, which handles the diversity of LLM output shapes.
8
12
  */
9
13
  export declare class OutputExtractor {
14
+ private normalizer;
10
15
  private static readonly INLINE_JSON_PATTERN;
11
16
  private static readonly STRUCTURED_PATTERNS;
12
17
  /**
@@ -25,28 +30,5 @@ export declare class OutputExtractor {
25
30
  private extractIssuesFromText;
26
31
  private inferPriorityFromContext;
27
32
  private inferSeverityFromContext;
28
- /** Resolved source objects from common nesting patterns. Reduces parameter passing across resolve methods. */
29
- private buildParseSources;
30
- private normalizeOutput;
31
- private resolveAgentFields;
32
- private attachFlatIssues;
33
- private asRecord;
34
- /**
35
- * Scan all top-level object values for one that contains 'score' or 'decision'.
36
- * Handles arbitrary wrapper names (validation, validations, validationResults, etc.)
37
- * without whitelisting specific field names.
38
- */
39
- private findWrapperWithScoreOrDecision;
40
- private resolveDecisionField;
41
- private resolveScoreField;
42
- private resolveCategories;
43
- private resolveIssuesFlat;
44
- private normalizeDecision;
45
- private parseCategories;
46
- private parseFindings;
47
- private parseIssues;
48
- private parseArtifacts;
49
- private normalizePriority;
50
- private normalizeSeverity;
51
33
  }
52
34
  //# sourceMappingURL=OutputExtractor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"OutputExtractor.d.ts","sourceRoot":"","sources":["../../src/parser/OutputExtractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,KAAK,EACV,YAAY,EAGZ,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAG5B;;;;;GAKG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAyD;IACpG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAazC;IAEF;;OAEG;IACH,OAAO,CACL,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,EACpB,OAAO,GAAE,iBAAsB,GAC9B,YAAY;IAKf;;OAEG;IACH,mBAAmB,CACjB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,EACpB,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB;IAoEnB,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,iBAAiB;IAoDzB,OAAO,CAAC,mBAAmB;IAqC3B,OAAO,CAAC,yBAAyB;IA0DjC,OAAO,CAAC,qBAAqB;IAwB7B,OAAO,CAAC,wBAAwB;IAQhC,OAAO,CAAC,wBAAwB;IAQhC,8GAA8G;IAC9G,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,eAAe;IA8BvB,OAAO,CAAC,kBAAkB;IA6B1B,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,QAAQ;IAMhB;;;;OAIG;IACH,OAAO,CAAC,8BAA8B;IAgBtC,OAAO,CAAC,oBAAoB;IAqC5B,OAAO,CAAC,iBAAiB;IA8DzB,OAAO,CAAC,iBAAiB;IA8EzB,OAAO,CAAC,iBAAiB;IAiDzB,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,WAAW;IAmGnB,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,iBAAiB;CAQ1B"}
1
+ {"version":3,"file":"OutputExtractor.d.ts","sourceRoot":"","sources":["../../src/parser/OutputExtractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAI5B;;;;;;;;;GASG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,UAAU,CAA0B;IAE5C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAyD;IACpG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAazC;IAEF;;OAEG;IACH,OAAO,CACL,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,EACpB,OAAO,GAAE,iBAAsB,GAC9B,YAAY;IAKf;;OAEG;IACH,mBAAmB,CACjB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,EACpB,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB;IAgFnB,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,iBAAiB;IAoDzB,OAAO,CAAC,mBAAmB;IAqC3B,OAAO,CAAC,yBAAyB;IA0DjC,OAAO,CAAC,qBAAqB;IAwB7B,OAAO,CAAC,wBAAwB;IAQhC,OAAO,CAAC,wBAAwB;CAOjC"}
@@ -1,11 +1,17 @@
1
1
  import { ParseError } from '../errors/index.js';
2
+ import { OutputNormalizer } from './OutputNormalizer.js';
2
3
  /**
3
- * Extracts structured output from LLM responses using a 3-strategy fallback:
4
- * 1. JSON code fence (highest confidence)
4
+ * Extracts structured output from LLM responses using a 4-strategy fallback:
5
+ * 0. AI SDK structured output (highest confidence — schema-validated by the SDK)
6
+ * 1. JSON code fence
5
7
  * 2. Inline JSON detection
6
8
  * 3. Structured text pattern matching (lowest confidence)
9
+ *
10
+ * Normalization of parsed JSON into the unified ParsedOutput type is delegated
11
+ * to OutputNormalizer, which handles the diversity of LLM output shapes.
7
12
  */
8
13
  export class OutputExtractor {
14
+ normalizer = new OutputNormalizer();
9
15
  static INLINE_JSON_PATTERN = /\{[\s\S]*?(?:"decision"|"status"|"score")[\s\S]*?\}/;
10
16
  static STRUCTURED_PATTERNS = {
11
17
  decision: /(?:decision|status|result)\s*[:=]\s*["']?(\w+)["']?/i,
@@ -33,11 +39,22 @@ export class OutputExtractor {
33
39
  */
34
40
  extractWithMetadata(content, agentType, options = {}) {
35
41
  const warnings = [];
42
+ // ── Extraction strategies (ordered by confidence) ──────────────────────
43
+ // Confidence values are heuristics encoding relative trust in each method.
44
+ // They were calibrated against Claude's output patterns and may need
45
+ // recalibration as new models (GPT, Gemini) are added. The 0.7 threshold
46
+ // in AgentExecutor gates EXTRACTION_FAILED decisions — any strategy below
47
+ // that threshold produces results that won't be trusted as real decisions.
48
+ //
49
+ // Strategy 1: JSON code fence (0.95) — model explicitly wrapped output
50
+ // Strategy 2: Whole/inline JSON (0.9/0.75) — found parseable JSON in text
51
+ // Strategy 3: Structured text (0.5) — regex matched decision/score patterns
52
+ // Strategy 4: Fallback (0.0) — nothing found, emit ERROR/0 defaults
36
53
  // Strategy 1: Try JSON code fence (highest confidence)
37
54
  const fenceResult = this.extractFromCodeFence(content, options);
38
55
  if (fenceResult) {
39
56
  return {
40
- output: this.normalizeOutput(fenceResult, agentType),
57
+ output: this.normalizer.normalizeOutput(fenceResult, agentType),
41
58
  method: 'json_code_fence',
42
59
  confidence: 0.95,
43
60
  warnings,
@@ -47,7 +64,7 @@ export class OutputExtractor {
47
64
  const wholeJsonResult = this.extractWholeJson(content);
48
65
  if (wholeJsonResult) {
49
66
  return {
50
- output: this.normalizeOutput(wholeJsonResult, agentType),
67
+ output: this.normalizer.normalizeOutput(wholeJsonResult, agentType),
51
68
  method: 'inline_json',
52
69
  confidence: 0.9,
53
70
  warnings,
@@ -58,7 +75,7 @@ export class OutputExtractor {
58
75
  if (inlineResult) {
59
76
  warnings.push('Extracted from inline JSON - consider using code fence for reliability');
60
77
  return {
61
- output: this.normalizeOutput(inlineResult, agentType),
78
+ output: this.normalizer.normalizeOutput(inlineResult, agentType),
62
79
  method: 'inline_json',
63
80
  confidence: 0.75,
64
81
  warnings,
@@ -215,7 +232,7 @@ export class OutputExtractor {
215
232
  }
216
233
  const output = {
217
234
  decision: decisionMatch
218
- ? this.normalizeDecision(decisionMatch[1] ?? '', agentType)
235
+ ? this.normalizer.normalizeDecision(decisionMatch[1] ?? '', agentType)
219
236
  : 'UNKNOWN',
220
237
  };
221
238
  if (scoreMatch?.[1]) {
@@ -291,527 +308,5 @@ export class OutputExtractor {
291
308
  return 'low';
292
309
  return 'medium';
293
310
  }
294
- /** Resolved source objects from common nesting patterns. Reduces parameter passing across resolve methods. */
295
- buildParseSources(obj) {
296
- const result = this.asRecord(obj['result']);
297
- const summary = this.asRecord(obj['summary']) ?? this.asRecord(result?.['summary']);
298
- const report = this.asRecord(obj['report']);
299
- const reportResults = this.asRecord(report?.['results']) ?? this.asRecord(obj['results']);
300
- const reportSummary = this.asRecord(report?.['summary']) ?? this.asRecord(reportResults?.['summary']);
301
- const validationSummary = this.findWrapperWithScoreOrDecision(obj);
302
- return { obj, result, summary, report, reportResults, reportSummary, validationSummary };
303
- }
304
- normalizeOutput(raw, agentType) {
305
- if (!raw || typeof raw !== 'object') {
306
- return { decision: 'ERROR' };
307
- }
308
- const obj = raw;
309
- const sources = this.buildParseSources(obj);
310
- const output = {
311
- decision: this.normalizeDecision(this.resolveDecisionField(sources), agentType),
312
- rawJson: raw,
313
- };
314
- // Resolve score
315
- const rawScore = this.resolveScoreField(sources);
316
- if (typeof rawScore === 'number') {
317
- output.score = rawScore;
318
- }
319
- else if (typeof rawScore === 'string') {
320
- output.score = parseFloat(rawScore);
321
- }
322
- this.resolveAgentFields(output, sources);
323
- if (Array.isArray(obj['artifacts'])) {
324
- output.artifacts = this.parseArtifacts(obj['artifacts']);
325
- }
326
- return output;
327
- }
328
- resolveAgentFields(output, sources) {
329
- const { obj, result, summary, report } = sources;
330
- // Resolve maxScore
331
- const rawMaxScore = obj['maxScore'] ?? obj['max_score']
332
- ?? result?.['max_score'] ?? result?.['maxScore']
333
- ?? summary?.['max_score'] ?? summary?.['maxScore']
334
- ?? obj['pass_threshold'];
335
- if (typeof rawMaxScore === 'number') {
336
- output.maxScore = rawMaxScore;
337
- }
338
- else if (typeof rawMaxScore === 'string') {
339
- output.maxScore = parseInt(rawMaxScore, 10);
340
- }
341
- // Resolve categories
342
- output.categories = this.resolveCategories(obj, result, report);
343
- // If no score found but categories exist, sum category scores
344
- if (output.score === undefined && output.categories && output.categories.length > 0) {
345
- output.score = output.categories.reduce((sum, c) => sum + c.score, 0);
346
- }
347
- // Resolve flat issues and attach to categories
348
- this.attachFlatIssues(output, sources);
349
- }
350
- attachFlatIssues(output, sources) {
351
- const flatIssues = this.resolveIssuesFlat(sources.obj, sources.result, sources.report, sources.validationSummary);
352
- if (flatIssues.length === 0)
353
- return;
354
- const issuesFinding = {
355
- criterion: 'Extracted findings',
356
- pointsEarned: 0,
357
- pointsPossible: 0,
358
- issues: flatIssues,
359
- };
360
- if (!output.categories || output.categories.length === 0) {
361
- output.categories = [{
362
- name: 'Extracted Issues',
363
- score: output.score ?? 0,
364
- maxScore: output.maxScore ?? 100,
365
- findings: [issuesFinding],
366
- }];
367
- }
368
- else {
369
- const emptyCategory = output.categories.find(c => c.findings.length === 0);
370
- if (emptyCategory) {
371
- emptyCategory.findings.push(issuesFinding);
372
- }
373
- else {
374
- output.categories.push({
375
- name: 'Extracted Issues',
376
- score: output.score ?? 0,
377
- maxScore: output.maxScore ?? 100,
378
- findings: [issuesFinding],
379
- });
380
- }
381
- }
382
- }
383
- asRecord(value) {
384
- return (value && typeof value === 'object' && !Array.isArray(value))
385
- ? value
386
- : undefined;
387
- }
388
- /**
389
- * Scan all top-level object values for one that contains 'score' or 'decision'.
390
- * Handles arbitrary wrapper names (validation, validations, validationResults, etc.)
391
- * without whitelisting specific field names.
392
- */
393
- findWrapperWithScoreOrDecision(obj) {
394
- // Skip known non-wrapper fields
395
- const skip = new Set(['issues', 'categories', 'recommendations', 'evidence',
396
- 'reasoning', 'reasoning_trace', 'notes', 'auto_fail_conditions', 'filesReviewed',
397
- 'files_reviewed', 'artifacts', 'result', 'summary', 'report']);
398
- for (const [key, value] of Object.entries(obj)) {
399
- if (skip.has(key))
400
- continue;
401
- const rec = this.asRecord(value);
402
- if (!rec)
403
- continue;
404
- if ('score' in rec || 'score_total' in rec || 'total_score' in rec || 'decision' in rec || 'status' in rec || 'breakdown' in rec || 'score_breakdown' in rec) {
405
- return rec;
406
- }
407
- }
408
- return undefined;
409
- }
410
- resolveDecisionField(ctx) {
411
- const { obj } = ctx;
412
- const sources = [ctx.obj, ctx.summary, ctx.result, ctx.report, ctx.reportResults, ctx.reportSummary, ctx.validationSummary];
413
- // Check each source for decision/final_decision fields
414
- for (const source of sources) {
415
- if (!source)
416
- continue;
417
- for (const key of ['decision', 'final_decision']) {
418
- const d = source[key];
419
- if (typeof d === 'string')
420
- return d;
421
- // Handle decision as object: { pass: true, label: "PASS" } or { result: "PASS" }
422
- if (d && typeof d === 'object') {
423
- const dObj = d;
424
- if (typeof dObj['result'] === 'string')
425
- return dObj['result'];
426
- if (typeof dObj['label'] === 'string')
427
- return dObj['label'];
428
- if (typeof dObj['value'] === 'string')
429
- return dObj['value'];
430
- if (typeof dObj['status'] === 'string')
431
- return dObj['status'];
432
- if (typeof dObj['pass'] === 'boolean')
433
- return dObj['pass'] ? 'PASS' : 'FAIL';
434
- }
435
- }
436
- }
437
- // Fallback to status field
438
- for (const source of sources) {
439
- if (!source)
440
- continue;
441
- if (typeof source['status'] === 'string')
442
- return source['status'];
443
- }
444
- // Fallback: check if summary is a string starting with a decision word
445
- if (typeof obj['summary'] === 'string') {
446
- const summaryFirst = obj['summary'].split(/[\s\-–—]+/)[0]?.toUpperCase();
447
- if (summaryFirst && ['PASS', 'FAIL', 'WARN', 'ERROR', 'COMPLETE'].includes(summaryFirst)) {
448
- return summaryFirst;
449
- }
450
- }
451
- return 'UNKNOWN';
452
- }
453
- resolveScoreField(ctx) {
454
- const { obj } = ctx;
455
- const sources = [ctx.obj, ctx.summary, ctx.result, ctx.report, ctx.reportResults, ctx.reportSummary, ctx.validationSummary];
456
- // Check each source for a score value
457
- for (const source of sources) {
458
- if (!source)
459
- continue;
460
- for (const scoreKey of ['score', 'total_score', 'score_total']) {
461
- const s = source[scoreKey];
462
- if (typeof s === 'number')
463
- return s;
464
- if (typeof s === 'string' && s.trim() !== '' && !isNaN(Number(s)))
465
- return s;
466
- // Handle score as object: { total: 85, ... }
467
- if (s && typeof s === 'object') {
468
- const sObj = s;
469
- for (const key of ['total', 'value', 'overall', 'final']) {
470
- if (typeof sObj[key] === 'number')
471
- return sObj[key];
472
- if (typeof sObj[key] === 'string')
473
- return sObj[key];
474
- }
475
- }
476
- }
477
- }
478
- // Note: validationSummary (from findWrapperWithScoreOrDecision) is already in the sources loop above.
479
- // Check scores object with named sub-scores (gpt-4.1-nano shape: { scores: { "Code Quality": 23, ... } })
480
- const scores = this.asRecord(obj['scores']);
481
- if (scores) {
482
- if (typeof scores['Total'] === 'number')
483
- return scores['Total'];
484
- if (typeof scores['total'] === 'number')
485
- return scores['total'];
486
- }
487
- // Check breakdown with sub-scores sum (search wrapper objects too)
488
- const wrapper = this.findWrapperWithScoreOrDecision(obj);
489
- const breakdown = this.asRecord(obj['breakdown'])
490
- ?? this.asRecord(obj['score_breakdown'])
491
- ?? this.asRecord(wrapper?.['breakdown'])
492
- ?? this.asRecord(wrapper?.['score_breakdown']);
493
- if (breakdown) {
494
- const values = [];
495
- for (const v of Object.values(breakdown)) {
496
- if (typeof v === 'number') {
497
- values.push(v);
498
- continue;
499
- }
500
- // Handle { points: N, deductions: N } shape
501
- const rec = this.asRecord(v);
502
- if (rec && typeof rec['points'] === 'number') {
503
- const deductions = typeof rec['deductions'] === 'number' ? rec['deductions'] : 0;
504
- values.push(rec['points'] - deductions);
505
- }
506
- }
507
- if (values.length > 0)
508
- return values.reduce((a, b) => a + b, 0);
509
- }
510
- // Check criteria with sub-scores sum (gpt-4.1-nano shape)
511
- const criteria = this.asRecord(obj['criteria']);
512
- if (criteria) {
513
- const values = [];
514
- for (const v of Object.values(criteria)) {
515
- if (typeof v === 'number') {
516
- values.push(v);
517
- continue;
518
- }
519
- const rec = this.asRecord(v);
520
- if (rec && typeof rec['score'] === 'number')
521
- values.push(rec['score']);
522
- }
523
- if (values.length > 0)
524
- return values.reduce((a, b) => a + b, 0);
525
- }
526
- return undefined;
527
- }
528
- resolveCategories(obj, result, report) {
529
- // Direct categories array
530
- for (const source of [obj, result, report]) {
531
- if (!source)
532
- continue;
533
- if (Array.isArray(source['categories'])) {
534
- return this.parseCategories(source['categories']);
535
- }
536
- }
537
- // Named scores object → synthetic categories (e.g., { scores: { "Code Quality": 23, ... } })
538
- const scores = this.asRecord(obj['scores']) ?? this.asRecord(report?.['scores']);
539
- if (scores) {
540
- const cats = [];
541
- for (const [name, value] of Object.entries(scores)) {
542
- if (typeof value === 'number' && name !== 'Total' && name !== 'total' && name !== 'pass_threshold') {
543
- cats.push({ name, score: value, maxScore: 100, findings: [] });
544
- }
545
- }
546
- if (cats.length > 0)
547
- return cats;
548
- }
549
- // Breakdown object → synthetic categories
550
- // Search top-level breakdown, score_breakdown, and inside any wrapper object
551
- const wrapper = this.findWrapperWithScoreOrDecision(obj);
552
- const breakdown = this.asRecord(obj['breakdown'])
553
- ?? this.asRecord(obj['score_breakdown'])
554
- ?? this.asRecord(wrapper?.['breakdown'])
555
- ?? this.asRecord(wrapper?.['score_breakdown']);
556
- if (breakdown) {
557
- const cats = [];
558
- for (const [name, value] of Object.entries(breakdown)) {
559
- if (typeof value === 'number') {
560
- cats.push({ name, score: value, maxScore: 100, findings: [] });
561
- }
562
- else {
563
- // Handle { points: N, deductions: N } shape
564
- const rec = this.asRecord(value);
565
- if (rec && typeof rec['points'] === 'number') {
566
- const points = rec['points'];
567
- const deductions = typeof rec['deductions'] === 'number' ? rec['deductions'] : 0;
568
- cats.push({ name, score: points - deductions, maxScore: 100, findings: [] });
569
- }
570
- }
571
- }
572
- if (cats.length > 0)
573
- return cats;
574
- }
575
- // Criteria object with nested scores → synthetic categories
576
- const criteria = this.asRecord(obj['criteria']);
577
- if (criteria) {
578
- const cats = [];
579
- for (const [name, value] of Object.entries(criteria)) {
580
- const rec = this.asRecord(value);
581
- if (rec && typeof rec['score'] === 'number') {
582
- cats.push({
583
- name,
584
- score: rec['score'],
585
- maxScore: 100,
586
- findings: this.parseIssues(Array.isArray(rec['issues']) ? rec['issues'] : []).map(issue => ({
587
- criterion: issue.title,
588
- pointsEarned: 0,
589
- pointsPossible: 0,
590
- issues: [issue],
591
- })),
592
- });
593
- }
594
- }
595
- if (cats.length > 0)
596
- return cats;
597
- }
598
- return undefined;
599
- }
600
- resolveIssuesFlat(obj, result, report, wrapper) {
601
- const issues = [];
602
- // Check multiple issue-like keys across all source objects
603
- const issueKeys = ['issues', 'recommendations', 'warnings', 'findings'];
604
- for (const source of [obj, result, report, wrapper]) {
605
- if (!source)
606
- continue;
607
- for (const key of issueKeys) {
608
- if (Array.isArray(source[key])) {
609
- issues.push(...this.parseIssues(source[key]));
610
- if (issues.length > 0)
611
- return issues;
612
- }
613
- // Nested: { issues: { items: [...] } } or { issues: { details: [...] } }
614
- const nested = this.asRecord(source[key]);
615
- if (nested) {
616
- const nestedArray = nested['items'] ?? nested['details'] ?? nested['list'];
617
- if (Array.isArray(nestedArray)) {
618
- issues.push(...this.parseIssues(nestedArray));
619
- if (issues.length > 0)
620
- return issues;
621
- }
622
- }
623
- }
624
- }
625
- // issues_found with warnings/suggestions (gpt-5-mini shape)
626
- for (const source of [obj, wrapper]) {
627
- if (!source)
628
- continue;
629
- const issuesFound = this.asRecord(source['issues_found']);
630
- if (issuesFound) {
631
- if (Array.isArray(issuesFound['critical'])) {
632
- issues.push(...this.parseIssues(issuesFound['critical']));
633
- }
634
- if (Array.isArray(issuesFound['warnings'])) {
635
- issues.push(...this.parseIssues(issuesFound['warnings']));
636
- }
637
- if (Array.isArray(issuesFound['suggestions'])) {
638
- issues.push(...this.parseIssues(issuesFound['suggestions']));
639
- }
640
- }
641
- }
642
- return issues;
643
- }
644
- normalizeDecision(decision, agentType) {
645
- // Strip emojis and non-ASCII symbols before processing
646
- const cleaned = decision.replace(/[\u{1F000}-\u{1FFFF}]|[\u{2600}-\u{27BF}]|[\u{FE00}-\u{FE0F}]|[\u{200D}]/gu, '').trim();
647
- const upper = cleaned.toUpperCase().trim();
648
- // Extract first word for labels like "PASS - Ready for next phase"
649
- const firstWord = upper.split(/[\s\-–—]+/)[0] ?? upper;
650
- if (agentType === 'validator') {
651
- if (['PASS', 'PASSED', 'OK', 'SUCCESS'].includes(firstWord))
652
- return 'PASS';
653
- if (['WARN', 'WARNING', 'CAUTION'].includes(firstWord))
654
- return 'WARN';
655
- if (['FAIL', 'FAILED', 'ERROR', 'REJECT'].includes(firstWord))
656
- return 'FAIL';
657
- }
658
- if (agentType === 'executor') {
659
- if (['SUCCESS', 'COMPLETE', 'DONE', 'PASS'].includes(firstWord))
660
- return 'COMPLETE';
661
- if (['PARTIAL', 'INCOMPLETE'].includes(firstWord))
662
- return 'PARTIAL';
663
- if (['FAIL', 'FAILED', 'ERROR'].includes(firstWord))
664
- return 'FAILED';
665
- }
666
- return upper;
667
- }
668
- parseCategories(raw) {
669
- return raw
670
- .filter((item) => typeof item === 'object' && item !== null)
671
- .map(item => ({
672
- name: String(item['name'] ?? item['category'] ?? 'Unknown'),
673
- score: Number(item['score'] ?? item['points'] ?? 0),
674
- maxScore: Number(item['maxScore'] ?? item['maxPoints'] ?? item['max_points'] ?? item['total'] ?? 100),
675
- findings: this.parseFindings(Array.isArray(item['findings']) ? item['findings'] : []),
676
- }));
677
- }
678
- parseFindings(raw) {
679
- return raw
680
- .filter((item) => typeof item === 'object' && item !== null)
681
- .map(item => ({
682
- criterion: String(item['criterion'] ?? item['name'] ?? 'Unknown'),
683
- pointsEarned: Number(item['pointsEarned'] ?? item['points_earned'] ?? item['score'] ?? 0),
684
- pointsPossible: Number(item['pointsPossible'] ?? item['points_possible'] ?? item['maxScore'] ?? item['maxPoints'] ?? 0),
685
- issues: this.parseIssues(Array.isArray(item['issues']) ? item['issues'] : []),
686
- }));
687
- }
688
- parseIssues(raw) {
689
- // Flatten grouped issues: [{severity: "CRITICAL", issues: [...]}, ...] → flat array
690
- const flatItems = [];
691
- for (const item of raw) {
692
- if (typeof item !== 'object' || item === null)
693
- continue;
694
- const rec = item;
695
- if (Array.isArray(rec['issues'])) {
696
- // This is a group — recurse into the nested issues array, inheriting severity
697
- const groupSeverity = rec['severity'];
698
- for (const sub of rec['issues']) {
699
- if (typeof sub === 'object' && sub !== null) {
700
- const subRec = sub;
701
- if (groupSeverity && !subRec['severity'])
702
- subRec['severity'] = groupSeverity;
703
- flatItems.push(subRec);
704
- }
705
- }
706
- }
707
- else {
708
- flatItems.push(rec);
709
- }
710
- }
711
- return flatItems
712
- .map(item => {
713
- // Resolve file path and line number from various shapes
714
- let filePath = item['filePath']
715
- ?? item['file_path']
716
- ?? item['file'];
717
- let lineNumber = typeof item['lineNumber'] === 'number'
718
- ? item['lineNumber']
719
- : typeof item['line_number'] === 'number'
720
- ? item['line_number']
721
- : typeof item['line'] === 'number'
722
- ? item['line']
723
- : typeof item['line_start'] === 'number'
724
- ? item['line_start']
725
- : undefined;
726
- // Handle line as string: "24-50" or "24"
727
- if (lineNumber === undefined && typeof item['line'] === 'string') {
728
- const lineMatch = item['line'].match(/^(\d+)/);
729
- if (lineMatch)
730
- lineNumber = parseInt(lineMatch[1], 10);
731
- }
732
- if (lineNumber === undefined && typeof item['line_number'] === 'string') {
733
- const lineMatch = item['line_number'].match(/^(\d+)/);
734
- if (lineMatch)
735
- lineNumber = parseInt(lineMatch[1], 10);
736
- }
737
- if (lineNumber === undefined && typeof item['lineNumber'] === 'string') {
738
- const lineMatch = item['lineNumber'].match(/^(\d+)/);
739
- if (lineMatch)
740
- lineNumber = parseInt(lineMatch[1], 10);
741
- }
742
- // Handle combined fields: "file_line" or "location" like "src/foo.ts:42-50"
743
- for (const combinedKey of ['file_line', 'location']) {
744
- if (!filePath && typeof item[combinedKey] === 'string') {
745
- const flMatch = item[combinedKey].match(/^([\w/.@-]+\.\w+):(\d+)/);
746
- if (flMatch) {
747
- filePath = flMatch[1];
748
- lineNumber = lineNumber ?? parseInt(flMatch[2], 10);
749
- }
750
- else if (combinedKey === 'file_line') {
751
- filePath = item[combinedKey];
752
- }
753
- }
754
- }
755
- // Handle locations array: [{ file: "...", line_start: N }]
756
- if (!filePath && Array.isArray(item['locations']) && item['locations'].length > 0) {
757
- const loc = this.asRecord(item['locations'][0]);
758
- if (loc) {
759
- filePath = loc['file'] ?? loc['filePath'];
760
- lineNumber = lineNumber ?? (typeof loc['line_start'] === 'number' ? loc['line_start'] : undefined);
761
- }
762
- }
763
- // Resolve title: prefer explicit title/message, fall back to issue/summary/description/name
764
- const hasExplicitTitle = item['title'] !== undefined || item['message'] !== undefined
765
- || item['issue'] !== undefined || item['summary'] !== undefined;
766
- const title = String(item['title'] ?? item['message'] ?? item['issue'] ?? item['summary']
767
- ?? item['name'] ?? item['description'] ?? 'Untitled Issue');
768
- // For description: if title consumed 'description', use explanation/suggestion/recommendation instead
769
- const detailsStr = typeof item['details'] === 'string' ? item['details'] : undefined;
770
- const description = hasExplicitTitle
771
- ? String(item['description'] ?? detailsStr ?? item['explanation'] ?? item['suggestion'] ?? item['recommendation'] ?? '')
772
- : String(item['explanation'] ?? detailsStr ?? item['suggestion'] ?? item['recommendation'] ?? item['description'] ?? '');
773
- return {
774
- title,
775
- priority: this.normalizePriority(item['priority'] ?? item['type']),
776
- severity: this.normalizeSeverity(item['severity']),
777
- failureCode: item['failureCode']
778
- ?? item['failure_code']
779
- ?? item['code'],
780
- filePath,
781
- lineNumber,
782
- description,
783
- };
784
- });
785
- }
786
- parseArtifacts(raw) {
787
- return raw
788
- .filter((item) => typeof item === 'object' && item !== null)
789
- .map(item => ({
790
- name: String(item['name'] ?? 'Untitled'),
791
- path: String(item['path'] ?? ''),
792
- size: typeof item['size'] === 'number' ? item['size'] : undefined,
793
- contentType: item['contentType'] ?? item['content_type'],
794
- }));
795
- }
796
- normalizePriority(value) {
797
- const str = String(value ?? 'suggested').toLowerCase();
798
- if (['critical', 'high', 'p0'].includes(str))
799
- return 'critical';
800
- if (['backlog', 'low', 'p2'].includes(str))
801
- return 'backlog';
802
- return 'suggested';
803
- }
804
- normalizeSeverity(value) {
805
- const str = String(value ?? 'medium').toLowerCase();
806
- if (str === 'critical')
807
- return 'critical';
808
- if (str === 'high')
809
- return 'high';
810
- if (str === 'low')
811
- return 'low';
812
- if (['info', 'informational', 'note'].includes(str))
813
- return 'info';
814
- return 'medium';
815
- }
816
311
  }
817
312
  //# sourceMappingURL=OutputExtractor.js.map