@exaudeus/workrail 3.7.1 → 3.7.3

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.
@@ -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,170 @@ 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_notes: 'Query matched session notes',
404
+ matched_notes_partial: 'Query partially matched session notes',
405
+ matched_workflow_id: 'Query matched workflow type',
406
+ matched_head_sha: 'Same git commit (HEAD SHA)',
407
+ matched_branch: 'Same git branch',
408
+ matched_repo_root: 'Same workspace/repository',
409
+ recency_fallback: 'No strong match signal (recent session)',
410
+ };
411
+ function formatRelativeTime(epochMs) {
412
+ const diffMs = Date.now() - epochMs;
413
+ if (diffMs < 0)
414
+ return 'just now';
415
+ const minutes = Math.floor(diffMs / 60000);
416
+ if (minutes < 1)
417
+ return 'just now';
418
+ if (minutes < 60)
419
+ return `${minutes} minute${minutes === 1 ? '' : 's'} ago`;
420
+ const hours = Math.floor(minutes / 60);
421
+ if (hours < 24)
422
+ return `${hours} hour${hours === 1 ? '' : 's'} ago`;
423
+ const days = Math.floor(hours / 24);
424
+ if (days < 7)
425
+ return `${days} day${days === 1 ? '' : 's'} ago`;
426
+ const weeks = Math.floor(days / 7);
427
+ if (weeks < 5)
428
+ return `${weeks} week${weeks === 1 ? '' : 's'} ago`;
429
+ const months = Math.floor(days / 30);
430
+ return `${months} month${months === 1 ? '' : 's'} ago`;
431
+ }
432
+ function formatResumeCandidate(c, index) {
433
+ const lines = [];
434
+ const matchLabel = c.whyMatched.map(w => WHY_MATCHED_LABELS[w] ?? w).join(', ');
435
+ const isWeak = c.whyMatched.every(w => w === 'recency_fallback');
436
+ const statusTag = c.isComplete ? ' (completed)' : '';
437
+ const heading = c.sessionTitle?.trim() || c.workflowId;
438
+ lines.push(`### Candidate ${index + 1}: \`${heading}\`${statusTag}${isWeak ? ' (weak match)' : ''}`);
439
+ lines.push(`- **Session**: \`${c.sessionId}\``);
440
+ lines.push(`- **Run**: \`${c.runId}\``);
441
+ lines.push(`- **Workflow**: \`${c.workflowId}\``);
442
+ lines.push(`- **Match reason**: ${matchLabel}`);
443
+ if (c.confidence) {
444
+ lines.push(`- **Confidence**: ${c.confidence}`);
445
+ }
446
+ if (c.matchExplanation) {
447
+ lines.push(`- **Why this ranked here**: ${c.matchExplanation}`);
448
+ }
449
+ if (c.gitBranch) {
450
+ lines.push(`- **Branch**: \`${c.gitBranch}\``);
451
+ }
452
+ if (c.pendingStepId) {
453
+ lines.push(`- **Current step**: \`${c.pendingStepId}\``);
454
+ }
455
+ else if (c.isComplete) {
456
+ lines.push('- **Status**: Workflow completed');
457
+ }
458
+ if (c.lastModifiedMs != null) {
459
+ lines.push(`- **Last active**: ${formatRelativeTime(c.lastModifiedMs)}`);
460
+ }
461
+ if (c.snippet) {
462
+ const preview = c.snippet.length > 200 ? c.snippet.slice(0, 200) + '...' : c.snippet;
463
+ lines.push(`- **Preview**: ${preview.replace(/\n/g, ' ')}`);
464
+ }
465
+ else {
466
+ lines.push('- **Preview**: (no recap notes available)');
467
+ }
468
+ if (c.isComplete) {
469
+ lines.push('');
470
+ lines.push('> This workflow has already completed. Resuming it will show the final state.');
471
+ }
472
+ lines.push('');
473
+ lines.push('To inspect or resume this candidate, call `continue_workflow` with:');
474
+ lines.push('```json');
475
+ lines.push(JSON.stringify(c.nextCall.params, null, 2));
476
+ lines.push('```');
477
+ lines.push('This `rehydrate` call restores the exact workflow state and shows the current step/context.');
478
+ return lines.join('\n');
479
+ }
480
+ const SEARCH_PARAMS_HELP = [
481
+ '**To narrow results, call `resume_session` again with any of these parameters:**',
482
+ '- `query`: Free text keywords from the session (e.g. "mr ownership", "ACEI-1234", "login feature")',
483
+ '- `runId`: Exact run ID if the user has one (e.g. "run_abc123def456")',
484
+ '- `sessionId`: Exact session ID if the user has one (e.g. "sess_abc123")',
485
+ '- `workspacePath`: Absolute path to the workspace (helps match by git branch/commit)',
486
+ '- `sameWorkspaceOnly`: Restrict results to the current repo/workspace when that is clearly what the user means',
487
+ ].join('\n');
488
+ function formatV2ResumeResponse(data) {
489
+ if (!isResumeSessionResponse(data))
490
+ return null;
491
+ const { candidates, totalEligible } = data;
492
+ const lines = [];
493
+ if (candidates.length === 0) {
494
+ lines.push('## No Resumable Sessions Found');
495
+ lines.push('');
496
+ lines.push(`Searched ${totalEligible} session(s) but none matched your query or workspace context.`);
497
+ lines.push('');
498
+ lines.push('**What to do**: Ask the user for more details about which session they want to resume. They might know:');
499
+ lines.push('- A description of what they were working on (pass as `query`)');
500
+ lines.push('- A run ID or session ID (pass as `runId` or `sessionId`)');
501
+ lines.push('- Or start a fresh workflow with `start_workflow`.');
502
+ lines.push('');
503
+ lines.push(SEARCH_PARAMS_HELP);
504
+ return { primary: lines.join('\n') };
505
+ }
506
+ const hasStrongMatch = candidates.some(c => !c.whyMatched.every(w => w === 'recency_fallback'));
507
+ const allRecencyFallback = !hasStrongMatch;
508
+ if (allRecencyFallback) {
509
+ lines.push('## Recent Workflow Sessions');
510
+ lines.push('');
511
+ lines.push(`No specific search criteria matched, so here are the **${candidates.length} most recent** sessions (out of ${totalEligible} total).`);
512
+ lines.push('');
513
+ 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.');
514
+ lines.push('');
515
+ for (let i = 0; i < candidates.length; i++) {
516
+ lines.push(formatResumeCandidate(candidates[i], i));
517
+ lines.push('');
518
+ }
519
+ if (totalEligible > candidates.length) {
520
+ lines.push('---');
521
+ lines.push(`${totalEligible - candidates.length} more session(s) not shown.`);
522
+ lines.push('');
523
+ }
524
+ lines.push(SEARCH_PARAMS_HELP);
525
+ }
526
+ else {
527
+ lines.push('## Resumable Workflow Sessions');
528
+ lines.push('');
529
+ lines.push(`Found **${totalEligible}** session(s) total. Showing the top ${candidates.length} ranked by match strength.`);
530
+ lines.push('');
531
+ const allWorkspaceDriven = candidates.every((c) => c.whyMatched.every((w) => w === 'matched_head_sha' || w === 'matched_branch'));
532
+ if (allWorkspaceDriven) {
533
+ lines.push('**Note**: These candidates are ranked primarily from current workspace git context (branch/commit), not from a strong text match on your query.');
534
+ lines.push('If the previews do not clearly match the user\'s request, inspect a candidate with `continue_workflow(..., intent: "rehydrate")` or ask for a more specific phrase / session ID.');
535
+ lines.push('');
536
+ }
537
+ const best = candidates[0];
538
+ const bestIsExact = best.whyMatched.includes('matched_exact_id');
539
+ if (bestIsExact) {
540
+ lines.push(`**Recommendation**: Candidate 1 is an exact ID match. Resume it directly.`);
541
+ }
542
+ else {
543
+ 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.`);
544
+ }
545
+ lines.push('');
546
+ for (let i = 0; i < candidates.length; i++) {
547
+ lines.push(formatResumeCandidate(candidates[i], i));
548
+ lines.push('');
549
+ }
550
+ if (totalEligible > candidates.length) {
551
+ lines.push('---');
552
+ lines.push(`${totalEligible - candidates.length} more session(s) not shown.`);
553
+ lines.push('');
554
+ lines.push(SEARCH_PARAMS_HELP);
555
+ }
556
+ }
557
+ return { primary: lines.join('\n') };
558
+ }
@@ -46,8 +46,8 @@ function findAliasFieldConflicts(value, aliasMap) {
46
46
  }
47
47
  exports.START_WORKFLOW_PROTOCOL = {
48
48
  canonicalParams: {
49
- required: ['workflowId'],
50
- optional: ['workspacePath'],
49
+ required: ['workflowId', 'workspacePath'],
50
+ optional: [],
51
51
  },
52
52
  descriptions: {
53
53
  standard: {
@@ -56,6 +56,7 @@ exports.START_WORKFLOW_PROTOCOL = {
56
56
  rules: [
57
57
  'Follow the returned step exactly; treat it as the user\'s current instruction.',
58
58
  'When the step is done, call continue_workflow with the returned continueToken.',
59
+ 'Always pass workspacePath. Shared MCP servers cannot safely infer which repo/workspace you mean.',
59
60
  'Only pass context on later continue_workflow calls if facts changed.',
60
61
  ],
61
62
  examplePayload: {
@@ -70,7 +71,7 @@ exports.START_WORKFLOW_PROTOCOL = {
70
71
  rules: [
71
72
  'Execute the returned step exactly as written.',
72
73
  'When the step is complete, call continue_workflow with the returned continueToken.',
73
- 'Pass workspacePath when available so WorkRail anchors the session to the correct workspace.',
74
+ 'Pass workspacePath on every call. Shared MCP servers cannot safely infer the correct workspace.',
74
75
  ],
75
76
  examplePayload: {
76
77
  workflowId: 'coding-task-workflow-agentic',
@@ -83,7 +84,7 @@ exports.START_WORKFLOW_PROTOCOL = {
83
84
  exports.CONTINUE_WORKFLOW_PROTOCOL = {
84
85
  canonicalParams: {
85
86
  required: ['continueToken'],
86
- optional: ['intent', 'context', 'output'],
87
+ optional: ['intent', 'context', 'output', 'workspacePath'],
87
88
  },
88
89
  aliasMap: {
89
90
  contextVariables: 'context',
@@ -95,6 +96,7 @@ exports.CONTINUE_WORKFLOW_PROTOCOL = {
95
96
  rules: [
96
97
  'Advance by sending output (and intent: "advance" if you want to be explicit).',
97
98
  'Rehydrate by omitting output (and intent: "rehydrate" if you want to be explicit).',
99
+ 'When rehydrating, always pass workspacePath so WorkRail can restore the correct repo/workspace context on shared servers.',
98
100
  'Put changed facts under context only.',
99
101
  'Round-trip continueToken exactly as returned by WorkRail; use the single-token API only.',
100
102
  'Notes (output.notesMarkdown): write for a human reader. Include what you did and key decisions, what you produced (files, tests, numbers), and anything notable (risks, open questions, deliberate omissions). Use markdown headings, bullets, bold, code refs. Be specific. Scope: THIS step only (WorkRail concatenates automatically). 10-30 lines ideal. Omitting notes blocks the step.',
@@ -114,6 +116,7 @@ exports.CONTINUE_WORKFLOW_PROTOCOL = {
114
116
  'Use continueToken exactly as returned by WorkRail.',
115
117
  'Use the single-token API only.',
116
118
  'Advance by sending output; rehydrate by omitting output.',
119
+ 'For rehydrate calls, pass workspacePath so WorkRail can restore the correct workspace context.',
117
120
  'Put updated facts in context only.',
118
121
  'Notes (output.notesMarkdown): write for a human reader. Include what you did and key decisions, what you produced (files, tests, numbers), and anything notable (risks, open questions, deliberate omissions). Use markdown headings, bullets, bold, code refs. Be specific. Scope: THIS step only (WorkRail concatenates automatically). 10-30 lines ideal. Omitting notes blocks the step.',
119
122
  ],
@@ -164,43 +167,43 @@ exports.CHECKPOINT_WORKFLOW_PROTOCOL = {
164
167
  };
165
168
  exports.RESUME_SESSION_PROTOCOL = {
166
169
  canonicalParams: {
167
- required: [],
168
- optional: ['query', 'gitBranch', 'gitHeadSha', 'workspacePath'],
170
+ required: ['workspacePath'],
171
+ optional: ['query', 'runId', 'sessionId', 'gitBranch', 'gitHeadSha'],
169
172
  },
170
173
  descriptions: {
171
174
  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.',
175
+ 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.',
176
+ 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
177
  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.',
178
+ '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.',
179
+ '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.',
180
+ 'Always pass workspacePath (from your system parameters). Shared MCP servers cannot safely infer the current workspace, and resume quality depends on the current repo identity.',
181
+ '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.',
182
+ 'To inspect or resume a candidate: call continue_workflow with the candidate\'s nextCall.params (continueToken and intent: "rehydrate"). This is the correct inspection path for v2 sessions and restores the full session context.',
183
+ 'If no candidates match, ask the user for more details or suggest starting a fresh workflow with start_workflow.',
181
184
  ],
182
185
  examplePayload: {
183
186
  workspacePath: '/Users/you/git/my-project',
184
187
  query: 'resume the coding task workflow for protocol drift',
185
188
  },
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.',
189
+ 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
190
  },
188
191
  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.',
192
+ 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.',
193
+ whenToUse: 'Call this when resuming a workflow. The user may provide a run ID, session ID, a description, or nothing.',
191
194
  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.',
195
+ 'If the user provides a run ID (run_...) or session ID (sess_...), pass it as runId or sessionId for exact lookup.',
196
+ 'If the user describes their task, pass their words as query to search session notes.',
197
+ 'Always pass workspacePath from your system parameters. Shared MCP servers cannot safely infer the current workspace.',
198
+ 'Present candidates to the user when there is ambiguity. The response explains match strength.',
199
+ 'To inspect or resume: call continue_workflow with the chosen candidate\'s nextCall.params (continueToken + intent: "rehydrate"). Do NOT use legacy session tools to inspect a v2 workflow resume candidate.',
200
+ 'If no candidates match, ask for more details or start a fresh workflow.',
198
201
  ],
199
202
  examplePayload: {
200
203
  workspacePath: '/Users/you/git/my-project',
201
204
  query: 'resume the coding task workflow for protocol drift',
202
205
  },
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.',
206
+ returns: 'Up to 5 ranked candidates with match signals, previews, and ready-to-use continuation templates.',
204
207
  },
205
208
  },
206
209
  };
@@ -2183,8 +2183,8 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
2183
2183
  bytes: z.ZodNumber;
2184
2184
  }, "strip", z.ZodTypeAny, {
2185
2185
  kind: "segment_closed";
2186
- sha256: string;
2187
2186
  sessionId: string;
2187
+ sha256: string;
2188
2188
  v: 1;
2189
2189
  manifestIndex: number;
2190
2190
  firstEventIndex: number;
@@ -2193,8 +2193,8 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
2193
2193
  bytes: number;
2194
2194
  }, {
2195
2195
  kind: "segment_closed";
2196
- sha256: string;
2197
2196
  sessionId: string;
2197
+ sha256: string;
2198
2198
  v: 1;
2199
2199
  manifestIndex: number;
2200
2200
  firstEventIndex: number;
@@ -4601,8 +4601,8 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
4601
4601
  })[];
4602
4602
  manifest: ({
4603
4603
  kind: "segment_closed";
4604
- sha256: string;
4605
4604
  sessionId: string;
4605
+ sha256: string;
4606
4606
  v: 1;
4607
4607
  manifestIndex: number;
4608
4608
  firstEventIndex: number;
@@ -5113,8 +5113,8 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
5113
5113
  })[];
5114
5114
  manifest: ({
5115
5115
  kind: "segment_closed";
5116
- sha256: string;
5117
5116
  sessionId: string;
5117
+ sha256: string;
5118
5118
  v: 1;
5119
5119
  manifestIndex: number;
5120
5120
  firstEventIndex: number;
@@ -7305,8 +7305,8 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
7305
7305
  bytes: z.ZodNumber;
7306
7306
  }, "strip", z.ZodTypeAny, {
7307
7307
  kind: "segment_closed";
7308
- sha256: string;
7309
7308
  sessionId: string;
7309
+ sha256: string;
7310
7310
  v: 1;
7311
7311
  manifestIndex: number;
7312
7312
  firstEventIndex: number;
@@ -7315,8 +7315,8 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
7315
7315
  bytes: number;
7316
7316
  }, {
7317
7317
  kind: "segment_closed";
7318
- sha256: string;
7319
7318
  sessionId: string;
7319
+ sha256: string;
7320
7320
  v: 1;
7321
7321
  manifestIndex: number;
7322
7322
  firstEventIndex: number;
@@ -9723,8 +9723,8 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
9723
9723
  })[];
9724
9724
  manifest: ({
9725
9725
  kind: "segment_closed";
9726
- sha256: string;
9727
9726
  sessionId: string;
9727
+ sha256: string;
9728
9728
  v: 1;
9729
9729
  manifestIndex: number;
9730
9730
  firstEventIndex: number;
@@ -10235,8 +10235,8 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
10235
10235
  })[];
10236
10236
  manifest: ({
10237
10237
  kind: "segment_closed";
10238
- sha256: string;
10239
10238
  sessionId: string;
10239
+ sha256: string;
10240
10240
  v: 1;
10241
10241
  manifestIndex: number;
10242
10242
  firstEventIndex: number;
@@ -10776,8 +10776,8 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
10776
10776
  })[];
10777
10777
  manifest: ({
10778
10778
  kind: "segment_closed";
10779
- sha256: string;
10780
10779
  sessionId: string;
10780
+ sha256: string;
10781
10781
  v: 1;
10782
10782
  manifestIndex: number;
10783
10783
  firstEventIndex: number;
@@ -11305,8 +11305,8 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
11305
11305
  })[];
11306
11306
  manifest: ({
11307
11307
  kind: "segment_closed";
11308
- sha256: string;
11309
11308
  sessionId: string;
11309
+ sha256: string;
11310
11310
  v: 1;
11311
11311
  manifestIndex: number;
11312
11312
  firstEventIndex: number;
@@ -11,8 +11,8 @@ export declare const ManifestRecordV1Schema: z.ZodDiscriminatedUnion<"kind", [z.
11
11
  bytes: z.ZodNumber;
12
12
  }, "strip", z.ZodTypeAny, {
13
13
  kind: "segment_closed";
14
- sha256: string;
15
14
  sessionId: string;
15
+ sha256: string;
16
16
  v: 1;
17
17
  manifestIndex: number;
18
18
  firstEventIndex: number;
@@ -21,8 +21,8 @@ export declare const ManifestRecordV1Schema: z.ZodDiscriminatedUnion<"kind", [z.
21
21
  bytes: number;
22
22
  }, {
23
23
  kind: "segment_closed";
24
- sha256: string;
25
24
  sessionId: string;
25
+ sha256: string;
26
26
  v: 1;
27
27
  manifestIndex: number;
28
28
  firstEventIndex: number;
@@ -14,13 +14,13 @@ export declare const StateTokenPayloadV1Schema: z.ZodObject<{
14
14
  nodeId: z.ZodEffects<z.ZodString, NodeId, string>;
15
15
  workflowHashRef: z.ZodEffects<z.ZodString, WorkflowHashRef, string>;
16
16
  }, "strip", z.ZodTypeAny, {
17
+ runId: string & {
18
+ readonly __brand: "v2.RunId";
19
+ };
17
20
  sessionId: string & {
18
21
  readonly __brand: "v2.SessionId";
19
22
  };
20
23
  tokenKind: "state";
21
- runId: string & {
22
- readonly __brand: "v2.RunId";
23
- };
24
24
  nodeId: string & {
25
25
  readonly __brand: "v2.NodeId";
26
26
  };
@@ -29,9 +29,9 @@ export declare const StateTokenPayloadV1Schema: z.ZodObject<{
29
29
  };
30
30
  tokenVersion: 1;
31
31
  }, {
32
+ runId: string;
32
33
  sessionId: string;
33
34
  tokenKind: "state";
34
- runId: string;
35
35
  nodeId: string;
36
36
  workflowHashRef: string;
37
37
  tokenVersion: 1;
@@ -52,13 +52,13 @@ export declare const AckTokenPayloadV1Schema: z.ZodObject<{
52
52
  nodeId: z.ZodEffects<z.ZodString, NodeId, string>;
53
53
  attemptId: z.ZodEffects<z.ZodString, AttemptId, string>;
54
54
  }, "strip", z.ZodTypeAny, {
55
+ runId: string & {
56
+ readonly __brand: "v2.RunId";
57
+ };
55
58
  sessionId: string & {
56
59
  readonly __brand: "v2.SessionId";
57
60
  };
58
61
  tokenKind: "ack";
59
- runId: string & {
60
- readonly __brand: "v2.RunId";
61
- };
62
62
  nodeId: string & {
63
63
  readonly __brand: "v2.NodeId";
64
64
  };
@@ -67,9 +67,9 @@ export declare const AckTokenPayloadV1Schema: z.ZodObject<{
67
67
  };
68
68
  tokenVersion: 1;
69
69
  }, {
70
+ runId: string;
70
71
  sessionId: string;
71
72
  tokenKind: "ack";
72
- runId: string;
73
73
  nodeId: string;
74
74
  attemptId: string;
75
75
  tokenVersion: 1;
@@ -90,13 +90,13 @@ export declare const CheckpointTokenPayloadV1Schema: z.ZodObject<{
90
90
  nodeId: z.ZodEffects<z.ZodString, NodeId, string>;
91
91
  attemptId: z.ZodEffects<z.ZodString, AttemptId, string>;
92
92
  }, "strip", z.ZodTypeAny, {
93
+ runId: string & {
94
+ readonly __brand: "v2.RunId";
95
+ };
93
96
  sessionId: string & {
94
97
  readonly __brand: "v2.SessionId";
95
98
  };
96
99
  tokenKind: "checkpoint";
97
- runId: string & {
98
- readonly __brand: "v2.RunId";
99
- };
100
100
  nodeId: string & {
101
101
  readonly __brand: "v2.NodeId";
102
102
  };
@@ -105,9 +105,9 @@ export declare const CheckpointTokenPayloadV1Schema: z.ZodObject<{
105
105
  };
106
106
  tokenVersion: 1;
107
107
  }, {
108
+ runId: string;
108
109
  sessionId: string;
109
110
  tokenKind: "checkpoint";
110
- runId: string;
111
111
  nodeId: string;
112
112
  attemptId: string;
113
113
  tokenVersion: 1;
@@ -128,13 +128,13 @@ export declare const TokenPayloadV1Schema: z.ZodDiscriminatedUnion<"tokenKind",
128
128
  nodeId: z.ZodEffects<z.ZodString, NodeId, string>;
129
129
  workflowHashRef: z.ZodEffects<z.ZodString, WorkflowHashRef, string>;
130
130
  }, "strip", z.ZodTypeAny, {
131
+ runId: string & {
132
+ readonly __brand: "v2.RunId";
133
+ };
131
134
  sessionId: string & {
132
135
  readonly __brand: "v2.SessionId";
133
136
  };
134
137
  tokenKind: "state";
135
- runId: string & {
136
- readonly __brand: "v2.RunId";
137
- };
138
138
  nodeId: string & {
139
139
  readonly __brand: "v2.NodeId";
140
140
  };
@@ -143,9 +143,9 @@ export declare const TokenPayloadV1Schema: z.ZodDiscriminatedUnion<"tokenKind",
143
143
  };
144
144
  tokenVersion: 1;
145
145
  }, {
146
+ runId: string;
146
147
  sessionId: string;
147
148
  tokenKind: "state";
148
- runId: string;
149
149
  nodeId: string;
150
150
  workflowHashRef: string;
151
151
  tokenVersion: 1;
@@ -157,13 +157,13 @@ export declare const TokenPayloadV1Schema: z.ZodDiscriminatedUnion<"tokenKind",
157
157
  nodeId: z.ZodEffects<z.ZodString, NodeId, string>;
158
158
  attemptId: z.ZodEffects<z.ZodString, AttemptId, string>;
159
159
  }, "strip", z.ZodTypeAny, {
160
+ runId: string & {
161
+ readonly __brand: "v2.RunId";
162
+ };
160
163
  sessionId: string & {
161
164
  readonly __brand: "v2.SessionId";
162
165
  };
163
166
  tokenKind: "ack";
164
- runId: string & {
165
- readonly __brand: "v2.RunId";
166
- };
167
167
  nodeId: string & {
168
168
  readonly __brand: "v2.NodeId";
169
169
  };
@@ -172,9 +172,9 @@ export declare const TokenPayloadV1Schema: z.ZodDiscriminatedUnion<"tokenKind",
172
172
  };
173
173
  tokenVersion: 1;
174
174
  }, {
175
+ runId: string;
175
176
  sessionId: string;
176
177
  tokenKind: "ack";
177
- runId: string;
178
178
  nodeId: string;
179
179
  attemptId: string;
180
180
  tokenVersion: 1;
@@ -186,13 +186,13 @@ export declare const TokenPayloadV1Schema: z.ZodDiscriminatedUnion<"tokenKind",
186
186
  nodeId: z.ZodEffects<z.ZodString, NodeId, string>;
187
187
  attemptId: z.ZodEffects<z.ZodString, AttemptId, string>;
188
188
  }, "strip", z.ZodTypeAny, {
189
+ runId: string & {
190
+ readonly __brand: "v2.RunId";
191
+ };
189
192
  sessionId: string & {
190
193
  readonly __brand: "v2.SessionId";
191
194
  };
192
195
  tokenKind: "checkpoint";
193
- runId: string & {
194
- readonly __brand: "v2.RunId";
195
- };
196
196
  nodeId: string & {
197
197
  readonly __brand: "v2.NodeId";
198
198
  };
@@ -201,9 +201,9 @@ export declare const TokenPayloadV1Schema: z.ZodDiscriminatedUnion<"tokenKind",
201
201
  };
202
202
  tokenVersion: 1;
203
203
  }, {
204
+ runId: string;
204
205
  sessionId: string;
205
206
  tokenKind: "checkpoint";
206
- runId: string;
207
207
  nodeId: string;
208
208
  attemptId: string;
209
209
  tokenVersion: 1;
@@ -2,12 +2,14 @@ import type { ResultAsync } from 'neverthrow';
2
2
  import type { DirectoryListingPortV2 } from '../../../ports/directory-listing.port.js';
3
3
  import type { DataDirPortV2 } from '../../../ports/data-dir.port.js';
4
4
  import type { SessionEventLogReadonlyStorePortV2 } from '../../../ports/session-event-log-store.port.js';
5
+ import type { SnapshotStorePortV2 } from '../../../ports/snapshot-store.port.js';
5
6
  import type { SessionSummaryProviderPortV2, SessionSummaryError } from '../../../ports/session-summary-provider.port.js';
6
7
  import type { HealthySessionSummary } from '../../../projections/resume-ranking.js';
7
8
  export interface LocalSessionSummaryProviderPorts {
8
9
  readonly directoryListing: DirectoryListingPortV2;
9
10
  readonly dataDir: DataDirPortV2;
10
11
  readonly sessionStore: SessionEventLogReadonlyStorePortV2;
12
+ readonly snapshotStore?: SnapshotStorePortV2;
11
13
  }
12
14
  export declare class LocalSessionSummaryProviderV2 implements SessionSummaryProviderPortV2 {
13
15
  private readonly ports;