@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
@@ -2,7 +2,7 @@ import type { z } from 'zod';
2
2
  import type { ResultAsync as RA } from 'neverthrow';
3
3
  import type { ToolContext, ToolResult, V2ToolContext } from '../types.js';
4
4
  import type { V2CheckpointWorkflowInput } from '../v2/tools.js';
5
- import { V2CheckpointWorkflowOutputSchema } from '../output-schemas.js';
5
+ import type { V2CheckpointWorkflowOutputSchema } from '../output-schemas.js';
6
6
  import { type ToolFailure } from './v2-execution-helpers.js';
7
7
  import type { ExecutionSessionGateErrorV2 } from '../../v2/usecases/execution-session-gate.js';
8
8
  import type { SessionEventLogStoreError } from '../../v2/ports/session-event-log-store.port.js';
@@ -4,7 +4,6 @@ exports.handleV2CheckpointWorkflow = handleV2CheckpointWorkflow;
4
4
  exports.executeCheckpoint = executeCheckpoint;
5
5
  const neverthrow_1 = require("neverthrow");
6
6
  const types_js_1 = require("../types.js");
7
- const output_schemas_js_1 = require("../output-schemas.js");
8
7
  const v2_token_ops_js_1 = require("./v2-token-ops.js");
9
8
  const v2_execution_helpers_js_1 = require("./v2-execution-helpers.js");
10
9
  const index_js_1 = require("../../v2/durable-core/ids/index.js");
@@ -107,11 +106,11 @@ function replayCheckpoint(events, dedupeKey, originalNode, sessionId, runId, nod
107
106
  .andThen((resumeTokenValue) => (0, v2_token_ops_js_1.mintContinueAndCheckpointTokens)({
108
107
  entry: { sessionId: String(sessionId), runId: String(runId), nodeId: String(nodeId), attemptId: String(attemptId), workflowHashRef },
109
108
  ports: tokenCodecPorts, aliasStore, entropy,
110
- }).andThen(({ continueToken }) => (0, neverthrow_1.okAsync)(output_schemas_js_1.V2CheckpointWorkflowOutputSchema.parse({
109
+ }).andThen(({ continueToken }) => (0, neverthrow_1.okAsync)({
111
110
  checkpointNodeId,
112
111
  resumeToken: resumeTokenValue,
113
112
  nextCall: { tool: 'continue_workflow', params: { continueToken } },
114
- }))))
113
+ })))
115
114
  .mapErr((e) => ({ kind: 'store_failed', cause: e }));
116
115
  }
117
116
  function writeCheckpoint(truth, dedupeKey, originalNode, sessionId, runId, nodeId, attemptId, checkpointNodeId, mintEventId, lock, sessionStore, tokenCodecPorts, aliasStore, entropy) {
@@ -161,7 +160,7 @@ function writeCheckpoint(truth, dedupeKey, originalNode, sessionId, runId, nodeI
161
160
  eventIndex: truth.events.length,
162
161
  createdByEventId: nodeCreatedEventId,
163
162
  }];
164
- return sessionStore.append(lock, { events: validated, snapshotPins })
163
+ return sessionStore.append(lock, { events: validated, snapshotPins }, truth)
165
164
  .mapErr((cause) => ({ kind: 'store_failed', cause }))
166
165
  .andThen(() => {
167
166
  const workflowHashRefRes = (0, workflow_hash_ref_js_1.deriveWorkflowHashRef)(originalNode.data.workflowHash);
@@ -170,11 +169,11 @@ function writeCheckpoint(truth, dedupeKey, originalNode, sessionId, runId, nodeI
170
169
  .andThen((resumeTokenValue) => (0, v2_token_ops_js_1.mintContinueAndCheckpointTokens)({
171
170
  entry: { sessionId: String(sessionId), runId: String(runId), nodeId: String(nodeId), attemptId: String(attemptId), workflowHashRef },
172
171
  ports: tokenCodecPorts, aliasStore, entropy,
173
- }).andThen(({ continueToken }) => (0, neverthrow_1.okAsync)(output_schemas_js_1.V2CheckpointWorkflowOutputSchema.parse({
172
+ }).andThen(({ continueToken }) => (0, neverthrow_1.okAsync)({
174
173
  checkpointNodeId: String(checkpointNodeId),
175
174
  resumeToken: resumeTokenValue,
176
175
  nextCall: { tool: 'continue_workflow', params: { continueToken } },
177
- }))))
176
+ })))
178
177
  .mapErr((e) => ({ kind: 'store_failed', cause: e }));
179
178
  });
180
179
  }
@@ -1,5 +1,6 @@
1
1
  import type { V2ContinueWorkflowInput } from '../../v2/tools.js';
2
- import { createWorkflow } from '../../../types/workflow.js';
2
+ import type { SessionIndex } from '../../../v2/durable-core/session-index.js';
3
+ import type { Workflow } from '../../../types/workflow.js';
3
4
  import { type AttemptId } from '../../../v2/durable-core/tokens/index.js';
4
5
  import { type SessionId, type RunId, type NodeId, type WorkflowHash } from '../../../v2/durable-core/ids/index.js';
5
6
  import type { LoadedSessionTruthV2 } from '../../../v2/ports/session-event-log-store.port.js';
@@ -21,7 +22,7 @@ export declare function advanceAndRecord(args: {
21
22
  readonly inputContext: JsonValue | undefined;
22
23
  readonly inputOutput: V2ContinueWorkflowInput['output'];
23
24
  readonly lock: WithHealthySessionLock;
24
- readonly pinnedWorkflow: ReturnType<typeof createWorkflow>;
25
+ readonly pinnedWorkflow: Workflow;
25
26
  readonly snapshotStore: import('../../../v2/ports/snapshot-store.port.js').SnapshotStorePortV2;
26
27
  readonly sessionStore: import('../../../v2/ports/session-event-log-store.port.js').SessionEventLogAppendStorePortV2 & import('../../../v2/ports/session-event-log-store.port.js').SessionEventLogReadonlyStorePortV2;
27
28
  readonly sha256: Sha256PortV2;
@@ -29,4 +30,5 @@ export declare function advanceAndRecord(args: {
29
30
  readonly mintNodeId: () => NodeId;
30
31
  readonly mintEventId: () => string;
31
32
  };
33
+ readonly lockedIndex: SessionIndex;
32
34
  }): RA<void, InternalError | SessionEventLogStoreError | SnapshotStoreError>;
@@ -3,18 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.advanceAndRecord = advanceAndRecord;
4
4
  const neverthrow_1 = require("neverthrow");
5
5
  const v2_advance_core_js_1 = require("../v2-advance-core.js");
6
- const constants_js_1 = require("../../../v2/durable-core/constants.js");
7
6
  function advanceAndRecord(args) {
8
7
  const { truth, sessionId, runId, nodeId, attemptId, workflowHash, dedupeKey, inputContext, inputOutput, lock, pinnedWorkflow, snapshotStore, sessionStore, sha256, idFactory } = args;
9
- const hasRun = truth.events.some((e) => e.kind === constants_js_1.EVENT_KIND.RUN_STARTED && e.scope?.runId === String(runId));
10
- const hasNode = truth.events.some((e) => e.kind === constants_js_1.EVENT_KIND.NODE_CREATED && e.scope?.runId === String(runId) && e.scope?.nodeId === String(nodeId));
8
+ const hasRun = args.lockedIndex.runStartedByRunId.has(String(runId));
9
+ const nodeCreated = args.lockedIndex.nodeCreatedByNodeId.get(String(nodeId));
10
+ const hasNode = nodeCreated !== undefined;
11
11
  if (!hasRun || !hasNode) {
12
12
  return (0, neverthrow_1.errAsync)({ kind: 'missing_node_or_run' });
13
13
  }
14
- const nodeCreated = truth.events.find((e) => e.kind === constants_js_1.EVENT_KIND.NODE_CREATED && e.scope?.nodeId === String(nodeId));
15
- if (!nodeCreated) {
16
- return (0, neverthrow_1.errAsync)({ kind: 'missing_node_or_run' });
17
- }
18
14
  if (String(nodeCreated.data.workflowHash) !== String(workflowHash)) {
19
15
  return (0, neverthrow_1.errAsync)({ kind: 'workflow_hash_mismatch' });
20
16
  }
@@ -38,6 +34,7 @@ function advanceAndRecord(args) {
38
34
  truth, sessionId, runId, attemptId, workflowHash, dedupeKey,
39
35
  inputContext, inputOutput, lock, pinnedWorkflow,
40
36
  ports: { snapshotStore, sessionStore, sha256, idFactory },
37
+ lockedIndex: args.lockedIndex,
41
38
  });
42
39
  }
43
40
  return (0, v2_advance_core_js_1.executeAdvanceCore)({
@@ -45,6 +42,7 @@ function advanceAndRecord(args) {
45
42
  truth, sessionId, runId, attemptId, workflowHash, dedupeKey,
46
43
  inputContext, inputOutput, lock, pinnedWorkflow,
47
44
  ports: { snapshotStore, sessionStore, sha256, idFactory },
45
+ lockedIndex: args.lockedIndex,
48
46
  });
49
47
  });
50
48
  }
@@ -1,19 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handleAdvanceIntent = handleAdvanceIntent;
4
- const workflow_js_1 = require("../../../types/workflow.js");
4
+ const workflow_object_cache_js_1 = require("./workflow-object-cache.js");
5
5
  const workflow_hash_ref_js_1 = require("../../../v2/durable-core/ids/workflow-hash-ref.js");
6
6
  const neverthrow_1 = require("neverthrow");
7
- const workflow_source_js_1 = require("../../../types/workflow-source.js");
8
7
  const workflow_definition_js_1 = require("../../../types/workflow-definition.js");
9
8
  const v2_error_mapping_js_1 = require("../v2-error-mapping.js");
10
- const constants_js_1 = require("../../../v2/durable-core/constants.js");
11
9
  const replay_js_1 = require("./replay.js");
12
10
  const advance_js_1 = require("./advance.js");
11
+ const sorted_event_log_js_1 = require("../../../v2/durable-core/sorted-event-log.js");
12
+ const session_index_js_1 = require("../../../v2/durable-core/session-index.js");
13
13
  function handleAdvanceIntent(args) {
14
14
  const { input, sessionId, runId, nodeId, attemptId, workflowHashRef, truth, gate, sessionStore, snapshotStore, pinnedStore, tokenCodecPorts, idFactory, sha256, aliasStore, entropy } = args;
15
15
  const dedupeKey = `advance_recorded:${sessionId}:${nodeId}:${attemptId}`;
16
- const runStarted = truth.events.find((e) => e.kind === constants_js_1.EVENT_KIND.RUN_STARTED && e.scope.runId === String(runId));
16
+ const preLockSortedResult = (0, sorted_event_log_js_1.asSortedEventLog)(truth.events);
17
+ if (preLockSortedResult.isErr()) {
18
+ return (0, neverthrow_1.errAsync)({
19
+ kind: 'invariant_violation',
20
+ message: `Session events are not sorted: ${preLockSortedResult.error.message}`,
21
+ });
22
+ }
23
+ const preLockIndex = (0, session_index_js_1.buildSessionIndex)(preLockSortedResult.value);
24
+ const runStarted = preLockIndex.runStartedByRunId.get(String(runId));
17
25
  if (!runStarted) {
18
26
  return (0, neverthrow_1.errAsync)({
19
27
  kind: 'token_unknown_node',
@@ -37,7 +45,7 @@ function handleAdvanceIntent(args) {
37
45
  suggestion: 'Use the continueToken returned by WorkRail for this run.',
38
46
  });
39
47
  }
40
- const nodeCreated = truth.events.find((e) => e.kind === constants_js_1.EVENT_KIND.NODE_CREATED && e.scope.nodeId === String(nodeId) && e.scope.runId === String(runId));
48
+ const nodeCreated = preLockIndex.nodeCreatedByNodeId.get(String(nodeId));
41
49
  if (!nodeCreated) {
42
50
  return (0, neverthrow_1.errAsync)({
43
51
  kind: 'token_unknown_node',
@@ -45,22 +53,24 @@ function handleAdvanceIntent(args) {
45
53
  suggestion: 'Use tokens returned by WorkRail for an existing node.',
46
54
  });
47
55
  }
48
- const nodeRefRes = (0, workflow_hash_ref_js_1.deriveWorkflowHashRef)(nodeCreated.data.workflowHash);
49
- if (nodeRefRes.isErr()) {
50
- return (0, neverthrow_1.errAsync)({
51
- kind: 'precondition_failed',
52
- message: nodeRefRes.error.message,
53
- suggestion: 'Re-pin the workflow via start_workflow.',
54
- });
55
- }
56
- if (String(nodeRefRes.value) !== String(workflowHashRef)) {
57
- return (0, neverthrow_1.errAsync)({
58
- kind: 'precondition_failed',
59
- message: 'workflowHash mismatch for this node.',
60
- suggestion: 'Use the continueToken returned by WorkRail for this node.',
61
- });
56
+ if (nodeCreated.data.workflowHash !== workflowHash) {
57
+ const nodeRefRes = (0, workflow_hash_ref_js_1.deriveWorkflowHashRef)(nodeCreated.data.workflowHash);
58
+ if (nodeRefRes.isErr()) {
59
+ return (0, neverthrow_1.errAsync)({
60
+ kind: 'precondition_failed',
61
+ message: nodeRefRes.error.message,
62
+ suggestion: 'Re-pin the workflow via start_workflow.',
63
+ });
64
+ }
65
+ if (String(nodeRefRes.value) !== String(workflowHashRef)) {
66
+ return (0, neverthrow_1.errAsync)({
67
+ kind: 'precondition_failed',
68
+ message: 'workflowHash mismatch for this node.',
69
+ suggestion: 'Use the continueToken returned by WorkRail for this node.',
70
+ });
71
+ }
62
72
  }
63
- const existing = truth.events.find((e) => e.kind === constants_js_1.EVENT_KIND.ADVANCE_RECORDED && e.dedupeKey === dedupeKey);
73
+ const existing = preLockIndex.advanceRecordedByDedupeKey.get(dedupeKey);
64
74
  return pinnedStore.get(workflowHash)
65
75
  .mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause }))
66
76
  .andThen((compiled) => {
@@ -75,7 +85,7 @@ function handleAdvanceIntent(args) {
75
85
  suggestion: 'Re-pin the workflow via start_workflow.',
76
86
  });
77
87
  }
78
- const pinnedWorkflow = (0, workflow_js_1.createWorkflow)(compiled.definition, (0, workflow_source_js_1.createBundledSource)());
88
+ const pinnedWorkflow = (0, workflow_object_cache_js_1.getCachedWorkflow)(workflowHash, compiled.definition);
79
89
  if (existing) {
80
90
  return (0, replay_js_1.replayFromRecordedAdvance)({
81
91
  recordedEvent: existing,
@@ -95,9 +105,17 @@ function handleAdvanceIntent(args) {
95
105
  }
96
106
  return gate
97
107
  .withHealthySessionLock(sessionId, (lock) => sessionStore.load(sessionId).andThen((truthLocked) => {
98
- const existingLocked = truthLocked.events.find((e) => e.kind === constants_js_1.EVENT_KIND.ADVANCE_RECORDED && e.dedupeKey === dedupeKey);
108
+ const lockedSortedResult = (0, sorted_event_log_js_1.asSortedEventLog)(truthLocked.events);
109
+ if (lockedSortedResult.isErr()) {
110
+ return (0, neverthrow_1.errAsync)({
111
+ kind: 'invariant_violation',
112
+ message: `Locked session events are not sorted: ${lockedSortedResult.error.message}`,
113
+ });
114
+ }
115
+ const lockedIndex = (0, session_index_js_1.buildSessionIndex)(lockedSortedResult.value);
116
+ const existingLocked = lockedIndex.advanceRecordedByDedupeKey.get(dedupeKey);
99
117
  if (existingLocked)
100
- return (0, neverthrow_1.okAsync)({ kind: 'replay', truth: truthLocked, recordedEvent: existingLocked });
118
+ return (0, neverthrow_1.okAsync)({ kind: 'replay', truth: truthLocked, recordedEvent: existingLocked, precomputedIndex: lockedIndex });
101
119
  return (0, advance_js_1.advanceAndRecord)({
102
120
  truth: truthLocked,
103
121
  sessionId,
@@ -114,9 +132,21 @@ function handleAdvanceIntent(args) {
114
132
  sessionStore,
115
133
  sha256,
116
134
  idFactory,
135
+ lockedIndex,
117
136
  }).andThen(() => sessionStore
118
137
  .load(sessionId)
119
- .map((truthAfter) => ({ kind: 'replay', truth: truthAfter, recordedEvent: null })));
138
+ .andThen((truthAfter) => {
139
+ const afterSortedResult = (0, sorted_event_log_js_1.asSortedEventLog)(truthAfter.events);
140
+ if (afterSortedResult.isErr()) {
141
+ return (0, neverthrow_1.errAsync)({
142
+ kind: 'invariant_violation',
143
+ message: `Post-advance session events are not sorted: ${afterSortedResult.error.message}`,
144
+ });
145
+ }
146
+ const index2 = (0, session_index_js_1.buildSessionIndex)(afterSortedResult.value);
147
+ const recordedEvent = index2.advanceRecordedByDedupeKey.get(dedupeKey) ?? null;
148
+ return (0, neverthrow_1.okAsync)({ kind: 'replay', truth: truthAfter, recordedEvent, precomputedIndex: index2 });
149
+ }));
120
150
  }))
121
151
  .mapErr((cause) => {
122
152
  if ((0, v2_error_mapping_js_1.isInternalError)(cause)) {
@@ -146,8 +176,7 @@ function handleAdvanceIntent(args) {
146
176
  })
147
177
  .andThen((res) => {
148
178
  const truth2 = res.truth;
149
- const recordedEvent = res.recordedEvent ??
150
- truth2.events.find((e) => e.kind === constants_js_1.EVENT_KIND.ADVANCE_RECORDED && e.dedupeKey === dedupeKey);
179
+ const recordedEvent = res.recordedEvent;
151
180
  if (!recordedEvent) {
152
181
  return (0, neverthrow_1.errAsync)({
153
182
  kind: 'invariant_violation',
@@ -157,6 +186,7 @@ function handleAdvanceIntent(args) {
157
186
  return (0, replay_js_1.replayFromRecordedAdvance)({
158
187
  recordedEvent,
159
188
  truth: truth2,
189
+ precomputedIndex: res.precomputedIndex,
160
190
  sessionId,
161
191
  runId,
162
192
  nodeId,
@@ -1,5 +1,5 @@
1
1
  import type { V2ContinueWorkflowInput } from '../../v2/tools.js';
2
- import { V2ContinueWorkflowOutputSchema } from '../../output-schemas.js';
2
+ import type { V2ContinueWorkflowOutputSchema } from '../../output-schemas.js';
3
3
  import { type SessionId, type RunId, type NodeId } from '../../../v2/durable-core/ids/index.js';
4
4
  import type { LoadedSessionTruthV2 } from '../../../v2/ports/session-event-log-store.port.js';
5
5
  import type { TokenCodecPorts } from '../../../v2/durable-core/tokens/token-codec-ports.js';
@@ -6,11 +6,10 @@ const binding_drift_js_1 = require("../../../v2/durable-core/domain/binding-drif
6
6
  const binding_registry_js_1 = require("../../../application/services/compiler/binding-registry.js");
7
7
  const v2_workspace_resolution_js_1 = require("../v2-workspace-resolution.js");
8
8
  const snapshot_state_js_1 = require("../../../v2/durable-core/projections/snapshot-state.js");
9
- const workflow_js_1 = require("../../../types/workflow.js");
9
+ const workflow_object_cache_js_1 = require("./workflow-object-cache.js");
10
10
  const index_js_1 = require("../../../v2/durable-core/ids/index.js");
11
11
  const workflow_hash_ref_js_1 = require("../../../v2/durable-core/ids/workflow-hash-ref.js");
12
12
  const neverthrow_1 = require("neverthrow");
13
- const workflow_source_js_1 = require("../../../types/workflow-source.js");
14
13
  const workflow_definition_js_1 = require("../../../types/workflow-definition.js");
15
14
  const v2_execution_helpers_js_1 = require("../v2-execution-helpers.js");
16
15
  const prompt_renderer_js_1 = require("../../../v2/durable-core/domain/prompt-renderer.js");
@@ -19,6 +18,7 @@ const v2_state_conversion_js_1 = require("../v2-state-conversion.js");
19
18
  const constants_js_1 = require("../../../v2/durable-core/constants.js");
20
19
  const index_js_2 = require("./index.js");
21
20
  const step_content_envelope_js_1 = require("../../step-content-envelope.js");
21
+ const assert_output_js_1 = require("../../assert-output.js");
22
22
  function handleRehydrateIntent(args) {
23
23
  const { input, sessionId, runId, nodeId, workflowHashRef, truth, tokenCodecPorts, pinnedStore, snapshotStore, idFactory, aliasStore, entropy, resolvedRootUris } = args;
24
24
  const runStarted = truth.events.find((e) => e.kind === constants_js_1.EVENT_KIND.RUN_STARTED && e.scope.runId === String(runId));
@@ -93,15 +93,15 @@ function handleRehydrateIntent(args) {
93
93
  if (!pending) {
94
94
  const preferences = (0, v2_execution_helpers_js_1.derivePreferencesOrDefault)({ truth, runId, nodeId });
95
95
  const nextIntent = (0, v2_state_conversion_js_1.deriveNextIntent)({ rehydrateOnly: true, isComplete, pending: null });
96
- const parsed = output_schemas_js_1.V2ContinueWorkflowOutputSchema.parse({
96
+ const parsed = (0, assert_output_js_1.assertOutput)({
97
97
  kind: 'ok',
98
98
  isComplete,
99
99
  pending: null,
100
100
  preferences,
101
101
  nextIntent,
102
102
  nextCall: null,
103
- ...(driftWarnings.length > 0 ? { warnings: driftWarnings } : {}),
104
- });
103
+ ...(driftWarnings.length > 0 ? { warnings: [...driftWarnings] } : {}),
104
+ }, assert_output_js_1.assertContinueTokenPresence);
105
105
  return (0, neverthrow_1.okAsync)({ response: parsed });
106
106
  }
107
107
  const attemptId = (0, v2_token_ops_js_1.newAttemptId)(idFactory);
@@ -115,7 +115,7 @@ function handleRehydrateIntent(args) {
115
115
  return (0, v2_token_ops_js_1.mintContinueAndCheckpointTokens)({ entry: entryBase, ports: tokenCodecPorts, aliasStore, entropy })
116
116
  .mapErr((failure) => ({ kind: 'token_signing_failed', cause: failure }))
117
117
  .andThen(({ continueToken: continueTokenValue, checkpointToken: checkpointTokenValue }) => {
118
- const wf = (0, workflow_js_1.createWorkflow)(pinned.definition, (0, workflow_source_js_1.createBundledSource)());
118
+ const wf = (0, workflow_object_cache_js_1.getCachedWorkflow)(workflowHash, pinned.definition);
119
119
  const metaRes = (0, prompt_renderer_js_1.renderPendingPrompt)({
120
120
  workflow: wf,
121
121
  stepId: String(pending.stepId),
@@ -138,7 +138,7 @@ function handleRehydrateIntent(args) {
138
138
  meta,
139
139
  references: pinned.resolvedReferences ?? buildPinnedReferencesFallback(pinned.definition.references ?? []),
140
140
  });
141
- const parsed = output_schemas_js_1.V2ContinueWorkflowOutputSchema.parse({
141
+ const parsed = (0, assert_output_js_1.assertOutput)({
142
142
  kind: 'ok',
143
143
  continueToken: continueTokenValue,
144
144
  checkpointToken: checkpointTokenValue,
@@ -147,8 +147,8 @@ function handleRehydrateIntent(args) {
147
147
  preferences,
148
148
  nextIntent,
149
149
  nextCall: (0, index_js_2.buildNextCall)({ continueToken: continueTokenValue, isComplete, pending: meta }),
150
- ...(driftWarnings.length > 0 ? { warnings: driftWarnings } : {}),
151
- });
150
+ ...(driftWarnings.length > 0 ? { warnings: [...driftWarnings] } : {}),
151
+ }, assert_output_js_1.assertContinueTokenPresence);
152
152
  return (0, neverthrow_1.okAsync)({ response: parsed, contentEnvelope });
153
153
  });
154
154
  });
@@ -1,7 +1,7 @@
1
- import { V2ContinueWorkflowOutputSchema } from '../../output-schemas.js';
1
+ import type { V2ContinueWorkflowOutputSchema } from '../../output-schemas.js';
2
2
  import type { ExecutionSnapshotFileV1 } from '../../../v2/durable-core/schemas/execution-snapshot/index.js';
3
3
  import { type AttemptId } from '../../../v2/durable-core/tokens/index.js';
4
- import { createWorkflow } from '../../../types/workflow.js';
4
+ import type { Workflow } from '../../../types/workflow.js';
5
5
  import type { DomainEventV1 } from '../../../v2/durable-core/schemas/session/index.js';
6
6
  import { type SessionId, type RunId, type NodeId, type WorkflowHash } from '../../../v2/durable-core/ids/index.js';
7
7
  import type { LoadedSessionTruthV2 } from '../../../v2/ports/session-event-log-store.port.js';
@@ -17,13 +17,14 @@ export declare function buildAdvancedReplayResponse(args: {
17
17
  readonly toNodeId: NodeId;
18
18
  readonly attemptId: AttemptId;
19
19
  readonly toSnapshot: ExecutionSnapshotFileV1;
20
- readonly workflow: ReturnType<typeof createWorkflow>;
20
+ readonly workflow: Workflow;
21
21
  readonly truth: LoadedSessionTruthV2;
22
22
  readonly workflowHash: WorkflowHash;
23
23
  readonly ports: TokenCodecPorts;
24
24
  readonly sha256: Sha256PortV2;
25
25
  readonly aliasStore: import('../../../v2/ports/token-alias-store.port.js').TokenAliasStorePortV2;
26
26
  readonly entropy: import('../../../v2/ports/random-entropy.port.js').RandomEntropyPortV2;
27
+ readonly precomputedIndex?: import('../../../v2/durable-core/session-index.js').SessionIndex;
27
28
  }): RA<z.infer<typeof V2ContinueWorkflowOutputSchema>, ContinueWorkflowError>;
28
29
  export declare function replayFromRecordedAdvance(args: {
29
30
  readonly recordedEvent: Extract<DomainEventV1, {
@@ -35,10 +36,11 @@ export declare function replayFromRecordedAdvance(args: {
35
36
  readonly nodeId: NodeId;
36
37
  readonly workflowHash: WorkflowHash;
37
38
  readonly attemptId: AttemptId;
38
- readonly pinnedWorkflow: ReturnType<typeof createWorkflow>;
39
+ readonly pinnedWorkflow: Workflow;
39
40
  readonly snapshotStore: import('../../../v2/ports/snapshot-store.port.js').SnapshotStorePortV2;
40
41
  readonly sha256: Sha256PortV2;
41
42
  readonly tokenCodecPorts: TokenCodecPorts;
42
43
  readonly aliasStore: import('../../../v2/ports/token-alias-store.port.js').TokenAliasStorePortV2;
43
44
  readonly entropy: import('../../../v2/ports/random-entropy.port.js').RandomEntropyPortV2;
45
+ readonly precomputedIndex?: import('../../../v2/durable-core/session-index.js').SessionIndex;
44
46
  }): RA<z.infer<typeof V2ContinueWorkflowOutputSchema>, ContinueWorkflowError>;
@@ -15,6 +15,8 @@ const v2_state_conversion_js_1 = require("../v2-state-conversion.js");
15
15
  const constants_js_1 = require("../../../v2/durable-core/constants.js");
16
16
  const index_js_2 = require("./index.js");
17
17
  const assessments_js_1 = require("../../../v2/projections/assessments.js");
18
+ const assert_output_js_1 = require("../../assert-output.js");
19
+ const sorted_event_log_js_1 = require("../../../v2/durable-core/sorted-event-log.js");
18
20
  function buildAdvancedReplayResponse(args) {
19
21
  const { sessionId, runId, fromNodeId, toNodeId, attemptId, toSnapshot, workflow, truth, workflowHash, ports, sha256, aliasStore, entropy } = args;
20
22
  const toNodeIdBranded = (0, index_js_1.asNodeId)(String(toNodeId));
@@ -75,29 +77,33 @@ function buildAdvancedReplayResponse(args) {
75
77
  runId: (0, index_js_1.asRunId)(String(runId)),
76
78
  nodeId: (0, index_js_1.asNodeId)(String(toNodeIdBranded)),
77
79
  rehydrateOnly: false,
80
+ precomputedIndex: args.precomputedIndex,
78
81
  });
79
82
  if (result.isErr()) {
80
83
  return (0, neverthrow_1.errAsync)({ kind: 'prompt_render_failed', message: result.error.message });
81
84
  }
82
85
  blockedMeta = result.value;
83
86
  }
84
- const preferences = (0, v2_execution_helpers_js_1.derivePreferencesOrDefault)({ truth, runId, nodeId: toNodeIdBranded });
87
+ const preferences = (0, v2_execution_helpers_js_1.derivePreferencesOrDefault)({ truth, runId, nodeId: toNodeIdBranded, precomputedIndex: args.precomputedIndex });
85
88
  const nextIntent = (0, v2_state_conversion_js_1.deriveNextIntent)({ rehydrateOnly: false, isComplete, pending: blockedMeta });
86
- return nextTokensMint.andThen((nextTokens) => retryContinueMint.andThen((retryContinueToken) => (0, neverthrow_1.okAsync)(output_schemas_js_1.V2ContinueWorkflowOutputSchema.parse({
87
- kind: 'blocked',
88
- continueToken: pending ? nextTokens.continueToken : undefined,
89
- checkpointToken: pending ? nextTokens.checkpointToken : undefined,
90
- isComplete,
91
- pending: (0, output_schemas_js_1.toPendingStep)(blockedMeta),
92
- preferences,
93
- nextIntent,
94
- nextCall: (0, index_js_2.buildNextCall)({ continueToken: pending ? nextTokens.continueToken : undefined, isComplete, pending: blockedMeta, retryContinueToken }),
95
- blockers,
96
- retryable,
97
- retryContinueToken,
98
- validation,
99
- assessmentFollowup,
100
- }))));
89
+ return nextTokensMint.andThen((nextTokens) => retryContinueMint.andThen((retryContinueToken) => {
90
+ const out = (0, assert_output_js_1.assertOutput)({
91
+ kind: 'blocked',
92
+ continueToken: pending ? nextTokens.continueToken : undefined,
93
+ checkpointToken: pending ? nextTokens.checkpointToken : undefined,
94
+ isComplete,
95
+ pending: (0, output_schemas_js_1.toPendingStep)(blockedMeta),
96
+ preferences,
97
+ nextIntent,
98
+ nextCall: (0, index_js_2.buildNextCall)({ continueToken: pending ? nextTokens.continueToken : undefined, isComplete, pending: blockedMeta, retryContinueToken }),
99
+ blockers,
100
+ retryable,
101
+ retryContinueToken,
102
+ validation,
103
+ assessmentFollowup,
104
+ }, assert_output_js_1.assertContinueTokenPresence);
105
+ return (0, neverthrow_1.okAsync)(out);
106
+ }));
101
107
  }
102
108
  let okMeta = null;
103
109
  if (pending) {
@@ -109,29 +115,38 @@ function buildAdvancedReplayResponse(args) {
109
115
  runId: (0, index_js_1.asRunId)(String(runId)),
110
116
  nodeId: (0, index_js_1.asNodeId)(String(toNodeIdBranded)),
111
117
  rehydrateOnly: false,
118
+ precomputedIndex: args.precomputedIndex,
112
119
  });
113
120
  if (result.isErr()) {
114
121
  return (0, neverthrow_1.errAsync)({ kind: 'prompt_render_failed', message: result.error.message });
115
122
  }
116
123
  okMeta = result.value;
117
124
  }
118
- const preferences = (0, v2_execution_helpers_js_1.derivePreferencesOrDefault)({ truth, runId, nodeId: toNodeIdBranded });
125
+ const preferences = (0, v2_execution_helpers_js_1.derivePreferencesOrDefault)({ truth, runId, nodeId: toNodeIdBranded, precomputedIndex: args.precomputedIndex });
119
126
  const nextIntent = (0, v2_state_conversion_js_1.deriveNextIntent)({ rehydrateOnly: false, isComplete, pending: okMeta });
120
127
  const stepContext = buildStepContext(truth.events, fromNodeId);
121
- return nextTokensMint.andThen((nextTokens) => (0, neverthrow_1.okAsync)(output_schemas_js_1.V2ContinueWorkflowOutputSchema.parse({
122
- kind: 'ok',
123
- continueToken: pending ? nextTokens.continueToken : undefined,
124
- checkpointToken: pending ? nextTokens.checkpointToken : undefined,
125
- isComplete,
126
- pending: (0, output_schemas_js_1.toPendingStep)(okMeta),
127
- preferences,
128
- nextIntent,
129
- nextCall: (0, index_js_2.buildNextCall)({ continueToken: pending ? nextTokens.continueToken : undefined, isComplete, pending: okMeta }),
130
- stepContext,
131
- })));
128
+ return nextTokensMint.andThen((nextTokens) => {
129
+ const out = (0, assert_output_js_1.assertOutput)({
130
+ kind: 'ok',
131
+ continueToken: pending ? nextTokens.continueToken : undefined,
132
+ checkpointToken: pending ? nextTokens.checkpointToken : undefined,
133
+ isComplete,
134
+ pending: (0, output_schemas_js_1.toPendingStep)(okMeta),
135
+ preferences,
136
+ nextIntent,
137
+ nextCall: (0, index_js_2.buildNextCall)({ continueToken: pending ? nextTokens.continueToken : undefined, isComplete, pending: okMeta }),
138
+ stepContext,
139
+ }, assert_output_js_1.assertContinueTokenPresence);
140
+ return (0, neverthrow_1.okAsync)(out);
141
+ });
132
142
  }
133
143
  function buildStepContext(events, completedNodeId) {
134
- const projection = (0, assessments_js_1.projectAssessmentsV2)(events);
144
+ const sortedEventsRes = (0, sorted_event_log_js_1.asSortedEventLog)(events);
145
+ if (sortedEventsRes.isErr()) {
146
+ console.warn(`[workrail:replay] stepContext events unsorted for node '${String(completedNodeId)}' — stepContext will be absent: ${sortedEventsRes.error.message}`);
147
+ return undefined;
148
+ }
149
+ const projection = (0, assessments_js_1.projectAssessmentsV2)(sortedEventsRes.value);
135
150
  if (projection.isErr()) {
136
151
  console.warn(`[workrail:replay] stepContext projection failed for node '${String(completedNodeId)}' — stepContext will be absent: ${projection.error.message}`);
137
152
  return undefined;
@@ -160,7 +175,8 @@ function replayFromRecordedAdvance(args) {
160
175
  });
161
176
  }
162
177
  const toNodeId = (0, index_js_1.asNodeId)(String(recordedEvent.data.outcome.toNodeId));
163
- const toNode = truth.events.find((e) => e.kind === constants_js_1.EVENT_KIND.NODE_CREATED && e.scope?.nodeId === String(toNodeId));
178
+ const toNode = args.precomputedIndex?.nodeCreatedByNodeId.get(String(toNodeId))
179
+ ?? truth.events.find((e) => e.kind === constants_js_1.EVENT_KIND.NODE_CREATED && e.scope?.nodeId === String(toNodeId));
164
180
  if (!toNode) {
165
181
  return (0, neverthrow_1.errAsync)({
166
182
  kind: 'invariant_violation',
@@ -191,6 +207,7 @@ function replayFromRecordedAdvance(args) {
191
207
  sha256,
192
208
  aliasStore,
193
209
  entropy,
210
+ precomputedIndex: args.precomputedIndex,
194
211
  });
195
212
  });
196
213
  }
@@ -1,6 +1,5 @@
1
1
  import type { V2ToolContext } from '../../types.js';
2
- import { V2StartWorkflowOutputSchema } from '../../output-schemas.js';
3
- import { createWorkflow } from '../../../types/workflow.js';
2
+ import type { V2StartWorkflowOutputSchema } from '../../output-schemas.js';
4
3
  import type { DomainEventV1 } from '../../../v2/durable-core/schemas/session/index.js';
5
4
  import { type SessionId, type RunId, type NodeId, type WorkflowHash } from '../../../v2/durable-core/ids/index.js';
6
5
  import type { Sha256PortV2 } from '../../../v2/ports/sha256.port.js';
@@ -26,7 +25,7 @@ export declare function loadAndPinWorkflow(args: {
26
25
  }): RA<{
27
26
  readonly workflow: import('../../../types/workflow.js').Workflow;
28
27
  readonly workflowHash: WorkflowHash;
29
- readonly pinnedWorkflow: ReturnType<typeof createWorkflow>;
28
+ readonly pinnedWorkflow: import('../../../types/workflow.js').Workflow;
30
29
  readonly firstStep: {
31
30
  readonly id: string;
32
31
  };
@@ -45,6 +44,7 @@ export declare function buildInitialEvents(args: {
45
44
  readonly idFactory: {
46
45
  readonly mintEventId: () => string;
47
46
  };
47
+ readonly goal: string;
48
48
  }): readonly DomainEventV1[];
49
49
  export declare function mintStartTokens(args: {
50
50
  readonly sessionId: SessionId;