@exaudeus/workrail 3.14.0 → 3.16.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 (156) hide show
  1. package/dist/application/services/validation-engine.js +4 -9
  2. package/dist/application/services/workflow-compiler.js +4 -6
  3. package/dist/application/services/workflow-service.d.ts +2 -0
  4. package/dist/application/services/workflow-service.js +3 -0
  5. package/dist/console/assets/index-BE5PAgPO.js +28 -0
  6. package/dist/console/assets/index-BZNM03t1.css +1 -0
  7. package/dist/console/index.html +2 -2
  8. package/dist/engine/engine-factory.js +2 -2
  9. package/dist/engine/types.d.ts +1 -1
  10. package/dist/env-flags.d.ts +1 -0
  11. package/dist/env-flags.js +4 -0
  12. package/dist/infrastructure/session/HttpServer.d.ts +3 -3
  13. package/dist/infrastructure/session/HttpServer.js +68 -74
  14. package/dist/infrastructure/storage/caching-workflow-storage.d.ts +2 -0
  15. package/dist/infrastructure/storage/caching-workflow-storage.js +15 -6
  16. package/dist/infrastructure/storage/file-workflow-storage.js +3 -4
  17. package/dist/infrastructure/storage/schema-validating-workflow-storage.js +9 -8
  18. package/dist/manifest.json +283 -219
  19. package/dist/mcp/assert-output.d.ts +37 -0
  20. package/dist/mcp/assert-output.js +52 -0
  21. package/dist/mcp/boundary-coercion.d.ts +1 -0
  22. package/dist/mcp/boundary-coercion.js +44 -0
  23. package/dist/mcp/dev-mode.d.ts +1 -0
  24. package/dist/mcp/dev-mode.js +4 -0
  25. package/dist/mcp/handler-factory.js +12 -9
  26. package/dist/mcp/handlers/session.js +8 -9
  27. package/dist/mcp/handlers/shared/request-workflow-reader.d.ts +5 -0
  28. package/dist/mcp/handlers/shared/request-workflow-reader.js +47 -2
  29. package/dist/mcp/handlers/v2-advance-core/assessment-consequences.d.ts +1 -1
  30. package/dist/mcp/handlers/v2-advance-core/assessment-consequences.js +4 -5
  31. package/dist/mcp/handlers/v2-advance-core/event-builders.d.ts +2 -0
  32. package/dist/mcp/handlers/v2-advance-core/event-builders.js +6 -6
  33. package/dist/mcp/handlers/v2-advance-core/index.d.ts +2 -0
  34. package/dist/mcp/handlers/v2-advance-core/index.js +5 -4
  35. package/dist/mcp/handlers/v2-advance-core/input-validation.d.ts +2 -0
  36. package/dist/mcp/handlers/v2-advance-core/input-validation.js +32 -9
  37. package/dist/mcp/handlers/v2-advance-core/outcome-blocked.d.ts +2 -0
  38. package/dist/mcp/handlers/v2-advance-core/outcome-blocked.js +2 -2
  39. package/dist/mcp/handlers/v2-advance-core/outcome-success.d.ts +2 -0
  40. package/dist/mcp/handlers/v2-advance-core/outcome-success.js +1 -1
  41. package/dist/mcp/handlers/v2-checkpoint.d.ts +1 -1
  42. package/dist/mcp/handlers/v2-checkpoint.js +5 -6
  43. package/dist/mcp/handlers/v2-execution/advance.d.ts +4 -2
  44. package/dist/mcp/handlers/v2-execution/advance.js +5 -7
  45. package/dist/mcp/handlers/v2-execution/continue-advance.js +56 -26
  46. package/dist/mcp/handlers/v2-execution/continue-rehydrate.d.ts +1 -1
  47. package/dist/mcp/handlers/v2-execution/continue-rehydrate.js +9 -9
  48. package/dist/mcp/handlers/v2-execution/replay.d.ts +6 -4
  49. package/dist/mcp/handlers/v2-execution/replay.js +47 -30
  50. package/dist/mcp/handlers/v2-execution/start.d.ts +3 -3
  51. package/dist/mcp/handlers/v2-execution/start.js +31 -12
  52. package/dist/mcp/handlers/v2-execution/workflow-object-cache.d.ts +5 -0
  53. package/dist/mcp/handlers/v2-execution/workflow-object-cache.js +19 -0
  54. package/dist/mcp/handlers/v2-execution-helpers.d.ts +1 -0
  55. package/dist/mcp/handlers/v2-execution-helpers.js +23 -7
  56. package/dist/mcp/handlers/v2-resume.d.ts +1 -1
  57. package/dist/mcp/handlers/v2-resume.js +3 -4
  58. package/dist/mcp/handlers/v2-state-conversion.js +5 -1
  59. package/dist/mcp/handlers/v2-workflow.d.ts +100 -0
  60. package/dist/mcp/handlers/v2-workflow.js +155 -31
  61. package/dist/mcp/handlers/workflow.d.ts +2 -5
  62. package/dist/mcp/handlers/workflow.js +15 -12
  63. package/dist/mcp/output-schemas.d.ts +123 -29
  64. package/dist/mcp/output-schemas.js +36 -18
  65. package/dist/mcp/server.js +70 -5
  66. package/dist/mcp/tool-call-timing.d.ts +24 -0
  67. package/dist/mcp/tool-call-timing.js +85 -0
  68. package/dist/mcp/tool-descriptions.js +17 -9
  69. package/dist/mcp/transports/http-entry.js +3 -2
  70. package/dist/mcp/transports/http-listener.d.ts +1 -0
  71. package/dist/mcp/transports/http-listener.js +25 -0
  72. package/dist/mcp/transports/shutdown-hooks.d.ts +4 -1
  73. package/dist/mcp/transports/shutdown-hooks.js +3 -2
  74. package/dist/mcp/transports/stdio-entry.js +6 -28
  75. package/dist/mcp/v2/tools.d.ts +6 -0
  76. package/dist/mcp/v2/tools.js +2 -0
  77. package/dist/mcp/v2-response-formatter.js +2 -4
  78. package/dist/mcp/validation/schema-introspection.d.ts +1 -0
  79. package/dist/mcp/validation/schema-introspection.js +15 -5
  80. package/dist/mcp/validation/suggestion-generator.js +2 -2
  81. package/dist/mcp/workflow-protocol-contracts.js +5 -1
  82. package/dist/runtime/adapters/node-process-signals.d.ts +1 -0
  83. package/dist/runtime/adapters/node-process-signals.js +5 -0
  84. package/dist/runtime/adapters/noop-process-signals.d.ts +1 -0
  85. package/dist/runtime/adapters/noop-process-signals.js +2 -0
  86. package/dist/runtime/ports/process-signals.d.ts +1 -0
  87. package/dist/types/workflow-definition.d.ts +3 -2
  88. package/dist/types/workflow.d.ts +3 -0
  89. package/dist/types/workflow.js +35 -26
  90. package/dist/v2/durable-core/domain/context-template-resolver.js +2 -2
  91. package/dist/v2/durable-core/domain/function-definition-expander.js +2 -17
  92. package/dist/v2/durable-core/domain/prompt-renderer.d.ts +1 -0
  93. package/dist/v2/durable-core/domain/prompt-renderer.js +23 -18
  94. package/dist/v2/durable-core/domain/recap-recovery.js +23 -16
  95. package/dist/v2/durable-core/domain/retrieval-contract.js +13 -7
  96. package/dist/v2/durable-core/session-index.d.ts +22 -0
  97. package/dist/v2/durable-core/session-index.js +58 -0
  98. package/dist/v2/durable-core/sorted-event-log.d.ts +6 -0
  99. package/dist/v2/durable-core/sorted-event-log.js +15 -0
  100. package/dist/v2/infra/local/fs/index.js +8 -8
  101. package/dist/v2/infra/local/session-store/index.d.ts +1 -1
  102. package/dist/v2/infra/local/session-store/index.js +71 -61
  103. package/dist/v2/infra/local/session-summary-provider/index.js +9 -4
  104. package/dist/v2/infra/local/snapshot-store/index.js +2 -1
  105. package/dist/v2/infra/local/workspace-anchor/index.js +4 -1
  106. package/dist/v2/ports/session-event-log-store.port.d.ts +1 -1
  107. package/dist/v2/projections/assessment-consequences.d.ts +2 -1
  108. package/dist/v2/projections/assessment-consequences.js +0 -5
  109. package/dist/v2/projections/assessments.d.ts +2 -1
  110. package/dist/v2/projections/assessments.js +2 -4
  111. package/dist/v2/projections/gaps.d.ts +2 -1
  112. package/dist/v2/projections/gaps.js +0 -5
  113. package/dist/v2/projections/preferences.d.ts +2 -1
  114. package/dist/v2/projections/preferences.js +0 -5
  115. package/dist/v2/projections/run-context.d.ts +2 -2
  116. package/dist/v2/projections/run-context.js +0 -5
  117. package/dist/v2/projections/run-dag.js +7 -1
  118. package/dist/v2/projections/run-execution-trace.d.ts +8 -0
  119. package/dist/v2/projections/run-execution-trace.js +124 -0
  120. package/dist/v2/projections/run-status-signals.d.ts +2 -2
  121. package/dist/v2/usecases/console-routes.d.ts +3 -1
  122. package/dist/v2/usecases/console-routes.js +149 -3
  123. package/dist/v2/usecases/console-service.d.ts +2 -0
  124. package/dist/v2/usecases/console-service.js +87 -26
  125. package/dist/v2/usecases/console-types.d.ts +65 -0
  126. package/dist/v2/usecases/worktree-service.js +87 -8
  127. package/package.json +7 -6
  128. package/spec/authoring-spec.json +82 -1
  129. package/spec/workflow-tags.json +132 -0
  130. package/spec/workflow.schema.json +21 -11
  131. package/workflows/adaptive-ticket-creation.json +33 -8
  132. package/workflows/architecture-scalability-audit.json +50 -9
  133. package/workflows/bug-investigation.agentic.v2.json +43 -14
  134. package/workflows/coding-task-workflow-agentic.json +57 -38
  135. package/workflows/coding-task-workflow-agentic.lean.v2.json +129 -34
  136. package/workflows/coding-task-workflow-agentic.v2.json +97 -30
  137. package/workflows/cross-platform-code-conversion.v2.json +175 -48
  138. package/workflows/document-creation-workflow.json +49 -12
  139. package/workflows/documentation-update-workflow.json +9 -2
  140. package/workflows/intelligent-test-case-generation.json +9 -2
  141. package/workflows/learner-centered-course-workflow.json +273 -266
  142. package/workflows/mr-review-workflow.agentic.v2.json +88 -14
  143. package/workflows/personal-learning-materials-creation-branched.json +181 -174
  144. package/workflows/presentation-creation.json +167 -160
  145. package/workflows/production-readiness-audit.json +61 -15
  146. package/workflows/relocation-workflow-us.json +21 -5
  147. package/workflows/routines/tension-driven-design.json +1 -1
  148. package/workflows/scoped-documentation-workflow.json +9 -2
  149. package/workflows/test-artifact-loop-control.json +1 -2
  150. package/workflows/ui-ux-design-workflow.json +334 -0
  151. package/workflows/workflow-diagnose-environment.json +7 -1
  152. package/workflows/workflow-for-workflows.json +514 -484
  153. package/workflows/workflow-for-workflows.v2.json +55 -11
  154. package/workflows/wr.discovery.json +118 -29
  155. package/dist/console/assets/index-DW78t31j.css +0 -1
  156. package/dist/console/assets/index-EsSXrC_a.js +0 -28
@@ -39,20 +39,22 @@ const jsonl_js_1 = require("../../../durable-core/canonical/jsonl.js");
39
39
  const index_js_1 = require("../../../durable-core/schemas/session/index.js");
40
40
  const constants_js_1 = require("../../../durable-core/constants.js");
41
41
  const path = __importStar(require("path"));
42
+ const _utf8Decoder = new TextDecoder();
43
+ const _createdEventsDirs = new Set();
42
44
  class LocalSessionEventLogStoreV2 {
43
45
  constructor(dataDir, fs, sha256) {
44
46
  this.dataDir = dataDir;
45
47
  this.fs = fs;
46
48
  this.sha256 = sha256;
47
49
  }
48
- append(lock, plan) {
50
+ append(lock, plan, preloadedTruth) {
49
51
  if (!lock.assertHeld()) {
50
52
  return (0, neverthrow_1.errAsync)({
51
53
  code: 'SESSION_STORE_INVARIANT_VIOLATION',
52
54
  message: 'WithHealthySessionLock used after gate callback ended (witness misuse-after-release)',
53
55
  });
54
56
  }
55
- return this.appendImpl(lock.sessionId, plan);
57
+ return this.appendImpl(lock.sessionId, plan, preloadedTruth);
56
58
  }
57
59
  load(sessionId) {
58
60
  return this.loadImpl(sessionId);
@@ -60,13 +62,20 @@ class LocalSessionEventLogStoreV2 {
60
62
  loadValidatedPrefix(sessionId) {
61
63
  return this.loadValidatedPrefixImpl(sessionId);
62
64
  }
63
- appendImpl(sessionId, plan) {
65
+ appendImpl(sessionId, plan, preloadedTruth) {
64
66
  const sessionDir = this.dataDir.sessionDir(sessionId);
65
67
  const eventsDir = this.dataDir.sessionEventsDir(sessionId);
66
68
  const manifestPath = this.dataDir.sessionManifestPath(sessionId);
67
- return this.fs.mkdirp(eventsDir)
68
- .mapErr(mapFsToStoreError)
69
- .andThen(() => this.loadTruthOrEmpty(sessionId))
69
+ const mkdirpResult = _createdEventsDirs.has(eventsDir)
70
+ ? (0, neverthrow_1.okAsync)(undefined)
71
+ : this.fs.mkdirp(eventsDir).mapErr(mapFsToStoreError).map(() => { _createdEventsDirs.add(eventsDir); });
72
+ const truthSource = preloadedTruth !== undefined
73
+ ? mkdirpResult.andThen(() => (0, neverthrow_1.okAsync)({
74
+ manifest: preloadedTruth.manifest,
75
+ events: preloadedTruth.events,
76
+ }))
77
+ : mkdirpResult.andThen(() => this.loadTruthOrEmpty(sessionId));
78
+ return truthSource
70
79
  .andThen(({ manifest, events: existingEvents }) => {
71
80
  const contiguityRes = validateManifestContiguity(manifest);
72
81
  if (contiguityRes.isErr())
@@ -116,24 +125,23 @@ class LocalSessionEventLogStoreV2 {
116
125
  sha256: digest,
117
126
  bytes: segmentBytes.length,
118
127
  };
119
- return this.appendManifestRecords(manifestPath, [segClosed])
120
- .andThen(() => {
121
- const pins = sortedPins(plan.snapshotPins);
122
- if (pins.length === 0)
123
- return (0, neverthrow_1.okAsync)(undefined);
124
- const startIndex = segClosed.manifestIndex + 1;
125
- const records = pins.map((p, i) => ({
126
- v: 1,
127
- manifestIndex: startIndex + i,
128
- sessionId,
129
- kind: constants_js_1.MANIFEST_KIND.SNAPSHOT_PINNED,
130
- eventIndex: p.eventIndex,
131
- snapshotRef: p.snapshotRef,
132
- createdByEventId: p.createdByEventId,
133
- }));
134
- return this.appendManifestRecords(manifestPath, records);
135
- });
128
+ const pins = sortedPins(plan.snapshotPins);
129
+ const startIndex = segClosed.manifestIndex + 1;
130
+ const pinRecords = pins.map((p, i) => ({
131
+ v: 1,
132
+ manifestIndex: startIndex + i,
133
+ sessionId,
134
+ kind: constants_js_1.MANIFEST_KIND.SNAPSHOT_PINNED,
135
+ eventIndex: p.eventIndex,
136
+ snapshotRef: p.snapshotRef,
137
+ createdByEventId: p.createdByEventId,
138
+ }));
139
+ return this.appendManifestRecords(manifestPath, [segClosed, ...pinRecords]);
136
140
  });
141
+ })
142
+ .orElse((error) => {
143
+ _createdEventsDirs.delete(eventsDir);
144
+ return (0, neverthrow_1.errAsync)(error);
137
145
  });
138
146
  }
139
147
  loadImpl(sessionId) {
@@ -147,7 +155,7 @@ class LocalSessionEventLogStoreV2 {
147
155
  if (segRes.isErr())
148
156
  return (0, neverthrow_1.errAsync)(segRes.error);
149
157
  const segments = manifest.filter((m) => m.kind === constants_js_1.MANIFEST_KIND.SEGMENT_CLOSED);
150
- return loadSegmentsRecursive({
158
+ return loadSegmentsParallel({
151
159
  segments,
152
160
  sessionDir,
153
161
  sha256: this.sha256,
@@ -334,17 +342,14 @@ function validateAppendPlan(sessionId, plan, expectedFirstEventIndex) {
334
342
  });
335
343
  }
336
344
  for (let i = 0; i < plan.events.length; i++) {
337
- const e = index_js_1.DomainEventV1Schema.safeParse(plan.events[i]);
338
- if (!e.success) {
339
- return (0, neverthrow_1.err)({ code: 'SESSION_STORE_INVARIANT_VIOLATION', message: `Invalid domain event at index ${i}` });
340
- }
341
- if (e.data.sessionId !== sessionId) {
345
+ const e = plan.events[i];
346
+ if (e.sessionId !== sessionId) {
342
347
  return (0, neverthrow_1.err)({
343
348
  code: 'SESSION_STORE_INVARIANT_VIOLATION',
344
349
  message: `Domain event sessionId mismatch at index ${i}`,
345
350
  });
346
351
  }
347
- if (i > 0 && plan.events[i].eventIndex !== plan.events[i - 1].eventIndex + 1) {
352
+ if (i > 0 && e.eventIndex !== plan.events[i - 1].eventIndex + 1) {
348
353
  return (0, neverthrow_1.err)({
349
354
  code: 'SESSION_STORE_INVARIANT_VIOLATION',
350
355
  message: `Non-contiguous eventIndex in AppendPlan at index ${i}`,
@@ -439,7 +444,7 @@ function parseJsonlText(text, schema) {
439
444
  return (0, neverthrow_1.ok)(out);
440
445
  }
441
446
  function parseJsonlLines(bytes, schema) {
442
- const text = new TextDecoder().decode(bytes);
447
+ const text = _utf8Decoder.decode(bytes);
443
448
  return parseJsonlText(text, schema);
444
449
  }
445
450
  function extractSnapshotPinsFromEvents(events) {
@@ -573,41 +578,46 @@ function processSegmentForSalvage(args) {
573
578
  });
574
579
  });
575
580
  }
576
- function loadSegmentsRecursive(args) {
581
+ function loadSegmentsParallel(args) {
577
582
  if (args.segments.length === 0)
578
583
  return (0, neverthrow_1.okAsync)([]);
579
- const [head, ...tail] = args.segments;
580
- const segmentPath = path.join(args.sessionDir, head.segmentRelPath);
581
- return args.fs.readFileBytes(segmentPath)
582
- .mapErr((e) => {
584
+ return neverthrow_1.ResultAsync.fromPromise(Promise.all(args.segments.map((seg) => args.fs.readFileBytes(path.join(args.sessionDir, seg.segmentRelPath))
585
+ .match((bytes) => ({ ok: true, bytes, seg }), (e) => {
583
586
  if (e.code === 'FS_NOT_FOUND') {
584
587
  return {
585
- code: 'SESSION_STORE_CORRUPTION_DETECTED',
586
- location: 'tail',
587
- reason: { code: 'missing_attested_segment', message: `Missing attested segment: ${head.segmentRelPath}` },
588
- message: `Missing attested segment: ${head.segmentRelPath}`,
588
+ ok: false,
589
+ error: {
590
+ code: 'SESSION_STORE_CORRUPTION_DETECTED',
591
+ location: 'tail',
592
+ reason: { code: 'missing_attested_segment', message: `Missing attested segment: ${seg.segmentRelPath}` },
593
+ message: `Missing attested segment: ${seg.segmentRelPath}`,
594
+ },
589
595
  };
590
596
  }
591
- return mapFsToStoreError(e);
592
- })
593
- .andThen((bytes) => {
594
- const actual = args.sha256.sha256(bytes);
595
- if (actual !== head.sha256) {
596
- return (0, neverthrow_1.errAsync)({
597
- code: 'SESSION_STORE_CORRUPTION_DETECTED',
598
- location: 'tail',
599
- reason: { code: 'digest_mismatch', message: `Segment digest mismatch: ${head.segmentRelPath}` },
600
- message: `Segment digest mismatch: ${head.segmentRelPath}`,
601
- });
597
+ return { ok: false, error: mapFsToStoreError(e) };
598
+ }))), (e) => ({ code: 'SESSION_STORE_IO_ERROR', message: String(e) })).andThen((readResults) => {
599
+ const allEvents = [];
600
+ for (const result of readResults) {
601
+ if (!result.ok)
602
+ return (0, neverthrow_1.err)(result.error);
603
+ const { bytes, seg } = result;
604
+ const actual = args.sha256.sha256(bytes);
605
+ if (actual !== seg.sha256) {
606
+ return (0, neverthrow_1.err)({
607
+ code: 'SESSION_STORE_CORRUPTION_DETECTED',
608
+ location: 'tail',
609
+ reason: { code: 'digest_mismatch', message: `Segment digest mismatch: ${seg.segmentRelPath}` },
610
+ message: `Segment digest mismatch: ${seg.segmentRelPath}`,
611
+ });
612
+ }
613
+ const parsedRes = parseJsonlLines(bytes, index_js_1.DomainEventV1Schema);
614
+ if (parsedRes.isErr())
615
+ return (0, neverthrow_1.err)(parsedRes.error);
616
+ const boundsResult = validateSegmentBounds(parsedRes.value, seg);
617
+ if (boundsResult.isErr())
618
+ return (0, neverthrow_1.err)(boundsResult.error);
619
+ allEvents.push(...parsedRes.value);
602
620
  }
603
- const parsedRes = parseJsonlLines(bytes, index_js_1.DomainEventV1Schema);
604
- if (parsedRes.isErr())
605
- return (0, neverthrow_1.errAsync)(parsedRes.error);
606
- const parsed = parsedRes.value;
607
- const boundsResult = validateSegmentBounds(parsed, head);
608
- if (boundsResult.isErr())
609
- return (0, neverthrow_1.errAsync)(boundsResult.error);
610
- return (0, neverthrow_1.okAsync)(parsed);
611
- })
612
- .andThen((events) => loadSegmentsRecursive({ ...args, segments: tail }).map((rest) => [...events, ...rest]));
621
+ return (0, neverthrow_1.ok)(allEvents);
622
+ });
613
623
  }
@@ -8,6 +8,7 @@ const session_health_js_1 = require("../../../projections/session-health.js");
8
8
  const run_dag_js_1 = require("../../../projections/run-dag.js");
9
9
  const node_outputs_js_1 = require("../../../projections/node-outputs.js");
10
10
  const run_context_js_1 = require("../../../projections/run-context.js");
11
+ const sorted_event_log_js_1 = require("../../../durable-core/sorted-event-log.js");
11
12
  const snapshot_state_js_1 = require("../../../durable-core/projections/snapshot-state.js");
12
13
  const index_js_1 = require("../../../durable-core/ids/index.js");
13
14
  const constants_js_1 = require("../../../durable-core/constants.js");
@@ -87,6 +88,10 @@ function projectSessionSummary(sessionId, truth, mtimeMs) {
87
88
  const workflow = extractWorkflowIdentity(truth.events, bestRun.run.runId);
88
89
  if (!workflow)
89
90
  return null;
91
+ const sortedEventsRes = (0, sorted_event_log_js_1.asSortedEventLog)(truth.events);
92
+ const sessionTitle = sortedEventsRes.isOk()
93
+ ? deriveSessionTitle(sortedEventsRes.value, bestRun.run.runId)
94
+ : null;
90
95
  const outputsRes = (0, node_outputs_js_1.projectNodeOutputsV2)(truth.events);
91
96
  const recapSnippet = outputsRes.isOk()
92
97
  ? extractAggregateRecap(outputsRes.value, bestRun.run, bestRun.tipNodeId)
@@ -102,7 +107,7 @@ function projectSessionSummary(sessionId, truth, mtimeMs) {
102
107
  recapSnippet,
103
108
  observations: extractObservations(truth.events),
104
109
  workflow,
105
- sessionTitle: deriveSessionTitle(truth.events, bestRun.run.runId),
110
+ sessionTitle,
106
111
  lastModifiedMs: mtimeMs,
107
112
  pendingStepId: null,
108
113
  isComplete: false,
@@ -150,8 +155,8 @@ function extractWorkflowIdentity(events, runId) {
150
155
  workflowHash: (0, index_js_1.asWorkflowHash)((0, index_js_1.asSha256Digest)(event.data.workflowHash)),
151
156
  };
152
157
  }
153
- function deriveSessionTitle(events, runId) {
154
- const contextRes = (0, run_context_js_1.projectRunContextV2)(events);
158
+ function deriveSessionTitle(sortedEvents, runId) {
159
+ const contextRes = (0, run_context_js_1.projectRunContextV2)(sortedEvents);
155
160
  if (contextRes.isOk()) {
156
161
  const runCtx = contextRes.value.byRunId[runId];
157
162
  if (runCtx) {
@@ -163,7 +168,7 @@ function deriveSessionTitle(events, runId) {
163
168
  }
164
169
  }
165
170
  }
166
- return extractTitleFromFirstRecap(events);
171
+ return extractTitleFromFirstRecap(sortedEvents);
167
172
  }
168
173
  function extractTitleFromFirstRecap(events) {
169
174
  const outputsRes = (0, node_outputs_js_1.projectNodeOutputsV2)(events);
@@ -5,6 +5,7 @@ const neverthrow_1 = require("neverthrow");
5
5
  const index_js_1 = require("../../../durable-core/ids/index.js");
6
6
  const index_js_2 = require("../../../durable-core/schemas/execution-snapshot/index.js");
7
7
  const jcs_js_1 = require("../../../durable-core/canonical/jcs.js");
8
+ const _utf8Decoder = new TextDecoder();
8
9
  class LocalSnapshotStoreV2 {
9
10
  constructor(dataDir, fs, crypto) {
10
11
  this.dataDir = dataDir;
@@ -41,7 +42,7 @@ class LocalSnapshotStoreV2 {
41
42
  .andThen((bytes) => {
42
43
  let parsed;
43
44
  try {
44
- parsed = JSON.parse(new TextDecoder().decode(bytes));
45
+ parsed = JSON.parse(_utf8Decoder.decode(bytes));
45
46
  }
46
47
  catch {
47
48
  return (0, neverthrow_1.errAsync)({
@@ -33,7 +33,10 @@ class LocalWorkspaceAnchorV2 {
33
33
  }
34
34
  async runGitCommands(cwd) {
35
35
  const anchors = [];
36
- const repoRoot = await this.gitCommand('git rev-parse --show-toplevel', cwd);
36
+ const gitCommonDir = await this.gitCommand('git rev-parse --path-format=absolute --git-common-dir', cwd);
37
+ if (!gitCommonDir)
38
+ return anchors;
39
+ const repoRoot = gitCommonDir.replace(/\/\.git\/?$/, '').trim() || null;
37
40
  if (!repoRoot)
38
41
  return anchors;
39
42
  const repoRootHash = hashRepoRoot(repoRoot);
@@ -48,5 +48,5 @@ export interface SessionEventLogReadonlyStorePortV2 {
48
48
  loadValidatedPrefix(sessionId: SessionId): ResultAsync<LoadedValidatedPrefixV2, SessionEventLogStoreError>;
49
49
  }
50
50
  export interface SessionEventLogAppendStorePortV2 {
51
- append(lock: WithHealthySessionLock, plan: AppendPlanV2): ResultAsync<void, SessionEventLogStoreError>;
51
+ append(lock: WithHealthySessionLock, plan: AppendPlanV2, preloadedTruth?: LoadedSessionTruthV2): ResultAsync<void, SessionEventLogStoreError>;
52
52
  }
@@ -1,5 +1,6 @@
1
1
  import type { Result } from 'neverthrow';
2
2
  import type { DomainEventV1 } from '../durable-core/schemas/session/index.js';
3
+ import type { SortedEventLog } from '../durable-core/sorted-event-log.js';
3
4
  import type { ProjectionError } from './projection-error.js';
4
5
  type AssessmentConsequenceAppliedEventV1 = Extract<DomainEventV1, {
5
6
  kind: 'assessment_consequence_applied';
@@ -14,6 +15,6 @@ export interface AppliedAssessmentConsequenceViewV2 {
14
15
  export interface AssessmentConsequencesProjectionV2 {
15
16
  readonly byNodeId: Readonly<Record<string, readonly AppliedAssessmentConsequenceViewV2[]>>;
16
17
  }
17
- export declare function projectAssessmentConsequencesV2(events: readonly DomainEventV1[]): Result<AssessmentConsequencesProjectionV2, ProjectionError>;
18
+ export declare function projectAssessmentConsequencesV2(events: SortedEventLog): Result<AssessmentConsequencesProjectionV2, ProjectionError>;
18
19
  export declare function getLatestAssessmentConsequenceForNode(projection: AssessmentConsequencesProjectionV2, nodeId: string): AppliedAssessmentConsequenceViewV2 | undefined;
19
20
  export {};
@@ -5,11 +5,6 @@ exports.getLatestAssessmentConsequenceForNode = getLatestAssessmentConsequenceFo
5
5
  const neverthrow_1 = require("neverthrow");
6
6
  const constants_js_1 = require("../durable-core/constants.js");
7
7
  function projectAssessmentConsequencesV2(events) {
8
- for (let i = 1; i < events.length; i++) {
9
- if (events[i].eventIndex < events[i - 1].eventIndex) {
10
- return (0, neverthrow_1.err)({ code: 'PROJECTION_INVARIANT_VIOLATION', message: 'Events must be sorted by eventIndex ascending' });
11
- }
12
- }
13
8
  const byNodeId = {};
14
9
  for (const event of events) {
15
10
  if (event.kind !== constants_js_1.EVENT_KIND.ASSESSMENT_CONSEQUENCE_APPLIED)
@@ -1,5 +1,6 @@
1
1
  import type { Result } from 'neverthrow';
2
2
  import type { DomainEventV1 } from '../durable-core/schemas/session/index.js';
3
+ import type { SortedEventLog } from '../durable-core/sorted-event-log.js';
3
4
  import type { ProjectionError } from './projection-error.js';
4
5
  type AssessmentRecordedEventV1 = Extract<DomainEventV1, {
5
6
  kind: 'assessment_recorded';
@@ -16,6 +17,6 @@ export interface RecordedAssessmentViewV2 {
16
17
  export interface AssessmentsProjectionV2 {
17
18
  readonly byNodeId: Readonly<Record<string, readonly RecordedAssessmentViewV2[]>>;
18
19
  }
19
- export declare function projectAssessmentsV2(events: readonly DomainEventV1[]): Result<AssessmentsProjectionV2, ProjectionError>;
20
+ export declare function projectAssessmentsV2(events: SortedEventLog): Result<AssessmentsProjectionV2, ProjectionError>;
20
21
  export declare function getLatestAssessmentForNode(projection: AssessmentsProjectionV2, nodeId: string): RecordedAssessmentViewV2 | undefined;
21
22
  export {};
@@ -5,10 +5,8 @@ exports.getLatestAssessmentForNode = getLatestAssessmentForNode;
5
5
  const neverthrow_1 = require("neverthrow");
6
6
  const constants_js_1 = require("../durable-core/constants.js");
7
7
  function projectAssessmentsV2(events) {
8
- for (let i = 1; i < events.length; i++) {
9
- if (events[i].eventIndex < events[i - 1].eventIndex) {
10
- return (0, neverthrow_1.err)({ code: 'PROJECTION_INVARIANT_VIOLATION', message: 'Events must be sorted by eventIndex ascending' });
11
- }
8
+ if (!events.some((e) => e.kind === constants_js_1.EVENT_KIND.ASSESSMENT_RECORDED)) {
9
+ return (0, neverthrow_1.ok)({ byNodeId: {} });
12
10
  }
13
11
  const byNodeId = {};
14
12
  for (const event of events) {
@@ -1,5 +1,6 @@
1
1
  import type { Result } from 'neverthrow';
2
2
  import type { DomainEventV1 } from '../durable-core/schemas/session/index.js';
3
+ import type { SortedEventLog } from '../durable-core/sorted-event-log.js';
3
4
  import type { ProjectionError } from './projection-error.js';
4
5
  type GapRecordedEventV1 = Extract<DomainEventV1, {
5
6
  kind: 'gap_recorded';
@@ -19,5 +20,5 @@ export interface GapsProjectionV2 {
19
20
  readonly resolvedGapIds: ReadonlySet<string>;
20
21
  readonly unresolvedCriticalByRunId: Readonly<Record<string, readonly GapV2[]>>;
21
22
  }
22
- export declare function projectGapsV2(events: readonly DomainEventV1[]): Result<GapsProjectionV2, ProjectionError>;
23
+ export declare function projectGapsV2(events: SortedEventLog): Result<GapsProjectionV2, ProjectionError>;
23
24
  export {};
@@ -4,11 +4,6 @@ exports.projectGapsV2 = projectGapsV2;
4
4
  const neverthrow_1 = require("neverthrow");
5
5
  const constants_js_1 = require("../durable-core/constants.js");
6
6
  function projectGapsV2(events) {
7
- for (let i = 1; i < events.length; i++) {
8
- if (events[i].eventIndex < events[i - 1].eventIndex) {
9
- return (0, neverthrow_1.err)({ code: 'PROJECTION_INVARIANT_VIOLATION', message: 'Events must be sorted by eventIndex ascending' });
10
- }
11
- }
12
7
  const byGapId = {};
13
8
  const resolved = new Set();
14
9
  for (const e of events) {
@@ -1,5 +1,6 @@
1
1
  import type { Result } from 'neverthrow';
2
2
  import type { DomainEventV1 } from '../durable-core/schemas/session/index.js';
3
+ import type { SortedEventLog } from '../durable-core/sorted-event-log.js';
3
4
  import type { AutonomyV2, RiskPolicyV2 } from '../durable-core/schemas/session/preferences.js';
4
5
  import type { ProjectionError } from './projection-error.js';
5
6
  type PreferencesChangedEventV1 = Extract<DomainEventV1, {
@@ -17,5 +18,5 @@ export interface NodePreferencesV2 {
17
18
  export interface PreferencesProjectionV2 {
18
19
  readonly byNodeId: Readonly<Record<string, NodePreferencesV2>>;
19
20
  }
20
- export declare function projectPreferencesV2(events: readonly DomainEventV1[], parentByNodeId: Readonly<Record<string, string | null>>): Result<PreferencesProjectionV2, ProjectionError>;
21
+ export declare function projectPreferencesV2(events: SortedEventLog, parentByNodeId: Readonly<Record<string, string | null>>): Result<PreferencesProjectionV2, ProjectionError>;
21
22
  export {};
@@ -5,11 +5,6 @@ const neverthrow_1 = require("neverthrow");
5
5
  const constants_js_1 = require("../durable-core/constants.js");
6
6
  const defaultPrefs = { autonomy: 'guided', riskPolicy: 'conservative' };
7
7
  function projectPreferencesV2(events, parentByNodeId) {
8
- for (let i = 1; i < events.length; i++) {
9
- if (events[i].eventIndex < events[i - 1].eventIndex) {
10
- return (0, neverthrow_1.err)({ code: 'PROJECTION_INVARIANT_VIOLATION', message: 'Events must be sorted by eventIndex ascending' });
11
- }
12
- }
13
8
  const changesByNodeId = {};
14
9
  for (const e of events) {
15
10
  if (e.kind !== constants_js_1.EVENT_KIND.PREFERENCES_CHANGED)
@@ -1,5 +1,5 @@
1
1
  import type { Result } from 'neverthrow';
2
- import type { DomainEventV1 } from '../durable-core/schemas/session/index.js';
2
+ import type { SortedEventLog } from '../durable-core/sorted-event-log.js';
3
3
  import type { JsonObject } from '../durable-core/canonical/json-types.js';
4
4
  import type { RunId } from '../durable-core/ids/index.js';
5
5
  import type { ProjectionError } from './projection-error.js';
@@ -13,4 +13,4 @@ export interface RunContextV2 {
13
13
  export interface RunContextProjectionV2 {
14
14
  readonly byRunId: Readonly<Record<string, RunContextV2>>;
15
15
  }
16
- export declare function projectRunContextV2(events: readonly DomainEventV1[]): Result<RunContextProjectionV2, ProjectionError>;
16
+ export declare function projectRunContextV2(events: SortedEventLog): Result<RunContextProjectionV2, ProjectionError>;
@@ -5,11 +5,6 @@ const neverthrow_1 = require("neverthrow");
5
5
  const constants_js_1 = require("../durable-core/constants.js");
6
6
  const index_js_1 = require("../durable-core/ids/index.js");
7
7
  function projectRunContextV2(events) {
8
- for (let i = 1; i < events.length; i++) {
9
- if (events[i].eventIndex < events[i - 1].eventIndex) {
10
- return (0, neverthrow_1.err)({ code: 'PROJECTION_INVARIANT_VIOLATION', message: 'Events must be sorted by eventIndex ascending' });
11
- }
12
- }
13
8
  const byRunId = {};
14
9
  for (const e of events) {
15
10
  if (e.kind !== constants_js_1.EVENT_KIND.CONTEXT_SET)
@@ -151,7 +151,13 @@ function projectRunDagV2(events) {
151
151
  createdAtEventIndex: e.eventIndex,
152
152
  };
153
153
  if (existing) {
154
- if (JSON.stringify(existing) !== JSON.stringify(node)) {
154
+ const differs = existing.nodeId !== node.nodeId ||
155
+ existing.nodeKind !== node.nodeKind ||
156
+ existing.parentNodeId !== node.parentNodeId ||
157
+ existing.workflowHash !== node.workflowHash ||
158
+ existing.snapshotRef !== node.snapshotRef ||
159
+ existing.createdAtEventIndex !== node.createdAtEventIndex;
160
+ if (differs) {
155
161
  return (0, neverthrow_1.err)({
156
162
  code: 'PROJECTION_CORRUPTION_DETECTED',
157
163
  message: `node_created conflict for runId=${runId} nodeId=${nodeId}`,
@@ -0,0 +1,8 @@
1
+ import type { Result } from 'neverthrow';
2
+ import type { DomainEventV1 } from '../durable-core/schemas/session/index.js';
3
+ import type { ProjectionError } from './projection-error.js';
4
+ import type { ConsoleExecutionTraceSummary } from '../usecases/console-types.js';
5
+ export interface RunExecutionTraceProjectionV2 {
6
+ readonly byRunId: Readonly<Record<string, ConsoleExecutionTraceSummary>>;
7
+ }
8
+ export declare function projectRunExecutionTraceV2(events: readonly DomainEventV1[]): Result<RunExecutionTraceProjectionV2, ProjectionError>;
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.projectRunExecutionTraceV2 = projectRunExecutionTraceV2;
4
+ const neverthrow_1 = require("neverthrow");
5
+ const constants_js_1 = require("../durable-core/constants.js");
6
+ const CONTEXT_KEYS_TO_ELEVATE = ['taskComplexity'];
7
+ function projectRunExecutionTraceV2(events) {
8
+ for (let i = 1; i < events.length; i++) {
9
+ if (events[i].eventIndex < events[i - 1].eventIndex) {
10
+ return (0, neverthrow_1.err)({
11
+ code: 'PROJECTION_INVARIANT_VIOLATION',
12
+ message: 'Events must be sorted by eventIndex ascending',
13
+ });
14
+ }
15
+ }
16
+ const itemsByRunId = {};
17
+ const contextFactsByRunId = {};
18
+ const pushItem = (runId, item) => {
19
+ const existing = itemsByRunId[runId] ?? [];
20
+ existing.push(item);
21
+ itemsByRunId[runId] = existing;
22
+ };
23
+ for (const event of events) {
24
+ switch (event.kind) {
25
+ case constants_js_1.EVENT_KIND.DECISION_TRACE_APPENDED: {
26
+ const runId = event.scope.runId;
27
+ const nodeRef = { kind: 'node_id', value: event.scope.nodeId };
28
+ for (const entry of event.data.entries) {
29
+ pushItem(runId, {
30
+ kind: entry.kind,
31
+ summary: entry.summary,
32
+ recordedAtEventIndex: event.eventIndex,
33
+ refs: [nodeRef, ...mapDecisionTraceRefs(entry.refs)],
34
+ });
35
+ }
36
+ break;
37
+ }
38
+ case constants_js_1.EVENT_KIND.DIVERGENCE_RECORDED: {
39
+ const runId = event.scope.runId;
40
+ const refs = [
41
+ { kind: 'node_id', value: event.scope.nodeId },
42
+ ];
43
+ if (event.data.relatedStepId) {
44
+ refs.push({ kind: 'step_id', value: event.data.relatedStepId });
45
+ }
46
+ pushItem(runId, {
47
+ kind: 'divergence',
48
+ summary: event.data.summary,
49
+ recordedAtEventIndex: event.eventIndex,
50
+ refs,
51
+ });
52
+ break;
53
+ }
54
+ case constants_js_1.EVENT_KIND.CONTEXT_SET: {
55
+ const context = event.data.context;
56
+ if (!context || typeof context !== 'object' || Array.isArray(context)) {
57
+ return (0, neverthrow_1.err)({
58
+ code: 'PROJECTION_CORRUPTION_DETECTED',
59
+ message: `context_set event has invalid context type (runId=${event.scope.runId}, eventId=${event.eventId})`,
60
+ });
61
+ }
62
+ const contextObj = context;
63
+ const facts = CONTEXT_KEYS_TO_ELEVATE.flatMap((key) => {
64
+ const value = contextObj[key];
65
+ if (value === undefined || value === null)
66
+ return [];
67
+ return [{ key, value: stringifyContextValue(value) }];
68
+ });
69
+ if (facts.length > 0) {
70
+ contextFactsByRunId[event.scope.runId] = facts;
71
+ }
72
+ break;
73
+ }
74
+ default:
75
+ break;
76
+ }
77
+ }
78
+ const runIds = new Set([
79
+ ...Object.keys(itemsByRunId),
80
+ ...Object.keys(contextFactsByRunId),
81
+ ]);
82
+ const byRunId = {};
83
+ for (const runId of runIds) {
84
+ byRunId[runId] = {
85
+ items: itemsByRunId[runId] ?? [],
86
+ contextFacts: contextFactsByRunId[runId] ?? [],
87
+ };
88
+ }
89
+ return (0, neverthrow_1.ok)({ byRunId });
90
+ }
91
+ function mapDecisionTraceRefs(refs) {
92
+ if (!refs || refs.length === 0)
93
+ return [];
94
+ const mapped = [];
95
+ for (const ref of refs) {
96
+ switch (ref.kind) {
97
+ case 'step_id':
98
+ if (ref.stepId) {
99
+ mapped.push({ kind: 'step_id', value: ref.stepId });
100
+ }
101
+ break;
102
+ case 'loop_id':
103
+ if (ref.loopId) {
104
+ mapped.push({ kind: 'loop_id', value: ref.loopId });
105
+ }
106
+ break;
107
+ case 'condition_id':
108
+ if (ref.conditionId) {
109
+ mapped.push({ kind: 'condition_id', value: ref.conditionId });
110
+ }
111
+ break;
112
+ default:
113
+ break;
114
+ }
115
+ }
116
+ return mapped;
117
+ }
118
+ function stringifyContextValue(value) {
119
+ if (typeof value === 'string')
120
+ return value;
121
+ if (typeof value === 'number' || typeof value === 'boolean')
122
+ return String(value);
123
+ return JSON.stringify(value);
124
+ }
@@ -1,5 +1,5 @@
1
1
  import type { Result } from 'neverthrow';
2
- import type { DomainEventV1 } from '../durable-core/schemas/session/index.js';
2
+ import type { SortedEventLog } from '../durable-core/sorted-event-log.js';
3
3
  import type { AutonomyV2, RiskPolicyV2 } from '../durable-core/schemas/session/preferences.js';
4
4
  import type { ProjectionError } from './projection-error.js';
5
5
  export interface PreferencesSnapshotV2 {
@@ -16,4 +16,4 @@ export interface RunStatusSignalsV2 {
16
16
  export interface RunStatusSignalsProjectionV2 {
17
17
  readonly byRunId: Readonly<Record<string, RunStatusSignalsV2>>;
18
18
  }
19
- export declare function projectRunStatusSignalsV2(events: readonly DomainEventV1[]): Result<RunStatusSignalsProjectionV2, ProjectionError>;
19
+ export declare function projectRunStatusSignalsV2(events: SortedEventLog): Result<RunStatusSignalsProjectionV2, ProjectionError>;
@@ -1,3 +1,5 @@
1
1
  import type { Application } from 'express';
2
2
  import type { ConsoleService } from './console-service.js';
3
- export declare function mountConsoleRoutes(app: Application, consoleService: ConsoleService): void;
3
+ import type { WorkflowService } from '../../application/services/workflow-service.js';
4
+ import type { ToolCallTimingRingBuffer } from '../../mcp/tool-call-timing.js';
5
+ export declare function mountConsoleRoutes(app: Application, consoleService: ConsoleService, workflowService?: WorkflowService, timingRingBuffer?: ToolCallTimingRingBuffer): () => void;