@exaudeus/workrail 3.7.0 → 3.7.2

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.
package/README.md CHANGED
@@ -245,8 +245,12 @@ support [conditions, loops, validation criteria](docs/authoring.md), and more.
245
245
 
246
246
  - [All Workflows](docs/workflows.md) – Full list with detailed descriptions
247
247
  - [Writing Workflows](docs/authoring.md) – Custom workflow creation guide
248
+ - [Workflow Authoring Principles (v2)](docs/authoring-v2.md) – Cross-workflow design rules and authoring principles
248
249
  - [Configuration](docs/configuration.md) – Git repos, environment variables, local paths
249
250
  - [Advanced Features](docs/advanced.md) – Loops, conditionals, validation
251
+ - **Integrations**
252
+ - [Claude Code Setup](docs/integrations/claude-code.md) – Complete guide for Desktop & CLI
253
+ - [Firebender Setup](docs/integrations/firebender.md) – Firebender integration
250
254
 
251
255
  ---
252
256
 
@@ -670,8 +670,8 @@
670
670
  "bytes": 883
671
671
  },
672
672
  "mcp/handler-factory.js": {
673
- "sha256": "1216fe5e913c832242b3c9c2d9954441300b08660db59fe9deac8a4b7dc583db",
674
- "bytes": 5310
673
+ "sha256": "6eb67d1501582d1cfcf777d7d930a6cf3cb32ccbd450c7083133745ef27d2661",
674
+ "bytes": 5381
675
675
  },
676
676
  "mcp/handlers/session.d.ts": {
677
677
  "sha256": "38926e69a0e4935d1dbcdc53848be9fff0e4d8f96827883da3216f217918fa82",
@@ -862,8 +862,8 @@
862
862
  "bytes": 471
863
863
  },
864
864
  "mcp/handlers/v2-resume.js": {
865
- "sha256": "726f92eb12aa0b28fafc4ceef354500dac7c4f18b85c4fe02a7997f869fe533f",
866
- "bytes": 3612
865
+ "sha256": "780e4611d4fb7d7f7fac8cddf576ada1e9ba752f167a678ebd03d94ee518e298",
866
+ "bytes": 3828
867
867
  },
868
868
  "mcp/handlers/v2-state-conversion.d.ts": {
869
869
  "sha256": "94bd06904ef58dd210ff17ffb75c2492beea8937eb06d99749e5d860c0e0d96b",
@@ -914,12 +914,12 @@
914
914
  "bytes": 7991
915
915
  },
916
916
  "mcp/output-schemas.d.ts": {
917
- "sha256": "450ac819bddaf668eb662b8327591df72b68d553fb16a7bd7797fadc68b8b8c8",
918
- "bytes": 51526
917
+ "sha256": "b33bd6e889811ee4de7bd3b4b4fd6d52042cb7a6bdd23c8bd38baa42775e39c3",
918
+ "bytes": 52320
919
919
  },
920
920
  "mcp/output-schemas.js": {
921
- "sha256": "73c8cd97868afd6f6e6f3deac1a3aabc38384f82c532be91bd5ea6ad2f744a49",
922
- "bytes": 13044
921
+ "sha256": "ba7dda4faec5a8bc1e497b41966b5033ab2930c9cae7b46e85ea1d25b39f79fa",
922
+ "bytes": 13715
923
923
  },
924
924
  "mcp/render-envelope.d.ts": {
925
925
  "sha256": "22e83e1aba52968a7136cf289125a217b5f462a5a66a1eebe4669006e3326fdb",
@@ -942,8 +942,8 @@
942
942
  "bytes": 960
943
943
  },
944
944
  "mcp/server.js": {
945
- "sha256": "1e5faf28495dff31b83b9f86ba07e05e14fb88c757f4e9e3460235f92c0602ab",
946
- "bytes": 11642
945
+ "sha256": "9689ba068eaac9f36f328fa5429b5abe84bb8bd0062f0a59b60dc361dea09cd2",
946
+ "bytes": 11677
947
947
  },
948
948
  "mcp/step-content-envelope.d.ts": {
949
949
  "sha256": "19bd63c4d4de1d5d93393d346625d28ffd1bebdc320d4ba4e694cb740ec97d3b",
@@ -1058,12 +1058,12 @@
1058
1058
  "bytes": 2674
1059
1059
  },
1060
1060
  "mcp/v2-response-formatter.d.ts": {
1061
- "sha256": "b0dfb56016eabbc61fdb9d1f668d35c3fba7e8060b70be5aa74356ae0334ff0e",
1062
- "bytes": 438
1061
+ "sha256": "3dd2bb5beef01afc8dca6b3df8bfd52283dc4393c5d85d2906a56bf4ae9806c9",
1062
+ "bytes": 527
1063
1063
  },
1064
1064
  "mcp/v2-response-formatter.js": {
1065
- "sha256": "76615380b865b264d8c124231bae44d285ae738a17dc3b7b2ff5906c08eee646",
1066
- "bytes": 14437
1065
+ "sha256": "f8a8cb5c1fe1b081eca9228a09732dde7247aaf396ff14556cc721c66eee5ab8",
1066
+ "bytes": 21118
1067
1067
  },
1068
1068
  "mcp/v2/tool-registry.d.ts": {
1069
1069
  "sha256": "d4d4927728c3cab1c014661d499dd0119538371bc6c5e821a4cd31df7abebedf",
@@ -1074,12 +1074,12 @@
1074
1074
  "bytes": 3276
1075
1075
  },
1076
1076
  "mcp/v2/tools.d.ts": {
1077
- "sha256": "8802e914f1eae0cfcd5b9b7838d00a3a87c8416c1ade1fce1ee57e7744bf64b9",
1078
- "bytes": 7010
1077
+ "sha256": "89fff4db9e2b8f734d7453801fbc404a1518fccafce2a942af6656a6cdc2dc1e",
1078
+ "bytes": 7228
1079
1079
  },
1080
1080
  "mcp/v2/tools.js": {
1081
- "sha256": "a275de4ce4ccf31d429ce2c32f86169485676aad6f41cfe9babc3f00df2495a1",
1082
- "bytes": 8838
1081
+ "sha256": "c8bfc63a04911d44f13e62c0d9357dacf62ec437dd21f5b6f09c289a973687cb",
1082
+ "bytes": 9406
1083
1083
  },
1084
1084
  "mcp/validation/bounded-json.d.ts": {
1085
1085
  "sha256": "82203ac6123d5c6989606c3b5405aaea99ab829c8958835f9ae3ba45b8bc8fd5",
@@ -1150,8 +1150,8 @@
1150
1150
  "bytes": 1863
1151
1151
  },
1152
1152
  "mcp/workflow-protocol-contracts.js": {
1153
- "sha256": "715cf1dfa780a62b5b5dc08a3da9cbba3d444dc75c1d68db0b5c2ede804c5bd3",
1154
- "bytes": 12840
1153
+ "sha256": "8da5f9dfb6f87ec12de5f6e925ae25b022a91432c430738bff1ba14fafeb03fb",
1154
+ "bytes": 13357
1155
1155
  },
1156
1156
  "mcp/workflow-tool-edition-selector.d.ts": {
1157
1157
  "sha256": "75194908a89aaf7dc48fb661aea099827e7032ec76daf8f2de91bd59d46764ef",
@@ -1802,7 +1802,7 @@
1802
1802
  "bytes": 3397
1803
1803
  },
1804
1804
  "v2/durable-core/schemas/export-bundle/index.d.ts": {
1805
- "sha256": "a0c0c9c904b11e79f403bec154e118583709d1440916b7e30b1f3101b87acbf2",
1805
+ "sha256": "6521642cefd019f75dba36890b71c20c5508e04baef584a09557a4b98f894321",
1806
1806
  "bytes": 479848
1807
1807
  },
1808
1808
  "v2/durable-core/schemas/export-bundle/index.js": {
@@ -1882,7 +1882,7 @@
1882
1882
  "bytes": 715
1883
1883
  },
1884
1884
  "v2/durable-core/schemas/session/manifest.d.ts": {
1885
- "sha256": "3eff60c200f81c87bb0e0946ba23000bc30fdd98678d316e5a401c189612408b",
1885
+ "sha256": "82840f71d8cde0d10834fe411c9ea494e043af93637c4ffb9c14fdf27bbb1b8c",
1886
1886
  "bytes": 1594
1887
1887
  },
1888
1888
  "v2/durable-core/schemas/session/manifest.js": {
@@ -1938,7 +1938,7 @@
1938
1938
  "bytes": 4205
1939
1939
  },
1940
1940
  "v2/durable-core/tokens/payloads.d.ts": {
1941
- "sha256": "f0428631b6addaa4c842a1abb4d9e4b1f26cb647b6f42a99060114ccb64eab28",
1941
+ "sha256": "4b84627aeee7c8f6d4c142e9ffb3511674d74d012601731d81193dfd6b95ba1c",
1942
1942
  "bytes": 6921
1943
1943
  },
1944
1944
  "v2/durable-core/tokens/payloads.js": {
@@ -2114,12 +2114,12 @@
2114
2114
  "bytes": 28456
2115
2115
  },
2116
2116
  "v2/infra/local/session-summary-provider/index.d.ts": {
2117
- "sha256": "59db8bf5e38f983eedd21ff902a24324f7bedbb5f3eade1e4b0d5742b28447c2",
2118
- "bytes": 1004
2117
+ "sha256": "291d1303ef9f1ee0889830c5b42964a2d9b8c7d27b36a9b55d710ca5950c32ca",
2118
+ "bytes": 1136
2119
2119
  },
2120
2120
  "v2/infra/local/session-summary-provider/index.js": {
2121
- "sha256": "1d8a543361c582f6e089f63f24318ff7caf97a78659ecdc5b89152b78d18c6aa",
2122
- "bytes": 5942
2121
+ "sha256": "93ca7df8ae1ae5e29c29470a6fb947e05b2def481c2b3d8decf4a5dbe8fd8e1c",
2122
+ "bytes": 7611
2123
2123
  },
2124
2124
  "v2/infra/local/sha256/index.d.ts": {
2125
2125
  "sha256": "8a727b7e54a38275ca6f9f1b8730f97cfc0a212df035df1bdc58e716e6824230",
@@ -2386,12 +2386,12 @@
2386
2386
  "bytes": 732
2387
2387
  },
2388
2388
  "v2/projections/resume-ranking.d.ts": {
2389
- "sha256": "ff128aa669f995b350a1e4ee8da6f0321f999d492e23975816d3f70caefa4dbb",
2390
- "bytes": 2595
2389
+ "sha256": "5f15f10b632b8e771c74e1f3823616f761be4142493d1142176583e6495528b2",
2390
+ "bytes": 3531
2391
2391
  },
2392
2392
  "v2/projections/resume-ranking.js": {
2393
- "sha256": "381dbac1868bc88bdfa3e3c13da94b1392ba3858f2bc86cc3c4331e919aac899",
2394
- "bytes": 4300
2393
+ "sha256": "b9d48c8e08ae8b794771b44fc592158422e832ff64633817fa11093f64282715",
2394
+ "bytes": 6782
2395
2395
  },
2396
2396
  "v2/projections/run-context.d.ts": {
2397
2397
  "sha256": "a4d57470a435ac9860f60b3244d1b828853995027cd510d8da42762d21b2a687",
@@ -2458,12 +2458,12 @@
2458
2458
  "bytes": 77
2459
2459
  },
2460
2460
  "v2/usecases/enumerate-sessions.d.ts": {
2461
- "sha256": "46da8960bdeb154f79dee443425260f3ce18c50a0db01e7ab60700432d864857",
2462
- "bytes": 699
2461
+ "sha256": "230eda9e0f377741cb9018edba4b8c26cecde9439e2db68b249b0cab3e9e50aa",
2462
+ "bytes": 809
2463
2463
  },
2464
2464
  "v2/usecases/enumerate-sessions.js": {
2465
- "sha256": "4ae73f47c1e8ebea0d9a48dc786e5e4d0442a38827c427378712e9d198410ae2",
2466
- "bytes": 1050
2465
+ "sha256": "7fd54b65e914487ea0cfbc60e7059aa1134b3f9850ae2af0fdba340f9aa0b282",
2466
+ "bytes": 1091
2467
2467
  },
2468
2468
  "v2/usecases/execution-session-gate.d.ts": {
2469
2469
  "sha256": "339c4a8e02a77416e725e063a57d39a20788244498ae2c7a31dc48d111af6280",
@@ -14,7 +14,7 @@ function toMcpResult(result) {
14
14
  switch (result.type) {
15
15
  case 'success': {
16
16
  if (!jsonResponsesOverride) {
17
- const formatted = (0, v2_response_formatter_js_1.formatV2ExecutionResponse)(result.data);
17
+ const formatted = (0, v2_response_formatter_js_1.formatV2ExecutionResponse)(result.data) ?? (0, v2_response_formatter_js_1.formatV2ResumeResponse)(result.data);
18
18
  if (formatted !== null) {
19
19
  const content = [{ type: 'text', text: formatted.primary }];
20
20
  if (formatted.references != null) {
@@ -25,6 +25,8 @@ async function handleV2ResumeSession(input, ctx) {
25
25
  gitHeadSha: input.gitHeadSha ?? anchorValue(anchors, 'git_head_sha'),
26
26
  gitBranch: input.gitBranch ?? anchorValue(anchors, 'git_branch'),
27
27
  freeTextQuery: input.query,
28
+ runId: input.runId,
29
+ sessionId: input.sessionId,
28
30
  };
29
31
  const resumeResult = await (0, resume_session_js_1.resumeSession)(query, v2.sessionSummaryProvider);
30
32
  if (resumeResult.isErr()) {
@@ -75,6 +77,9 @@ function mintCandidateTokens(candidates, ports) {
75
77
  resumeToken: resumeTokenRes.value,
76
78
  snippet: candidate.snippet,
77
79
  whyMatched: [...candidate.whyMatched],
80
+ pendingStepId: candidate.pendingStepId,
81
+ isComplete: candidate.isComplete,
82
+ lastModifiedMs: candidate.lastModifiedMs,
78
83
  nextCall: {
79
84
  tool: 'continue_workflow',
80
85
  params: { continueToken: resumeTokenRes.value, intent: 'rehydrate' },
@@ -1162,7 +1162,10 @@ export declare const V2ResumeSessionOutputSchema: z.ZodObject<{
1162
1162
  workflowId: z.ZodString;
1163
1163
  resumeToken: z.ZodString;
1164
1164
  snippet: z.ZodString;
1165
- whyMatched: z.ZodArray<z.ZodEnum<["matched_head_sha", "matched_branch", "matched_notes", "matched_workflow_id", "recency_fallback"]>, "many">;
1165
+ pendingStepId: z.ZodNullable<z.ZodString>;
1166
+ isComplete: z.ZodBoolean;
1167
+ lastModifiedMs: z.ZodNullable<z.ZodNumber>;
1168
+ whyMatched: z.ZodArray<z.ZodEnum<["matched_exact_id", "matched_head_sha", "matched_branch", "matched_notes", "matched_notes_partial", "matched_workflow_id", "recency_fallback"]>, "many">;
1166
1169
  nextCall: z.ZodObject<{
1167
1170
  tool: z.ZodLiteral<"continue_workflow">;
1168
1171
  params: z.ZodObject<{
@@ -1190,8 +1193,9 @@ export declare const V2ResumeSessionOutputSchema: z.ZodObject<{
1190
1193
  }>;
1191
1194
  }, "strip", z.ZodTypeAny, {
1192
1195
  workflowId: string;
1193
- sessionId: string;
1194
1196
  runId: string;
1197
+ sessionId: string;
1198
+ isComplete: boolean;
1195
1199
  nextCall: {
1196
1200
  params: {
1197
1201
  continueToken: string;
@@ -1201,11 +1205,14 @@ export declare const V2ResumeSessionOutputSchema: z.ZodObject<{
1201
1205
  };
1202
1206
  resumeToken: string;
1203
1207
  snippet: string;
1204
- whyMatched: ("matched_head_sha" | "matched_branch" | "matched_notes" | "matched_workflow_id" | "recency_fallback")[];
1208
+ pendingStepId: string | null;
1209
+ lastModifiedMs: number | null;
1210
+ whyMatched: ("matched_exact_id" | "matched_head_sha" | "matched_branch" | "matched_notes" | "matched_notes_partial" | "matched_workflow_id" | "recency_fallback")[];
1205
1211
  }, {
1206
1212
  workflowId: string;
1207
- sessionId: string;
1208
1213
  runId: string;
1214
+ sessionId: string;
1215
+ isComplete: boolean;
1209
1216
  nextCall: {
1210
1217
  params: {
1211
1218
  continueToken: string;
@@ -1215,14 +1222,17 @@ export declare const V2ResumeSessionOutputSchema: z.ZodObject<{
1215
1222
  };
1216
1223
  resumeToken: string;
1217
1224
  snippet: string;
1218
- whyMatched: ("matched_head_sha" | "matched_branch" | "matched_notes" | "matched_workflow_id" | "recency_fallback")[];
1225
+ pendingStepId: string | null;
1226
+ lastModifiedMs: number | null;
1227
+ whyMatched: ("matched_exact_id" | "matched_head_sha" | "matched_branch" | "matched_notes" | "matched_notes_partial" | "matched_workflow_id" | "recency_fallback")[];
1219
1228
  }>, "many">;
1220
1229
  totalEligible: z.ZodNumber;
1221
1230
  }, "strip", z.ZodTypeAny, {
1222
1231
  candidates: {
1223
1232
  workflowId: string;
1224
- sessionId: string;
1225
1233
  runId: string;
1234
+ sessionId: string;
1235
+ isComplete: boolean;
1226
1236
  nextCall: {
1227
1237
  params: {
1228
1238
  continueToken: string;
@@ -1232,14 +1242,17 @@ export declare const V2ResumeSessionOutputSchema: z.ZodObject<{
1232
1242
  };
1233
1243
  resumeToken: string;
1234
1244
  snippet: string;
1235
- whyMatched: ("matched_head_sha" | "matched_branch" | "matched_notes" | "matched_workflow_id" | "recency_fallback")[];
1245
+ pendingStepId: string | null;
1246
+ lastModifiedMs: number | null;
1247
+ whyMatched: ("matched_exact_id" | "matched_head_sha" | "matched_branch" | "matched_notes" | "matched_notes_partial" | "matched_workflow_id" | "recency_fallback")[];
1236
1248
  }[];
1237
1249
  totalEligible: number;
1238
1250
  }, {
1239
1251
  candidates: {
1240
1252
  workflowId: string;
1241
- sessionId: string;
1242
1253
  runId: string;
1254
+ sessionId: string;
1255
+ isComplete: boolean;
1243
1256
  nextCall: {
1244
1257
  params: {
1245
1258
  continueToken: string;
@@ -1249,7 +1262,9 @@ export declare const V2ResumeSessionOutputSchema: z.ZodObject<{
1249
1262
  };
1250
1263
  resumeToken: string;
1251
1264
  snippet: string;
1252
- whyMatched: ("matched_head_sha" | "matched_branch" | "matched_notes" | "matched_workflow_id" | "recency_fallback")[];
1265
+ pendingStepId: string | null;
1266
+ lastModifiedMs: number | null;
1267
+ whyMatched: ("matched_exact_id" | "matched_head_sha" | "matched_branch" | "matched_notes" | "matched_notes_partial" | "matched_workflow_id" | "recency_fallback")[];
1253
1268
  }[];
1254
1269
  totalEligible: number;
1255
1270
  }>;
@@ -244,14 +244,21 @@ exports.V2ResumeSessionOutputSchema = zod_1.z.object({
244
244
  workflowId: zod_1.z.string().min(1),
245
245
  resumeToken: zod_1.z.string().regex(token_patterns_js_1.STATE_TOKEN_PATTERN, 'Invalid resumeToken format'),
246
246
  snippet: zod_1.z.string().max(1024),
247
+ pendingStepId: zod_1.z.string().nullable().describe('The current pending step ID (e.g. "phase-3-implement") if the workflow is in progress. ' +
248
+ 'Null if the workflow is complete or the step could not be determined.'),
249
+ isComplete: zod_1.z.boolean().describe('Whether the workflow run has completed. Completed sessions are deprioritized in ranking.'),
250
+ lastModifiedMs: zod_1.z.number().nullable().describe('Filesystem modification time (epoch ms) of the session. Null if unavailable.'),
247
251
  whyMatched: zod_1.z.array(zod_1.z.enum([
252
+ 'matched_exact_id',
248
253
  'matched_head_sha',
249
254
  'matched_branch',
250
255
  'matched_notes',
256
+ 'matched_notes_partial',
251
257
  'matched_workflow_id',
252
258
  'recency_fallback',
253
259
  ])).describe('Match signals explaining why this candidate was ranked. ' +
254
- 'matched_head_sha/branch/notes/workflow_id = strong signal. ' +
260
+ 'matched_exact_id/head_sha/branch/notes = strong signal. ' +
261
+ 'matched_notes_partial = moderate signal (some query words matched). ' +
255
262
  'recency_fallback = no strong signal; inspect the snippet before resuming.'),
256
263
  nextCall: exports.V2ResumeNextCallSchema,
257
264
  })).max(5),
@@ -129,6 +129,7 @@ async function createToolContext() {
129
129
  directoryListing,
130
130
  dataDir,
131
131
  sessionStore,
132
+ snapshotStore,
132
133
  }),
133
134
  };
134
135
  console.error('[FeatureFlags] v2 tools enabled');
@@ -147,17 +147,23 @@ export declare const V2ContinueWorkflowInput: z.ZodEffects<z.ZodEffects<z.ZodObj
147
147
  export type V2ContinueWorkflowInput = z.infer<typeof V2ContinueWorkflowInput>;
148
148
  export declare const V2ResumeSessionInput: z.ZodObject<{
149
149
  query: z.ZodOptional<z.ZodString>;
150
+ runId: z.ZodOptional<z.ZodString>;
151
+ sessionId: z.ZodOptional<z.ZodString>;
150
152
  gitBranch: z.ZodOptional<z.ZodString>;
151
153
  gitHeadSha: z.ZodOptional<z.ZodString>;
152
154
  workspacePath: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
153
155
  }, "strict", z.ZodTypeAny, {
154
156
  workspacePath?: string | undefined;
155
157
  query?: string | undefined;
158
+ runId?: string | undefined;
159
+ sessionId?: string | undefined;
156
160
  gitBranch?: string | undefined;
157
161
  gitHeadSha?: string | undefined;
158
162
  }, {
159
163
  workspacePath?: string | undefined;
160
164
  query?: string | undefined;
165
+ runId?: string | undefined;
166
+ sessionId?: string | undefined;
161
167
  gitBranch?: string | undefined;
162
168
  gitHeadSha?: string | undefined;
163
169
  }>;
@@ -85,7 +85,12 @@ exports.V2ContinueWorkflowInput = exports.V2ContinueWorkflowInputShape
85
85
  });
86
86
  exports.V2ResumeSessionInput = zod_1.z.object({
87
87
  query: zod_1.z.string().min(1).max(256).optional().describe('Free text search to find a relevant session. Matches against recap notes and workflow IDs. ' +
88
+ 'Tip: use the user\'s exact words (e.g. "mr ownership", "ACEI-1234"). ' +
88
89
  'Without query, only git-context matching runs — the semantic (notes) tier is skipped.'),
90
+ runId: zod_1.z.string().regex(/^run_[a-z0-9]+$/).optional().describe('Exact run ID to find (e.g. "run_tbi2ag7njfjgc2aitt4qg5eaiq"). ' +
91
+ 'When provided, the matching session is returned as the sole top-priority candidate.'),
92
+ sessionId: zod_1.z.string().regex(/^sess_[a-zA-Z0-9_]+$/).optional().describe('Exact session ID to find (e.g. "sess_s5o2ieem4mwypoqnn6ztzyyag4"). ' +
93
+ 'When provided, the matching session is returned as the sole top-priority candidate.'),
89
94
  gitBranch: zod_1.z.string().max(256).optional().describe('Git branch name to match against session observations. Overrides auto-detected branch.'),
90
95
  gitHeadSha: zod_1.z.string().regex(/^[0-9a-f]{40}$/).optional().describe('Git HEAD SHA to match against session observations. Overrides auto-detected HEAD.'),
91
96
  workspacePath: zod_1.z.string()
@@ -9,3 +9,4 @@ export interface FormattedResponse {
9
9
  readonly supplements?: readonly FormattedSupplement[];
10
10
  }
11
11
  export declare function formatV2ExecutionResponse(data: unknown): FormattedResponse | null;
12
+ export declare function formatV2ResumeResponse(data: unknown): FormattedResponse | null;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.formatV2ExecutionResponse = formatV2ExecutionResponse;
4
+ exports.formatV2ResumeResponse = formatV2ResumeResponse;
4
5
  const render_envelope_js_1 = require("./render-envelope.js");
5
6
  const response_supplements_js_1 = require("./response-supplements.js");
6
7
  function isV2ExecutionResponse(data) {
@@ -388,3 +389,150 @@ function formatV2Clean(data) {
388
389
  primary: formatCleanSuccess(data),
389
390
  };
390
391
  }
392
+ function isResumeSessionResponse(data) {
393
+ if (typeof data !== 'object' || data === null)
394
+ return false;
395
+ const d = data;
396
+ return (Array.isArray(d.candidates) &&
397
+ typeof d.totalEligible === 'number' &&
398
+ !('pending' in d) &&
399
+ !('nextIntent' in d));
400
+ }
401
+ const WHY_MATCHED_LABELS = {
402
+ matched_exact_id: 'Exact ID match',
403
+ matched_head_sha: 'Same git commit (HEAD SHA)',
404
+ matched_branch: 'Same git branch',
405
+ matched_notes: 'Query matched session notes',
406
+ matched_notes_partial: 'Query partially matched session notes',
407
+ matched_workflow_id: 'Query matched workflow type',
408
+ recency_fallback: 'No strong match signal (recent session)',
409
+ };
410
+ function formatRelativeTime(epochMs) {
411
+ const diffMs = Date.now() - epochMs;
412
+ if (diffMs < 0)
413
+ return 'just now';
414
+ const minutes = Math.floor(diffMs / 60000);
415
+ if (minutes < 1)
416
+ return 'just now';
417
+ if (minutes < 60)
418
+ return `${minutes} minute${minutes === 1 ? '' : 's'} ago`;
419
+ const hours = Math.floor(minutes / 60);
420
+ if (hours < 24)
421
+ return `${hours} hour${hours === 1 ? '' : 's'} ago`;
422
+ const days = Math.floor(hours / 24);
423
+ if (days < 7)
424
+ return `${days} day${days === 1 ? '' : 's'} ago`;
425
+ const weeks = Math.floor(days / 7);
426
+ if (weeks < 5)
427
+ return `${weeks} week${weeks === 1 ? '' : 's'} ago`;
428
+ const months = Math.floor(days / 30);
429
+ return `${months} month${months === 1 ? '' : 's'} ago`;
430
+ }
431
+ function formatResumeCandidate(c, index) {
432
+ const lines = [];
433
+ const matchLabel = c.whyMatched.map(w => WHY_MATCHED_LABELS[w] ?? w).join(', ');
434
+ const isWeak = c.whyMatched.every(w => w === 'recency_fallback');
435
+ const statusTag = c.isComplete ? ' (completed)' : '';
436
+ lines.push(`### Candidate ${index + 1}: \`${c.workflowId}\`${statusTag}${isWeak ? ' (weak match)' : ''}`);
437
+ lines.push(`- **Session**: \`${c.sessionId}\``);
438
+ lines.push(`- **Run**: \`${c.runId}\``);
439
+ lines.push(`- **Match reason**: ${matchLabel}`);
440
+ if (c.pendingStepId) {
441
+ lines.push(`- **Current step**: \`${c.pendingStepId}\``);
442
+ }
443
+ else if (c.isComplete) {
444
+ lines.push('- **Status**: Workflow completed');
445
+ }
446
+ if (c.lastModifiedMs != null) {
447
+ lines.push(`- **Last active**: ${formatRelativeTime(c.lastModifiedMs)}`);
448
+ }
449
+ if (c.snippet) {
450
+ const preview = c.snippet.length > 200 ? c.snippet.slice(0, 200) + '...' : c.snippet;
451
+ lines.push(`- **Preview**: ${preview.replace(/\n/g, ' ')}`);
452
+ }
453
+ else {
454
+ lines.push('- **Preview**: (no recap notes available)');
455
+ }
456
+ if (c.isComplete) {
457
+ lines.push('');
458
+ lines.push('> This workflow has already completed. Resuming it will show the final state.');
459
+ }
460
+ lines.push('');
461
+ lines.push('To resume, call `continue_workflow` with:');
462
+ lines.push('```json');
463
+ lines.push(JSON.stringify(c.nextCall.params, null, 2));
464
+ lines.push('```');
465
+ return lines.join('\n');
466
+ }
467
+ const SEARCH_PARAMS_HELP = [
468
+ '**To narrow results, call `resume_session` again with any of these parameters:**',
469
+ '- `query`: Free text keywords from the session (e.g. "mr ownership", "ACEI-1234", "login feature")',
470
+ '- `runId`: Exact run ID if the user has one (e.g. "run_abc123def456")',
471
+ '- `sessionId`: Exact session ID if the user has one (e.g. "sess_abc123")',
472
+ '- `workspacePath`: Absolute path to the workspace (helps match by git branch/commit)',
473
+ ].join('\n');
474
+ function formatV2ResumeResponse(data) {
475
+ if (!isResumeSessionResponse(data))
476
+ return null;
477
+ const { candidates, totalEligible } = data;
478
+ const lines = [];
479
+ if (candidates.length === 0) {
480
+ lines.push('## No Resumable Sessions Found');
481
+ lines.push('');
482
+ lines.push(`Searched ${totalEligible} session(s) but none matched your query or workspace context.`);
483
+ lines.push('');
484
+ lines.push('**What to do**: Ask the user for more details about which session they want to resume. They might know:');
485
+ lines.push('- A description of what they were working on (pass as `query`)');
486
+ lines.push('- A run ID or session ID (pass as `runId` or `sessionId`)');
487
+ lines.push('- Or start a fresh workflow with `start_workflow`.');
488
+ lines.push('');
489
+ lines.push(SEARCH_PARAMS_HELP);
490
+ return { primary: lines.join('\n') };
491
+ }
492
+ const hasStrongMatch = candidates.some(c => !c.whyMatched.every(w => w === 'recency_fallback'));
493
+ const allRecencyFallback = !hasStrongMatch;
494
+ if (allRecencyFallback) {
495
+ lines.push('## Recent Workflow Sessions');
496
+ lines.push('');
497
+ lines.push(`No specific search criteria matched, so here are the **${candidates.length} most recent** sessions (out of ${totalEligible} total).`);
498
+ lines.push('');
499
+ lines.push('**Action required**: Present these to the user and ask which one they want to resume. If none of these are right, ask the user to describe what they were working on so you can search more specifically.');
500
+ lines.push('');
501
+ for (let i = 0; i < candidates.length; i++) {
502
+ lines.push(formatResumeCandidate(candidates[i], i));
503
+ lines.push('');
504
+ }
505
+ if (totalEligible > candidates.length) {
506
+ lines.push('---');
507
+ lines.push(`${totalEligible - candidates.length} more session(s) not shown.`);
508
+ lines.push('');
509
+ }
510
+ lines.push(SEARCH_PARAMS_HELP);
511
+ }
512
+ else {
513
+ lines.push('## Resumable Workflow Sessions');
514
+ lines.push('');
515
+ lines.push(`Found **${totalEligible}** session(s) total. Showing the top ${candidates.length} ranked by match strength.`);
516
+ lines.push('');
517
+ const best = candidates[0];
518
+ const bestIsExact = best.whyMatched.includes('matched_exact_id');
519
+ if (bestIsExact) {
520
+ lines.push(`**Recommendation**: Candidate 1 is an exact ID match. Resume it directly.`);
521
+ }
522
+ else {
523
+ lines.push(`**Recommendation**: Candidate 1 has the strongest match signal. Present the top candidates to the user and let them confirm which one to resume.`);
524
+ }
525
+ lines.push('');
526
+ for (let i = 0; i < candidates.length; i++) {
527
+ lines.push(formatResumeCandidate(candidates[i], i));
528
+ lines.push('');
529
+ }
530
+ if (totalEligible > candidates.length) {
531
+ lines.push('---');
532
+ lines.push(`${totalEligible - candidates.length} more session(s) not shown.`);
533
+ lines.push('');
534
+ lines.push(SEARCH_PARAMS_HELP);
535
+ }
536
+ }
537
+ return { primary: lines.join('\n') };
538
+ }
@@ -165,42 +165,42 @@ exports.CHECKPOINT_WORKFLOW_PROTOCOL = {
165
165
  exports.RESUME_SESSION_PROTOCOL = {
166
166
  canonicalParams: {
167
167
  required: [],
168
- optional: ['query', 'gitBranch', 'gitHeadSha', 'workspacePath'],
168
+ optional: ['query', 'runId', 'sessionId', 'gitBranch', 'gitHeadSha', 'workspacePath'],
169
169
  },
170
170
  descriptions: {
171
171
  standard: {
172
- purpose: 'Find and reconnect to an existing WorkRail v2 workflow session.',
173
- whenToUse: 'Use this when you need to resume a previously started workflow but no longer have the latest continueToken in chat context.',
172
+ purpose: 'Find and reconnect to an existing WorkRail workflow session. WorkRail is a workflow engine that persists session state across chat conversations. When a user says "resume my workflow", this is the tool to call.',
173
+ whenToUse: 'Use this when the user wants to resume, continue, or reconnect to a previously started workflow. The user may provide a session ID, run ID, a description of what they were working on, or nothing at all. This tool searches stored sessions and returns the best matches.',
174
174
  rules: [
175
- 'Always pass query with the user\'s stated topic or intent (e.g. "resume the ACEI-1234 workflow"). Without query, only git-context matching runs and the right session may not surface.',
176
- 'Pass workspacePath when available so WorkRail can match sessions to the correct workspace and git context.',
177
- 'Pick the best candidate, then call continue_workflow using its nextCall template no manual parameter construction needed.',
178
- 'Do not call read_session to resume; use the nextCall from the chosen candidate.',
179
- 'If candidates is empty, no eligible session exists call start_workflow to begin a new session instead.',
180
- 'If all candidates have whyMatched: ["recency_fallback"], the match had no strong signal (git or notes). Verify the snippet before resuming.',
175
+ 'If the user provides a run ID (run_...) or session ID (sess_...), pass it as runId or sessionId for an exact match. This is the most reliable way to find a specific session.',
176
+ 'If the user describes what they were working on (e.g. "the mr ownership task"), pass their words as query. This searches session recap notes and workflow IDs for matching keywords.',
177
+ 'Always pass workspacePath (from your system parameters) so WorkRail can also match by git context (branch, commit).',
178
+ 'The response includes ranked candidates with match explanations and ready-to-use continuation templates. Present the top candidates to the user if there is ambiguity.',
179
+ 'To resume a candidate: call continue_workflow with the candidate\'s nextCall.params (continueToken and intent: "rehydrate"). The response will give you the full session context.',
180
+ 'If no candidates match, ask the user for more details or suggest starting a fresh workflow with start_workflow.',
181
181
  ],
182
182
  examplePayload: {
183
183
  workspacePath: '/Users/you/git/my-project',
184
184
  query: 'resume the coding task workflow for protocol drift',
185
185
  },
186
- returns: 'Up to 5 ranked candidates, each with whyMatched explaining the match signal and a nextCall template for continue_workflow. If candidates is empty, call start_workflow.',
186
+ returns: 'Up to 5 ranked candidates with match signals, session previews, and ready-to-use continuation templates. The response explains which candidate to pick and exactly how to resume it.',
187
187
  },
188
188
  authoritative: {
189
- purpose: 'Find an existing WorkRail v2 session and reconnect to it deterministically.',
190
- whenToUse: 'Call this when resuming a workflow without the latest in-chat token block.',
189
+ purpose: 'Find an existing WorkRail workflow session and reconnect to it. WorkRail persists workflow state across chat conversations. When a user says "resume my workflow", call this tool.',
190
+ whenToUse: 'Call this when resuming a workflow. The user may provide a run ID, session ID, a description, or nothing.',
191
191
  rules: [
192
- 'Always pass query with the user\'s stated topic or intent. Semantic (notes) matching only runs when query is provided.',
193
- 'Pass workspacePath set to the current workspace whenever possible.',
194
- 'Pick the best candidate and call continue_workflow with its nextCall — the resumeToken is already embedded in nextCall.params.continueToken.',
195
- 'Do not invent token values or call read_session to resume execution.',
196
- 'If candidates is empty, no eligible session exists call start_workflow instead.',
197
- 'whyMatched values: matched_head_sha / matched_branch / matched_notes = strong signal; recency_fallback = no signal, verify snippet before resuming.',
192
+ 'If the user provides a run ID (run_...) or session ID (sess_...), pass it as runId or sessionId for exact lookup.',
193
+ 'If the user describes their task, pass their words as query to search session notes.',
194
+ 'Always pass workspacePath from your system parameters for git-context matching.',
195
+ 'Present candidates to the user when there is ambiguity. The response explains match strength.',
196
+ 'To resume: call continue_workflow with the chosen candidate\'s nextCall.params (continueToken + intent: "rehydrate").',
197
+ 'If no candidates match, ask for more details or start a fresh workflow.',
198
198
  ],
199
199
  examplePayload: {
200
200
  workspacePath: '/Users/you/git/my-project',
201
201
  query: 'resume the coding task workflow for protocol drift',
202
202
  },
203
- returns: 'Up to 5 ranked candidates, each with whyMatched confidence signals and a pre-built nextCall. Empty candidates means no session found — call start_workflow.',
203
+ returns: 'Up to 5 ranked candidates with match signals, previews, and ready-to-use continuation templates.',
204
204
  },
205
205
  },
206
206
  };