@gracker/smartperfetto 1.0.15 → 1.0.17

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 (189) hide show
  1. package/dist/agent/agents/base/baseAgent.d.ts.map +1 -1
  2. package/dist/agent/agents/base/baseAgent.js +5 -1
  3. package/dist/agent/agents/base/baseAgent.js.map +1 -1
  4. package/dist/agent/context/enhancedSessionContext.d.ts +5 -0
  5. package/dist/agent/context/enhancedSessionContext.d.ts.map +1 -1
  6. package/dist/agent/context/enhancedSessionContext.js +13 -0
  7. package/dist/agent/context/enhancedSessionContext.js.map +1 -1
  8. package/dist/agent/core/conclusionContract.d.ts +23 -1
  9. package/dist/agent/core/conclusionContract.d.ts.map +1 -1
  10. package/dist/agent/core/conclusionGenerator.d.ts.map +1 -1
  11. package/dist/agent/core/conclusionGenerator.js +223 -27
  12. package/dist/agent/core/conclusionGenerator.js.map +1 -1
  13. package/dist/agent/core/executors/directSkillExecutor.d.ts.map +1 -1
  14. package/dist/agent/core/executors/directSkillExecutor.js +6 -12
  15. package/dist/agent/core/executors/directSkillExecutor.js.map +1 -1
  16. package/dist/agent/core/orchestratorTypes.d.ts +6 -0
  17. package/dist/agent/core/orchestratorTypes.d.ts.map +1 -1
  18. package/dist/agent/core/orchestratorTypes.js.map +1 -1
  19. package/dist/agent/experts/crossDomain/moduleExpertInvoker.d.ts.map +1 -1
  20. package/dist/agent/experts/crossDomain/moduleExpertInvoker.js +4 -1
  21. package/dist/agent/experts/crossDomain/moduleExpertInvoker.js.map +1 -1
  22. package/dist/agent/scene/sceneStage1Runner.d.ts +1 -1
  23. package/dist/agent/scene/sceneStage1Runner.d.ts.map +1 -1
  24. package/dist/agent/scene/sceneStage1Runner.js +1 -1
  25. package/dist/agent/scene/sceneStage1Runner.js.map +1 -1
  26. package/dist/agent/scene/sceneStoryService.d.ts +1 -1
  27. package/dist/agent/scene/sceneStoryService.d.ts.map +1 -1
  28. package/dist/agent/scene/sceneStoryService.js +4 -1
  29. package/dist/agent/scene/sceneStoryService.js.map +1 -1
  30. package/dist/agent/types/agentProtocol.d.ts.map +1 -1
  31. package/dist/agent/types/agentProtocol.js +4 -1
  32. package/dist/agent/types/agentProtocol.js.map +1 -1
  33. package/dist/agent/types.d.ts +7 -0
  34. package/dist/agent/types.d.ts.map +1 -1
  35. package/dist/agent/types.js.map +1 -1
  36. package/dist/agentOpenAI/openAiConfig.d.ts +2 -0
  37. package/dist/agentOpenAI/openAiConfig.d.ts.map +1 -1
  38. package/dist/agentOpenAI/openAiConfig.js +3 -0
  39. package/dist/agentOpenAI/openAiConfig.js.map +1 -1
  40. package/dist/agentOpenAI/openAiRuntime.d.ts +8 -0
  41. package/dist/agentOpenAI/openAiRuntime.d.ts.map +1 -1
  42. package/dist/agentOpenAI/openAiRuntime.js +293 -58
  43. package/dist/agentOpenAI/openAiRuntime.js.map +1 -1
  44. package/dist/agentRuntime/runtimeHealth.d.ts +1 -0
  45. package/dist/agentRuntime/runtimeHealth.d.ts.map +1 -1
  46. package/dist/agentv3/artifactStore.d.ts +5 -0
  47. package/dist/agentv3/artifactStore.d.ts.map +1 -1
  48. package/dist/agentv3/artifactStore.js +3 -0
  49. package/dist/agentv3/artifactStore.js.map +1 -1
  50. package/dist/agentv3/claudeMcpServer.d.ts.map +1 -1
  51. package/dist/agentv3/claudeMcpServer.js +125 -86
  52. package/dist/agentv3/claudeMcpServer.js.map +1 -1
  53. package/dist/agentv3/claudeRuntime.d.ts.map +1 -1
  54. package/dist/agentv3/claudeRuntime.js +147 -65
  55. package/dist/agentv3/claudeRuntime.js.map +1 -1
  56. package/dist/agentv3/claudeSseBridge.d.ts +6 -0
  57. package/dist/agentv3/claudeSseBridge.d.ts.map +1 -1
  58. package/dist/agentv3/claudeSseBridge.js +1 -0
  59. package/dist/agentv3/claudeSseBridge.js.map +1 -1
  60. package/dist/agentv3/sessionStateSnapshot.d.ts +23 -0
  61. package/dist/agentv3/sessionStateSnapshot.d.ts.map +1 -1
  62. package/dist/assistant/application/agentAnalyzeSessionService.d.ts +6 -0
  63. package/dist/assistant/application/agentAnalyzeSessionService.d.ts.map +1 -1
  64. package/dist/assistant/application/agentAnalyzeSessionService.js +9 -3
  65. package/dist/assistant/application/agentAnalyzeSessionService.js.map +1 -1
  66. package/dist/assistant/application/assistantApplicationService.d.ts.map +1 -1
  67. package/dist/assistant/application/assistantApplicationService.js +3 -1
  68. package/dist/assistant/application/assistantApplicationService.js.map +1 -1
  69. package/dist/cli-user/commands/report.js +64 -0
  70. package/dist/cli-user/commands/report.js.map +1 -1
  71. package/dist/cli-user/io/paths.d.ts +3 -0
  72. package/dist/cli-user/io/paths.d.ts.map +1 -1
  73. package/dist/cli-user/io/paths.js +6 -0
  74. package/dist/cli-user/io/paths.js.map +1 -1
  75. package/dist/cli-user/io/sessionStore.d.ts +1 -0
  76. package/dist/cli-user/io/sessionStore.d.ts.map +1 -1
  77. package/dist/cli-user/io/sessionStore.js +5 -0
  78. package/dist/cli-user/io/sessionStore.js.map +1 -1
  79. package/dist/cli-user/repl/renderer.d.ts +8 -0
  80. package/dist/cli-user/repl/renderer.d.ts.map +1 -1
  81. package/dist/cli-user/repl/renderer.js.map +1 -1
  82. package/dist/cli-user/services/cliAnalyzeService.d.ts +3 -0
  83. package/dist/cli-user/services/cliAnalyzeService.d.ts.map +1 -1
  84. package/dist/cli-user/services/cliAnalyzeService.js +103 -1
  85. package/dist/cli-user/services/cliAnalyzeService.js.map +1 -1
  86. package/dist/cli-user/services/turnPersistence.d.ts +0 -10
  87. package/dist/cli-user/services/turnPersistence.d.ts.map +1 -1
  88. package/dist/cli-user/services/turnPersistence.js +62 -0
  89. package/dist/cli-user/services/turnPersistence.js.map +1 -1
  90. package/dist/routes/agentReportRoutes.d.ts +1 -0
  91. package/dist/routes/agentReportRoutes.d.ts.map +1 -1
  92. package/dist/routes/agentReportRoutes.js +13 -2
  93. package/dist/routes/agentReportRoutes.js.map +1 -1
  94. package/dist/routes/agentResumeRoutes.d.ts.map +1 -1
  95. package/dist/routes/agentResumeRoutes.js +51 -5
  96. package/dist/routes/agentResumeRoutes.js.map +1 -1
  97. package/dist/routes/agentRoutes.d.ts.map +1 -1
  98. package/dist/routes/agentRoutes.js +524 -130
  99. package/dist/routes/agentRoutes.js.map +1 -1
  100. package/dist/scripts/verifyAgentSseScrolling.js +142 -2
  101. package/dist/scripts/verifyAgentSseScrolling.js.map +1 -1
  102. package/dist/services/agentEventStore.d.ts.map +1 -1
  103. package/dist/services/agentEventStore.js +13 -3
  104. package/dist/services/agentEventStore.js.map +1 -1
  105. package/dist/services/agentReportData.d.ts +3 -0
  106. package/dist/services/agentReportData.d.ts.map +1 -1
  107. package/dist/services/agentReportData.js.map +1 -1
  108. package/dist/services/agentResultNormalizer.d.ts +15 -3
  109. package/dist/services/agentResultNormalizer.d.ts.map +1 -1
  110. package/dist/services/agentResultNormalizer.js +344 -6
  111. package/dist/services/agentResultNormalizer.js.map +1 -1
  112. package/dist/services/analysisResultSnapshotPipeline.d.ts +3 -0
  113. package/dist/services/analysisResultSnapshotPipeline.d.ts.map +1 -1
  114. package/dist/services/analysisResultSnapshotPipeline.js +3 -0
  115. package/dist/services/analysisResultSnapshotPipeline.js.map +1 -1
  116. package/dist/services/analysisResultSnapshotStore.d.ts.map +1 -1
  117. package/dist/services/analysisResultSnapshotStore.js +34 -2
  118. package/dist/services/analysisResultSnapshotStore.js.map +1 -1
  119. package/dist/services/enterpriseSchema.d.ts.map +1 -1
  120. package/dist/services/enterpriseSchema.js +11 -0
  121. package/dist/services/enterpriseSchema.js.map +1 -1
  122. package/dist/services/evidence/evidenceContractBuilder.d.ts +11 -0
  123. package/dist/services/evidence/evidenceContractBuilder.d.ts.map +1 -0
  124. package/dist/services/evidence/evidenceContractBuilder.js +546 -0
  125. package/dist/services/evidence/evidenceContractBuilder.js.map +1 -0
  126. package/dist/services/finalResultQualityGate.d.ts +18 -0
  127. package/dist/services/finalResultQualityGate.d.ts.map +1 -0
  128. package/dist/services/finalResultQualityGate.js +283 -0
  129. package/dist/services/finalResultQualityGate.js.map +1 -0
  130. package/dist/services/htmlReportGenerator.d.ts +8 -1
  131. package/dist/services/htmlReportGenerator.d.ts.map +1 -1
  132. package/dist/services/htmlReportGenerator.js +129 -42
  133. package/dist/services/htmlReportGenerator.js.map +1 -1
  134. package/dist/services/persistAgentSession.d.ts +2 -0
  135. package/dist/services/persistAgentSession.d.ts.map +1 -1
  136. package/dist/services/persistAgentSession.js +17 -1
  137. package/dist/services/persistAgentSession.js.map +1 -1
  138. package/dist/services/processIdentity/identityContractMapper.d.ts +14 -0
  139. package/dist/services/processIdentity/identityContractMapper.d.ts.map +1 -0
  140. package/dist/services/processIdentity/identityContractMapper.js +135 -0
  141. package/dist/services/processIdentity/identityContractMapper.js.map +1 -0
  142. package/dist/services/processIdentity/types.d.ts +5 -0
  143. package/dist/services/processIdentity/types.d.ts.map +1 -1
  144. package/dist/services/processIdentity/types.js.map +1 -1
  145. package/dist/services/skillEngine/skillExecutor.d.ts +14 -2
  146. package/dist/services/skillEngine/skillExecutor.d.ts.map +1 -1
  147. package/dist/services/skillEngine/skillExecutor.js +133 -13
  148. package/dist/services/skillEngine/skillExecutor.js.map +1 -1
  149. package/dist/services/skillEngine/types.d.ts +2 -0
  150. package/dist/services/skillEngine/types.d.ts.map +1 -1
  151. package/dist/services/verifier/claimVerificationRunner.d.ts +20 -0
  152. package/dist/services/verifier/claimVerificationRunner.d.ts.map +1 -0
  153. package/dist/services/verifier/claimVerificationRunner.js +88 -0
  154. package/dist/services/verifier/claimVerificationRunner.js.map +1 -0
  155. package/dist/services/verifier/deterministicClaimVerifier.d.ts +8 -0
  156. package/dist/services/verifier/deterministicClaimVerifier.d.ts.map +1 -0
  157. package/dist/services/verifier/deterministicClaimVerifier.js +178 -0
  158. package/dist/services/verifier/deterministicClaimVerifier.js.map +1 -0
  159. package/dist/types/claimVerification.d.ts +38 -0
  160. package/dist/types/claimVerification.d.ts.map +1 -0
  161. package/dist/types/claimVerification.js +6 -0
  162. package/dist/types/claimVerification.js.map +1 -0
  163. package/dist/types/dataContract.d.ts +32 -0
  164. package/dist/types/dataContract.d.ts.map +1 -1
  165. package/dist/types/dataContract.js +7 -0
  166. package/dist/types/dataContract.js.map +1 -1
  167. package/dist/types/evidenceContract.d.ts +100 -0
  168. package/dist/types/evidenceContract.d.ts.map +1 -0
  169. package/dist/types/evidenceContract.js +6 -0
  170. package/dist/types/evidenceContract.js.map +1 -0
  171. package/dist/types/identityContract.d.ts +57 -0
  172. package/dist/types/identityContract.d.ts.map +1 -0
  173. package/dist/types/identityContract.js +6 -0
  174. package/dist/types/identityContract.js.map +1 -0
  175. package/dist/types/multiTraceComparison.d.ts +3 -0
  176. package/dist/types/multiTraceComparison.d.ts.map +1 -1
  177. package/package.json +3 -2
  178. package/skills/atomic/process_identity_resolver.skill.yaml +130 -1
  179. package/skills/atomic/process_slice_cpu_hotspots.skill.yaml +321 -0
  180. package/skills/atomic/startup_slow_reasons.skill.yaml +102 -17
  181. package/skills/composite/startup_analysis.skill.yaml +16 -0
  182. package/strategies/anr.strategy.md +2 -2
  183. package/strategies/game.strategy.md +1 -1
  184. package/strategies/general.strategy.md +1 -1
  185. package/strategies/prompt-openai-final-report-continuation-en.template.md +12 -0
  186. package/strategies/prompt-openai-final-report-continuation-zh.template.md +12 -0
  187. package/strategies/prompt-output-format.template.md +1 -1
  188. package/strategies/scrolling.strategy.md +1 -0
  189. package/strategies/startup.strategy.md +4 -1
@@ -53,6 +53,7 @@ const htmlReportGenerator_1 = require("../services/htmlReportGenerator");
53
53
  const agentReportData_1 = require("../services/agentReportData");
54
54
  const persistAgentSession_1 = require("../services/persistAgentSession");
55
55
  const comparisonAppendixService_1 = require("../services/comparisonAppendixService");
56
+ const finalResultQualityGate_1 = require("../services/finalResultQualityGate");
56
57
  const agentResultNormalizer_1 = require("../services/agentResultNormalizer");
57
58
  const reportRoutes_1 = require("./reportRoutes");
58
59
  const sessionPersistenceService_1 = require("../services/sessionPersistenceService");
@@ -92,6 +93,7 @@ const analysisRunStore_1 = require("../services/analysisRunStore");
92
93
  const agentAnalyzeSessionService_1 = require("../assistant/application/agentAnalyzeSessionService");
93
94
  const assistantResultContract_1 = require("../assistant/contracts/assistantResultContract");
94
95
  const analysisResultSnapshotPipeline_1 = require("../services/analysisResultSnapshotPipeline");
96
+ const claimVerificationRunner_1 = require("../services/verifier/claimVerificationRunner");
95
97
  // DataEnvelope types for v2.0 data contract
96
98
  const dataContract_1 = require("../types/dataContract");
97
99
  const skillExecutor_1 = require("../services/skillEngine/skillExecutor");
@@ -100,6 +102,7 @@ const feedbackEnricher_1 = require("../agentv3/selfImprove/feedbackEnricher");
100
102
  const toolNarration_1 = require("../agentv3/toolNarration");
101
103
  const analysisPatternMemory_1 = require("../agentv3/analysisPatternMemory");
102
104
  const runtimePaths_1 = require("../runtimePaths");
105
+ const COMPLETED_ANALYSIS_SSE_EVENTS_QUALITY_GATE_VERSION = 1;
103
106
  const router = express_1.default.Router();
104
107
  const REQUEST_ID_HEADER = 'x-request-id';
105
108
  const MAX_REQUEST_ID_LENGTH = 128;
@@ -363,6 +366,61 @@ function persistBufferedAgentEvent(session, event) {
363
366
  });
364
367
  }
365
368
  }
369
+ function sanitizePersistedAnalysisCompletedEvent(session, event) {
370
+ if (event.eventType !== 'analysis_completed')
371
+ return event;
372
+ let payload;
373
+ try {
374
+ payload = JSON.parse(event.eventData || '{}');
375
+ }
376
+ catch {
377
+ return event;
378
+ }
379
+ const data = payload?.data && typeof payload.data === 'object'
380
+ ? payload.data
381
+ : payload;
382
+ const conclusion = typeof data?.conclusion === 'string'
383
+ ? data.conclusion
384
+ : typeof data?.answer === 'string'
385
+ ? data.answer
386
+ : '';
387
+ if (!conclusion.trim())
388
+ return event;
389
+ const result = {
390
+ sessionId: session.sessionId,
391
+ success: data?.success !== false,
392
+ findings: Array.isArray(data?.findings) ? data.findings : [],
393
+ hypotheses: Array.isArray(data?.hypotheses) ? data.hypotheses : [],
394
+ conclusion,
395
+ confidence: typeof data?.confidence === 'number' ? data.confidence : 0.5,
396
+ rounds: typeof data?.rounds === 'number' ? data.rounds : 1,
397
+ totalDurationMs: typeof data?.totalDurationMs === 'number' ? data.totalDurationMs : 0,
398
+ partial: data?.partial === true ? true : undefined,
399
+ terminationReason: data?.terminationReason,
400
+ terminationMessage: data?.terminationMessage,
401
+ conclusionContract: data?.conclusionContract,
402
+ claimSupport: data?.claimSupport,
403
+ claimVerificationResult: data?.claimVerificationResult,
404
+ identityResolutions: data?.identityResolutions,
405
+ };
406
+ const issue = (0, finalResultQualityGate_1.applyFinalResultQualityGate)({ result, query: session.query });
407
+ if (!issue)
408
+ return event;
409
+ const nextData = {
410
+ ...data,
411
+ confidence: result.confidence,
412
+ partial: result.partial,
413
+ terminationReason: result.terminationReason,
414
+ terminationMessage: result.terminationMessage,
415
+ };
416
+ const nextPayload = payload?.data && typeof payload.data === 'object'
417
+ ? { ...payload, data: nextData }
418
+ : { ...payload, ...nextData };
419
+ return {
420
+ ...event,
421
+ eventData: JSON.stringify(nextPayload),
422
+ };
423
+ }
366
424
  function replayPersistedAgentEvents(session, res, lastEventId) {
367
425
  const scope = agentEventScopeFromSession(session);
368
426
  if (!scope)
@@ -386,9 +444,10 @@ function replayPersistedAgentEvents(session, res, lastEventId) {
386
444
  let lastCursor = lastEventId;
387
445
  for (const event of events) {
388
446
  try {
447
+ const replayEvent = sanitizePersistedAnalysisCompletedEvent(session, event);
389
448
  res.write(`id: ${event.cursor}\n`);
390
- res.write(`event: ${event.eventType}\n`);
391
- res.write(`data: ${event.eventData}\n\n`);
449
+ res.write(`event: ${replayEvent.eventType}\n`);
450
+ res.write(`data: ${replayEvent.eventData}\n\n`);
392
451
  replayed++;
393
452
  lastCursor = event.cursor;
394
453
  if (sessionSseReplay_1.TERMINAL_SSE_EVENT_TYPES.has(event.eventType)) {
@@ -412,6 +471,52 @@ function sendReplayableSessionEvent(session, res, eventType, payload) {
412
471
  streamProjector.sendEvent(res, eventType, payload, event.seqId);
413
472
  return event.seqId;
414
473
  }
474
+ function appendAndPersistReplayableSessionEvent(session, eventType, payload) {
475
+ const event = (0, sessionSseReplay_1.appendReplayableSseEvent)(session, eventType, payload);
476
+ persistBufferedAgentEvent(session, {
477
+ cursor: event.seqId,
478
+ eventType: event.eventType,
479
+ eventData: event.eventData,
480
+ createdAt: Date.now(),
481
+ });
482
+ return event;
483
+ }
484
+ function writeBufferedSessionEvent(res, event) {
485
+ res.write(`id: ${event.seqId}\n`);
486
+ res.write(`event: ${event.eventType}\n`);
487
+ res.write(`data: ${event.eventData}\n\n`);
488
+ }
489
+ function loadPersistedCompletedAnalysisSseEvents(session) {
490
+ const scope = agentEventScopeFromSession(session);
491
+ if (!scope)
492
+ return [];
493
+ const events = (0, agentEventStore_1.listSerializedAgentEventsAfter)(scope, scope.runId, 0)
494
+ .filter(event => event.eventType === 'snapshot_created' ||
495
+ event.eventType === 'analysis_completed' ||
496
+ event.eventType === 'scene_reconstruction_completed' ||
497
+ event.eventType === 'end')
498
+ .map(event => sanitizePersistedAnalysisCompletedEvent(session, event))
499
+ .map(event => ({
500
+ seqId: event.cursor,
501
+ eventType: event.eventType,
502
+ eventData: event.eventData,
503
+ }));
504
+ if (!events.some(event => event.eventType === 'analysis_completed') ||
505
+ !events.some(event => event.eventType === 'end')) {
506
+ return [];
507
+ }
508
+ session.sseEventSeq = Math.max(session.sseEventSeq || 0, ...events.map(event => event.seqId));
509
+ const existing = new Set(session.sseEventBuffer.map(event => `${event.seqId}:${event.eventType}`));
510
+ for (const event of events) {
511
+ const key = `${event.seqId}:${event.eventType}`;
512
+ if (!existing.has(key))
513
+ session.sseEventBuffer.push(event);
514
+ }
515
+ if (session.sseEventBuffer.length > streamProjector_1.SSE_RING_BUFFER_SIZE) {
516
+ session.sseEventBuffer.splice(0, session.sseEventBuffer.length - streamProjector_1.SSE_RING_BUFFER_SIZE);
517
+ }
518
+ return events;
519
+ }
415
520
  function resolveSessionContextForReview(sessionId) {
416
521
  const activeSession = assistantAppService.getSession(sessionId);
417
522
  if (activeSession) {
@@ -530,12 +635,44 @@ function buildTurnSeverityCounts(turn) {
530
635
  function toJsonSafe(value) {
531
636
  return JSON.parse(JSON.stringify(value, (_key, v) => (typeof v === 'bigint' ? v.toString() : v)));
532
637
  }
638
+ function buildDisplayTurnResult(turn) {
639
+ if (!turn.result)
640
+ return turn.result;
641
+ const message = typeof turn.result.message === 'string' ? turn.result.message : '';
642
+ const resultForGate = {
643
+ sessionId: turn.id,
644
+ success: turn.result.success !== false,
645
+ findings: Array.isArray(turn.findings) ? turn.findings : [],
646
+ hypotheses: [],
647
+ conclusion: message,
648
+ confidence: typeof turn.result.confidence === 'number' ? turn.result.confidence : 0.5,
649
+ rounds: 1,
650
+ totalDurationMs: 0,
651
+ partial: turn.result.partial,
652
+ terminationReason: turn.result.terminationReason,
653
+ terminationMessage: turn.result.terminationMessage,
654
+ conclusionContract: turn.result.conclusionContract,
655
+ claimSupport: turn.result.claimSupport,
656
+ claimVerificationResult: turn.result.claimVerificationResult,
657
+ identityResolutions: turn.result.identityResolutions,
658
+ };
659
+ (0, finalResultQualityGate_1.applyFinalResultQualityGate)({ result: resultForGate, query: turn.query });
660
+ return {
661
+ ...turn.result,
662
+ message: resultForGate.conclusion,
663
+ confidence: resultForGate.confidence,
664
+ partial: resultForGate.partial,
665
+ terminationReason: resultForGate.terminationReason,
666
+ terminationMessage: resultForGate.terminationMessage,
667
+ };
668
+ }
533
669
  function buildTurnSummary(turn) {
534
- const confidence = typeof turn.result?.confidence === 'number'
535
- ? turn.result.confidence
670
+ const displayResult = buildDisplayTurnResult(turn);
671
+ const confidence = typeof displayResult?.confidence === 'number'
672
+ ? displayResult.confidence
536
673
  : undefined;
537
- const sanitizedConclusion = typeof turn.result?.message === 'string'
538
- ? normalizeNarrativeForClient(turn.result.message)
674
+ const sanitizedConclusion = typeof displayResult?.message === 'string'
675
+ ? normalizeNarrativeForClient(displayResult.message)
539
676
  : '';
540
677
  const conclusionPreview = sanitizedConclusion
541
678
  ? sanitizedConclusion.replace(/\s+/g, ' ').slice(0, 240)
@@ -551,7 +688,10 @@ function buildTurnSummary(turn) {
551
688
  aspects: Array.isArray(turn.intent?.aspects) ? turn.intent.aspects : [],
552
689
  },
553
690
  completed: !!turn.completed,
554
- success: typeof turn.result?.success === 'boolean' ? turn.result.success : null,
691
+ success: typeof displayResult?.success === 'boolean' ? displayResult.success : null,
692
+ partial: displayResult?.partial === true,
693
+ terminationReason: displayResult?.terminationReason,
694
+ terminationMessage: displayResult?.terminationMessage,
555
695
  confidence,
556
696
  findingCount: Array.isArray(turn.findings) ? turn.findings.length : 0,
557
697
  severityCounts: buildTurnSeverityCounts(turn),
@@ -560,15 +700,16 @@ function buildTurnSummary(turn) {
560
700
  }
561
701
  function buildTurnDetail(turn) {
562
702
  const summary = buildTurnSummary(turn);
703
+ const displayResult = buildDisplayTurnResult(turn);
563
704
  return {
564
705
  ...summary,
565
706
  intent: toJsonSafe(turn.intent),
566
- result: turn.result
707
+ result: displayResult
567
708
  ? toJsonSafe({
568
- ...turn.result,
569
- message: typeof turn.result.message === 'string'
570
- ? normalizeNarrativeForClient(turn.result.message)
571
- : turn.result.message,
709
+ ...displayResult,
710
+ message: typeof displayResult.message === 'string'
711
+ ? normalizeNarrativeForClient(displayResult.message)
712
+ : displayResult.message,
572
713
  })
573
714
  : null,
574
715
  findings: toJsonSafe(turn.findings || []),
@@ -607,10 +748,38 @@ function buildRecoveredResultFromContext(sessionId, context) {
607
748
  partial: turn.result.partial,
608
749
  terminationReason: turn.result.terminationReason,
609
750
  terminationMessage: turn.result.terminationMessage,
751
+ conclusionContract: turn.result.conclusionContract,
752
+ claimSupport: turn.result.claimSupport,
753
+ claimVerificationResult: turn.result.claimVerificationResult,
754
+ identityResolutions: turn.result.identityResolutions,
610
755
  };
611
756
  }
757
+ function annotateRecoveredResultQuality(sessionId, session, result, query) {
758
+ const issue = (0, finalResultQualityGate_1.applyFinalResultQualityGate)({
759
+ result,
760
+ query: query || session.query,
761
+ });
762
+ if (!issue)
763
+ return;
764
+ const context = enhancedSessionContext_1.sessionContextManager.get(sessionId, session.traceId) ||
765
+ enhancedSessionContext_1.sessionContextManager.get(sessionId);
766
+ context?.annotateLatestCompletedTurn({
767
+ success: result.success,
768
+ findings: result.findings,
769
+ message: result.conclusion,
770
+ confidence: result.confidence,
771
+ partial: result.partial,
772
+ terminationReason: result.terminationReason,
773
+ terminationMessage: result.terminationMessage,
774
+ conclusionContract: result.conclusionContract,
775
+ claimSupport: result.claimSupport,
776
+ claimVerificationResult: result.claimVerificationResult,
777
+ identityResolutions: result.identityResolutions,
778
+ });
779
+ }
612
780
  function recoverResultForSessionIfNeeded(sessionId, session) {
613
781
  if (session.result) {
782
+ annotateRecoveredResultQuality(sessionId, session, session.result);
614
783
  return session.result;
615
784
  }
616
785
  const resolved = resolveSessionContextForReview(sessionId);
@@ -627,6 +796,7 @@ function recoverResultForSessionIfNeeded(sessionId, session) {
627
796
  if (latestTurn?.query) {
628
797
  session.query = latestTurn.query;
629
798
  }
799
+ annotateRecoveredResultQuality(sessionId, session, recovered, latestTurn?.query);
630
800
  return recovered;
631
801
  }
632
802
  function buildFallbackIntentFromQuery(query) {
@@ -1146,6 +1316,15 @@ function handleSessionStream(req, res, sessionId) {
1146
1316
  timestamp: Date.now(),
1147
1317
  ...buildStreamObservability(session),
1148
1318
  });
1319
+ if (lastEventId !== null &&
1320
+ (session.status === 'completed' || session.status === 'quota_exceeded')) {
1321
+ recoverResultForSessionIfNeeded(sessionId, session);
1322
+ if (session.result) {
1323
+ sendAgentDrivenResult(res, session);
1324
+ res.end();
1325
+ return;
1326
+ }
1327
+ }
1149
1328
  let ringReplayAfter = lastEventId;
1150
1329
  if (lastEventId !== null) {
1151
1330
  const persistedReplay = replayPersistedAgentEvents(session, res, lastEventId);
@@ -1180,11 +1359,8 @@ function handleSessionStream(req, res, sessionId) {
1180
1359
  recoverResultForSessionIfNeeded(sessionId, session);
1181
1360
  if (session.result) {
1182
1361
  sendAgentDrivenResult(res, session);
1183
- sendReplayableSessionEvent(session, res, 'end', {
1184
- timestamp: Date.now(),
1185
- ...buildStreamObservability(session),
1186
- });
1187
1362
  res.end();
1363
+ assistantAppService.removeSseClient(sessionId, res);
1188
1364
  return;
1189
1365
  }
1190
1366
  }
@@ -1260,22 +1436,41 @@ router.get('/:sessionId/status', (req, res) => {
1260
1436
  query: session.query,
1261
1437
  findings: recoveredResult.findings,
1262
1438
  });
1263
- const conclusionContract = recoveredResult.conclusionContract ||
1264
- (0, agentResultNormalizer_1.deriveConclusionContractForNarrative)(recoveredResult.conclusion, {
1265
- mode: recoveredResult.rounds > 1 ? 'focused_answer' : 'initial_report',
1266
- sceneId: sceneIdHint,
1267
- }) ||
1439
+ const conclusionContract = (0, agentResultNormalizer_1.deriveEvidenceBackedConclusionContractForNarrative)(recoveredResult.conclusion, session.dataEnvelopes || [], {
1440
+ existingContract: recoveredResult.conclusionContract,
1441
+ mode: recoveredResult.rounds > 1 ? 'focused_answer' : 'initial_report',
1442
+ sceneId: sceneIdHint,
1443
+ }) ||
1268
1444
  undefined;
1445
+ const qualityArtifacts = recoveredResult.claimSupport &&
1446
+ recoveredResult.claimVerificationResult &&
1447
+ recoveredResult.identityResolutions
1448
+ ? {
1449
+ claimSupport: recoveredResult.claimSupport,
1450
+ claimVerificationResult: recoveredResult.claimVerificationResult,
1451
+ identityResolutions: recoveredResult.identityResolutions,
1452
+ }
1453
+ : ensureAnalysisQualityArtifacts(session, conclusionContract, recoveredResult);
1454
+ const completedPayload = ensureCompletedAnalysisResultPayload(session);
1455
+ const finalArtifacts = completedPayload?.finalArtifacts;
1456
+ const normalizedCompletedConclusion = completedPayload?.normalizedConclusion || conclusion;
1457
+ const normalizedCompletedContract = completedPayload?.normalizedConclusionContract || conclusionContract;
1269
1458
  response.result = {
1270
- answer: conclusion,
1271
- conclusion,
1272
- conclusionContract,
1459
+ answer: normalizedCompletedConclusion,
1460
+ conclusion: normalizedCompletedConclusion,
1461
+ conclusionContract: normalizedCompletedContract,
1462
+ claimSupport: qualityArtifacts.claimSupport,
1463
+ claimVerificationResult: qualityArtifacts.claimVerificationResult,
1464
+ identityResolutions: qualityArtifacts.identityResolutions,
1273
1465
  confidence: recoveredResult.confidence,
1274
1466
  totalDurationMs: recoveredResult.totalDurationMs,
1275
1467
  rounds: recoveredResult.rounds,
1276
1468
  partial: recoveredResult.partial,
1277
1469
  terminationReason: recoveredResult.terminationReason,
1278
1470
  terminationMessage: recoveredResult.terminationMessage,
1471
+ reportUrl: finalArtifacts?.reportUrl,
1472
+ reportError: finalArtifacts?.reportError,
1473
+ resultSnapshotId: finalArtifacts?.resultSnapshotId,
1279
1474
  findings: recoveredResult.findings,
1280
1475
  findingsCount: recoveredResult.findings.length,
1281
1476
  resultContract,
@@ -2233,6 +2428,7 @@ async function detectScenesQuick(traceProcessorService, traceId) {
2233
2428
  normalizeNarrativeForClient,
2234
2429
  buildClientFindings,
2235
2430
  buildSessionResultContract,
2431
+ getCompletedPayload: ensureCompletedAnalysisResultPayload,
2236
2432
  });
2237
2433
  // ============================================================================
2238
2434
  // Agent-Driven Analysis Helper Functions (Phase 2-4)
@@ -2311,9 +2507,14 @@ async function runAgentDrivenAnalysis(sessionId, query, traceId, options = {}) {
2311
2507
  console.log(`[AgentRoutes.AgentDriven] Received event: ${update.type}`, update.content?.phase);
2312
2508
  logger.debug('Stream', `Update: ${update.type}`, update.content);
2313
2509
  const normalizedUpdate = augmentConclusionUpdateWithEvidenceIndex(session, normalizeAgentDrivenUpdate(update));
2314
- // Broadcast the original event so the frontend receives raw events
2315
- // (answer_token, thought, agent_response, conclusion, etc.) for rendering.
2316
- broadcastToAgentDrivenClients(sessionId, normalizedUpdate);
2510
+ // Final narrative is emitted through analysis_completed after deterministic
2511
+ // evidence/claim verification has run. Suppress early conclusion events so
2512
+ // clients do not render an unverified terminal answer.
2513
+ const shouldBroadcastOriginalUpdate = normalizedUpdate.type !== 'conclusion' &&
2514
+ normalizedUpdate.type !== 'answer_token';
2515
+ if (shouldBroadcastOriginalUpdate) {
2516
+ broadcastToAgentDrivenClients(sessionId, normalizedUpdate);
2517
+ }
2317
2518
  // Also derive a conversation_step for the timeline/observability layer.
2318
2519
  const conversationStep = buildConversationStepUpdate(session, normalizedUpdate);
2319
2520
  if (conversationStep) {
@@ -2361,7 +2562,7 @@ async function runAgentDrivenAnalysis(sessionId, query, traceId, options = {}) {
2361
2562
  // (answer_token, thought, conclusion, etc.) are already broadcast above
2362
2563
  // and remapping would cause duplicate delivery to the frontend.
2363
2564
  const eventType = mapToAgentDrivenEventType(normalizedUpdate);
2364
- if (eventType !== normalizedUpdate.type) {
2565
+ if (shouldBroadcastOriginalUpdate && eventType !== normalizedUpdate.type) {
2365
2566
  broadcastToAgentDrivenClients(sessionId, {
2366
2567
  type: eventType,
2367
2568
  content: normalizedUpdate.content,
@@ -2440,6 +2641,7 @@ async function runAgentDrivenAnalysis(sessionId, query, traceId, options = {}) {
2440
2641
  });
2441
2642
  console.log('[AgentRoutes.AgentDriven] analyze completed, success:', result.success);
2442
2643
  session.result = result;
2644
+ delete session.completedAnalysisFinalArtifacts;
2443
2645
  // Accumulate hypotheses across turns (deduplicate by id)
2444
2646
  const existingIds = new Set(session.hypotheses.map(h => h.id));
2445
2647
  for (const h of result.hypotheses) {
@@ -2454,10 +2656,6 @@ async function runAgentDrivenAnalysis(sessionId, query, traceId, options = {}) {
2454
2656
  }
2455
2657
  }
2456
2658
  const terminalRunStatus = terminalRunStatusForResult(result);
2457
- session.status = terminalRunStatus === 'quota_exceeded'
2458
- ? 'quota_exceeded'
2459
- : result.success ? 'completed' : 'failed';
2460
- markSessionRunStatus(session, terminalRunStatus);
2461
2659
  // Record conclusion in cross-turn history
2462
2660
  if (!session.conclusionHistory)
2463
2661
  session.conclusionHistory = [];
@@ -2496,12 +2694,83 @@ async function runAgentDrivenAnalysis(sessionId, query, traceId, options = {}) {
2496
2694
  };
2497
2695
  }
2498
2696
  }
2697
+ if (result.success || result.partial === true) {
2698
+ const sceneIdHint = resolveConclusionSceneIdHint({
2699
+ sessionId,
2700
+ query,
2701
+ findings: result.findings,
2702
+ });
2703
+ const normalizedConclusionContract = ((0, agentResultNormalizer_1.deriveEvidenceBackedConclusionContractForNarrative)(result.conclusion, session.dataEnvelopes || [], {
2704
+ existingContract: result.conclusionContract,
2705
+ mode: result.rounds > 1 ? 'focused_answer' : 'initial_report',
2706
+ sceneId: sceneIdHint,
2707
+ }) ||
2708
+ undefined);
2709
+ if (normalizedConclusionContract) {
2710
+ result.conclusionContract = normalizedConclusionContract;
2711
+ }
2712
+ ensureAnalysisQualityArtifacts(session, normalizedConclusionContract);
2713
+ }
2714
+ const finalQualityIssue = (0, finalResultQualityGate_1.applyFinalResultQualityGate)({ result, query });
2715
+ if (finalQualityIssue) {
2716
+ const message = result.terminationMessage || finalQualityIssue.message;
2717
+ logger.warn('AgentDrivenAnalysis', 'Final result quality gate marked result partial', {
2718
+ code: finalQualityIssue.code,
2719
+ conclusionChars: result.conclusion.length,
2720
+ findingsCount: result.findings.length,
2721
+ hasConclusionContract: !!result.conclusionContract,
2722
+ claimVerifierStatus: result.claimVerificationResult?.status,
2723
+ runId: session.activeRun?.runId,
2724
+ requestId: session.activeRun?.requestId,
2725
+ });
2726
+ const update = {
2727
+ type: 'degraded',
2728
+ content: {
2729
+ module: 'agentRoutes',
2730
+ fallback: 'final_result_quality_gate',
2731
+ code: finalQualityIssue.code,
2732
+ partial: true,
2733
+ message,
2734
+ },
2735
+ timestamp: Date.now(),
2736
+ };
2737
+ broadcastToAgentDrivenClients(sessionId, update);
2738
+ const conversationStep = buildConversationStepUpdate(session, update);
2739
+ if (conversationStep) {
2740
+ appendConversationStep(session, conversationStep);
2741
+ broadcastToAgentDrivenClients(sessionId, conversationStep);
2742
+ }
2743
+ }
2744
+ const currentTurn = session.runSequence || 1;
2745
+ const latestConclusionHistory = session.conclusionHistory?.[session.conclusionHistory.length - 1];
2746
+ if (latestConclusionHistory?.turn === currentTurn) {
2747
+ latestConclusionHistory.confidence = result.confidence ?? latestConclusionHistory.confidence;
2748
+ }
2749
+ enhancedSessionContext_1.sessionContextManager.get(sessionId, traceId)?.annotateLatestCompletedTurn({
2750
+ success: result.success,
2751
+ findings: result.findings,
2752
+ message: result.conclusion,
2753
+ confidence: result.confidence,
2754
+ partial: result.partial,
2755
+ terminationReason: result.terminationReason,
2756
+ terminationMessage: result.terminationMessage,
2757
+ conclusionContract: result.conclusionContract,
2758
+ claimSupport: result.claimSupport,
2759
+ claimVerificationResult: result.claimVerificationResult,
2760
+ identityResolutions: result.identityResolutions,
2761
+ });
2762
+ session.status = terminalRunStatus === 'quota_exceeded'
2763
+ ? 'quota_exceeded'
2764
+ : result.success ? 'completed' : 'failed';
2765
+ markSessionRunStatus(session, terminalRunStatus);
2499
2766
  // Log completion details
2500
2767
  logger.info('AgentDrivenAnalysis', 'Agent-driven analysis completed', {
2501
2768
  confidence: result.confidence,
2502
2769
  rounds: result.rounds,
2503
2770
  findingsCount: result.findings.length,
2504
2771
  hypothesesCount: result.hypotheses.length,
2772
+ claimSupportCount: result.claimSupport?.length || 0,
2773
+ claimVerifierStatus: result.claimVerificationResult?.status,
2505
2774
  partial: result.partial,
2506
2775
  terminationReason: result.terminationReason,
2507
2776
  runId: session.activeRun?.runId,
@@ -2524,14 +2793,11 @@ async function runAgentDrivenAnalysis(sessionId, query, traceId, options = {}) {
2524
2793
  // Send final result
2525
2794
  const clientCount = session.sseClients.length;
2526
2795
  logger.info('AgentRoutes', 'Sending agent-driven result', { clientCount });
2796
+ ensureCompletedAnalysisSseEvents(session);
2527
2797
  session.sseClients.forEach((client, index) => {
2528
2798
  try {
2529
2799
  logger.info('AgentRoutes', `Sending agent-driven result to client ${index + 1}/${clientCount}`);
2530
2800
  sendAgentDrivenResult(client, session);
2531
- sendReplayableSessionEvent(session, client, 'end', {
2532
- timestamp: Date.now(),
2533
- ...buildStreamObservability(session),
2534
- });
2535
2801
  }
2536
2802
  catch (e) {
2537
2803
  logger.error('AgentRoutes', `Error sending agent-driven result to client ${index + 1}`, e);
@@ -3945,7 +4211,7 @@ function normalizeNarrativeForClient(narrative) {
3945
4211
  }
3946
4212
  function conclusionHasEvidenceIndex(conclusion) {
3947
4213
  const text = conclusion || '';
3948
- return /(^|\n)\s*##\s*证据表索引\b/.test(text);
4214
+ return /(^|\n)\s*##\s*证据(?:表)?索引\b/.test(text);
3949
4215
  }
3950
4216
  function markdownCell(value, maxLen = 80) {
3951
4217
  return String(value ?? '')
@@ -3954,19 +4220,7 @@ function markdownCell(value, maxLen = 80) {
3954
4220
  .trim()
3955
4221
  .slice(0, maxLen) || '-';
3956
4222
  }
3957
- function envelopeRowCount(env) {
3958
- const rows = env?.data?.rows;
3959
- if (Array.isArray(rows))
3960
- return String(rows.length);
3961
- const summaryContent = env?.data?.summary?.content;
3962
- if (typeof summaryContent === 'string') {
3963
- const matched = summaryContent.match(/Total rows:\s*(\d+)/i);
3964
- if (matched)
3965
- return matched[1];
3966
- }
3967
- return '-';
3968
- }
3969
- function buildConclusionEvidenceIndex(envelopes, maxItems = 8) {
4223
+ function buildConclusionEvidenceIndex(envelopes, maxItems = 3) {
3970
4224
  if (!Array.isArray(envelopes) || envelopes.length === 0)
3971
4225
  return '';
3972
4226
  const seen = new Set();
@@ -3983,22 +4237,21 @@ function buildConclusionEvidenceIndex(envelopes, maxItems = 8) {
3983
4237
  if (seen.has(key))
3984
4238
  continue;
3985
4239
  seen.add(key);
3986
- const phase = markdownCell(meta.planPhaseTitle || meta.planPhaseId || '-');
3987
4240
  const source = markdownCell(meta.source || meta.skillId || 'execute_sql');
3988
- const evidence = markdownCell(meta.evidenceRefId || meta.sourceToolCallId || '-', 48);
3989
- candidates.push(`| ${phase} | ${title} | ${source} | ${envelopeRowCount(env)} | ${evidence} |`);
4241
+ const evidence = markdownCell(meta.evidenceRefId || meta.sourceToolCallId || '-', 36);
4242
+ candidates.push({ title, source, evidence });
3990
4243
  }
3991
4244
  if (candidates.length === 0)
3992
4245
  return '';
3993
4246
  const rows = candidates.slice(0, maxItems);
3994
4247
  const omitted = Math.max(0, candidates.length - rows.length);
4248
+ const summary = rows
4249
+ .map(item => `${item.title}(${item.source} / ${item.evidence})`)
4250
+ .join(';');
3995
4251
  return [
3996
- '## 证据表索引',
4252
+ '## 证据索引',
3997
4253
  '',
3998
- '| 阶段 | 表/摘要 | 来源 | 行数 | 证据 ID |',
3999
- '|---|---|---|---:|---|',
4000
- ...rows,
4001
- omitted > 0 ? `| - | 其余 ${omitted} 份证据 | - | - | - |` : '',
4254
+ `关键数据来源:${summary}${omitted > 0 ? `;其余 ${omitted} 份结构化证据见报告数据详情。` : '。'}`,
4002
4255
  ].filter(Boolean).join('\n');
4003
4256
  }
4004
4257
  function appendEvidenceIndexIfMissing(conclusion, envelopes) {
@@ -4030,53 +4283,67 @@ function augmentConclusionUpdateWithEvidenceIndex(session, update) {
4030
4283
  },
4031
4284
  };
4032
4285
  }
4033
- /**
4034
- * Send agent-driven analysis result to SSE client
4035
- */
4036
- function sendAgentDrivenResult(res, session) {
4037
- const result = session.result;
4286
+ function ensureAnalysisQualityArtifacts(session, conclusionContract, resultOverride) {
4287
+ const result = resultOverride || session.result;
4038
4288
  if (!result)
4039
- return;
4040
- const observability = buildStreamObservability(session);
4041
- const replayOnlyScene = isSceneReplayOnlyQuery(session.query);
4042
- const hasEvidenceBackedConclusion = result.success || result.partial === true;
4043
- const normalizedConclusion = replayOnlyScene
4044
- ? buildSceneReplayNarrative(session.scenes || [])
4045
- : hasEvidenceBackedConclusion ? appendEvidenceIndexIfMissing(normalizeNarrativeForClient(result.conclusion), session.dataEnvelopes || []) : normalizeNarrativeForClient(result.conclusion);
4046
- const sceneIdHint = replayOnlyScene
4047
- ? undefined
4048
- : resolveConclusionSceneIdHint({
4049
- sessionId: session.sessionId,
4050
- query: session.query,
4051
- findings: result.findings,
4289
+ return {};
4290
+ if (result.claimSupport &&
4291
+ result.claimVerificationResult &&
4292
+ result.identityResolutions) {
4293
+ session.claimSupport = result.claimSupport;
4294
+ session.claimVerificationResult = result.claimVerificationResult;
4295
+ session.identityResolutions = result.identityResolutions;
4296
+ const context = enhancedSessionContext_1.sessionContextManager.get(session.sessionId, session.traceId);
4297
+ context?.annotateLatestCompletedTurn({
4298
+ conclusionContract,
4299
+ claimSupport: result.claimSupport,
4300
+ claimVerificationResult: result.claimVerificationResult,
4301
+ identityResolutions: result.identityResolutions,
4052
4302
  });
4053
- // Fallback: re-derive contract if the orchestrator didn't populate it.
4054
- // Note: mode heuristic uses rounds (available here) as proxy for turnCount
4055
- // (which only the orchestrator knows). Both signal "multi-interaction" analysis.
4056
- const normalizedConclusionContract = replayOnlyScene
4057
- ? undefined
4058
- : hasEvidenceBackedConclusion ? (result.conclusionContract ||
4059
- (0, agentResultNormalizer_1.deriveConclusionContractForNarrative)(result.conclusion, {
4060
- mode: result.rounds > 1 ? 'focused_answer' : 'initial_report',
4061
- sceneId: sceneIdHint,
4062
- }) ||
4063
- undefined) : undefined;
4064
- const resultForClient = normalizedConclusion === result.conclusion && normalizedConclusionContract === result.conclusionContract
4065
- ? result
4066
- : { ...result, conclusion: normalizedConclusion, conclusionContract: normalizedConclusionContract };
4067
- const clientFindings = replayOnlyScene ? [] : buildClientFindings(result.findings, session.scenes || []);
4068
- const resultContract = buildSessionResultContract(session, clientFindings);
4069
- // Generate HTML report
4070
- let reportUrl;
4071
- let reportError;
4072
- const reportId = `agent-report-${session.sessionId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
4073
- let resultSnapshotId;
4074
- let resultSnapshotEventData;
4075
- let reportLease = null;
4076
- if (!hasEvidenceBackedConclusion) {
4077
- reportError = `analysis did not complete successfully (${result.terminationReason || 'failed'})`;
4303
+ return {
4304
+ claimSupport: result.claimSupport,
4305
+ claimVerificationResult: result.claimVerificationResult,
4306
+ identityResolutions: result.identityResolutions,
4307
+ };
4308
+ }
4309
+ const artifacts = (0, claimVerificationRunner_1.runClaimVerification)({
4310
+ conclusionContract,
4311
+ dataEnvelopes: session.dataEnvelopes || [],
4312
+ comparisonReportSection: session.comparisonReportSection,
4313
+ policy: 'record_only',
4314
+ });
4315
+ result.claimSupport = artifacts.claimSupport;
4316
+ result.claimVerificationResult = artifacts.claimVerificationResult;
4317
+ result.identityResolutions = artifacts.identityResolutions;
4318
+ const context = enhancedSessionContext_1.sessionContextManager.get(session.sessionId, session.traceId);
4319
+ context?.annotateLatestCompletedTurn({
4320
+ conclusionContract,
4321
+ claimSupport: artifacts.claimSupport,
4322
+ claimVerificationResult: artifacts.claimVerificationResult,
4323
+ identityResolutions: artifacts.identityResolutions,
4324
+ });
4325
+ session.claimSupport = artifacts.claimSupport;
4326
+ session.claimVerificationResult = artifacts.claimVerificationResult;
4327
+ session.identityResolutions = artifacts.identityResolutions;
4328
+ return {
4329
+ claimSupport: artifacts.claimSupport,
4330
+ claimVerificationResult: artifacts.claimVerificationResult,
4331
+ identityResolutions: artifacts.identityResolutions,
4332
+ };
4333
+ }
4334
+ function ensureCompletedAnalysisFinalArtifacts(session, input) {
4335
+ const cached = session.completedAnalysisFinalArtifacts;
4336
+ if (cached)
4337
+ return cached;
4338
+ const result = input.result;
4339
+ const finalArtifacts = { generatedAt: Date.now() };
4340
+ let reportId;
4341
+ if (!input.hasEvidenceBackedConclusion) {
4342
+ finalArtifacts.reportError = `analysis did not complete successfully (${result.terminationReason || 'failed'})`;
4078
4343
  }
4079
4344
  else {
4345
+ let reportLease = null;
4346
+ reportId = `agent-report-${session.sessionId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
4080
4347
  try {
4081
4348
  if (enterpriseLeasesEnabled()) {
4082
4349
  const scope = leaseScopeFromSession(session);
@@ -4100,18 +4367,15 @@ function sendAgentDrivenResult(res, session) {
4100
4367
  }
4101
4368
  }
4102
4369
  const generator = (0, htmlReportGenerator_1.getHTMLReportGenerator)();
4103
- // Report assembly (cumulative findings dedup, empty-conclusion fallback,
4104
- // snapshot-first analysisNotes/Plan/Flags) lives in the shared builder so
4105
- // the CLI path produces identical output.
4106
4370
  const reportData = (0, agentReportData_1.buildAgentDrivenReportData)({
4107
4371
  session,
4108
- result: resultForClient,
4372
+ result: input.resultForClient,
4109
4373
  });
4110
4374
  console.log(`[AgentRoutes] Generating HTML report, data keys:`, {
4111
4375
  hasResult: !!result,
4112
- conclusionLength: normalizedConclusion?.length || 0,
4113
- conclusionPreview: (normalizedConclusion || '').substring(0, 100),
4114
- hasConclusionContract: !!normalizedConclusionContract,
4376
+ conclusionLength: input.normalizedConclusion?.length || 0,
4377
+ conclusionPreview: (input.normalizedConclusion || '').substring(0, 100),
4378
+ hasConclusionContract: !!input.normalizedConclusionContract,
4115
4379
  findingsCount: result.findings?.length || 0,
4116
4380
  hypothesesCount: session.hypotheses?.length || 0,
4117
4381
  dialogueCount: session.agentDialogue?.length || 0,
@@ -4123,9 +4387,10 @@ function sendAgentDrivenResult(res, session) {
4123
4387
  snapshotNotes: session._lastSnapshot?.analysisNotes?.length ?? 'n/a',
4124
4388
  snapshotPlan: !!session._lastSnapshot?.analysisPlan,
4125
4389
  snapshotFlags: session._lastSnapshot?.uncertaintyFlags?.length ?? 'n/a',
4390
+ claimSupportCount: input.qualityArtifacts.claimSupport?.length || 0,
4391
+ claimVerifierStatus: input.qualityArtifacts.claimVerificationResult?.status,
4126
4392
  });
4127
4393
  const html = generator.generateAgentDrivenHTML(reportData);
4128
- // Store report
4129
4394
  (0, reportRoutes_1.persistReport)(reportId, {
4130
4395
  html,
4131
4396
  generatedAt: Date.now(),
@@ -4137,13 +4402,15 @@ function sendAgentDrivenResult(res, session) {
4137
4402
  userId: session.userId,
4138
4403
  visibility: 'private',
4139
4404
  });
4140
- reportUrl = `/api/reports/${reportId}`;
4405
+ finalArtifacts.reportId = reportId;
4406
+ finalArtifacts.reportUrl = `/api/reports/${reportId}`;
4141
4407
  console.log(`[AgentRoutes] Generated agent-driven HTML report: ${reportId} (${html.length} bytes)`);
4142
4408
  }
4143
4409
  catch (error) {
4144
- reportError = error.message || 'Unknown error';
4410
+ reportId = undefined;
4411
+ finalArtifacts.reportError = error.message || 'Unknown error';
4145
4412
  console.error('[AgentRoutes] Failed to generate agent-driven HTML report:', {
4146
- error: reportError,
4413
+ error: finalArtifacts.reportError,
4147
4414
  stack: error.stack?.split('\n').slice(0, 5).join('\n'),
4148
4415
  resultConclusion: result?.conclusion ? `${result.conclusion.length} chars` : 'EMPTY/NULL',
4149
4416
  resultConfidence: result?.confidence,
@@ -4155,7 +4422,7 @@ function sendAgentDrivenResult(res, session) {
4155
4422
  const scope = leaseScopeFromSession(session);
4156
4423
  if (scope) {
4157
4424
  try {
4158
- (0, traceProcessorLeaseStore_1.getTraceProcessorLeaseStore)().releaseHolder(scope, reportLease.id, 'report_generation', reportId);
4425
+ (0, traceProcessorLeaseStore_1.getTraceProcessorLeaseStore)().releaseHolder(scope, reportLease.id, 'report_generation', finalArtifacts.reportId || reportId || 'report_generation');
4159
4426
  }
4160
4427
  catch (releaseError) {
4161
4428
  console.warn(`[AgentRoutes] Failed to release report_generation lease ${reportLease.id}: ${releaseError.message}`);
@@ -4164,7 +4431,7 @@ function sendAgentDrivenResult(res, session) {
4164
4431
  }
4165
4432
  }
4166
4433
  }
4167
- if (hasEvidenceBackedConclusion) {
4434
+ if (input.hasEvidenceBackedConclusion) {
4168
4435
  try {
4169
4436
  const resultSnapshot = (0, analysisResultSnapshotPipeline_1.persistCompletedAnalysisResultSnapshot)({
4170
4437
  tenantId: session.tenantId,
@@ -4173,20 +4440,23 @@ function sendAgentDrivenResult(res, session) {
4173
4440
  traceId: session.traceId,
4174
4441
  sessionId: session.sessionId,
4175
4442
  runId: session.lastRun?.runId || session.activeRun?.runId,
4176
- reportId: reportUrl ? reportId : undefined,
4443
+ reportId: finalArtifacts.reportId,
4177
4444
  query: session.query,
4178
4445
  traceLabel: session.traceId,
4179
- conclusion: normalizedConclusion,
4180
- conclusionContract: normalizedConclusionContract,
4446
+ conclusion: input.normalizedConclusion,
4447
+ conclusionContract: input.normalizedConclusionContract,
4448
+ claimSupport: input.qualityArtifacts.claimSupport,
4449
+ claimVerificationResult: input.qualityArtifacts.claimVerificationResult,
4450
+ identityResolutions: input.qualityArtifacts.identityResolutions,
4181
4451
  confidence: result.confidence,
4182
4452
  partial: result.partial,
4183
4453
  terminationReason: result.terminationReason,
4184
4454
  terminationMessage: result.terminationMessage,
4185
4455
  dataEnvelopes: session.dataEnvelopes,
4186
4456
  });
4187
- resultSnapshotId = resultSnapshot?.id;
4457
+ finalArtifacts.resultSnapshotId = resultSnapshot?.id;
4188
4458
  if (resultSnapshot) {
4189
- resultSnapshotEventData = {
4459
+ finalArtifacts.resultSnapshotEventData = {
4190
4460
  snapshotId: resultSnapshot.id,
4191
4461
  status: resultSnapshot.status,
4192
4462
  sceneType: resultSnapshot.sceneType,
@@ -4209,24 +4479,134 @@ function sendAgentDrivenResult(res, session) {
4209
4479
  });
4210
4480
  }
4211
4481
  }
4212
- if (resultSnapshotEventData) {
4213
- sendReplayableSessionEvent(session, res, 'snapshot_created', {
4482
+ session.completedAnalysisFinalArtifacts = finalArtifacts;
4483
+ return finalArtifacts;
4484
+ }
4485
+ function ensureCompletedAnalysisResultPayload(session) {
4486
+ const result = session.result;
4487
+ if (!result)
4488
+ return undefined;
4489
+ const replayOnlyScene = isSceneReplayOnlyQuery(session.query);
4490
+ const hasEvidenceBackedConclusion = result.success || result.partial === true;
4491
+ const normalizedConclusion = replayOnlyScene
4492
+ ? buildSceneReplayNarrative(session.scenes || [])
4493
+ : hasEvidenceBackedConclusion ? appendEvidenceIndexIfMissing(normalizeNarrativeForClient(result.conclusion), session.dataEnvelopes || []) : normalizeNarrativeForClient(result.conclusion);
4494
+ const sceneIdHint = replayOnlyScene
4495
+ ? undefined
4496
+ : resolveConclusionSceneIdHint({
4497
+ sessionId: session.sessionId,
4498
+ query: session.query,
4499
+ findings: result.findings,
4500
+ });
4501
+ const normalizedConclusionContract = replayOnlyScene
4502
+ ? undefined
4503
+ : hasEvidenceBackedConclusion ? ((0, agentResultNormalizer_1.deriveEvidenceBackedConclusionContractForNarrative)(result.conclusion, session.dataEnvelopes || [], {
4504
+ existingContract: result.conclusionContract,
4505
+ mode: result.rounds > 1 ? 'focused_answer' : 'initial_report',
4506
+ sceneId: sceneIdHint,
4507
+ }) ||
4508
+ undefined) : undefined;
4509
+ const qualityArtifacts = hasEvidenceBackedConclusion && !replayOnlyScene
4510
+ ? ensureAnalysisQualityArtifacts(session, normalizedConclusionContract)
4511
+ : {};
4512
+ if (normalizedConclusionContract) {
4513
+ result.conclusionContract = normalizedConclusionContract;
4514
+ }
4515
+ if (!replayOnlyScene) {
4516
+ const readPathQualityIssue = (0, finalResultQualityGate_1.applyFinalResultQualityGate)({ result, query: session.query });
4517
+ if (readPathQualityIssue) {
4518
+ enhancedSessionContext_1.sessionContextManager.get(session.sessionId, session.traceId)?.annotateLatestCompletedTurn({
4519
+ success: result.success,
4520
+ findings: result.findings,
4521
+ message: result.conclusion,
4522
+ confidence: result.confidence,
4523
+ partial: result.partial,
4524
+ terminationReason: result.terminationReason,
4525
+ terminationMessage: result.terminationMessage,
4526
+ conclusionContract: result.conclusionContract,
4527
+ claimSupport: result.claimSupport,
4528
+ claimVerificationResult: result.claimVerificationResult,
4529
+ identityResolutions: result.identityResolutions,
4530
+ });
4531
+ }
4532
+ }
4533
+ const resultForClient = normalizedConclusion === result.conclusion &&
4534
+ normalizedConclusionContract === result.conclusionContract &&
4535
+ qualityArtifacts.claimSupport === result.claimSupport &&
4536
+ qualityArtifacts.claimVerificationResult === result.claimVerificationResult &&
4537
+ qualityArtifacts.identityResolutions === result.identityResolutions
4538
+ ? result
4539
+ : {
4540
+ ...result,
4541
+ conclusion: normalizedConclusion,
4542
+ conclusionContract: normalizedConclusionContract,
4543
+ ...qualityArtifacts,
4544
+ };
4545
+ const clientFindings = replayOnlyScene ? [] : buildClientFindings(result.findings, session.scenes || []);
4546
+ const resultContract = buildSessionResultContract(session, clientFindings);
4547
+ const finalArtifacts = ensureCompletedAnalysisFinalArtifacts(session, {
4548
+ result,
4549
+ hasEvidenceBackedConclusion,
4550
+ normalizedConclusion,
4551
+ normalizedConclusionContract,
4552
+ qualityArtifacts,
4553
+ resultForClient: resultForClient,
4554
+ });
4555
+ return {
4556
+ result,
4557
+ replayOnlyScene,
4558
+ normalizedConclusion,
4559
+ normalizedConclusionContract,
4560
+ qualityArtifacts,
4561
+ clientFindings,
4562
+ resultContract,
4563
+ finalArtifacts,
4564
+ };
4565
+ }
4566
+ /**
4567
+ * Send agent-driven analysis result to SSE client
4568
+ */
4569
+ function ensureCompletedAnalysisSseEvents(session) {
4570
+ const cached = session.completedAnalysisSseEvents;
4571
+ if (cached?.length &&
4572
+ session.completedAnalysisSseEventsQualityGateVersion ===
4573
+ COMPLETED_ANALYSIS_SSE_EVENTS_QUALITY_GATE_VERSION) {
4574
+ return cached;
4575
+ }
4576
+ const completedPayload = ensureCompletedAnalysisResultPayload(session);
4577
+ if (!completedPayload) {
4578
+ const persisted = loadPersistedCompletedAnalysisSseEvents(session);
4579
+ if (persisted.length > 0) {
4580
+ session.completedAnalysisSseEvents = persisted;
4581
+ delete session.completedAnalysisSseEventsQualityGateVersion;
4582
+ return persisted;
4583
+ }
4584
+ return [];
4585
+ }
4586
+ const { result, normalizedConclusion, normalizedConclusionContract, qualityArtifacts, clientFindings, resultContract, finalArtifacts, } = completedPayload;
4587
+ const observability = buildStreamObservability(session);
4588
+ const events = [];
4589
+ if (finalArtifacts.resultSnapshotEventData) {
4590
+ events.push(appendAndPersistReplayableSessionEvent(session, 'snapshot_created', {
4214
4591
  type: 'snapshot_created',
4215
4592
  architecture: 'agent-driven',
4216
4593
  ...observability,
4217
- data: resultSnapshotEventData,
4594
+ data: finalArtifacts.resultSnapshotEventData,
4218
4595
  timestamp: Date.now(),
4219
- });
4596
+ }));
4220
4597
  }
4221
4598
  // Send analysis_completed event with full result. Keep it replayable so a
4222
4599
  // reconnect between conclusion and report generation can recover reportUrl.
4223
- sendReplayableSessionEvent(session, res, 'analysis_completed', {
4600
+ events.push(appendAndPersistReplayableSessionEvent(session, 'analysis_completed', {
4224
4601
  type: 'analysis_completed',
4225
4602
  architecture: 'agent-driven',
4226
4603
  ...observability,
4227
4604
  data: {
4228
4605
  conclusion: normalizedConclusion,
4229
4606
  conclusionContract: normalizedConclusionContract,
4607
+ claimSupport: qualityArtifacts.claimSupport,
4608
+ claimVerificationResult: qualityArtifacts.claimVerificationResult,
4609
+ identityResolutions: qualityArtifacts.identityResolutions,
4230
4610
  confidence: result.confidence,
4231
4611
  rounds: result.rounds,
4232
4612
  totalDurationMs: result.totalDurationMs,
@@ -4246,8 +4626,8 @@ function sendAgentDrivenResult(res, session) {
4246
4626
  agentDialogueCount: session.agentDialogue.length,
4247
4627
  conversationTimelineCount: session.conversationSteps.length,
4248
4628
  conversationTimeline: session.conversationSteps,
4249
- reportUrl,
4250
- reportError,
4629
+ reportUrl: finalArtifacts.reportUrl,
4630
+ reportError: finalArtifacts.reportError,
4251
4631
  comparisonReportSection: session.comparisonReportSection
4252
4632
  ? {
4253
4633
  source: session.comparisonReportSection.source,
@@ -4257,14 +4637,15 @@ function sendAgentDrivenResult(res, session) {
4257
4637
  evidencePack: session.comparisonReportSection.evidencePack,
4258
4638
  }
4259
4639
  : undefined,
4260
- resultSnapshotId,
4640
+ resultSnapshotId: finalArtifacts.resultSnapshotId,
4261
4641
  observability,
4642
+ terminalRunStatus: session.status === 'quota_exceeded' ? 'quota_exceeded' : 'completed',
4262
4643
  },
4263
4644
  timestamp: Date.now(),
4264
- });
4645
+ }));
4265
4646
  // Backward-compatible scene reconstruction payload (used by the legacy /scene-reconstruct clients).
4266
4647
  if ((session.scenes?.length || 0) > 0 || (session.trackEvents?.length || 0) > 0) {
4267
- sendReplayableSessionEvent(session, res, 'scene_reconstruction_completed', {
4648
+ events.push(appendAndPersistReplayableSessionEvent(session, 'scene_reconstruction_completed', {
4268
4649
  type: 'scene_reconstruction_completed',
4269
4650
  ...observability,
4270
4651
  data: {
@@ -4292,7 +4673,20 @@ function sendAgentDrivenResult(res, session) {
4292
4673
  observability,
4293
4674
  },
4294
4675
  timestamp: Date.now(),
4295
- });
4676
+ }));
4677
+ }
4678
+ events.push(appendAndPersistReplayableSessionEvent(session, 'end', {
4679
+ timestamp: Date.now(),
4680
+ ...observability,
4681
+ }));
4682
+ session.completedAnalysisSseEvents = events;
4683
+ session.completedAnalysisSseEventsQualityGateVersion =
4684
+ COMPLETED_ANALYSIS_SSE_EVENTS_QUALITY_GATE_VERSION;
4685
+ return events;
4686
+ }
4687
+ function sendAgentDrivenResult(res, session) {
4688
+ for (const event of ensureCompletedAnalysisSseEvents(session)) {
4689
+ writeBufferedSessionEvent(res, event);
4296
4690
  }
4297
4691
  }
4298
4692
  (0, agentLogsRoutes_1.registerAgentLogsRoutes)(router);