@exaudeus/workrail 3.3.0 → 3.4.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 (74) hide show
  1. package/dist/application/services/compiler/binding-registry.d.ts +3 -0
  2. package/dist/application/services/compiler/binding-registry.js +71 -0
  3. package/dist/application/services/compiler/resolve-bindings.d.ts +18 -0
  4. package/dist/application/services/compiler/resolve-bindings.js +162 -0
  5. package/dist/application/services/compiler/sentinel-scan.d.ts +9 -0
  6. package/dist/application/services/compiler/sentinel-scan.js +37 -0
  7. package/dist/application/services/validation-engine.js +104 -0
  8. package/dist/application/services/workflow-compiler.d.ts +10 -2
  9. package/dist/application/services/workflow-compiler.js +25 -6
  10. package/dist/application/services/workflow-validation-pipeline.js +8 -1
  11. package/dist/cli.js +2 -2
  12. package/dist/engine/engine-factory.js +1 -1
  13. package/dist/index.d.ts +2 -1
  14. package/dist/index.js +4 -2
  15. package/dist/manifest.json +149 -101
  16. package/dist/mcp/handler-factory.d.ts +1 -1
  17. package/dist/mcp/handler-factory.js +2 -2
  18. package/dist/mcp/handlers/v2-checkpoint.js +5 -5
  19. package/dist/mcp/handlers/v2-error-mapping.js +4 -4
  20. package/dist/mcp/handlers/v2-execution/continue-advance.js +2 -2
  21. package/dist/mcp/handlers/v2-execution/continue-rehydrate.d.ts +1 -0
  22. package/dist/mcp/handlers/v2-execution/continue-rehydrate.js +76 -60
  23. package/dist/mcp/handlers/v2-execution/index.js +86 -44
  24. package/dist/mcp/handlers/v2-execution-helpers.js +1 -1
  25. package/dist/mcp/handlers/v2-resume.js +10 -5
  26. package/dist/mcp/handlers/v2-token-ops.d.ts +1 -1
  27. package/dist/mcp/handlers/v2-token-ops.js +5 -5
  28. package/dist/mcp/handlers/v2-workspace-resolution.d.ts +1 -0
  29. package/dist/mcp/handlers/v2-workspace-resolution.js +12 -0
  30. package/dist/mcp/index.d.ts +4 -1
  31. package/dist/mcp/index.js +6 -2
  32. package/dist/mcp/output-schemas.d.ts +148 -8
  33. package/dist/mcp/output-schemas.js +22 -4
  34. package/dist/mcp/server.d.ts +6 -4
  35. package/dist/mcp/server.js +2 -57
  36. package/dist/mcp/tool-descriptions.js +9 -158
  37. package/dist/mcp/transports/http-entry.js +6 -25
  38. package/dist/mcp/transports/shutdown-hooks.d.ts +5 -0
  39. package/dist/mcp/transports/shutdown-hooks.js +38 -0
  40. package/dist/mcp/transports/stdio-entry.js +6 -28
  41. package/dist/mcp/v2/tool-registry.js +2 -1
  42. package/dist/mcp/v2/tools.d.ts +28 -11
  43. package/dist/mcp/v2/tools.js +28 -4
  44. package/dist/mcp/v2-response-formatter.js +28 -1
  45. package/dist/mcp/validation/suggestion-generator.d.ts +1 -1
  46. package/dist/mcp/validation/suggestion-generator.js +13 -3
  47. package/dist/mcp/workflow-protocol-contracts.d.ts +31 -0
  48. package/dist/mcp/workflow-protocol-contracts.js +207 -0
  49. package/dist/mcp-server.d.ts +3 -1
  50. package/dist/mcp-server.js +6 -2
  51. package/dist/types/workflow-definition.d.ts +7 -0
  52. package/dist/types/workflow-definition.js +1 -0
  53. package/dist/v2/durable-core/domain/binding-drift.d.ts +8 -0
  54. package/dist/v2/durable-core/domain/binding-drift.js +29 -0
  55. package/dist/v2/durable-core/domain/reason-model.js +2 -2
  56. package/dist/v2/durable-core/schemas/compiled-workflow/index.d.ts +12 -0
  57. package/dist/v2/durable-core/schemas/compiled-workflow/index.js +2 -0
  58. package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +56 -56
  59. package/dist/v2/durable-core/schemas/session/events.d.ts +16 -16
  60. package/dist/v2/durable-core/schemas/session/gaps.d.ts +6 -6
  61. package/dist/v2/projections/resume-ranking.d.ts +1 -0
  62. package/dist/v2/projections/resume-ranking.js +1 -0
  63. package/dist/v2/read-only/v1-to-v2-shim.js +27 -10
  64. package/dist/v2/usecases/resume-session.d.ts +5 -1
  65. package/dist/v2/usecases/resume-session.js +4 -1
  66. package/package.json +1 -1
  67. package/spec/workflow.schema.json +44 -0
  68. package/workflows/coding-task-workflow-agentic.json +15 -15
  69. package/workflows/coding-task-workflow-agentic.lean.v2.json +10 -10
  70. package/workflows/coding-task-workflow-agentic.v2.json +12 -12
  71. package/workflows/coding-task-workflow-with-loops.json +2 -2
  72. package/workflows/document-creation-workflow.json +1 -1
  73. package/workflows/exploration-workflow.json +3 -3
  74. package/workflows/mr-review-workflow.agentic.v2.json +11 -11
@@ -286,6 +286,31 @@ export declare const V2NextCallSchema: z.ZodNullable<z.ZodObject<{
286
286
  };
287
287
  tool: "continue_workflow";
288
288
  }>>;
289
+ export declare const V2ResumeNextCallSchema: z.ZodObject<{
290
+ tool: z.ZodLiteral<"continue_workflow">;
291
+ params: z.ZodObject<{
292
+ continueToken: z.ZodString;
293
+ intent: z.ZodLiteral<"rehydrate">;
294
+ }, "strip", z.ZodTypeAny, {
295
+ continueToken: string;
296
+ intent: "rehydrate";
297
+ }, {
298
+ continueToken: string;
299
+ intent: "rehydrate";
300
+ }>;
301
+ }, "strip", z.ZodTypeAny, {
302
+ params: {
303
+ continueToken: string;
304
+ intent: "rehydrate";
305
+ };
306
+ tool: "continue_workflow";
307
+ }, {
308
+ params: {
309
+ continueToken: string;
310
+ intent: "rehydrate";
311
+ };
312
+ tool: "continue_workflow";
313
+ }>;
289
314
  export declare const V2BlockerReportSchema: z.ZodEffects<z.ZodObject<{
290
315
  blockers: z.ZodReadonly<z.ZodArray<z.ZodObject<{
291
316
  code: z.ZodEnum<["USER_ONLY_DEPENDENCY", "MISSING_REQUIRED_OUTPUT", "INVALID_REQUIRED_OUTPUT", "MISSING_REQUIRED_NOTES", "MISSING_CONTEXT_KEY", "CONTEXT_BUDGET_EXCEEDED", "REQUIRED_CAPABILITY_UNKNOWN", "REQUIRED_CAPABILITY_UNAVAILABLE", "INVARIANT_VIOLATION", "STORAGE_CORRUPTION_DETECTED"]>;
@@ -458,6 +483,23 @@ export declare const V2BlockerReportSchema: z.ZodEffects<z.ZodObject<{
458
483
  suggestedFix?: string | undefined;
459
484
  }[];
460
485
  }>;
486
+ export type { BindingDriftWarning as V2BindingDriftWarning } from '../v2/durable-core/domain/binding-drift.js';
487
+ export declare const V2BindingDriftWarningSchema: z.ZodObject<{
488
+ code: z.ZodLiteral<"BINDING_DRIFT">;
489
+ slotId: z.ZodString;
490
+ pinnedValue: z.ZodString;
491
+ currentValue: z.ZodString;
492
+ }, "strip", z.ZodTypeAny, {
493
+ code: "BINDING_DRIFT";
494
+ slotId: string;
495
+ pinnedValue: string;
496
+ currentValue: string;
497
+ }, {
498
+ code: "BINDING_DRIFT";
499
+ slotId: string;
500
+ pinnedValue: string;
501
+ currentValue: string;
502
+ }>;
461
503
  export declare const V2ContinueWorkflowOutputSchema: z.ZodEffects<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
462
504
  kind: z.ZodLiteral<"ok">;
463
505
  continueToken: z.ZodOptional<z.ZodString>;
@@ -510,6 +552,22 @@ export declare const V2ContinueWorkflowOutputSchema: z.ZodEffects<z.ZodDiscrimin
510
552
  };
511
553
  tool: "continue_workflow";
512
554
  }>>;
555
+ warnings: z.ZodOptional<z.ZodArray<z.ZodObject<{
556
+ code: z.ZodLiteral<"BINDING_DRIFT">;
557
+ slotId: z.ZodString;
558
+ pinnedValue: z.ZodString;
559
+ currentValue: z.ZodString;
560
+ }, "strip", z.ZodTypeAny, {
561
+ code: "BINDING_DRIFT";
562
+ slotId: string;
563
+ pinnedValue: string;
564
+ currentValue: string;
565
+ }, {
566
+ code: "BINDING_DRIFT";
567
+ slotId: string;
568
+ pinnedValue: string;
569
+ currentValue: string;
570
+ }>, "many">>;
513
571
  }, "strip", z.ZodTypeAny, {
514
572
  kind: "ok";
515
573
  pending: {
@@ -530,6 +588,12 @@ export declare const V2ContinueWorkflowOutputSchema: z.ZodEffects<z.ZodDiscrimin
530
588
  };
531
589
  tool: "continue_workflow";
532
590
  } | null;
591
+ warnings?: {
592
+ code: "BINDING_DRIFT";
593
+ slotId: string;
594
+ pinnedValue: string;
595
+ currentValue: string;
596
+ }[] | undefined;
533
597
  continueToken?: string | undefined;
534
598
  checkpointToken?: string | undefined;
535
599
  }, {
@@ -552,6 +616,12 @@ export declare const V2ContinueWorkflowOutputSchema: z.ZodEffects<z.ZodDiscrimin
552
616
  };
553
617
  tool: "continue_workflow";
554
618
  } | null;
619
+ warnings?: {
620
+ code: "BINDING_DRIFT";
621
+ slotId: string;
622
+ pinnedValue: string;
623
+ currentValue: string;
624
+ }[] | undefined;
555
625
  continueToken?: string | undefined;
556
626
  checkpointToken?: string | undefined;
557
627
  }>, z.ZodObject<{
@@ -910,6 +980,12 @@ export declare const V2ContinueWorkflowOutputSchema: z.ZodEffects<z.ZodDiscrimin
910
980
  };
911
981
  tool: "continue_workflow";
912
982
  } | null;
983
+ warnings?: {
984
+ code: "BINDING_DRIFT";
985
+ slotId: string;
986
+ pinnedValue: string;
987
+ currentValue: string;
988
+ }[] | undefined;
913
989
  continueToken?: string | undefined;
914
990
  checkpointToken?: string | undefined;
915
991
  } | {
@@ -982,6 +1058,12 @@ export declare const V2ContinueWorkflowOutputSchema: z.ZodEffects<z.ZodDiscrimin
982
1058
  };
983
1059
  tool: "continue_workflow";
984
1060
  } | null;
1061
+ warnings?: {
1062
+ code: "BINDING_DRIFT";
1063
+ slotId: string;
1064
+ pinnedValue: string;
1065
+ currentValue: string;
1066
+ }[] | undefined;
985
1067
  continueToken?: string | undefined;
986
1068
  checkpointToken?: string | undefined;
987
1069
  } | {
@@ -1039,37 +1121,95 @@ export declare const V2ResumeSessionOutputSchema: z.ZodObject<{
1039
1121
  candidates: z.ZodArray<z.ZodObject<{
1040
1122
  sessionId: z.ZodString;
1041
1123
  runId: z.ZodString;
1042
- stateToken: z.ZodString;
1124
+ workflowId: z.ZodString;
1125
+ resumeToken: z.ZodString;
1043
1126
  snippet: z.ZodString;
1044
1127
  whyMatched: z.ZodArray<z.ZodEnum<["matched_head_sha", "matched_branch", "matched_notes", "matched_workflow_id", "recency_fallback"]>, "many">;
1128
+ nextCall: z.ZodObject<{
1129
+ tool: z.ZodLiteral<"continue_workflow">;
1130
+ params: z.ZodObject<{
1131
+ continueToken: z.ZodString;
1132
+ intent: z.ZodLiteral<"rehydrate">;
1133
+ }, "strip", z.ZodTypeAny, {
1134
+ continueToken: string;
1135
+ intent: "rehydrate";
1136
+ }, {
1137
+ continueToken: string;
1138
+ intent: "rehydrate";
1139
+ }>;
1140
+ }, "strip", z.ZodTypeAny, {
1141
+ params: {
1142
+ continueToken: string;
1143
+ intent: "rehydrate";
1144
+ };
1145
+ tool: "continue_workflow";
1146
+ }, {
1147
+ params: {
1148
+ continueToken: string;
1149
+ intent: "rehydrate";
1150
+ };
1151
+ tool: "continue_workflow";
1152
+ }>;
1045
1153
  }, "strip", z.ZodTypeAny, {
1154
+ workflowId: string;
1046
1155
  sessionId: string;
1047
1156
  runId: string;
1048
- stateToken: string;
1157
+ nextCall: {
1158
+ params: {
1159
+ continueToken: string;
1160
+ intent: "rehydrate";
1161
+ };
1162
+ tool: "continue_workflow";
1163
+ };
1164
+ resumeToken: string;
1049
1165
  snippet: string;
1050
1166
  whyMatched: ("matched_head_sha" | "matched_branch" | "matched_notes" | "matched_workflow_id" | "recency_fallback")[];
1051
1167
  }, {
1168
+ workflowId: string;
1052
1169
  sessionId: string;
1053
1170
  runId: string;
1054
- stateToken: string;
1171
+ nextCall: {
1172
+ params: {
1173
+ continueToken: string;
1174
+ intent: "rehydrate";
1175
+ };
1176
+ tool: "continue_workflow";
1177
+ };
1178
+ resumeToken: string;
1055
1179
  snippet: string;
1056
1180
  whyMatched: ("matched_head_sha" | "matched_branch" | "matched_notes" | "matched_workflow_id" | "recency_fallback")[];
1057
1181
  }>, "many">;
1058
1182
  totalEligible: z.ZodNumber;
1059
1183
  }, "strip", z.ZodTypeAny, {
1060
1184
  candidates: {
1185
+ workflowId: string;
1061
1186
  sessionId: string;
1062
1187
  runId: string;
1063
- stateToken: string;
1188
+ nextCall: {
1189
+ params: {
1190
+ continueToken: string;
1191
+ intent: "rehydrate";
1192
+ };
1193
+ tool: "continue_workflow";
1194
+ };
1195
+ resumeToken: string;
1064
1196
  snippet: string;
1065
1197
  whyMatched: ("matched_head_sha" | "matched_branch" | "matched_notes" | "matched_workflow_id" | "recency_fallback")[];
1066
1198
  }[];
1067
1199
  totalEligible: number;
1068
1200
  }, {
1069
1201
  candidates: {
1202
+ workflowId: string;
1070
1203
  sessionId: string;
1071
1204
  runId: string;
1072
- stateToken: string;
1205
+ nextCall: {
1206
+ params: {
1207
+ continueToken: string;
1208
+ intent: "rehydrate";
1209
+ };
1210
+ tool: "continue_workflow";
1211
+ };
1212
+ resumeToken: string;
1073
1213
  snippet: string;
1074
1214
  whyMatched: ("matched_head_sha" | "matched_branch" | "matched_notes" | "matched_workflow_id" | "recency_fallback")[];
1075
1215
  }[];
@@ -1077,7 +1217,7 @@ export declare const V2ResumeSessionOutputSchema: z.ZodObject<{
1077
1217
  }>;
1078
1218
  export declare const V2CheckpointWorkflowOutputSchema: z.ZodObject<{
1079
1219
  checkpointNodeId: z.ZodString;
1080
- stateToken: z.ZodString;
1220
+ resumeToken: z.ZodString;
1081
1221
  nextCall: z.ZodNullable<z.ZodObject<{
1082
1222
  tool: z.ZodLiteral<"continue_workflow">;
1083
1223
  params: z.ZodObject<{
@@ -1105,7 +1245,7 @@ export declare const V2CheckpointWorkflowOutputSchema: z.ZodObject<{
1105
1245
  };
1106
1246
  tool: "continue_workflow";
1107
1247
  } | null;
1108
- stateToken: string;
1248
+ resumeToken: string;
1109
1249
  checkpointNodeId: string;
1110
1250
  }, {
1111
1251
  nextCall: {
@@ -1114,7 +1254,7 @@ export declare const V2CheckpointWorkflowOutputSchema: z.ZodObject<{
1114
1254
  };
1115
1255
  tool: "continue_workflow";
1116
1256
  } | null;
1117
- stateToken: string;
1257
+ resumeToken: string;
1118
1258
  checkpointNodeId: string;
1119
1259
  }>;
1120
1260
  export declare const V2StartWorkflowOutputSchema: z.ZodEffects<z.ZodObject<{
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OpenDashboardOutputSchema = exports.ReadSessionSchemaOutputSchema = exports.ReadSessionOutputSchema = exports.UpdateSessionOutputSchema = exports.CreateSessionOutputSchema = exports.V2StartWorkflowOutputSchema = exports.V2CheckpointWorkflowOutputSchema = exports.V2ResumeSessionOutputSchema = exports.V2ContinueWorkflowOutputSchema = exports.V2BlockerReportSchema = exports.V2NextCallSchema = exports.V2NextIntentSchema = exports.V2PreferencesSchema = exports.V2PendingStepSchema = exports.V2WorkflowInspectOutputSchema = exports.V2WorkflowListOutputSchema = exports.V2WorkflowListItemSchema = exports.WorkflowGetSchemaOutputSchema = exports.WorkflowValidateJsonOutputSchema = exports.WorkflowNextOutputSchema = exports.WorkflowGetOutputSchema = exports.WorkflowListOutputSchema = exports.WorkflowSummarySchema = exports.JsonValueSchema = void 0;
3
+ exports.OpenDashboardOutputSchema = exports.ReadSessionSchemaOutputSchema = exports.ReadSessionOutputSchema = exports.UpdateSessionOutputSchema = exports.CreateSessionOutputSchema = exports.V2StartWorkflowOutputSchema = exports.V2CheckpointWorkflowOutputSchema = exports.V2ResumeSessionOutputSchema = exports.V2ContinueWorkflowOutputSchema = exports.V2BindingDriftWarningSchema = exports.V2BlockerReportSchema = exports.V2ResumeNextCallSchema = exports.V2NextCallSchema = exports.V2NextIntentSchema = exports.V2PreferencesSchema = exports.V2PendingStepSchema = exports.V2WorkflowInspectOutputSchema = exports.V2WorkflowListOutputSchema = exports.V2WorkflowListItemSchema = exports.WorkflowGetSchemaOutputSchema = exports.WorkflowValidateJsonOutputSchema = exports.WorkflowNextOutputSchema = exports.WorkflowGetOutputSchema = exports.WorkflowListOutputSchema = exports.WorkflowSummarySchema = exports.JsonValueSchema = void 0;
4
4
  exports.toPendingStep = toPendingStep;
5
5
  const zod_1 = require("zod");
6
6
  const state_js_1 = require("../domain/execution/state.js");
@@ -94,6 +94,13 @@ exports.V2NextCallSchema = zod_1.z.object({
94
94
  tool: zod_1.z.literal('continue_workflow'),
95
95
  params: zod_1.z.object({ continueToken: zod_1.z.string().min(1) }),
96
96
  }).nullable();
97
+ exports.V2ResumeNextCallSchema = zod_1.z.object({
98
+ tool: zod_1.z.literal('continue_workflow'),
99
+ params: zod_1.z.object({
100
+ continueToken: zod_1.z.string().min(1),
101
+ intent: zod_1.z.literal('rehydrate'),
102
+ }),
103
+ });
97
104
  function utf8ByteLength(s) {
98
105
  return new TextEncoder().encode(s).length;
99
106
  }
@@ -182,6 +189,12 @@ exports.V2BlockerReportSchema = zod_1.z
182
189
  });
183
190
  const checkpointTokenSchema = zod_1.z.string().regex(token_patterns_js_1.CHECKPOINT_TOKEN_PATTERN, 'Invalid checkpointToken format').optional();
184
191
  const continueTokenSchema = zod_1.z.string().regex(token_patterns_js_1.CONTINUE_TOKEN_PATTERN, 'Invalid continueToken format').optional();
192
+ exports.V2BindingDriftWarningSchema = zod_1.z.object({
193
+ code: zod_1.z.literal('BINDING_DRIFT'),
194
+ slotId: zod_1.z.string().min(1),
195
+ pinnedValue: zod_1.z.string().min(1),
196
+ currentValue: zod_1.z.string().min(1),
197
+ });
185
198
  const V2ContinueWorkflowOkSchema = zod_1.z.object({
186
199
  kind: zod_1.z.literal('ok'),
187
200
  continueToken: continueTokenSchema,
@@ -191,6 +204,7 @@ const V2ContinueWorkflowOkSchema = zod_1.z.object({
191
204
  preferences: exports.V2PreferencesSchema,
192
205
  nextIntent: exports.V2NextIntentSchema,
193
206
  nextCall: exports.V2NextCallSchema,
207
+ warnings: zod_1.z.array(exports.V2BindingDriftWarningSchema).optional(),
194
208
  });
195
209
  const V2ContinueWorkflowBlockedSchema = zod_1.z.object({
196
210
  kind: zod_1.z.literal('blocked'),
@@ -219,7 +233,8 @@ exports.V2ResumeSessionOutputSchema = zod_1.z.object({
219
233
  candidates: zod_1.z.array(zod_1.z.object({
220
234
  sessionId: zod_1.z.string().min(1),
221
235
  runId: zod_1.z.string().min(1),
222
- stateToken: zod_1.z.string().regex(token_patterns_js_1.STATE_TOKEN_PATTERN, 'Invalid stateToken format'),
236
+ workflowId: zod_1.z.string().min(1),
237
+ resumeToken: zod_1.z.string().regex(token_patterns_js_1.STATE_TOKEN_PATTERN, 'Invalid resumeToken format'),
223
238
  snippet: zod_1.z.string().max(1024),
224
239
  whyMatched: zod_1.z.array(zod_1.z.enum([
225
240
  'matched_head_sha',
@@ -227,13 +242,16 @@ exports.V2ResumeSessionOutputSchema = zod_1.z.object({
227
242
  'matched_notes',
228
243
  'matched_workflow_id',
229
244
  'recency_fallback',
230
- ])),
245
+ ])).describe('Match signals explaining why this candidate was ranked. ' +
246
+ 'matched_head_sha/branch/notes/workflow_id = strong signal. ' +
247
+ 'recency_fallback = no strong signal; inspect the snippet before resuming.'),
248
+ nextCall: exports.V2ResumeNextCallSchema,
231
249
  })).max(5),
232
250
  totalEligible: zod_1.z.number().int().min(0),
233
251
  });
234
252
  exports.V2CheckpointWorkflowOutputSchema = zod_1.z.object({
235
253
  checkpointNodeId: zod_1.z.string().min(1),
236
- stateToken: zod_1.z.string().regex(token_patterns_js_1.STATE_TOKEN_PATTERN, 'Invalid stateToken format'),
254
+ resumeToken: zod_1.z.string().regex(token_patterns_js_1.STATE_TOKEN_PATTERN, 'Invalid resumeToken format'),
237
255
  nextCall: exports.V2NextCallSchema.describe('Pre-built template for your next continue_workflow call. ' +
238
256
  'After checkpoint, use this to rehydrate and continue working on the current step.'),
239
257
  });
@@ -1,5 +1,5 @@
1
1
  import type { ToolContext } from './types.js';
2
- import { WorkspaceRootsManager } from './workspace-roots-manager.js';
2
+ import { WorkspaceRootsManager, type RootsReader } from './workspace-roots-manager.js';
3
3
  import { type ToolAnnotations } from './tool-factory.js';
4
4
  import type { WrappedToolHandler } from './types/workflow-tool-edition.js';
5
5
  interface Tool {
@@ -12,10 +12,12 @@ export declare function createToolContext(): Promise<ToolContext>;
12
12
  export interface ComposedServer {
13
13
  readonly server: import('@modelcontextprotocol/sdk/server/index.js').Server;
14
14
  readonly ctx: ToolContext;
15
- readonly rootsManager: WorkspaceRootsManager;
15
+ readonly rootsReader: RootsReader;
16
16
  readonly tools: readonly Tool[];
17
17
  readonly handlers: Record<string, WrappedToolHandler>;
18
18
  }
19
- export declare function composeServer(): Promise<ComposedServer>;
20
- export declare function startServer(): Promise<void>;
19
+ export interface ComposedServerInternal extends ComposedServer {
20
+ readonly rootsManager: WorkspaceRootsManager;
21
+ }
22
+ export declare function composeServer(): Promise<ComposedServerInternal>;
21
23
  export {};
@@ -35,7 +35,6 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.createToolContext = createToolContext;
37
37
  exports.composeServer = composeServer;
38
- exports.startServer = startServer;
39
38
  const zod_to_json_schema_js_1 = require("./zod-to-json-schema.js");
40
39
  const container_js_1 = require("../di/container.js");
41
40
  const tokens_js_1 = require("../di/tokens.js");
@@ -186,8 +185,7 @@ async function composeServer() {
186
185
  }
187
186
  const rootsManager = new workspace_roots_manager_js_1.WorkspaceRootsManager();
188
187
  const { Server } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/server/index.js')));
189
- const { StdioServerTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/server/stdio.js')));
190
- const { CallToolRequestSchema, ListToolsRequestSchema, RootsListChangedNotificationSchema, } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/types.js')));
188
+ const { CallToolRequestSchema, ListToolsRequestSchema, } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/types.js')));
191
189
  const server = new Server({
192
190
  name: 'workrail-server',
193
191
  version: '0.1.0',
@@ -224,58 +222,5 @@ async function composeServer() {
224
222
  : ctx;
225
223
  return handler(args ?? {}, requestCtx);
226
224
  });
227
- return { server, ctx, rootsManager, tools, handlers };
228
- }
229
- async function startServer() {
230
- const { server, ctx, rootsManager } = await composeServer();
231
- const { StdioServerTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/server/stdio.js')));
232
- const { RootsListChangedNotificationSchema, } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/types.js')));
233
- server.setNotificationHandler(RootsListChangedNotificationSchema, async () => {
234
- try {
235
- const result = await server.listRoots();
236
- rootsManager.updateRootUris(result.roots.map((r) => r.uri));
237
- console.error(`[Roots] Updated workspace roots: ${result.roots.map((r) => r.uri).join(', ') || '(none)'}`);
238
- }
239
- catch {
240
- console.error('[Roots] Failed to fetch updated roots after change notification');
241
- }
242
- });
243
- const transport = new StdioServerTransport();
244
- await server.connect(transport);
245
- try {
246
- const result = await server.listRoots();
247
- rootsManager.updateRootUris(result.roots.map((r) => r.uri));
248
- console.error(`[Roots] Initial workspace roots: ${result.roots.map((r) => r.uri).join(', ') || '(none)'}`);
249
- }
250
- catch {
251
- console.error('[Roots] Client does not support roots/list; workspace context will use server CWD fallback');
252
- }
253
- console.error('WorkRail MCP Server running on stdio');
254
- const shutdownEvents = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ShutdownEvents);
255
- const processSignals = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessSignals);
256
- const terminator = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessTerminator);
257
- processSignals.on('SIGINT', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGINT' }));
258
- processSignals.on('SIGTERM', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGTERM' }));
259
- processSignals.on('SIGHUP', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGHUP' }));
260
- process.stdin.on('end', () => {
261
- console.error('[MCP] stdin closed, initiating shutdown');
262
- shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGHUP' });
263
- });
264
- let shutdownStarted = false;
265
- shutdownEvents.onShutdown((event) => {
266
- if (shutdownStarted)
267
- return;
268
- shutdownStarted = true;
269
- void (async () => {
270
- try {
271
- console.error(`[Shutdown] Requested by ${event.signal}. Stopping services...`);
272
- await ctx.httpServer?.stop();
273
- terminator.terminate({ kind: 'success' });
274
- }
275
- catch (err) {
276
- console.error('[Shutdown] Error while stopping services:', err);
277
- terminator.terminate({ kind: 'failure' });
278
- }
279
- })();
280
- });
225
+ return { server, ctx, rootsManager, rootsReader: rootsManager, tools, handlers };
281
226
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DESCRIPTIONS = void 0;
4
+ const workflow_protocol_contracts_js_1 = require("./workflow-protocol-contracts.js");
4
5
  exports.DESCRIPTIONS = {
5
6
  standard: {
6
7
  discover_workflows: `Your primary tool for any complex or multi-step request. Call this FIRST to see if a reliable, pre-defined workflow exists, as this is the preferred method over improvisation.
@@ -63,79 +64,10 @@ Returns:
63
64
  Pass workspacePath when available so project-scoped workflow variants are resolved against the correct workspace.
64
65
 
65
66
  Remember: inspecting is read-only. Call start_workflow when ready to begin.`,
66
- start_workflow: `Begin following a workflow's step-by-step instructions (WorkRail v2, feature-flagged).
67
-
68
- The workflow represents the user's plan for this task. Each step will tell you exactly what to do. Your job is to execute each step's instructions and report back.
69
-
70
- The response contains your first step's instructions as the main content, with tokens in a JSON code block at the end. The step title is the heading, and the step prompt is the body.
71
-
72
- What to do:
73
- 1. Read the step instructions (the main body of the response) and execute them exactly
74
- 2. When done, call continue_workflow with the stateToken and ackToken from the JSON block at the end
75
- 3. Add output.notesMarkdown documenting your work (see notes guidance below)
76
- 4. Don't predict what comes next — the workflow will tell you
77
-
78
- Notes guidance: Write output.notesMarkdown for a human reader who will reference it later. Include what you did, key decisions and trade-offs, what you produced (files, endpoints, test results), and anything notable (risks, open questions, things you deliberately skipped). Use markdown formatting. Be specific — names, paths, numbers. 10–30 lines is ideal; too short is worse than too long.
79
-
80
- Workspace anchoring: Pass workspacePath (the "Workspace:" path from your system parameters) so this session can be found by resume_session in future chats. Without it, session discovery may not work.
81
-
82
- Context auto-loads: If you provide context at start, WorkRail remembers it. On future continue_workflow calls, only pass context if you have NEW information to add.`,
83
- continue_workflow: `Get the next step in the workflow (WorkRail v2, feature-flagged).
84
-
85
- QUICK START — How to call back after completing a step:
86
- Copy the stateToken and ackToken from the JSON code block at the end of the previous response. Just add your output.
87
-
88
- Two modes:
89
-
90
- ADVANCE (with ackToken):
91
- - "I completed the current step; give me the next one"
92
- - Requires: stateToken + ackToken (from the JSON block in previous response)
93
- - Optional: output (your work summary), context (if facts changed)
94
- - Result: WorkRail advances to next step and returns it
95
-
96
- REHYDRATE (without ackToken):
97
- - "Remind me what the current step is" (after rewind or lost context)
98
- - Requires: stateToken only
99
- - Do NOT include ackToken or output
100
- - Result: Same pending step returned; no advancement; side-effect-free
101
-
102
- Intent is auto-inferred: ackToken present → advance, ackToken absent → rehydrate.
103
- You can set intent explicitly if you prefer, but it's optional.
104
-
105
- Reading the response:
106
- The response is natural language with your step instructions as the main content. A JSON code block at the end contains the tokens for your next call. The response tells you directly what to do — execute the step, retry with corrections, wait for user input, or acknowledge completion.
107
-
108
- Parameters:
109
- - stateToken (required): From the JSON block in the previous response
110
- - ackToken (required for advance): From the JSON block in the previous response
111
- - intent (optional): "advance" or "rehydrate" — auto-inferred from ackToken if omitted
112
- - context (optional): NEW facts only. Omit if unchanged — WorkRail auto-loads previous context
113
- - output.notesMarkdown (advance only): Recap of THIS step — what you did, key decisions, what you produced, anything notable. Write for a human reviewer. Use markdown, be specific (names, paths, numbers). 10–30 lines; never accumulate previous steps.
114
-
115
- The workflow is the user's structured instructions. Follow each step exactly as described.`,
116
- checkpoint_workflow: `Save a checkpoint on the current workflow step (WorkRail v2, feature-flagged).
117
-
118
- Creates a durable checkpoint on your current step without advancing. Useful for saving progress on long-running steps.
119
-
120
- Requires: checkpointToken (from the most recent start_workflow or continue_workflow response).
121
-
122
- Idempotent: calling with the same checkpointToken multiple times is safe and returns the same result.
123
-
124
- Returns: checkpointNodeId + a fresh stateToken.`,
125
- resume_session: `Find and reconnect to an existing workflow session (WorkRail v2, feature-flagged).
126
-
127
- Use this when you need to resume a previously started workflow but don't have the stateToken (e.g., new chat, lost context).
128
-
129
- Pass workspacePath (the "Workspace:" path from your system parameters) to match sessions anchored to your current workspace. Without it, git-based tier matching won't work.
130
-
131
- WorkRail ranks sessions using a 5-tier matching algorithm:
132
- 1. Exact git HEAD SHA match
133
- 2. Git branch match (exact or prefix)
134
- 3. Free text match against session notes
135
- 4. Free text match against workflow ID
136
- 5. Recency fallback
137
-
138
- Returns: Up to 5 ranked candidates, each with a stateToken you can use with continue_workflow.`,
67
+ start_workflow: (0, workflow_protocol_contracts_js_1.renderProtocolDescription)(workflow_protocol_contracts_js_1.START_WORKFLOW_PROTOCOL, 'standard'),
68
+ continue_workflow: (0, workflow_protocol_contracts_js_1.renderProtocolDescription)(workflow_protocol_contracts_js_1.CONTINUE_WORKFLOW_PROTOCOL, 'standard'),
69
+ checkpoint_workflow: (0, workflow_protocol_contracts_js_1.renderProtocolDescription)(workflow_protocol_contracts_js_1.CHECKPOINT_WORKFLOW_PROTOCOL, 'standard'),
70
+ resume_session: (0, workflow_protocol_contracts_js_1.renderProtocolDescription)(workflow_protocol_contracts_js_1.RESUME_SESSION_PROTOCOL, 'standard'),
139
71
  },
140
72
  authoritative: {
141
73
  discover_workflows: `Check for workflows that apply to the user's request. Workflows are the user's pre-defined instructions that you MUST follow when they exist.
@@ -209,90 +141,9 @@ Parameters:
209
141
  - workspacePath: optional absolute workspace path for correct project-scoped workflow resolution
210
142
 
211
143
  This is read-only. Call start_workflow when ready to commit to following the workflow.`,
212
- start_workflow: `Begin executing the user's workflow instructions (WorkRail v2, feature-flagged).
213
-
214
- The workflow is the USER'S VOICE expressing their plan for this task. Each step is a DIRECT INSTRUCTION from the user (or workflow author representing user intent). You MUST execute each step exactly as specified.
215
-
216
- The response contains your first step's instructions as the main content (heading + body), with tokens in a JSON code block at the end.
217
-
218
- REQUIRED BEHAVIOR:
219
- 1. Execute the step instructions (the main body of the response) EXACTLY as described
220
- 2. When done, call continue_workflow with the stateToken and ackToken from the JSON block at the end
221
- 3. Add output.notesMarkdown documenting your work — write for a human reader who will reference it later. Include what you did, key decisions/trade-offs, what you produced (files, endpoints, test results), and anything notable (risks, open questions, deliberate omissions). Use markdown formatting, be specific (names, paths, numbers). 10–30 lines; too short is worse than too long.
222
- 4. Round-trip tokens UNCHANGED (don't decode, inspect, or modify them)
223
- 5. Follow the workflow to completion — don't improvise alternative approaches
224
-
225
- Workspace anchoring (IMPORTANT):
226
- - Pass workspacePath set to the "Workspace:" value from your system parameters
227
- - This anchors the session to your workspace so resume_session can find it in future chats
228
- - Example: workspacePath: "/Users/you/git/my-project"
229
-
230
- Context handling:
231
- - Pass context at start to establish baseline facts
232
- - WorkRail auto-loads context on subsequent calls
233
- - Only pass context again if facts have CHANGED (e.g., user provided new information)`,
234
- continue_workflow: `Get your next INSTRUCTION from the workflow (WorkRail v2, feature-flagged).
235
-
236
- The workflow represents the USER'S PLAN. The step returned is a DIRECT INSTRUCTION you MUST follow.
237
-
238
- HOW TO CALL — Copy the stateToken and ackToken from the JSON code block at the end of the previous response. Add output if desired.
239
-
240
- Two modes:
241
-
242
- ADVANCE (with ackToken):
243
- - Purpose: "I completed the current step; give me the next instruction"
244
- - Requires: stateToken + ackToken (from the JSON block in the previous response)
245
- - Optional: output (your work summary), context (if facts changed)
246
- - Result: WorkRail advances to next step
247
- - Idempotent: Safe to retry with same tokens if unsure
248
-
249
- REHYDRATE (without ackToken):
250
- - Purpose: "Remind me what the current step is" (after rewind/lost context)
251
- - Requires: stateToken only
252
- - Do NOT include ackToken or output
253
- - Result: Same pending step returned; no advancement
254
- - Side-effect-free: No durable writes; pure state recovery
255
-
256
- Intent is auto-inferred: ackToken present → advance, ackToken absent → rehydrate.
257
- You can set intent explicitly if you prefer, but it's optional.
258
-
259
- REQUIRED BEHAVIOR:
260
- 1. Execute the step EXACTLY as described in the response body
261
- 2. When done, call continue_workflow with tokens from the JSON block — do NOT construct params manually
262
- 3. Do NOT predict what comes next — call continue_workflow and the workflow will tell you
263
- 4. Do NOT skip steps, combine steps, or improvise your own approach
264
-
265
- Reading the response:
266
- The response is natural language. The step instructions are the main content (heading + body). A JSON code block at the end has the tokens for your next call. The response tells you directly what to do — execute the step, retry with corrections, wait for user input, or acknowledge completion.
267
-
268
- Parameters:
269
- - stateToken (required): From the JSON block in the previous response
270
- - ackToken (required for advance): From the JSON block in the previous response
271
- - intent (optional): "advance" or "rehydrate" — auto-inferred from ackToken if omitted
272
- - context (optional): NEW facts only (auto-merges with previous). Omit if unchanged
273
- - output.notesMarkdown (advance only): Recap of THIS step — what you did, key decisions, what you produced, anything notable. Write for a human reviewer. Use markdown, be specific (names, paths, numbers). 10–30 lines; never accumulate previous steps.
274
-
275
- The workflow is the user's structured will. Follow it exactly — it may validate, loop, or branch in ways you don't predict.`,
276
- checkpoint_workflow: `Save a checkpoint on the current workflow step (WorkRail v2, feature-flagged).
277
-
278
- Creates a durable checkpoint without advancing. Use for long-running steps to save progress.
279
-
280
- Requires: checkpointToken from the most recent response. Idempotent.
281
-
282
- Returns: checkpointNodeId + fresh stateToken.`,
283
- resume_session: `Find and reconnect to an existing workflow session (WorkRail v2, feature-flagged).
284
-
285
- Call this when resuming a workflow without a stateToken. WorkRail ranks sessions deterministically:
286
- 1. Exact git HEAD SHA match (tier 1)
287
- 2. Git branch match (tier 2)
288
- 3. Notes content match (tier 3)
289
- 4. Workflow ID match (tier 4)
290
- 5. Recency (tier 5)
291
-
292
- IMPORTANT: Pass workspacePath set to the "Workspace:" value from your system parameters.
293
- Without it, tier 1 and tier 2 matching won't work (git context defaults to server directory, not yours).
294
- Example: workspacePath: "/Users/you/git/my-project"
295
-
296
- Returns: Up to 5 candidates with stateTokens. Use the best match's stateToken with continue_workflow.`,
144
+ start_workflow: (0, workflow_protocol_contracts_js_1.renderProtocolDescription)(workflow_protocol_contracts_js_1.START_WORKFLOW_PROTOCOL, 'authoritative'),
145
+ continue_workflow: (0, workflow_protocol_contracts_js_1.renderProtocolDescription)(workflow_protocol_contracts_js_1.CONTINUE_WORKFLOW_PROTOCOL, 'authoritative'),
146
+ checkpoint_workflow: (0, workflow_protocol_contracts_js_1.renderProtocolDescription)(workflow_protocol_contracts_js_1.CHECKPOINT_WORKFLOW_PROTOCOL, 'authoritative'),
147
+ resume_session: (0, workflow_protocol_contracts_js_1.renderProtocolDescription)(workflow_protocol_contracts_js_1.RESUME_SESSION_PROTOCOL, 'authoritative'),
297
148
  },
298
149
  };
@@ -39,8 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.startHttpServer = startHttpServer;
40
40
  const server_js_1 = require("../server.js");
41
41
  const http_listener_js_1 = require("./http-listener.js");
42
- const container_js_1 = require("../../di/container.js");
43
- const tokens_js_1 = require("../../di/tokens.js");
42
+ const shutdown_hooks_js_1 = require("./shutdown-hooks.js");
44
43
  const crypto = __importStar(require("crypto"));
45
44
  const express_1 = __importDefault(require("express"));
46
45
  async function startHttpServer(port) {
@@ -60,28 +59,10 @@ async function startHttpServer(port) {
60
59
  const boundPort = listener.getBoundPort();
61
60
  console.error('[Transport] WorkRail MCP Server running on HTTP');
62
61
  console.error(`[Transport] MCP endpoint: http://localhost:${boundPort}/mcp`);
63
- const shutdownEvents = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ShutdownEvents);
64
- const processSignals = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessSignals);
65
- const terminator = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessTerminator);
66
- processSignals.on('SIGINT', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGINT' }));
67
- processSignals.on('SIGTERM', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGTERM' }));
68
- processSignals.on('SIGHUP', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGHUP' }));
69
- let shutdownStarted = false;
70
- shutdownEvents.onShutdown((event) => {
71
- if (shutdownStarted)
72
- return;
73
- shutdownStarted = true;
74
- void (async () => {
75
- try {
76
- console.error(`[Shutdown] Requested by ${event.signal}. Stopping services...`);
77
- await listener.stop();
78
- await ctx.httpServer?.stop();
79
- terminator.terminate({ kind: 'success' });
80
- }
81
- catch (err) {
82
- console.error('[Shutdown] Error while stopping services:', err);
83
- terminator.terminate({ kind: 'failure' });
84
- }
85
- })();
62
+ (0, shutdown_hooks_js_1.wireShutdownHooks)({
63
+ onBeforeTerminate: async () => {
64
+ await listener.stop();
65
+ await ctx.httpServer?.stop();
66
+ },
86
67
  });
87
68
  }