@exaudeus/workrail 3.15.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 (139) 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/console/assets/index-BE5PAgPO.js +28 -0
  4. package/dist/console/assets/index-BZNM03t1.css +1 -0
  5. package/dist/console/index.html +2 -2
  6. package/dist/env-flags.d.ts +1 -0
  7. package/dist/env-flags.js +4 -0
  8. package/dist/infrastructure/session/HttpServer.d.ts +3 -3
  9. package/dist/infrastructure/session/HttpServer.js +68 -74
  10. package/dist/infrastructure/storage/caching-workflow-storage.d.ts +2 -0
  11. package/dist/infrastructure/storage/caching-workflow-storage.js +15 -6
  12. package/dist/infrastructure/storage/file-workflow-storage.js +3 -4
  13. package/dist/infrastructure/storage/schema-validating-workflow-storage.js +9 -8
  14. package/dist/manifest.json +257 -193
  15. package/dist/mcp/assert-output.d.ts +37 -0
  16. package/dist/mcp/assert-output.js +52 -0
  17. package/dist/mcp/boundary-coercion.d.ts +1 -0
  18. package/dist/mcp/boundary-coercion.js +44 -0
  19. package/dist/mcp/dev-mode.d.ts +1 -0
  20. package/dist/mcp/dev-mode.js +4 -0
  21. package/dist/mcp/handler-factory.js +12 -9
  22. package/dist/mcp/handlers/session.js +8 -9
  23. package/dist/mcp/handlers/v2-advance-core/event-builders.d.ts +2 -0
  24. package/dist/mcp/handlers/v2-advance-core/event-builders.js +6 -6
  25. package/dist/mcp/handlers/v2-advance-core/index.d.ts +2 -0
  26. package/dist/mcp/handlers/v2-advance-core/index.js +4 -3
  27. package/dist/mcp/handlers/v2-advance-core/input-validation.d.ts +2 -0
  28. package/dist/mcp/handlers/v2-advance-core/input-validation.js +32 -9
  29. package/dist/mcp/handlers/v2-advance-core/outcome-blocked.d.ts +2 -0
  30. package/dist/mcp/handlers/v2-advance-core/outcome-blocked.js +1 -1
  31. package/dist/mcp/handlers/v2-advance-core/outcome-success.d.ts +2 -0
  32. package/dist/mcp/handlers/v2-advance-core/outcome-success.js +1 -1
  33. package/dist/mcp/handlers/v2-checkpoint.d.ts +1 -1
  34. package/dist/mcp/handlers/v2-checkpoint.js +5 -6
  35. package/dist/mcp/handlers/v2-execution/advance.d.ts +4 -2
  36. package/dist/mcp/handlers/v2-execution/advance.js +5 -7
  37. package/dist/mcp/handlers/v2-execution/continue-advance.js +56 -26
  38. package/dist/mcp/handlers/v2-execution/continue-rehydrate.d.ts +1 -1
  39. package/dist/mcp/handlers/v2-execution/continue-rehydrate.js +9 -9
  40. package/dist/mcp/handlers/v2-execution/replay.d.ts +6 -4
  41. package/dist/mcp/handlers/v2-execution/replay.js +47 -30
  42. package/dist/mcp/handlers/v2-execution/start.d.ts +2 -3
  43. package/dist/mcp/handlers/v2-execution/start.js +11 -11
  44. package/dist/mcp/handlers/v2-execution/workflow-object-cache.d.ts +5 -0
  45. package/dist/mcp/handlers/v2-execution/workflow-object-cache.js +19 -0
  46. package/dist/mcp/handlers/v2-execution-helpers.d.ts +1 -0
  47. package/dist/mcp/handlers/v2-execution-helpers.js +23 -7
  48. package/dist/mcp/handlers/v2-resume.d.ts +1 -1
  49. package/dist/mcp/handlers/v2-resume.js +3 -4
  50. package/dist/mcp/handlers/v2-state-conversion.js +5 -1
  51. package/dist/mcp/handlers/v2-workflow.d.ts +80 -0
  52. package/dist/mcp/handlers/v2-workflow.js +36 -21
  53. package/dist/mcp/handlers/workflow.d.ts +2 -5
  54. package/dist/mcp/handlers/workflow.js +15 -12
  55. package/dist/mcp/output-schemas.d.ts +20 -27
  56. package/dist/mcp/output-schemas.js +5 -7
  57. package/dist/mcp/server.js +22 -4
  58. package/dist/mcp/tool-call-timing.d.ts +24 -0
  59. package/dist/mcp/tool-call-timing.js +85 -0
  60. package/dist/mcp/transports/http-entry.js +3 -2
  61. package/dist/mcp/transports/http-listener.d.ts +1 -0
  62. package/dist/mcp/transports/http-listener.js +25 -0
  63. package/dist/mcp/transports/shutdown-hooks.d.ts +4 -1
  64. package/dist/mcp/transports/shutdown-hooks.js +3 -2
  65. package/dist/mcp/transports/stdio-entry.js +6 -28
  66. package/dist/mcp/v2-response-formatter.js +2 -4
  67. package/dist/mcp/validation/schema-introspection.d.ts +1 -0
  68. package/dist/mcp/validation/schema-introspection.js +15 -5
  69. package/dist/mcp/validation/suggestion-generator.js +2 -2
  70. package/dist/runtime/adapters/node-process-signals.d.ts +1 -0
  71. package/dist/runtime/adapters/node-process-signals.js +5 -0
  72. package/dist/runtime/adapters/noop-process-signals.d.ts +1 -0
  73. package/dist/runtime/adapters/noop-process-signals.js +2 -0
  74. package/dist/runtime/ports/process-signals.d.ts +1 -0
  75. package/dist/types/workflow-definition.d.ts +2 -0
  76. package/dist/types/workflow.d.ts +3 -0
  77. package/dist/types/workflow.js +35 -26
  78. package/dist/v2/durable-core/domain/context-template-resolver.js +2 -2
  79. package/dist/v2/durable-core/domain/function-definition-expander.js +2 -17
  80. package/dist/v2/durable-core/domain/prompt-renderer.d.ts +1 -0
  81. package/dist/v2/durable-core/domain/prompt-renderer.js +23 -18
  82. package/dist/v2/durable-core/domain/recap-recovery.js +23 -16
  83. package/dist/v2/durable-core/domain/retrieval-contract.js +13 -7
  84. package/dist/v2/durable-core/session-index.d.ts +22 -0
  85. package/dist/v2/durable-core/session-index.js +58 -0
  86. package/dist/v2/durable-core/sorted-event-log.d.ts +6 -0
  87. package/dist/v2/durable-core/sorted-event-log.js +15 -0
  88. package/dist/v2/infra/local/fs/index.js +8 -8
  89. package/dist/v2/infra/local/session-store/index.d.ts +1 -1
  90. package/dist/v2/infra/local/session-store/index.js +71 -61
  91. package/dist/v2/infra/local/session-summary-provider/index.js +9 -4
  92. package/dist/v2/infra/local/snapshot-store/index.js +2 -1
  93. package/dist/v2/ports/session-event-log-store.port.d.ts +1 -1
  94. package/dist/v2/projections/assessment-consequences.d.ts +2 -1
  95. package/dist/v2/projections/assessment-consequences.js +0 -5
  96. package/dist/v2/projections/assessments.d.ts +2 -1
  97. package/dist/v2/projections/assessments.js +2 -4
  98. package/dist/v2/projections/gaps.d.ts +2 -1
  99. package/dist/v2/projections/gaps.js +0 -5
  100. package/dist/v2/projections/preferences.d.ts +2 -1
  101. package/dist/v2/projections/preferences.js +0 -5
  102. package/dist/v2/projections/run-context.d.ts +2 -2
  103. package/dist/v2/projections/run-context.js +0 -5
  104. package/dist/v2/projections/run-dag.js +7 -1
  105. package/dist/v2/projections/run-execution-trace.d.ts +8 -0
  106. package/dist/v2/projections/run-execution-trace.js +124 -0
  107. package/dist/v2/projections/run-status-signals.d.ts +2 -2
  108. package/dist/v2/usecases/console-routes.d.ts +3 -1
  109. package/dist/v2/usecases/console-routes.js +123 -25
  110. package/dist/v2/usecases/console-service.d.ts +1 -0
  111. package/dist/v2/usecases/console-service.js +83 -25
  112. package/dist/v2/usecases/console-types.d.ts +53 -0
  113. package/dist/v2/usecases/worktree-service.js +32 -1
  114. package/package.json +6 -5
  115. package/spec/workflow.schema.json +18 -0
  116. package/workflows/adaptive-ticket-creation.json +23 -16
  117. package/workflows/architecture-scalability-audit.json +29 -22
  118. package/workflows/bug-investigation.agentic.v2.json +7 -0
  119. package/workflows/coding-task-workflow-agentic.json +7 -0
  120. package/workflows/coding-task-workflow-agentic.lean.v2.json +16 -8
  121. package/workflows/coding-task-workflow-agentic.v2.json +7 -0
  122. package/workflows/cross-platform-code-conversion.v2.json +7 -0
  123. package/workflows/document-creation-workflow.json +15 -8
  124. package/workflows/documentation-update-workflow.json +15 -8
  125. package/workflows/intelligent-test-case-generation.json +7 -0
  126. package/workflows/learner-centered-course-workflow.json +9 -2
  127. package/workflows/mr-review-workflow.agentic.v2.json +7 -0
  128. package/workflows/personal-learning-materials-creation-branched.json +15 -8
  129. package/workflows/presentation-creation.json +12 -5
  130. package/workflows/production-readiness-audit.json +7 -0
  131. package/workflows/relocation-workflow-us.json +39 -32
  132. package/workflows/scoped-documentation-workflow.json +33 -26
  133. package/workflows/ui-ux-design-workflow.json +7 -0
  134. package/workflows/workflow-diagnose-environment.json +6 -0
  135. package/workflows/workflow-for-workflows.json +7 -0
  136. package/workflows/workflow-for-workflows.v2.json +23 -11
  137. package/workflows/wr.discovery.json +8 -1
  138. package/dist/console/assets/index-BZYIjrzJ.js +0 -28
  139. package/dist/console/assets/index-OLCKbDdm.css +0 -1
@@ -0,0 +1,37 @@
1
+ export declare function assertOutput<T>(data: T, check: (data: T) => void): T;
2
+ type BlockerPointer = {
3
+ readonly kind: 'context_key';
4
+ readonly key: string;
5
+ } | {
6
+ readonly kind: 'context_budget';
7
+ } | {
8
+ readonly kind: 'output_contract';
9
+ readonly contractRef: string;
10
+ } | {
11
+ readonly kind: 'capability';
12
+ readonly capability: string;
13
+ } | {
14
+ readonly kind: 'assessment_dimension';
15
+ readonly assessmentId: string;
16
+ readonly dimensionId: string;
17
+ } | {
18
+ readonly kind: 'workflow_step';
19
+ readonly stepId: string;
20
+ };
21
+ type BlockerLike = {
22
+ readonly code: string;
23
+ readonly pointer: BlockerPointer;
24
+ };
25
+ type BlockerReportLike = {
26
+ readonly blockers: readonly BlockerLike[];
27
+ };
28
+ export declare function assertBlockerReportInvariants(report: BlockerReportLike): void;
29
+ type PendingLike = {
30
+ readonly stepId: string;
31
+ } | null;
32
+ type ContinueTokenHolder = {
33
+ readonly continueToken?: string | null | undefined;
34
+ readonly pending: PendingLike;
35
+ };
36
+ export declare function assertContinueTokenPresence(data: ContinueTokenHolder): void;
37
+ export {};
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertOutput = assertOutput;
4
+ exports.assertBlockerReportInvariants = assertBlockerReportInvariants;
5
+ exports.assertContinueTokenPresence = assertContinueTokenPresence;
6
+ function assertOutput(data, check) {
7
+ if (process.env['WORKRAIL_DEV'] === '1') {
8
+ check(data);
9
+ }
10
+ return data;
11
+ }
12
+ function assertBlockerReportInvariants(report) {
13
+ const keyFor = (b) => {
14
+ const p = b.pointer;
15
+ let ptrStable;
16
+ switch (p.kind) {
17
+ case 'context_key':
18
+ ptrStable = p.key;
19
+ break;
20
+ case 'output_contract':
21
+ ptrStable = p.contractRef;
22
+ break;
23
+ case 'capability':
24
+ ptrStable = p.capability;
25
+ break;
26
+ case 'assessment_dimension':
27
+ ptrStable = `${p.assessmentId}|${p.dimensionId}`;
28
+ break;
29
+ case 'workflow_step':
30
+ ptrStable = p.stepId;
31
+ break;
32
+ case 'context_budget':
33
+ ptrStable = '';
34
+ break;
35
+ default: {
36
+ const _exhaustive = p;
37
+ ptrStable = String(_exhaustive);
38
+ }
39
+ }
40
+ return `${b.code}|${p.kind}|${String(ptrStable)}`;
41
+ };
42
+ for (let i = 1; i < report.blockers.length; i++) {
43
+ if (keyFor(report.blockers[i - 1]) > keyFor(report.blockers[i])) {
44
+ throw new Error('assertOutput: blockers must be deterministically sorted by composite key');
45
+ }
46
+ }
47
+ }
48
+ function assertContinueTokenPresence(data) {
49
+ if (data.pending != null && data.continueToken == null) {
50
+ throw new Error('assertOutput: continueToken is required when a pending step exists');
51
+ }
52
+ }
@@ -1,2 +1,3 @@
1
1
  import { z } from 'zod';
2
+ export declare function buildCoercionFn(shapeSchema: z.ZodObject<z.ZodRawShape>, aliasMap?: Readonly<Record<string, string>>): (args: unknown) => unknown;
2
3
  export declare function coerceJsonStringObjectFields(args: unknown, shapeSchema: z.ZodObject<z.ZodRawShape>, aliasMap?: Readonly<Record<string, string>>): unknown;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildCoercionFn = buildCoercionFn;
3
4
  exports.coerceJsonStringObjectFields = coerceJsonStringObjectFields;
4
5
  const zod_1 = require("zod");
6
+ const schema_introspection_js_1 = require("./validation/schema-introspection.js");
5
7
  function unwrapToBaseType(schema) {
6
8
  let current = schema;
7
9
  for (;;) {
@@ -32,6 +34,48 @@ function tryParseJsonObject(value) {
32
34
  return null;
33
35
  }
34
36
  }
37
+ function buildCoercionFn(shapeSchema, aliasMap) {
38
+ const shape = (0, schema_introspection_js_1.getCachedShape)(shapeSchema);
39
+ const objectFields = new Set();
40
+ for (const [key, fieldSchema] of Object.entries(shape)) {
41
+ if (expectsObjectValue(fieldSchema)) {
42
+ objectFields.add(key);
43
+ }
44
+ }
45
+ const aliasesToCoerce = new Set();
46
+ if (aliasMap) {
47
+ for (const [alias, canonical] of Object.entries(aliasMap)) {
48
+ if (objectFields.has(canonical)) {
49
+ aliasesToCoerce.add(alias);
50
+ }
51
+ }
52
+ }
53
+ const hasCoercibleFields = objectFields.size > 0 || aliasesToCoerce.size > 0;
54
+ return (args) => {
55
+ if (!hasCoercibleFields)
56
+ return args;
57
+ if (typeof args !== 'object' || args === null)
58
+ return args;
59
+ const input = args;
60
+ let result = null;
61
+ const coerceField = (key) => {
62
+ const value = input[key];
63
+ if (typeof value !== 'string')
64
+ return;
65
+ const parsed = tryParseJsonObject(value);
66
+ if (parsed === null)
67
+ return;
68
+ if (result === null)
69
+ result = { ...input };
70
+ result[key] = parsed;
71
+ };
72
+ for (const key of objectFields)
73
+ coerceField(key);
74
+ for (const alias of aliasesToCoerce)
75
+ coerceField(alias);
76
+ return result ?? args;
77
+ };
78
+ }
35
79
  function coerceJsonStringObjectFields(args, shapeSchema, aliasMap) {
36
80
  if (typeof args !== 'object' || args === null)
37
81
  return args;
@@ -0,0 +1 @@
1
+ export declare const DEV_MODE: boolean;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEV_MODE = void 0;
4
+ exports.DEV_MODE = process.env['WORKRAIL_DEV'] === '1';
@@ -36,7 +36,7 @@ function toMcpResult(result) {
36
36
  return {
37
37
  content: [{
38
38
  type: 'text',
39
- text: JSON.stringify(jsonPayload, null, 2),
39
+ text: JSON.stringify(jsonPayload),
40
40
  }],
41
41
  };
42
42
  }
@@ -49,17 +49,18 @@ function toMcpResult(result) {
49
49
  message: result.message,
50
50
  retry: result.retry,
51
51
  ...(result.details !== undefined ? { details: result.details } : {}),
52
- }, null, 2),
52
+ }),
53
53
  }],
54
54
  isError: true,
55
55
  };
56
56
  }
57
57
  }
58
58
  function createHandler(schema, handler, shapeSchema, aliasMap) {
59
+ const coerce = shapeSchema !== undefined
60
+ ? (0, boundary_coercion_js_1.buildCoercionFn)(shapeSchema, aliasMap)
61
+ : null;
59
62
  return async (args, ctx) => {
60
- const normalizedArgs = shapeSchema !== undefined
61
- ? (0, boundary_coercion_js_1.coerceJsonStringObjectFields)(args, shapeSchema, aliasMap)
62
- : args;
63
+ const normalizedArgs = coerce !== null ? coerce(args) : args;
63
64
  const parseResult = schema.safeParse(normalizedArgs);
64
65
  if (!parseResult.success) {
65
66
  const introspectionSchema = shapeSchema ?? schema;
@@ -87,10 +88,12 @@ function createHandler(schema, handler, shapeSchema, aliasMap) {
87
88
  };
88
89
  }
89
90
  function createValidatingHandler(schema, preValidate, handler, shapeSchema, aliasMap) {
91
+ const coerce = shapeSchema !== undefined
92
+ ? (0, boundary_coercion_js_1.buildCoercionFn)(shapeSchema, aliasMap)
93
+ : null;
94
+ const innerHandler = createHandler(schema, handler, shapeSchema);
90
95
  return async (args, ctx) => {
91
- const normalizedArgs = shapeSchema !== undefined
92
- ? (0, boundary_coercion_js_1.coerceJsonStringObjectFields)(args, shapeSchema, aliasMap)
93
- : args;
96
+ const normalizedArgs = coerce !== null ? coerce(args) : args;
94
97
  const pre = preValidate(normalizedArgs);
95
98
  if (!pre.ok) {
96
99
  const error = pre.error;
@@ -110,6 +113,6 @@ function createValidatingHandler(schema, preValidate, handler, shapeSchema, alia
110
113
  }
111
114
  return toMcpResult(error);
112
115
  }
113
- return createHandler(schema, handler, shapeSchema, aliasMap)(normalizedArgs, ctx);
116
+ return innerHandler(normalizedArgs, ctx);
114
117
  };
115
118
  }
@@ -5,7 +5,6 @@ exports.handleUpdateSession = handleUpdateSession;
5
5
  exports.handleReadSession = handleReadSession;
6
6
  exports.handleOpenDashboard = handleOpenDashboard;
7
7
  const types_js_1 = require("../types.js");
8
- const output_schemas_js_1 = require("../output-schemas.js");
9
8
  const SESSION_SCHEMA_OVERVIEW = {
10
9
  description: 'Bug Investigation Session Data Structure',
11
10
  mainSections: {
@@ -58,13 +57,13 @@ async function handleCreateSession(input, ctx) {
58
57
  const session = res.value;
59
58
  const baseUrl = httpServer.getBaseUrl();
60
59
  const dashboardUrl = baseUrl ? `${baseUrl}?session=${input.sessionId}` : null;
61
- const payload = output_schemas_js_1.CreateSessionOutputSchema.parse({
60
+ const payload = {
62
61
  sessionId: session.id,
63
62
  workflowId: session.workflowId,
64
63
  path: sessionManager.getSessionPath(input.workflowId, input.sessionId),
65
64
  dashboardUrl,
66
65
  createdAt: session.createdAt,
67
- });
66
+ };
68
67
  return (0, types_js_1.success)(payload);
69
68
  }
70
69
  async function handleUpdateSession(input, ctx) {
@@ -81,7 +80,7 @@ async function handleUpdateSession(input, ctx) {
81
80
  }
82
81
  return (0, types_js_1.errNotRetryable)('INTERNAL_ERROR', res.error.message);
83
82
  }
84
- const payload = output_schemas_js_1.UpdateSessionOutputSchema.parse({ updatedAt: new Date().toISOString() });
83
+ const payload = { updatedAt: new Date().toISOString() };
85
84
  return (0, types_js_1.success)(payload);
86
85
  }
87
86
  async function handleReadSession(input, ctx) {
@@ -90,10 +89,10 @@ async function handleReadSession(input, ctx) {
90
89
  return guardError;
91
90
  const sessionManager = ctx.sessionManager;
92
91
  if (input.path === '$schema') {
93
- const payload = output_schemas_js_1.ReadSessionSchemaOutputSchema.parse({
92
+ const payload = {
94
93
  query: '$schema',
95
94
  schema: SESSION_SCHEMA_OVERVIEW,
96
- });
95
+ };
97
96
  return (0, types_js_1.success)(payload);
98
97
  }
99
98
  const res = await sessionManager.readSession(input.workflowId, input.sessionId, input.path);
@@ -105,10 +104,10 @@ async function handleReadSession(input, ctx) {
105
104
  }
106
105
  return (0, types_js_1.errNotRetryable)('INTERNAL_ERROR', res.error.message);
107
106
  }
108
- const payload = output_schemas_js_1.ReadSessionOutputSchema.parse({
107
+ const payload = {
109
108
  query: input.path ?? '(full session)',
110
109
  data: res.value,
111
- });
110
+ };
112
111
  return (0, types_js_1.success)(payload);
113
112
  }
114
113
  async function handleOpenDashboard(input, ctx) {
@@ -118,7 +117,7 @@ async function handleOpenDashboard(input, ctx) {
118
117
  const httpServer = ctx.httpServer;
119
118
  try {
120
119
  const url = await httpServer.openDashboard(input.sessionId);
121
- const payload = output_schemas_js_1.OpenDashboardOutputSchema.parse({ url });
120
+ const payload = { url };
122
121
  return (0, types_js_1.success)(payload);
123
122
  }
124
123
  catch (err) {
@@ -10,8 +10,10 @@ import type { V2ContinueWorkflowInput } from '../../v2/tools.js';
10
10
  import { type OutputToAppend } from '../../../v2/durable-core/domain/outputs.js';
11
11
  import type { InternalError } from '../v2-error-mapping.js';
12
12
  import type { AdvanceCorePorts } from './index.js';
13
+ import type { SessionIndex } from '../../../v2/durable-core/session-index.js';
13
14
  type BuildAppendPlanArgs = {
14
15
  readonly truth: LoadedSessionTruthV2;
16
+ readonly lockedIndex: SessionIndex;
15
17
  readonly sessionId: SessionId;
16
18
  readonly runId: RunId;
17
19
  readonly currentNodeId: NodeId;
@@ -11,15 +11,15 @@ const outputs_js_1 = require("../../../v2/durable-core/domain/outputs.js");
11
11
  const ack_advance_append_plan_js_1 = require("../../../v2/durable-core/domain/ack-advance-append-plan.js");
12
12
  const constants_js_1 = require("../../../v2/durable-core/constants.js");
13
13
  function buildAndAppendPlan(args) {
14
- const { truth, sessionId, runId, currentNodeId, attemptId, workflowHash, extraEventsToAppend, sessionStore, idFactory, lock } = args;
15
- const nextEventIndex = truth.events.length === 0 ? 0 : truth.events[truth.events.length - 1].eventIndex + 1;
14
+ const { truth, lockedIndex, sessionId, runId, currentNodeId, attemptId, workflowHash, extraEventsToAppend, sessionStore, idFactory, lock } = args;
15
+ const nextEventIndex = lockedIndex.nextEventIndex;
16
16
  const evtAdvanceRecorded = idFactory.mintEventId();
17
17
  if (args.kind === 'blocked') {
18
18
  const toNodeId = String(idFactory.mintNodeId());
19
19
  const evtNodeCreated = idFactory.mintEventId();
20
20
  const evtEdgeCreated = idFactory.mintEventId();
21
21
  const outputEventIds = (args.outputsToAppend ?? []).map(() => idFactory.mintEventId());
22
- const hasChildren = truth.events.some((e) => e.kind === constants_js_1.EVENT_KIND.EDGE_CREATED && e.data.fromNodeId === String(currentNodeId));
22
+ const hasChildren = lockedIndex.hasChildEdgeByFromNodeId.has(String(currentNodeId));
23
23
  const causeKind = hasChildren ? constants_js_1.EDGE_CAUSE.NON_TIP_ADVANCE : constants_js_1.EDGE_CAUSE.INTENTIONAL_FORK;
24
24
  const planRes = (0, ack_advance_append_plan_js_1.buildAckAdvanceAppendPlanV1)({
25
25
  sessionId: String(sessionId),
@@ -44,12 +44,12 @@ function buildAndAppendPlan(args) {
44
44
  });
45
45
  if (planRes.isErr())
46
46
  return (0, neverthrow_1.errAsync)({ kind: 'invariant_violation', message: planRes.error.message });
47
- return sessionStore.append(lock, planRes.value);
47
+ return sessionStore.append(lock, planRes.value, truth);
48
48
  }
49
49
  const toNodeId = String(idFactory.mintNodeId());
50
50
  const evtNodeCreated = idFactory.mintEventId();
51
51
  const evtEdgeCreated = idFactory.mintEventId();
52
- const hasChildren = truth.events.some((e) => e.kind === constants_js_1.EVENT_KIND.EDGE_CREATED && e.data.fromNodeId === String(currentNodeId));
52
+ const hasChildren = lockedIndex.hasChildEdgeByFromNodeId.has(String(currentNodeId));
53
53
  const causeKind = hasChildren ? constants_js_1.EDGE_CAUSE.NON_TIP_ADVANCE : constants_js_1.EDGE_CAUSE.INTENTIONAL_FORK;
54
54
  const normalizedOutputs = (0, outputs_js_1.normalizeOutputsForAppend)(args.outputsToAppend);
55
55
  const outputEventIds = normalizedOutputs.map(() => idFactory.mintEventId());
@@ -76,7 +76,7 @@ function buildAndAppendPlan(args) {
76
76
  });
77
77
  if (planRes.isErr())
78
78
  return (0, neverthrow_1.errAsync)({ kind: 'invariant_violation', message: planRes.error.message });
79
- return sessionStore.append(lock, planRes.value);
79
+ return sessionStore.append(lock, planRes.value, truth);
80
80
  }
81
81
  function buildNotesOutputs(allowNotesAppend, attemptId, inputOutput) {
82
82
  if (!allowNotesAppend || !inputOutput?.notesMarkdown)
@@ -1,4 +1,5 @@
1
1
  import { ResultAsync as RA } from 'neverthrow';
2
+ import type { SessionIndex } from '../../../v2/durable-core/session-index.js';
2
3
  import type { ExecutionSnapshotFileV1, EngineStateV1 } from '../../../v2/durable-core/schemas/execution-snapshot/index.js';
3
4
  import type { SessionId, RunId, NodeId, WorkflowHash } from '../../../v2/durable-core/ids/index.js';
4
5
  import type { AttemptId } from '../../../v2/durable-core/tokens/index.js';
@@ -63,4 +64,5 @@ export declare function executeAdvanceCore(args: {
63
64
  readonly lock: WithHealthySessionLock;
64
65
  readonly pinnedWorkflow: ReturnType<typeof createWorkflow>;
65
66
  readonly ports: AdvanceCorePorts;
67
+ readonly lockedIndex: SessionIndex;
66
68
  }): RA<void, InternalError | SessionEventLogStoreError | SnapshotStoreError>;
@@ -39,6 +39,7 @@ function executeAdvanceCore(args) {
39
39
  }
40
40
  const validatedRes = (0, input_validation_js_1.validateAdvanceInputs)({
41
41
  truth, runId, currentNodeId, inputContext, inputOutput, pinnedWorkflow, pendingStep,
42
+ precomputedIndex: args.lockedIndex,
42
43
  });
43
44
  if (validatedRes.isErr())
44
45
  return errAsync(validatedRes.error);
@@ -71,7 +72,7 @@ function executeAdvanceCore(args) {
71
72
  const ctx = { truth, sessionId, runId, currentNodeId, attemptId, workflowHash, inputOutput, pinnedWorkflow, engineState, pendingStep };
72
73
  const computed = { reasons: effectiveReasons, outputRequirement, validation: evalValidation };
73
74
  const portsLocal = { snapshotStore, sessionStore, sha256, idFactory };
74
- return (0, outcome_blocked_js_1.buildBlockedOutcome)({ mode, snap, ctx, computed, v, lock, ports: portsLocal });
75
+ return (0, outcome_blocked_js_1.buildBlockedOutcome)({ mode, snap, ctx, computed, v, lock, ports: portsLocal, lockedIndex: args.lockedIndex });
75
76
  }
76
77
  const validation = phase.validation;
77
78
  const effectiveValidation = v.assessmentValidation && !v.assessmentValidation.validation.valid
@@ -110,9 +111,9 @@ function executeAdvanceCore(args) {
110
111
  const computed = { reasons, outputRequirement, validation: effectiveValidation };
111
112
  const ports = { snapshotStore, sessionStore, sha256, idFactory };
112
113
  if (shouldBlockNow) {
113
- return (0, outcome_blocked_js_1.buildBlockedOutcome)({ mode, snap, ctx, computed, v, lock, ports });
114
+ return (0, outcome_blocked_js_1.buildBlockedOutcome)({ mode, snap, ctx, computed, v, lock, ports, lockedIndex: args.lockedIndex });
114
115
  }
115
- return (0, outcome_success_js_1.buildSuccessOutcome)({ mode, ctx, computed, v, lock, ports });
116
+ return (0, outcome_success_js_1.buildSuccessOutcome)({ mode, ctx, computed, v, lock, ports, lockedIndex: args.lockedIndex });
116
117
  });
117
118
  }
118
119
  function derivePendingStepForMode(mode, engineState) {
@@ -9,6 +9,7 @@ import type { AssessmentArtifactV1 } from '../../../v2/durable-core/schemas/arti
9
9
  import type { RecordedAssessmentV1 } from '../../../v2/durable-core/domain/assessment-record.js';
10
10
  import type { TriggeredAssessmentConsequenceV1 } from './assessment-consequences.js';
11
11
  import type { InternalError } from '../v2-error-mapping.js';
12
+ import type { SessionIndex } from '../../../v2/durable-core/session-index.js';
12
13
  export interface ValidatedAdvanceInputs {
13
14
  readonly pendingStep: {
14
15
  readonly stepId: string;
@@ -50,4 +51,5 @@ export declare function validateAdvanceInputs(args: {
50
51
  readonly iteration: number;
51
52
  }[];
52
53
  };
54
+ readonly precomputedIndex?: SessionIndex;
53
55
  }): Result<ValidatedAdvanceInputs, InternalError>;
@@ -5,14 +5,28 @@ const neverthrow_1 = require("neverthrow");
5
5
  const workflow_js_1 = require("../../../types/workflow.js");
6
6
  const run_context_js_1 = require("../../../v2/projections/run-context.js");
7
7
  const preferences_js_1 = require("../../../v2/projections/preferences.js");
8
+ const sorted_event_log_js_1 = require("../../../v2/durable-core/sorted-event-log.js");
8
9
  const context_merge_js_1 = require("../../../v2/durable-core/domain/context-merge.js");
9
10
  const constants_js_1 = require("../../../v2/durable-core/constants.js");
10
11
  const assessment_validation_js_1 = require("./assessment-validation.js");
11
12
  const assessment_consequences_js_1 = require("./assessment-consequences.js");
12
13
  function validateAdvanceInputs(args) {
13
14
  const { truth, runId, currentNodeId, inputContext, inputOutput, pinnedWorkflow, pendingStep } = args;
14
- const storedContextRes = (0, run_context_js_1.projectRunContextV2)(truth.events);
15
- const storedContext = storedContextRes.isOk() ? storedContextRes.value.byRunId[String(runId)]?.context : undefined;
15
+ let sortedEvents;
16
+ let storedContext;
17
+ if (args.precomputedIndex) {
18
+ sortedEvents = args.precomputedIndex.sortedEvents;
19
+ storedContext = args.precomputedIndex.runContextByRunId.get(String(runId));
20
+ }
21
+ else {
22
+ const sortedEventsRes = (0, sorted_event_log_js_1.asSortedEventLog)(truth.events);
23
+ if (sortedEventsRes.isErr()) {
24
+ return (0, neverthrow_1.err)({ kind: 'invariant_violation', message: sortedEventsRes.error.message });
25
+ }
26
+ sortedEvents = sortedEventsRes.value;
27
+ const storedContextRes = (0, run_context_js_1.projectRunContextV2)(sortedEvents);
28
+ storedContext = storedContextRes.isOk() ? storedContextRes.value.byRunId[String(runId)]?.context : undefined;
29
+ }
16
30
  const inputContextObj = inputContext && typeof inputContext === 'object' && inputContext !== null && !Array.isArray(inputContext)
17
31
  ? inputContext
18
32
  : undefined;
@@ -39,14 +53,23 @@ function validateAdvanceInputs(args) {
39
53
  const notesOptional = outputContract !== undefined ||
40
54
  (step !== null && step !== undefined && 'notesOptional' in step && step.notesOptional === true);
41
55
  const parentByNodeId = {};
42
- for (const e of truth.events) {
43
- if (e.kind !== constants_js_1.EVENT_KIND.NODE_CREATED)
44
- continue;
45
- if (e.scope?.runId !== String(runId))
46
- continue;
47
- parentByNodeId[String(e.scope.nodeId)] = e.data.parentNodeId;
56
+ if (args.precomputedIndex) {
57
+ for (const [nodeId, evt] of args.precomputedIndex.nodeCreatedByNodeId) {
58
+ if (evt.scope.runId === String(runId)) {
59
+ parentByNodeId[nodeId] = evt.data.parentNodeId ?? null;
60
+ }
61
+ }
62
+ }
63
+ else {
64
+ for (const e of truth.events) {
65
+ if (e.kind !== constants_js_1.EVENT_KIND.NODE_CREATED)
66
+ continue;
67
+ if (e.scope?.runId !== String(runId))
68
+ continue;
69
+ parentByNodeId[String(e.scope.nodeId)] = e.data.parentNodeId;
70
+ }
48
71
  }
49
- const prefs = (0, preferences_js_1.projectPreferencesV2)(truth.events, parentByNodeId);
72
+ const prefs = (0, preferences_js_1.projectPreferencesV2)(sortedEvents, parentByNodeId);
50
73
  const effectivePrefs = prefs.isOk() ? prefs.value.byNodeId[String(currentNodeId)]?.effective : undefined;
51
74
  const rawAutonomy = effectivePrefs?.autonomy ?? 'guided';
52
75
  const rawRiskPolicy = effectivePrefs?.riskPolicy ?? 'conservative';
@@ -1,4 +1,5 @@
1
1
  import { ResultAsync as RA } from 'neverthrow';
2
+ import type { SessionIndex } from '../../../v2/durable-core/session-index.js';
2
3
  import type { ExecutionSnapshotFileV1 } from '../../../v2/durable-core/schemas/execution-snapshot/index.js';
3
4
  import type { SessionEventLogStoreError } from '../../../v2/ports/session-event-log-store.port.js';
4
5
  import type { SnapshotStoreError } from '../../../v2/ports/snapshot-store.port.js';
@@ -15,4 +16,5 @@ export declare function buildBlockedOutcome(args: {
15
16
  readonly v: ValidatedAdvanceInputs;
16
17
  readonly lock: WithHealthySessionLock;
17
18
  readonly ports: AdvanceCorePorts;
19
+ readonly lockedIndex: SessionIndex;
18
20
  }): RA<void, InternalError | SessionEventLogStoreError | SnapshotStoreError>;
@@ -102,7 +102,7 @@ function buildBlockedOutcome(args) {
102
102
  return snapshotStore.putExecutionSnapshotV1(blockedSnapshotRes.value).andThen((blockedSnapshotRef) => {
103
103
  return (0, event_builders_js_1.buildAndAppendPlan)({
104
104
  kind: 'blocked',
105
- truth, sessionId, runId, currentNodeId, attemptId, workflowHash,
105
+ truth, lockedIndex: args.lockedIndex, sessionId, runId, currentNodeId, attemptId, workflowHash,
106
106
  extraEventsToAppend, blockers: blockersRes.value, snapshotRef: blockedSnapshotRef,
107
107
  outputsToAppend,
108
108
  sessionStore, idFactory, lock,
@@ -1,4 +1,5 @@
1
1
  import { ResultAsync as RA } from 'neverthrow';
2
+ import type { SessionIndex } from '../../../v2/durable-core/session-index.js';
2
3
  import type { SessionEventLogStoreError } from '../../../v2/ports/session-event-log-store.port.js';
3
4
  import type { SnapshotStoreError } from '../../../v2/ports/snapshot-store.port.js';
4
5
  import type { WithHealthySessionLock } from '../../../v2/durable-core/ids/with-healthy-session-lock.js';
@@ -12,4 +13,5 @@ export declare function buildSuccessOutcome(args: {
12
13
  readonly v: ValidatedAdvanceInputs;
13
14
  readonly lock: WithHealthySessionLock;
14
15
  readonly ports: AdvanceCorePorts;
16
+ readonly lockedIndex: SessionIndex;
15
17
  }): RA<void, InternalError | SessionEventLogStoreError | SnapshotStoreError>;
@@ -149,7 +149,7 @@ function buildSuccessOutcome(args) {
149
149
  const outputsToAppend = [...notesOutputs, ...artifactOutputsRes.value];
150
150
  return (0, event_builders_js_1.buildAndAppendPlan)({
151
151
  kind: 'advanced',
152
- truth, sessionId, runId, currentNodeId, attemptId, workflowHash,
152
+ truth, lockedIndex: args.lockedIndex, sessionId, runId, currentNodeId, attemptId, workflowHash,
153
153
  extraEventsToAppend, toNodeKind: successNodeKind(mode),
154
154
  snapshotRef: newSnapshotRef, outputsToAppend,
155
155
  sessionStore, idFactory, lock,
@@ -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
  }