@exaudeus/workrail 3.15.0 → 3.17.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 (164) hide show
  1. package/dist/application/services/workflow-service.d.ts +2 -0
  2. package/dist/application/services/workflow-service.js +3 -0
  3. package/dist/application/use-cases/raw-workflow-file-scanner.js +10 -13
  4. package/dist/cli/commands/index.d.ts +1 -1
  5. package/dist/cli/commands/index.js +2 -1
  6. package/dist/cli/commands/init.d.ts +10 -0
  7. package/dist/cli/commands/init.js +72 -0
  8. package/dist/cli.js +13 -1
  9. package/dist/config/config-file.d.ts +8 -0
  10. package/dist/config/config-file.js +141 -0
  11. package/dist/config/feature-flags.js +8 -0
  12. package/dist/console/assets/index-BZNM03t1.css +1 -0
  13. package/dist/console/assets/index-BwJelCXK.js +28 -0
  14. package/dist/console/index.html +2 -2
  15. package/dist/di/container.d.ts +1 -0
  16. package/dist/di/container.js +24 -7
  17. package/dist/infrastructure/session/HttpServer.d.ts +3 -4
  18. package/dist/infrastructure/session/HttpServer.js +58 -106
  19. package/dist/infrastructure/storage/caching-workflow-storage.d.ts +2 -0
  20. package/dist/infrastructure/storage/caching-workflow-storage.js +15 -6
  21. package/dist/infrastructure/storage/file-workflow-storage.js +3 -4
  22. package/dist/infrastructure/storage/schema-validating-workflow-storage.js +9 -8
  23. package/dist/manifest.json +303 -247
  24. package/dist/mcp/assert-output.d.ts +37 -0
  25. package/dist/mcp/assert-output.js +53 -0
  26. package/dist/mcp/boundary-coercion.d.ts +1 -0
  27. package/dist/mcp/boundary-coercion.js +44 -0
  28. package/dist/mcp/dev-mode.d.ts +2 -0
  29. package/dist/mcp/dev-mode.js +16 -0
  30. package/dist/mcp/handler-factory.d.ts +1 -1
  31. package/dist/mcp/handler-factory.js +20 -16
  32. package/dist/mcp/handlers/session.js +8 -9
  33. package/dist/mcp/handlers/shared/request-workflow-reader.d.ts +1 -0
  34. package/dist/mcp/handlers/shared/request-workflow-reader.js +90 -20
  35. package/dist/mcp/handlers/v2-advance-core/event-builders.d.ts +2 -0
  36. package/dist/mcp/handlers/v2-advance-core/event-builders.js +6 -6
  37. package/dist/mcp/handlers/v2-advance-core/index.d.ts +2 -0
  38. package/dist/mcp/handlers/v2-advance-core/index.js +4 -3
  39. package/dist/mcp/handlers/v2-advance-core/input-validation.d.ts +2 -0
  40. package/dist/mcp/handlers/v2-advance-core/input-validation.js +32 -9
  41. package/dist/mcp/handlers/v2-advance-core/outcome-blocked.d.ts +2 -0
  42. package/dist/mcp/handlers/v2-advance-core/outcome-blocked.js +1 -1
  43. package/dist/mcp/handlers/v2-advance-core/outcome-success.d.ts +2 -0
  44. package/dist/mcp/handlers/v2-advance-core/outcome-success.js +1 -1
  45. package/dist/mcp/handlers/v2-checkpoint.d.ts +1 -1
  46. package/dist/mcp/handlers/v2-checkpoint.js +5 -6
  47. package/dist/mcp/handlers/v2-execution/advance.d.ts +4 -2
  48. package/dist/mcp/handlers/v2-execution/advance.js +5 -7
  49. package/dist/mcp/handlers/v2-execution/continue-advance.d.ts +1 -0
  50. package/dist/mcp/handlers/v2-execution/continue-advance.js +59 -27
  51. package/dist/mcp/handlers/v2-execution/continue-rehydrate.d.ts +2 -1
  52. package/dist/mcp/handlers/v2-execution/continue-rehydrate.js +11 -10
  53. package/dist/mcp/handlers/v2-execution/index.js +2 -0
  54. package/dist/mcp/handlers/v2-execution/replay.d.ts +8 -4
  55. package/dist/mcp/handlers/v2-execution/replay.js +50 -30
  56. package/dist/mcp/handlers/v2-execution/start.d.ts +2 -3
  57. package/dist/mcp/handlers/v2-execution/start.js +58 -30
  58. package/dist/mcp/handlers/v2-execution/workflow-object-cache.d.ts +5 -0
  59. package/dist/mcp/handlers/v2-execution/workflow-object-cache.js +19 -0
  60. package/dist/mcp/handlers/v2-execution-helpers.d.ts +1 -0
  61. package/dist/mcp/handlers/v2-execution-helpers.js +23 -7
  62. package/dist/mcp/handlers/v2-resume.d.ts +1 -1
  63. package/dist/mcp/handlers/v2-resume.js +3 -4
  64. package/dist/mcp/handlers/v2-state-conversion.js +5 -1
  65. package/dist/mcp/handlers/v2-workflow.d.ts +80 -0
  66. package/dist/mcp/handlers/v2-workflow.js +40 -23
  67. package/dist/mcp/handlers/workflow.d.ts +2 -5
  68. package/dist/mcp/handlers/workflow.js +15 -12
  69. package/dist/mcp/output-schemas.d.ts +25 -27
  70. package/dist/mcp/output-schemas.js +7 -7
  71. package/dist/mcp/server.js +23 -4
  72. package/dist/mcp/tool-call-timing.d.ts +24 -0
  73. package/dist/mcp/tool-call-timing.js +85 -0
  74. package/dist/mcp/transports/http-entry.js +3 -2
  75. package/dist/mcp/transports/http-listener.d.ts +1 -0
  76. package/dist/mcp/transports/http-listener.js +25 -0
  77. package/dist/mcp/transports/shutdown-hooks.d.ts +4 -1
  78. package/dist/mcp/transports/shutdown-hooks.js +3 -2
  79. package/dist/mcp/transports/stdio-entry.js +6 -28
  80. package/dist/mcp/v2-response-formatter.d.ts +1 -1
  81. package/dist/mcp/v2-response-formatter.js +2 -5
  82. package/dist/mcp/validation/schema-introspection.d.ts +1 -0
  83. package/dist/mcp/validation/schema-introspection.js +15 -5
  84. package/dist/mcp/validation/suggestion-generator.js +2 -2
  85. package/dist/runtime/adapters/node-process-signals.d.ts +1 -0
  86. package/dist/runtime/adapters/node-process-signals.js +5 -0
  87. package/dist/runtime/adapters/noop-process-signals.d.ts +1 -0
  88. package/dist/runtime/adapters/noop-process-signals.js +2 -0
  89. package/dist/runtime/ports/process-signals.d.ts +1 -0
  90. package/dist/types/workflow-definition.d.ts +5 -1
  91. package/dist/types/workflow-definition.js +2 -0
  92. package/dist/types/workflow.d.ts +3 -0
  93. package/dist/types/workflow.js +35 -26
  94. package/dist/v2/durable-core/domain/context-template-resolver.js +2 -2
  95. package/dist/v2/durable-core/domain/function-definition-expander.js +2 -17
  96. package/dist/v2/durable-core/domain/prompt-renderer.d.ts +2 -0
  97. package/dist/v2/durable-core/domain/prompt-renderer.js +22 -18
  98. package/dist/v2/durable-core/domain/recap-recovery.js +23 -16
  99. package/dist/v2/durable-core/domain/retrieval-contract.js +13 -7
  100. package/dist/v2/durable-core/schemas/compiled-workflow/index.js +4 -3
  101. package/dist/v2/durable-core/session-index.d.ts +22 -0
  102. package/dist/v2/durable-core/session-index.js +58 -0
  103. package/dist/v2/durable-core/sorted-event-log.d.ts +6 -0
  104. package/dist/v2/durable-core/sorted-event-log.js +15 -0
  105. package/dist/v2/infra/local/fs/index.js +8 -8
  106. package/dist/v2/infra/local/pinned-workflow-store/index.d.ts +2 -0
  107. package/dist/v2/infra/local/pinned-workflow-store/index.js +49 -0
  108. package/dist/v2/infra/local/remembered-roots-store/index.d.ts +3 -1
  109. package/dist/v2/infra/local/remembered-roots-store/index.js +6 -3
  110. package/dist/v2/infra/local/session-store/index.d.ts +1 -1
  111. package/dist/v2/infra/local/session-store/index.js +71 -61
  112. package/dist/v2/infra/local/session-summary-provider/index.js +9 -4
  113. package/dist/v2/infra/local/snapshot-store/index.js +2 -1
  114. package/dist/v2/infra/local/workspace-anchor/index.js +4 -2
  115. package/dist/v2/ports/pinned-workflow-store.port.d.ts +2 -0
  116. package/dist/v2/ports/session-event-log-store.port.d.ts +1 -1
  117. package/dist/v2/projections/assessment-consequences.d.ts +2 -1
  118. package/dist/v2/projections/assessment-consequences.js +0 -5
  119. package/dist/v2/projections/assessments.d.ts +2 -1
  120. package/dist/v2/projections/assessments.js +2 -4
  121. package/dist/v2/projections/gaps.d.ts +2 -1
  122. package/dist/v2/projections/gaps.js +0 -5
  123. package/dist/v2/projections/preferences.d.ts +2 -1
  124. package/dist/v2/projections/preferences.js +0 -5
  125. package/dist/v2/projections/run-context.d.ts +2 -2
  126. package/dist/v2/projections/run-context.js +0 -5
  127. package/dist/v2/projections/run-dag.js +7 -1
  128. package/dist/v2/projections/run-execution-trace.d.ts +8 -0
  129. package/dist/v2/projections/run-execution-trace.js +124 -0
  130. package/dist/v2/projections/run-status-signals.d.ts +2 -2
  131. package/dist/v2/usecases/console-routes.d.ts +3 -1
  132. package/dist/v2/usecases/console-routes.js +124 -25
  133. package/dist/v2/usecases/console-service.d.ts +1 -0
  134. package/dist/v2/usecases/console-service.js +83 -25
  135. package/dist/v2/usecases/console-types.d.ts +53 -0
  136. package/dist/v2/usecases/worktree-service.js +32 -1
  137. package/package.json +6 -5
  138. package/spec/workflow.schema.json +18 -0
  139. package/workflows/adaptive-ticket-creation.json +23 -16
  140. package/workflows/architecture-scalability-audit.json +29 -22
  141. package/workflows/bug-investigation.agentic.v2.json +7 -0
  142. package/workflows/coding-task-workflow-agentic.json +7 -0
  143. package/workflows/coding-task-workflow-agentic.lean.v2.json +16 -8
  144. package/workflows/coding-task-workflow-agentic.v2.json +7 -0
  145. package/workflows/cross-platform-code-conversion.v2.json +7 -0
  146. package/workflows/document-creation-workflow.json +15 -8
  147. package/workflows/documentation-update-workflow.json +15 -8
  148. package/workflows/intelligent-test-case-generation.json +7 -0
  149. package/workflows/learner-centered-course-workflow.json +9 -2
  150. package/workflows/mr-review-workflow.agentic.v2.json +7 -0
  151. package/workflows/personal-learning-materials-creation-branched.json +15 -8
  152. package/workflows/presentation-creation.json +12 -5
  153. package/workflows/production-readiness-audit.json +7 -0
  154. package/workflows/relocation-workflow-us.json +39 -32
  155. package/workflows/scoped-documentation-workflow.json +33 -26
  156. package/workflows/ui-ux-design-workflow.json +7 -0
  157. package/workflows/workflow-diagnose-environment.json +6 -0
  158. package/workflows/workflow-for-workflows.json +7 -0
  159. package/workflows/workflow-for-workflows.v2.json +23 -11
  160. package/workflows/wr.discovery.json +8 -1
  161. package/dist/console/assets/index-BZYIjrzJ.js +0 -28
  162. package/dist/console/assets/index-OLCKbDdm.css +0 -1
  163. package/dist/mcp/handlers/v2-resolve-refs-envelope.d.ts +0 -5
  164. package/dist/mcp/handlers/v2-resolve-refs-envelope.js +0 -17
@@ -13,6 +13,7 @@ const constants_js_1 = require("../constants.js");
13
13
  const validation_requirements_extractor_js_1 = require("./validation-requirements-extractor.js");
14
14
  const index_js_2 = require("../schemas/artifacts/index.js");
15
15
  const run_context_js_1 = require("../../projections/run-context.js");
16
+ const sorted_event_log_js_1 = require("../sorted-event-log.js");
16
17
  const condition_evaluator_js_1 = require("../../../utils/condition-evaluator.js");
17
18
  const context_template_resolver_js_1 = require("./context-template-resolver.js");
18
19
  const retrieval_contract_js_1 = require("./retrieval-contract.js");
@@ -86,15 +87,7 @@ function buildRecoverySegments(args) {
86
87
  ];
87
88
  }
88
89
  function resolveParentLoopStep(workflow, stepId) {
89
- for (const step of workflow.definition.steps) {
90
- if ((0, workflow_js_1.isLoopStepDefinition)(step) && Array.isArray(step.body)) {
91
- for (const bodyStep of step.body) {
92
- if (bodyStep.id === stepId)
93
- return step;
94
- }
95
- }
96
- }
97
- return undefined;
90
+ return workflow.parentLoopByStepId.get(stepId);
98
91
  }
99
92
  function buildLoopRenderContext(loopStep, iteration, sessionContext) {
100
93
  const iterationVar = loopStep.loop.iterationVar || 'currentIteration';
@@ -238,11 +231,13 @@ function renderPendingPrompt(args) {
238
231
  const isExitStep = outputContract?.contractRef === index_js_2.LOOP_CONTROL_CONTRACT_REF;
239
232
  const loopStep = resolveParentLoopStep(args.workflow, args.stepId);
240
233
  const maxIterations = loopStep?.loop.maxIterations;
241
- const sessionContext = (0, run_context_js_1.projectRunContextV2)(args.truth.events).match((ok) => (ok.byRunId[String(args.runId)]?.context ?? {}), (e) => {
242
- console.warn(`[prompt-renderer] Context projection failed for step '${args.stepId}' — ` +
243
- `{{varName}} tokens will render as [unset:...]: ${e.message}`);
244
- return {};
245
- });
234
+ const sessionContext = args.precomputedIndex
235
+ ? (args.precomputedIndex.runContextByRunId.get(String(args.runId)) ?? {})
236
+ : (0, sorted_event_log_js_1.asSortedEventLog)(args.truth.events).andThen((sorted) => (0, run_context_js_1.projectRunContextV2)(sorted)).match((ok) => (ok.byRunId[String(args.runId)]?.context ?? {}), (e) => {
237
+ console.warn(`[prompt-renderer] Context projection failed for step '${args.stepId}' — ` +
238
+ `{{varName}} tokens will render as [unset:...]: ${e.message}`);
239
+ return {};
240
+ });
246
241
  const loopIterationFrame = args.loopPath.at(-1);
247
242
  const loopRenderContext = loopStep && loopIterationFrame
248
243
  ? buildLoopRenderContext(loopStep, loopIterationFrame.iteration, sessionContext)
@@ -250,7 +245,7 @@ function renderPendingPrompt(args) {
250
245
  const renderContext = { ...sessionContext, ...loopRenderContext };
251
246
  const basePrompt = (0, context_template_resolver_js_1.resolveContextTemplates)(step.prompt ?? '', renderContext);
252
247
  const baseTitle = (0, context_template_resolver_js_1.resolveContextTemplates)(step.title, renderContext);
253
- const cleanResponseFormat = process.env.WORKRAIL_CLEAN_RESPONSE_FORMAT === 'true';
248
+ const cleanResponseFormat = args.cleanResponseFormat ?? false;
254
249
  const loopBanner = buildLoopContextBanner({ loopPath: args.loopPath, isExitStep, maxIterations, cleanFormat: cleanResponseFormat });
255
250
  const validationCriteria = step.validationCriteria;
256
251
  const requirements = (0, validation_requirements_extractor_js_1.extractValidationRequirements)(validationCriteria);
@@ -279,7 +274,9 @@ function renderPendingPrompt(args) {
279
274
  if (cleanResponseFormat) {
280
275
  return '';
281
276
  }
282
- const hasPriorNotes = hasPriorNotesInRun({ truth: args.truth, runId: args.runId });
277
+ const hasPriorNotes = args.precomputedIndex
278
+ ? args.precomputedIndex.hasPriorNotesByRunId.has(String(args.runId))
279
+ : hasPriorNotesInRun({ truth: args.truth, runId: args.runId });
283
280
  if (hasPriorNotes && !args.rehydrateOnly) {
284
281
  return '\n\n**NOTES REQUIRED (System):** Include `output.notesMarkdown` when advancing.\n\n' +
285
282
  'Scope: this step only — WorkRail concatenates notes automatically.\n' +
@@ -321,8 +318,15 @@ function renderPendingPrompt(args) {
321
318
  const fragmentSuffix = promptFragments && promptFragments.length > 0
322
319
  ? assembleFragmentedPrompt(promptFragments, renderContext)
323
320
  : '';
324
- const enhancedPrompt = loopBanner + basePrompt + requirementsSection + contractSection + assessmentSection + notesSection
325
- + (fragmentSuffix ? '\n\n' + fragmentSuffix : '');
321
+ const enhancedPrompt = [
322
+ loopBanner,
323
+ basePrompt,
324
+ requirementsSection,
325
+ contractSection,
326
+ assessmentSection,
327
+ notesSection,
328
+ fragmentSuffix ? '\n\n' + fragmentSuffix : '',
329
+ ].join('');
326
330
  if (!args.rehydrateOnly) {
327
331
  return (0, neverthrow_1.ok)({ stepId: args.stepId, title: baseTitle, prompt: enhancedPrompt, agentRole, requireConfirmation });
328
332
  }
@@ -6,18 +6,21 @@ exports.buildChildSummary = buildChildSummary;
6
6
  const neverthrow_1 = require("neverthrow");
7
7
  const constants_js_1 = require("../constants.js");
8
8
  function collectAncestryRecap(args) {
9
- const buildChain = (cur, visited) => {
10
- if (!cur || visited.has(cur))
11
- return [];
12
- const nodeData = args.dag.nodesById[cur];
13
- const parent = nodeData?.parentNodeId ?? null;
14
- const newVisited = new Set([...visited, cur]);
15
- return [cur, ...buildChain(parent, newVisited)];
9
+ const buildChain = (start) => {
10
+ const result = [];
11
+ const visited = new Set();
12
+ let cur = start;
13
+ while (cur !== null && !visited.has(cur)) {
14
+ visited.add(cur);
15
+ result.push(cur);
16
+ cur = args.dag.nodesById[cur]?.parentNodeId ?? null;
17
+ }
18
+ return result;
16
19
  };
17
20
  const startNode = args.includeCurrentNode
18
21
  ? String(args.nodeId)
19
22
  : args.dag.nodesById[String(args.nodeId)]?.parentNodeId ?? null;
20
- const chain = buildChain(startNode, new Set());
23
+ const chain = buildChain(startNode);
21
24
  const recaps = chain.flatMap((nodeId) => {
22
25
  const nodeOutputs = args.outputs.nodesById[nodeId];
23
26
  if (!nodeOutputs)
@@ -35,15 +38,19 @@ function collectAncestryRecap(args) {
35
38
  return (0, neverthrow_1.ok)([...recaps].reverse());
36
39
  }
37
40
  function collectDownstreamRecap(args) {
38
- const buildPathBackward = (cur, visited) => {
39
- if (!cur || cur === String(args.fromNodeId) || visited.has(cur))
40
- return [];
41
- const nodeData = args.dag.nodesById[cur];
42
- const parent = nodeData?.parentNodeId ?? null;
43
- const newVisited = new Set([...visited, cur]);
44
- return [cur, ...buildPathBackward(parent, newVisited)];
41
+ const buildPathBackward = (start) => {
42
+ const result = [];
43
+ const visited = new Set();
44
+ const fromId = String(args.fromNodeId);
45
+ let cur = start;
46
+ while (cur !== null && cur !== fromId && !visited.has(cur)) {
47
+ visited.add(cur);
48
+ result.push(cur);
49
+ cur = args.dag.nodesById[cur]?.parentNodeId ?? null;
50
+ }
51
+ return result;
45
52
  };
46
- const pathBackward = buildPathBackward(String(args.toNodeId), new Set());
53
+ const pathBackward = buildPathBackward(String(args.toNodeId));
47
54
  const recaps = [...pathBackward].reverse().flatMap((nodeId) => {
48
55
  const nodeOutputs = args.outputs.nodesById[nodeId];
49
56
  if (!nodeOutputs)
@@ -63,17 +63,21 @@ exports.RESUME_PREVIEW_CONTRACT = {
63
63
  };
64
64
  const encoder = new TextEncoder();
65
65
  const decoder = new TextDecoder('utf-8');
66
+ const TIER_PRIORITY = Object.fromEntries(exports.REHYDRATE_RETRIEVAL_CONTRACT.tiers.map((t) => [t.tier, t.priority]));
67
+ const TIER_RETENTION = Object.fromEntries(exports.REHYDRATE_RETRIEVAL_CONTRACT.tiers.map((t) => [t.tier, t.retention]));
68
+ const RESUME_PREVIEW_TIER_PRIORITY = Object.fromEntries(exports.RESUME_PREVIEW_CONTRACT.tiers.map((t) => [t.tier, t.priority]));
69
+ const RESUME_PREVIEW_TIER_MAX_BYTES = Object.fromEntries(exports.RESUME_PREVIEW_CONTRACT.tiers.map((t) => [t.tier, t.maxBytes]));
66
70
  function getTierPriority(tier) {
67
- return exports.REHYDRATE_RETRIEVAL_CONTRACT.tiers.find((candidate) => candidate.tier === tier)?.priority ?? Number.MAX_SAFE_INTEGER;
71
+ return TIER_PRIORITY[tier] ?? Number.MAX_SAFE_INTEGER;
68
72
  }
69
73
  function getTierRetention(tier) {
70
- return exports.REHYDRATE_RETRIEVAL_CONTRACT.tiers.find((candidate) => candidate.tier === tier)?.retention ?? 'tail';
74
+ return TIER_RETENTION[tier] ?? 'tail';
71
75
  }
72
76
  function getResumePreviewTierPriority(tier) {
73
- return exports.RESUME_PREVIEW_CONTRACT.tiers.find((candidate) => candidate.tier === tier)?.priority ?? Number.MAX_SAFE_INTEGER;
77
+ return RESUME_PREVIEW_TIER_PRIORITY[tier] ?? Number.MAX_SAFE_INTEGER;
74
78
  }
75
79
  function getResumePreviewTierMaxBytes(tier) {
76
- return exports.RESUME_PREVIEW_CONTRACT.tiers.find((candidate) => candidate.tier === tier)?.maxBytes ?? exports.RESUME_PREVIEW_CONTRACT.budgetBytes;
80
+ return RESUME_PREVIEW_TIER_MAX_BYTES[tier] ?? exports.RESUME_PREVIEW_CONTRACT.budgetBytes;
77
81
  }
78
82
  function compareAscii(a, b) {
79
83
  return a < b ? -1 : a > b ? 1 : 0;
@@ -283,7 +287,8 @@ function renderBudgetedRehydrateRecovery(args) {
283
287
  const initiallyIncludedTiers = tiersInOrder.filter((tier) => (sectionsByTier.get(tier) ?? []).length > 0);
284
288
  let includedTiers = initiallyIncludedTiers;
285
289
  let recoveryText = renderFromTiers(includedTiers);
286
- while (encoder.encode(recoveryText).length > constants_js_1.RECOVERY_BUDGET_BYTES) {
290
+ let recoveryBytes = encoder.encode(recoveryText).length;
291
+ while (recoveryBytes > constants_js_1.RECOVERY_BUDGET_BYTES) {
287
292
  const droppableTierIndex = [...includedTiers]
288
293
  .reverse()
289
294
  .findIndex((tier) => getTierRetention(tier) === 'tail');
@@ -293,9 +298,10 @@ function renderBudgetedRehydrateRecovery(args) {
293
298
  const actualIndex = includedTiers.length - 1 - droppableTierIndex;
294
299
  includedTiers = includedTiers.filter((_, index) => index !== actualIndex);
295
300
  recoveryText = renderFromTiers(includedTiers);
301
+ recoveryBytes = encoder.encode(recoveryText).length;
296
302
  }
297
303
  const omittedTierCount = initiallyIncludedTiers.length - includedTiers.length;
298
- const needsSuffix = omittedTierCount > 0 || encoder.encode(recoveryText).length > constants_js_1.RECOVERY_BUDGET_BYTES || includedTiers.length === 0;
304
+ const needsSuffix = omittedTierCount > 0 || recoveryBytes > constants_js_1.RECOVERY_BUDGET_BYTES || includedTiers.length === 0;
299
305
  const finalText = recoveryText.length === 0
300
306
  ? trimFinalRecoveryText(args.header, initiallyIncludedTiers.length)
301
307
  : !needsSuffix
@@ -305,6 +311,6 @@ function renderBudgetedRehydrateRecovery(args) {
305
311
  text: finalText,
306
312
  includedTiers,
307
313
  omittedTierCount,
308
- truncatedWithinTier: encoder.encode(recoveryText).length > constants_js_1.RECOVERY_BUDGET_BYTES || includedTiers.length === 0,
314
+ truncatedWithinTier: recoveryBytes > constants_js_1.RECOVERY_BUDGET_BYTES || includedTiers.length === 0,
309
315
  };
310
316
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CompiledWorkflowSnapshotSchema = exports.CompiledWorkflowSnapshotV1Schema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const json_zod_js_1 = require("../../canonical/json-zod.js");
6
+ const workflow_definition_js_1 = require("../../../../types/workflow-definition.js");
6
7
  const CompiledWorkflowSnapshotV1PreviewSchema = zod_1.z.object({
7
8
  schemaVersion: zod_1.z.literal(1),
8
9
  sourceKind: zod_1.z.literal('v1_preview'),
@@ -33,7 +34,7 @@ const CompiledWorkflowSnapshotV1PinnedSchema = zod_1.z.object({
33
34
  source: zod_1.z.string().min(1),
34
35
  purpose: zod_1.z.string().min(1),
35
36
  authoritative: zod_1.z.boolean(),
36
- resolveFrom: zod_1.z.enum(['workspace', 'package']),
37
+ resolveFrom: zod_1.z.enum(workflow_definition_js_1.RESOLVE_FROM_VALUES),
37
38
  status: zod_1.z.literal('resolved'),
38
39
  resolvedPath: zod_1.z.string().min(1),
39
40
  }),
@@ -43,7 +44,7 @@ const CompiledWorkflowSnapshotV1PinnedSchema = zod_1.z.object({
43
44
  source: zod_1.z.string().min(1),
44
45
  purpose: zod_1.z.string().min(1),
45
46
  authoritative: zod_1.z.boolean(),
46
- resolveFrom: zod_1.z.enum(['workspace', 'package']),
47
+ resolveFrom: zod_1.z.enum(workflow_definition_js_1.RESOLVE_FROM_VALUES),
47
48
  status: zod_1.z.literal('unresolved'),
48
49
  }),
49
50
  zod_1.z.object({
@@ -52,7 +53,7 @@ const CompiledWorkflowSnapshotV1PinnedSchema = zod_1.z.object({
52
53
  source: zod_1.z.string().min(1),
53
54
  purpose: zod_1.z.string().min(1),
54
55
  authoritative: zod_1.z.boolean(),
55
- resolveFrom: zod_1.z.enum(['workspace', 'package']),
56
+ resolveFrom: zod_1.z.enum(workflow_definition_js_1.RESOLVE_FROM_VALUES),
56
57
  status: zod_1.z.literal('pinned'),
57
58
  }),
58
59
  ])).optional(),
@@ -0,0 +1,22 @@
1
+ import type { Brand } from '../../runtime/brand.js';
2
+ import type { SortedEventLog } from './sorted-event-log.js';
3
+ import type { DomainEventV1 } from './schemas/session/index.js';
4
+ import type { JsonObject } from './canonical/json-types.js';
5
+ export interface SessionIndexData {
6
+ readonly sortedEvents: SortedEventLog;
7
+ readonly runStartedByRunId: ReadonlyMap<string, Extract<DomainEventV1, {
8
+ kind: 'run_started';
9
+ }>>;
10
+ readonly nodeCreatedByNodeId: ReadonlyMap<string, Extract<DomainEventV1, {
11
+ kind: 'node_created';
12
+ }>>;
13
+ readonly hasChildEdgeByFromNodeId: ReadonlySet<string>;
14
+ readonly advanceRecordedByDedupeKey: ReadonlyMap<string, Extract<DomainEventV1, {
15
+ kind: 'advance_recorded';
16
+ }>>;
17
+ readonly nextEventIndex: number;
18
+ readonly hasPriorNotesByRunId: ReadonlySet<string>;
19
+ readonly runContextByRunId: ReadonlyMap<string, JsonObject>;
20
+ }
21
+ export type SessionIndex = Brand<SessionIndexData, 'v2.SessionIndex'>;
22
+ export declare function buildSessionIndex(events: SortedEventLog): SessionIndex;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSessionIndex = buildSessionIndex;
4
+ const constants_js_1 = require("./constants.js");
5
+ function buildSessionIndex(events) {
6
+ const runStartedByRunId = new Map();
7
+ const nodeCreatedByNodeId = new Map();
8
+ const hasChildEdgeByFromNodeId = new Set();
9
+ const advanceRecordedByDedupeKey = new Map();
10
+ const hasPriorNotesByRunId = new Set();
11
+ const runContextByRunId = new Map();
12
+ for (const event of events) {
13
+ switch (event.kind) {
14
+ case 'run_started':
15
+ runStartedByRunId.set(event.scope.runId, event);
16
+ break;
17
+ case 'node_created':
18
+ nodeCreatedByNodeId.set(event.scope.nodeId, event);
19
+ break;
20
+ case 'edge_created':
21
+ hasChildEdgeByFromNodeId.add(event.data.fromNodeId);
22
+ break;
23
+ case 'advance_recorded':
24
+ advanceRecordedByDedupeKey.set(event.dedupeKey, event);
25
+ break;
26
+ case 'node_output_appended': {
27
+ const outputEvt = event;
28
+ if (outputEvt.data.outputChannel === constants_js_1.OUTPUT_CHANNEL.RECAP &&
29
+ outputEvt.data.payload.payloadKind === constants_js_1.PAYLOAD_KIND.NOTES) {
30
+ hasPriorNotesByRunId.add(event.scope.runId);
31
+ }
32
+ break;
33
+ }
34
+ case 'context_set': {
35
+ const ctx = event.data.context;
36
+ if (ctx && typeof ctx === 'object' && !Array.isArray(ctx)) {
37
+ runContextByRunId.set(event.scope.runId, ctx);
38
+ }
39
+ break;
40
+ }
41
+ default:
42
+ break;
43
+ }
44
+ }
45
+ const lastEvent = events[events.length - 1];
46
+ const nextEventIndex = lastEvent !== undefined ? lastEvent.eventIndex + 1 : 0;
47
+ const data = {
48
+ sortedEvents: events,
49
+ runStartedByRunId,
50
+ nodeCreatedByNodeId,
51
+ hasChildEdgeByFromNodeId,
52
+ advanceRecordedByDedupeKey,
53
+ nextEventIndex,
54
+ hasPriorNotesByRunId,
55
+ runContextByRunId,
56
+ };
57
+ return data;
58
+ }
@@ -0,0 +1,6 @@
1
+ import type { Result } from 'neverthrow';
2
+ import type { Brand } from '../../runtime/brand.js';
3
+ import type { DomainEventV1 } from './schemas/session/index.js';
4
+ import type { ProjectionError } from '../projections/projection-error.js';
5
+ export type SortedEventLog = Brand<readonly DomainEventV1[], 'v2.SortedEventLog'>;
6
+ export declare function asSortedEventLog(events: readonly DomainEventV1[]): Result<SortedEventLog, ProjectionError>;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.asSortedEventLog = asSortedEventLog;
4
+ const neverthrow_1 = require("neverthrow");
5
+ function asSortedEventLog(events) {
6
+ for (let i = 1; i < events.length; i++) {
7
+ if (events[i].eventIndex <= events[i - 1].eventIndex) {
8
+ return (0, neverthrow_1.err)({
9
+ code: 'PROJECTION_INVARIANT_VIOLATION',
10
+ message: 'Events must be sorted by eventIndex strictly ascending (no duplicates)',
11
+ });
12
+ }
13
+ }
14
+ return (0, neverthrow_1.ok)(events);
15
+ }
@@ -166,18 +166,18 @@ class NodeFileSystemV2 {
166
166
  readdirWithMtime(dirPath) {
167
167
  return neverthrow_1.ResultAsync.fromPromise((async () => {
168
168
  const entries = await fs.readdir(dirPath);
169
+ const results = await Promise.allSettled(entries.map((name) => fs.stat(path.join(dirPath, name)).then((s) => ({ name, mtimeMs: s.mtimeMs }))));
169
170
  const withMtime = [];
170
171
  let skipped = 0;
171
- for (const name of entries) {
172
- try {
173
- const stats = await fs.stat(path.join(dirPath, name));
174
- withMtime.push({ name, mtimeMs: stats.mtimeMs });
172
+ for (let i = 0; i < results.length; i++) {
173
+ const result = results[i];
174
+ if (result.status === 'fulfilled') {
175
+ withMtime.push(result.value);
175
176
  }
176
- catch (e) {
177
- const code = nodeErrorCode(e);
177
+ else {
178
+ const code = nodeErrorCode(result.reason);
178
179
  skipped++;
179
- console.error(`[workrail:session-enum] Skipping ${name}: stat failed (${code ?? 'unknown'}: ${e instanceof Error ? e.message : String(e)})`);
180
- continue;
180
+ console.error(`[workrail:session-enum] Skipping ${entries[i]}: stat failed (${code ?? 'unknown'}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)})`);
181
181
  }
182
182
  }
183
183
  if (skipped > 0) {
@@ -9,5 +9,7 @@ export declare class LocalPinnedWorkflowStoreV2 implements PinnedWorkflowStorePo
9
9
  private readonly fs;
10
10
  constructor(dataDir: DataDirPortV2, fs: FileSystemPortV2);
11
11
  get(workflowHash: WorkflowHash): ResultAsync<CompiledWorkflowSnapshot | null, PinnedWorkflowStoreError>;
12
+ list(): ResultAsync<readonly WorkflowHash[], PinnedWorkflowStoreError>;
13
+ prune(_olderThanMs: number): ResultAsync<number, PinnedWorkflowStoreError>;
12
14
  put(workflowHash: WorkflowHash, compiled: CompiledWorkflowSnapshot): ResultAsync<void, PinnedWorkflowStoreError>;
13
15
  }
@@ -1,6 +1,40 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.LocalPinnedWorkflowStoreV2 = void 0;
37
+ const nodePath = __importStar(require("node:path"));
4
38
  const neverthrow_1 = require("neverthrow");
5
39
  const index_js_1 = require("../../../durable-core/schemas/compiled-workflow/index.js");
6
40
  const jcs_js_1 = require("../../../durable-core/canonical/jcs.js");
@@ -43,6 +77,21 @@ class LocalPinnedWorkflowStoreV2 {
43
77
  return (0, neverthrow_1.okAsync)(validated.data);
44
78
  });
45
79
  }
80
+ list() {
81
+ const dir = this.dataDir.pinnedWorkflowsDir();
82
+ return this.fs.readdir(dir)
83
+ .orElse((e) => {
84
+ if (e.code === 'FS_NOT_FOUND')
85
+ return (0, neverthrow_1.okAsync)([]);
86
+ return (0, neverthrow_1.errAsync)(mapFsToStoreError(e));
87
+ })
88
+ .map((entries) => entries
89
+ .filter((name) => name.endsWith('.json') && !name.endsWith('.tmp'))
90
+ .map((name) => nodePath.basename(name, '.json')));
91
+ }
92
+ prune(_olderThanMs) {
93
+ return (0, neverthrow_1.okAsync)(0);
94
+ }
46
95
  put(workflowHash, compiled) {
47
96
  const dir = this.dataDir.pinnedWorkflowsDir();
48
97
  const filePath = this.dataDir.pinnedWorkflowPath(workflowHash);
@@ -1,11 +1,13 @@
1
1
  import type { ResultAsync } from 'neverthrow';
2
2
  import type { DataDirPortV2 } from '../../../ports/data-dir.port.js';
3
3
  import type { FileSystemPortV2 } from '../../../ports/fs.port.js';
4
+ import type { TimeClockPortV2 } from '../../../ports/time-clock.port.js';
4
5
  import type { RememberedRootRecordV2, RememberedRootsStoreError, RememberedRootsStorePortV2 } from '../../../ports/remembered-roots-store.port.js';
5
6
  export declare class LocalRememberedRootsStoreV2 implements RememberedRootsStorePortV2 {
6
7
  private readonly dataDir;
7
8
  private readonly fs;
8
- constructor(dataDir: DataDirPortV2, fs: FileSystemPortV2);
9
+ private readonly clock;
10
+ constructor(dataDir: DataDirPortV2, fs: FileSystemPortV2, clock: TimeClockPortV2);
9
11
  listRoots(): ResultAsync<readonly string[], RememberedRootsStoreError>;
10
12
  listRootRecords(): ResultAsync<readonly RememberedRootRecordV2[], RememberedRootsStoreError>;
11
13
  rememberRoot(rootPath: string): ResultAsync<void, RememberedRootsStoreError>;
@@ -9,6 +9,7 @@ const zod_1 = require("zod");
9
9
  const neverthrow_1 = require("neverthrow");
10
10
  const jcs_js_1 = require("../../../durable-core/canonical/jcs.js");
11
11
  const REMEMBERED_ROOTS_LOCK_RETRY_MS = 250;
12
+ const TTL_30_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
12
13
  const RememberedRootRecordSchema = zod_1.z.object({
13
14
  path: zod_1.z.string(),
14
15
  addedAtMs: zod_1.z.number().int().nonnegative(),
@@ -48,9 +49,10 @@ function normalizeRootRecords(roots) {
48
49
  return normalized;
49
50
  }
50
51
  class LocalRememberedRootsStoreV2 {
51
- constructor(dataDir, fs) {
52
+ constructor(dataDir, fs, clock) {
52
53
  this.dataDir = dataDir;
53
54
  this.fs = fs;
55
+ this.clock = clock;
54
56
  }
55
57
  listRoots() {
56
58
  return this.listRootRecords().map((roots) => roots.map((root) => root.path));
@@ -88,7 +90,7 @@ class LocalRememberedRootsStoreV2 {
88
90
  }
89
91
  rememberRoot(rootPath) {
90
92
  const normalizedRoot = path_1.default.resolve(rootPath);
91
- const nowMs = Date.now();
93
+ const nowMs = this.clock.nowMs();
92
94
  return this.withLock(() => this.listRootRecords().andThen((roots) => {
93
95
  const existing = roots.find((root) => root.path === normalizedRoot);
94
96
  const nextRoots = existing
@@ -104,7 +106,8 @@ class LocalRememberedRootsStoreV2 {
104
106
  source: 'explicit_workspace_path',
105
107
  },
106
108
  ];
107
- return this.persist(nextRoots);
109
+ const withEviction = nextRoots.filter((r) => r.lastSeenAtMs >= nowMs - TTL_30_DAYS_MS);
110
+ return this.persist(withEviction);
108
111
  }));
109
112
  }
110
113
  persist(roots) {
@@ -10,7 +10,7 @@ export declare class LocalSessionEventLogStoreV2 implements SessionEventLogReado
10
10
  private readonly fs;
11
11
  private readonly sha256;
12
12
  constructor(dataDir: DataDirPortV2, fs: FileSystemPortV2, sha256: Sha256PortV2);
13
- append(lock: WithHealthySessionLock, plan: AppendPlanV2): ResultAsync<void, SessionEventLogStoreError>;
13
+ append(lock: WithHealthySessionLock, plan: AppendPlanV2, preloadedTruth?: LoadedSessionTruthV2): ResultAsync<void, SessionEventLogStoreError>;
14
14
  load(sessionId: SessionId): ResultAsync<LoadedSessionTruthV2, SessionEventLogStoreError>;
15
15
  loadValidatedPrefix(sessionId: SessionId): ResultAsync<LoadedValidatedPrefixV2, SessionEventLogStoreError>;
16
16
  private appendImpl;