@exaudeus/workrail 3.75.0 → 3.77.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 (95) hide show
  1. package/dist/console-ui/assets/index-D9pYbwS0.js +28 -0
  2. package/dist/console-ui/index.html +1 -1
  3. package/dist/coordinators/adaptive-pipeline.d.ts +8 -0
  4. package/dist/coordinators/context-assembly.d.ts +4 -0
  5. package/dist/coordinators/context-assembly.js +156 -0
  6. package/dist/coordinators/modes/full-pipeline.d.ts +1 -1
  7. package/dist/coordinators/modes/full-pipeline.js +140 -27
  8. package/dist/coordinators/modes/implement-shared.d.ts +3 -2
  9. package/dist/coordinators/modes/implement-shared.js +16 -6
  10. package/dist/coordinators/modes/implement.js +49 -3
  11. package/dist/coordinators/pipeline-run-context.d.ts +1811 -0
  12. package/dist/coordinators/pipeline-run-context.js +114 -0
  13. package/dist/daemon/context-loader.d.ts +1 -1
  14. package/dist/daemon/core/agent-client.d.ts +7 -0
  15. package/dist/daemon/core/agent-client.js +31 -0
  16. package/dist/daemon/core/index.d.ts +6 -0
  17. package/dist/daemon/core/index.js +19 -0
  18. package/dist/daemon/core/session-context.d.ts +14 -0
  19. package/dist/daemon/core/session-context.js +24 -0
  20. package/dist/daemon/core/session-result.d.ts +10 -0
  21. package/dist/daemon/core/session-result.js +92 -0
  22. package/dist/daemon/core/system-prompt.d.ts +6 -0
  23. package/dist/daemon/core/system-prompt.js +151 -0
  24. package/dist/daemon/io/conversation-log.d.ts +2 -0
  25. package/dist/daemon/io/conversation-log.js +45 -0
  26. package/dist/daemon/io/execution-stats.d.ts +7 -0
  27. package/dist/daemon/io/execution-stats.js +86 -0
  28. package/dist/daemon/io/index.d.ts +5 -0
  29. package/dist/daemon/io/index.js +24 -0
  30. package/dist/daemon/io/session-notes-loader.d.ts +4 -0
  31. package/dist/daemon/io/session-notes-loader.js +45 -0
  32. package/dist/daemon/io/soul-loader.d.ts +3 -0
  33. package/dist/daemon/io/soul-loader.js +68 -0
  34. package/dist/daemon/io/workspace-context-loader.d.ts +17 -0
  35. package/dist/daemon/io/workspace-context-loader.js +137 -0
  36. package/dist/daemon/runner/agent-loop-runner.d.ts +28 -0
  37. package/dist/daemon/runner/agent-loop-runner.js +250 -0
  38. package/dist/daemon/runner/construct-tools.d.ts +5 -0
  39. package/dist/daemon/runner/construct-tools.js +30 -0
  40. package/dist/daemon/runner/finalize-session.d.ts +3 -0
  41. package/dist/daemon/runner/finalize-session.js +75 -0
  42. package/dist/daemon/runner/index.d.ts +8 -0
  43. package/dist/daemon/runner/index.js +18 -0
  44. package/dist/daemon/runner/pre-agent-session.d.ts +7 -0
  45. package/dist/daemon/runner/pre-agent-session.js +227 -0
  46. package/dist/daemon/runner/runner-types.d.ts +73 -0
  47. package/dist/daemon/runner/runner-types.js +39 -0
  48. package/dist/daemon/runner/tool-schemas.d.ts +1 -0
  49. package/dist/daemon/runner/tool-schemas.js +151 -0
  50. package/dist/daemon/session-scope.d.ts +1 -1
  51. package/dist/daemon/startup-recovery.d.ts +20 -0
  52. package/dist/daemon/startup-recovery.js +323 -0
  53. package/dist/daemon/state/index.d.ts +6 -0
  54. package/dist/daemon/state/index.js +14 -0
  55. package/dist/daemon/state/session-state.d.ts +23 -0
  56. package/dist/daemon/state/session-state.js +44 -0
  57. package/dist/daemon/state/stuck-detection.d.ts +22 -0
  58. package/dist/daemon/state/stuck-detection.js +25 -0
  59. package/dist/daemon/state/terminal-signal.d.ts +9 -0
  60. package/dist/daemon/state/terminal-signal.js +10 -0
  61. package/dist/daemon/tools/file-tools.d.ts +1 -1
  62. package/dist/daemon/turn-end/detect-stuck.d.ts +2 -2
  63. package/dist/daemon/turn-end/detect-stuck.js +2 -2
  64. package/dist/daemon/turn-end/step-injector.d.ts +1 -1
  65. package/dist/daemon/types.d.ts +105 -0
  66. package/dist/daemon/types.js +11 -0
  67. package/dist/daemon/workflow-enricher.d.ts +16 -0
  68. package/dist/daemon/workflow-enricher.js +58 -0
  69. package/dist/daemon/workflow-runner.d.ts +13 -277
  70. package/dist/daemon/workflow-runner.js +63 -1421
  71. package/dist/manifest.json +280 -56
  72. package/dist/trigger/coordinator-deps.d.ts +1 -1
  73. package/dist/trigger/coordinator-deps.js +131 -0
  74. package/dist/trigger/delivery-client.d.ts +1 -1
  75. package/dist/trigger/delivery-pipeline.d.ts +1 -1
  76. package/dist/trigger/notification-service.d.ts +1 -1
  77. package/dist/trigger/trigger-listener.js +6 -2
  78. package/dist/trigger/trigger-router.d.ts +2 -2
  79. package/dist/v2/durable-core/domain/artifact-contract-validator.js +99 -0
  80. package/dist/v2/durable-core/schemas/artifacts/discovery-handoff.d.ts +39 -0
  81. package/dist/v2/durable-core/schemas/artifacts/discovery-handoff.js +10 -1
  82. package/dist/v2/durable-core/schemas/artifacts/index.d.ts +2 -1
  83. package/dist/v2/durable-core/schemas/artifacts/index.js +12 -1
  84. package/dist/v2/durable-core/schemas/artifacts/phase-handoff.d.ts +89 -0
  85. package/dist/v2/durable-core/schemas/artifacts/phase-handoff.js +56 -0
  86. package/docs/authoring-v2.md +12 -0
  87. package/docs/ideas/backlog.md +639 -25
  88. package/docs/reference/worktrain-daemon-invariants.md +33 -49
  89. package/docs/vision.md +5 -15
  90. package/package.json +2 -2
  91. package/workflows/coding-task-workflow-agentic.json +9 -6
  92. package/workflows/mr-review-workflow.agentic.v2.json +2 -2
  93. package/workflows/wr.discovery.json +2 -1
  94. package/workflows/wr.shaping.json +7 -4
  95. package/dist/console-ui/assets/index-BvBihscd.js +0 -28
@@ -0,0 +1,5 @@
1
+ export { loadDaemonSoul, WORKRAIL_DIR, DAEMON_SOUL_DEFAULT, DAEMON_SOUL_TEMPLATE } from './soul-loader.js';
2
+ export { loadWorkspaceContext, stripFrontmatter, WORKSPACE_CONTEXT_MAX_BYTES, MAX_GLOB_FILES_PER_PATTERN, WORKSPACE_CONTEXT_CANDIDATE_PATHS, } from './workspace-context-loader.js';
3
+ export { loadSessionNotes, MAX_SESSION_RECAP_NOTES, MAX_SESSION_NOTE_CHARS } from './session-notes-loader.js';
4
+ export { appendConversationMessages } from './conversation-log.js';
5
+ export { writeExecutionStats, writeStuckOutboxEntry, DAEMON_STATS_DIR } from './execution-stats.js';
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DAEMON_STATS_DIR = exports.writeStuckOutboxEntry = exports.writeExecutionStats = exports.appendConversationMessages = exports.MAX_SESSION_NOTE_CHARS = exports.MAX_SESSION_RECAP_NOTES = exports.loadSessionNotes = exports.WORKSPACE_CONTEXT_CANDIDATE_PATHS = exports.MAX_GLOB_FILES_PER_PATTERN = exports.WORKSPACE_CONTEXT_MAX_BYTES = exports.stripFrontmatter = exports.loadWorkspaceContext = exports.DAEMON_SOUL_TEMPLATE = exports.DAEMON_SOUL_DEFAULT = exports.WORKRAIL_DIR = exports.loadDaemonSoul = void 0;
4
+ var soul_loader_js_1 = require("./soul-loader.js");
5
+ Object.defineProperty(exports, "loadDaemonSoul", { enumerable: true, get: function () { return soul_loader_js_1.loadDaemonSoul; } });
6
+ Object.defineProperty(exports, "WORKRAIL_DIR", { enumerable: true, get: function () { return soul_loader_js_1.WORKRAIL_DIR; } });
7
+ Object.defineProperty(exports, "DAEMON_SOUL_DEFAULT", { enumerable: true, get: function () { return soul_loader_js_1.DAEMON_SOUL_DEFAULT; } });
8
+ Object.defineProperty(exports, "DAEMON_SOUL_TEMPLATE", { enumerable: true, get: function () { return soul_loader_js_1.DAEMON_SOUL_TEMPLATE; } });
9
+ var workspace_context_loader_js_1 = require("./workspace-context-loader.js");
10
+ Object.defineProperty(exports, "loadWorkspaceContext", { enumerable: true, get: function () { return workspace_context_loader_js_1.loadWorkspaceContext; } });
11
+ Object.defineProperty(exports, "stripFrontmatter", { enumerable: true, get: function () { return workspace_context_loader_js_1.stripFrontmatter; } });
12
+ Object.defineProperty(exports, "WORKSPACE_CONTEXT_MAX_BYTES", { enumerable: true, get: function () { return workspace_context_loader_js_1.WORKSPACE_CONTEXT_MAX_BYTES; } });
13
+ Object.defineProperty(exports, "MAX_GLOB_FILES_PER_PATTERN", { enumerable: true, get: function () { return workspace_context_loader_js_1.MAX_GLOB_FILES_PER_PATTERN; } });
14
+ Object.defineProperty(exports, "WORKSPACE_CONTEXT_CANDIDATE_PATHS", { enumerable: true, get: function () { return workspace_context_loader_js_1.WORKSPACE_CONTEXT_CANDIDATE_PATHS; } });
15
+ var session_notes_loader_js_1 = require("./session-notes-loader.js");
16
+ Object.defineProperty(exports, "loadSessionNotes", { enumerable: true, get: function () { return session_notes_loader_js_1.loadSessionNotes; } });
17
+ Object.defineProperty(exports, "MAX_SESSION_RECAP_NOTES", { enumerable: true, get: function () { return session_notes_loader_js_1.MAX_SESSION_RECAP_NOTES; } });
18
+ Object.defineProperty(exports, "MAX_SESSION_NOTE_CHARS", { enumerable: true, get: function () { return session_notes_loader_js_1.MAX_SESSION_NOTE_CHARS; } });
19
+ var conversation_log_js_1 = require("./conversation-log.js");
20
+ Object.defineProperty(exports, "appendConversationMessages", { enumerable: true, get: function () { return conversation_log_js_1.appendConversationMessages; } });
21
+ var execution_stats_js_1 = require("./execution-stats.js");
22
+ Object.defineProperty(exports, "writeExecutionStats", { enumerable: true, get: function () { return execution_stats_js_1.writeExecutionStats; } });
23
+ Object.defineProperty(exports, "writeStuckOutboxEntry", { enumerable: true, get: function () { return execution_stats_js_1.writeStuckOutboxEntry; } });
24
+ Object.defineProperty(exports, "DAEMON_STATS_DIR", { enumerable: true, get: function () { return execution_stats_js_1.DAEMON_STATS_DIR; } });
@@ -0,0 +1,4 @@
1
+ import type { V2ToolContext } from '../../mcp/types.js';
2
+ export declare const MAX_SESSION_RECAP_NOTES = 3;
3
+ export declare const MAX_SESSION_NOTE_CHARS = 800;
4
+ export declare function loadSessionNotes(continueToken: string, ctx: V2ToolContext): Promise<readonly string[]>;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_SESSION_NOTE_CHARS = exports.MAX_SESSION_RECAP_NOTES = void 0;
4
+ exports.loadSessionNotes = loadSessionNotes;
5
+ const v2_token_ops_js_1 = require("../../mcp/handlers/v2-token-ops.js");
6
+ const index_js_1 = require("../../v2/durable-core/ids/index.js");
7
+ const node_outputs_js_1 = require("../../v2/projections/node-outputs.js");
8
+ exports.MAX_SESSION_RECAP_NOTES = 3;
9
+ exports.MAX_SESSION_NOTE_CHARS = 800;
10
+ async function loadSessionNotes(continueToken, ctx) {
11
+ try {
12
+ const resolvedResult = await (0, v2_token_ops_js_1.parseContinueTokenOrFail)(continueToken, ctx.v2.tokenCodecPorts, ctx.v2.tokenAliasStore);
13
+ if (resolvedResult.isErr()) {
14
+ console.warn(`[WorkflowRunner] Warning: could not decode continueToken for session recap: ${resolvedResult.error.message}`);
15
+ return [];
16
+ }
17
+ const sessionId = (0, index_js_1.asSessionId)(resolvedResult.value.sessionId);
18
+ const loadResult = await ctx.v2.sessionStore.load(sessionId);
19
+ if (loadResult.isErr()) {
20
+ console.warn(`[WorkflowRunner] Warning: could not load session store for recap: ${loadResult.error.code} -- ${loadResult.error.message}`);
21
+ return [];
22
+ }
23
+ const projectionResult = (0, node_outputs_js_1.projectNodeOutputsV2)(loadResult.value.events);
24
+ if (projectionResult.isErr()) {
25
+ console.warn(`[WorkflowRunner] Warning: could not project session outputs for recap: ${projectionResult.error.code} -- ${projectionResult.error.message}`);
26
+ return [];
27
+ }
28
+ const allNotes = [];
29
+ for (const nodeView of Object.values(projectionResult.value.nodesById)) {
30
+ for (const output of nodeView.currentByChannel.recap) {
31
+ if (output.payload.payloadKind === 'notes') {
32
+ const note = output.payload.notesMarkdown.length > exports.MAX_SESSION_NOTE_CHARS
33
+ ? output.payload.notesMarkdown.slice(0, exports.MAX_SESSION_NOTE_CHARS) + '\n[truncated]'
34
+ : output.payload.notesMarkdown;
35
+ allNotes.push(note);
36
+ }
37
+ }
38
+ }
39
+ return allNotes.slice(-exports.MAX_SESSION_RECAP_NOTES);
40
+ }
41
+ catch (err) {
42
+ console.warn(`[WorkflowRunner] Warning: unexpected error loading session notes for recap: ${err instanceof Error ? err.message : String(err)}`);
43
+ return [];
44
+ }
45
+ }
@@ -0,0 +1,3 @@
1
+ export { DAEMON_SOUL_DEFAULT, DAEMON_SOUL_TEMPLATE } from '../soul-template.js';
2
+ export declare const WORKRAIL_DIR: string;
3
+ export declare function loadDaemonSoul(resolvedPath?: string): Promise<string>;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.WORKRAIL_DIR = exports.DAEMON_SOUL_TEMPLATE = exports.DAEMON_SOUL_DEFAULT = void 0;
37
+ exports.loadDaemonSoul = loadDaemonSoul;
38
+ const fs = __importStar(require("node:fs/promises"));
39
+ const path = __importStar(require("node:path"));
40
+ const os = __importStar(require("node:os"));
41
+ const soul_template_js_1 = require("../soul-template.js");
42
+ var soul_template_js_2 = require("../soul-template.js");
43
+ Object.defineProperty(exports, "DAEMON_SOUL_DEFAULT", { enumerable: true, get: function () { return soul_template_js_2.DAEMON_SOUL_DEFAULT; } });
44
+ Object.defineProperty(exports, "DAEMON_SOUL_TEMPLATE", { enumerable: true, get: function () { return soul_template_js_2.DAEMON_SOUL_TEMPLATE; } });
45
+ exports.WORKRAIL_DIR = path.join(os.homedir(), '.workrail');
46
+ async function loadDaemonSoul(resolvedPath) {
47
+ const soulPath = resolvedPath ?? path.join(exports.WORKRAIL_DIR, 'daemon-soul.md');
48
+ try {
49
+ return await fs.readFile(soulPath, 'utf8');
50
+ }
51
+ catch (err) {
52
+ const isEnoent = err instanceof Error && 'code' in err && err.code === 'ENOENT';
53
+ if (isEnoent) {
54
+ try {
55
+ await fs.mkdir(path.dirname(soulPath), { recursive: true });
56
+ await fs.writeFile(soulPath, soul_template_js_1.DAEMON_SOUL_TEMPLATE, 'utf8');
57
+ console.log(`[WorkflowRunner] Created daemon-soul.md template at ${soulPath}`);
58
+ }
59
+ catch (writeErr) {
60
+ console.warn(`[WorkflowRunner] Warning: could not write daemon-soul.md template: ${writeErr instanceof Error ? writeErr.message : String(writeErr)}`);
61
+ }
62
+ }
63
+ else {
64
+ console.warn(`[WorkflowRunner] Warning: could not read daemon-soul.md: ${err instanceof Error ? err.message : String(err)}`);
65
+ }
66
+ return soul_template_js_1.DAEMON_SOUL_DEFAULT;
67
+ }
68
+ }
@@ -0,0 +1,17 @@
1
+ export declare const WORKSPACE_CONTEXT_MAX_BYTES: number;
2
+ export declare const MAX_GLOB_FILES_PER_PATTERN = 20;
3
+ type LiteralCandidatePath = {
4
+ readonly kind: 'literal';
5
+ readonly relativePath: string;
6
+ };
7
+ type GlobCandidatePath = {
8
+ readonly kind: 'glob';
9
+ readonly pattern: string;
10
+ readonly stripFrontmatter: boolean;
11
+ readonly sort: 'alpha';
12
+ };
13
+ type WorkspaceContextCandidate = LiteralCandidatePath | GlobCandidatePath;
14
+ export declare const WORKSPACE_CONTEXT_CANDIDATE_PATHS: readonly WorkspaceContextCandidate[];
15
+ export declare function stripFrontmatter(content: string): string;
16
+ export declare function loadWorkspaceContext(workspacePath: string): Promise<string | null>;
17
+ export {};
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.WORKSPACE_CONTEXT_CANDIDATE_PATHS = exports.MAX_GLOB_FILES_PER_PATTERN = exports.WORKSPACE_CONTEXT_MAX_BYTES = void 0;
37
+ exports.stripFrontmatter = stripFrontmatter;
38
+ exports.loadWorkspaceContext = loadWorkspaceContext;
39
+ const fs = __importStar(require("node:fs/promises"));
40
+ const path = __importStar(require("node:path"));
41
+ const tinyglobby_1 = require("tinyglobby");
42
+ exports.WORKSPACE_CONTEXT_MAX_BYTES = 32 * 1024;
43
+ exports.MAX_GLOB_FILES_PER_PATTERN = 20;
44
+ exports.WORKSPACE_CONTEXT_CANDIDATE_PATHS = [
45
+ { kind: 'literal', relativePath: '.claude/CLAUDE.md' },
46
+ { kind: 'literal', relativePath: 'CLAUDE.md' },
47
+ { kind: 'literal', relativePath: 'CLAUDE.local.md' },
48
+ { kind: 'literal', relativePath: 'AGENTS.md' },
49
+ { kind: 'literal', relativePath: '.github/AGENTS.md' },
50
+ { kind: 'glob', pattern: '.cursor/rules/*.mdc', stripFrontmatter: true, sort: 'alpha' },
51
+ { kind: 'literal', relativePath: '.cursorrules' },
52
+ { kind: 'glob', pattern: '.windsurf/rules/*.md', stripFrontmatter: true, sort: 'alpha' },
53
+ { kind: 'glob', pattern: '.firebender/rules/*.mdc', stripFrontmatter: true, sort: 'alpha' },
54
+ { kind: 'literal', relativePath: '.firebender/AGENTS.md' },
55
+ { kind: 'literal', relativePath: '.github/copilot-instructions.md' },
56
+ { kind: 'glob', pattern: '.continue/rules/*.md', stripFrontmatter: false, sort: 'alpha' },
57
+ ];
58
+ function stripFrontmatter(content) {
59
+ if (!content.startsWith('---\n') && !content.startsWith('---\r\n'))
60
+ return content;
61
+ const endIdx = content.indexOf('\n---', 4);
62
+ if (endIdx === -1)
63
+ return content;
64
+ return content.slice(endIdx + 4).trimStart();
65
+ }
66
+ async function loadWorkspaceContext(workspacePath) {
67
+ const parts = [];
68
+ const injectedPaths = [];
69
+ let combinedBytes = 0;
70
+ let truncated = false;
71
+ function accumulateFile(relativePath, content) {
72
+ const contentBytes = Buffer.byteLength(content, 'utf8');
73
+ if (combinedBytes + contentBytes > exports.WORKSPACE_CONTEXT_MAX_BYTES) {
74
+ const remaining = exports.WORKSPACE_CONTEXT_MAX_BYTES - combinedBytes;
75
+ const truncatedContent = content.slice(0, remaining);
76
+ parts.push(`### ${relativePath}\n${truncatedContent}`);
77
+ injectedPaths.push(relativePath);
78
+ truncated = true;
79
+ }
80
+ else {
81
+ parts.push(`### ${relativePath}\n${content}`);
82
+ injectedPaths.push(relativePath);
83
+ combinedBytes += contentBytes;
84
+ }
85
+ }
86
+ for (const entry of exports.WORKSPACE_CONTEXT_CANDIDATE_PATHS) {
87
+ if (truncated)
88
+ break;
89
+ if (entry.kind === 'literal') {
90
+ const fullPath = path.join(workspacePath, entry.relativePath);
91
+ let content;
92
+ try {
93
+ content = await fs.readFile(fullPath, 'utf8');
94
+ }
95
+ catch (err) {
96
+ const isEnoent = err instanceof Error && 'code' in err && err.code === 'ENOENT';
97
+ if (!isEnoent) {
98
+ console.warn(`[WorkflowRunner] Skipping ${fullPath}: ${err instanceof Error ? err.message : String(err)}`);
99
+ }
100
+ continue;
101
+ }
102
+ accumulateFile(entry.relativePath, content);
103
+ }
104
+ else {
105
+ const matches = await (0, tinyglobby_1.glob)(entry.pattern, { cwd: workspacePath, absolute: false });
106
+ const sorted = [...matches].sort();
107
+ if (sorted.length > exports.MAX_GLOB_FILES_PER_PATTERN) {
108
+ console.warn(`[WorkflowRunner] ${entry.pattern}: ${sorted.length} files found, capped at ${exports.MAX_GLOB_FILES_PER_PATTERN}`);
109
+ }
110
+ for (const relativePath of sorted.slice(0, exports.MAX_GLOB_FILES_PER_PATTERN)) {
111
+ if (truncated)
112
+ break;
113
+ const fullPath = path.join(workspacePath, relativePath);
114
+ let content;
115
+ try {
116
+ content = await fs.readFile(fullPath, 'utf8');
117
+ }
118
+ catch (err) {
119
+ const isEnoent = err instanceof Error && 'code' in err && err.code === 'ENOENT';
120
+ if (!isEnoent) {
121
+ console.warn(`[WorkflowRunner] Skipping ${fullPath}: ${err instanceof Error ? err.message : String(err)}`);
122
+ }
123
+ continue;
124
+ }
125
+ accumulateFile(relativePath, entry.stripFrontmatter ? stripFrontmatter(content) : content);
126
+ }
127
+ }
128
+ }
129
+ if (parts.length === 0)
130
+ return null;
131
+ let combined = parts.join('\n\n');
132
+ if (truncated) {
133
+ combined += '\n\n[Workspace context truncated: combined size exceeded 32 KB limit. Some files may be missing.]';
134
+ }
135
+ console.log(`[WorkflowRunner] Injecting workspace context from: ${injectedPaths.join(', ')}`);
136
+ return combined;
137
+ }
@@ -0,0 +1,28 @@
1
+ import type { AgentLoop, AgentEvent, AgentLoopCallbacks } from '../agent-loop.js';
2
+ import type { V2ToolContext } from '../../mcp/types.js';
3
+ import type { DaemonRegistry } from '../../v2/infra/in-memory/daemon-registry/index.js';
4
+ import type { DaemonEventEmitter } from '../daemon-events.js';
5
+ import type { SessionState } from '../state/session-state.js';
6
+ import type { StuckConfig } from '../state/stuck-detection.js';
7
+ import { ActiveSessionSet } from '../active-sessions.js';
8
+ import type { WorkflowTrigger } from '../types.js';
9
+ import type { PreAgentSession, AgentReadySession, SessionOutcome } from './runner-types.js';
10
+ import type { runWorkflow } from '../workflow-runner.js';
11
+ import type { EnricherResult } from '../workflow-enricher.js';
12
+ export interface TurnEndSubscriberContext {
13
+ readonly agent: AgentLoop;
14
+ readonly state: SessionState;
15
+ readonly stuckConfig: StuckConfig;
16
+ readonly sessionId: string;
17
+ readonly workflowId: string;
18
+ readonly emitter: DaemonEventEmitter | undefined;
19
+ readonly conversationPath: string;
20
+ readonly lastFlushedRef: {
21
+ count: number;
22
+ };
23
+ readonly stuckRepeatThreshold: number;
24
+ }
25
+ export declare function buildTurnEndSubscriber(ctx: TurnEndSubscriberContext): (event: AgentEvent) => Promise<void>;
26
+ export declare function buildAgentCallbacks(sessionId: string, state: SessionState, modelId: string, emitter: DaemonEventEmitter | undefined, stuckRepeatThreshold: number, workflowId?: string): AgentLoopCallbacks;
27
+ export declare function buildAgentReadySession(preAgentSession: PreAgentSession, trigger: WorkflowTrigger, ctx: V2ToolContext, apiKey: string, sessionId: string, emitter: DaemonEventEmitter | undefined, daemonRegistry: DaemonRegistry | undefined, activeSessionSet: ActiveSessionSet | undefined, runWorkflowFn: typeof runWorkflow, enricherResult?: EnricherResult): Promise<AgentReadySession>;
28
+ export declare function runAgentLoop(session: AgentReadySession, trigger: WorkflowTrigger, conversationPath: string): Promise<SessionOutcome>;
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildTurnEndSubscriber = buildTurnEndSubscriber;
4
+ exports.buildAgentCallbacks = buildAgentCallbacks;
5
+ exports.buildAgentReadySession = buildAgentReadySession;
6
+ exports.runAgentLoop = runAgentLoop;
7
+ const agent_loop_js_1 = require("../agent-loop.js");
8
+ const index_js_1 = require("../state/index.js");
9
+ const session_context_js_1 = require("../core/session-context.js");
10
+ const assert_never_js_1 = require("../../runtime/assert-never.js");
11
+ const index_js_2 = require("../io/index.js");
12
+ const index_js_3 = require("../io/index.js");
13
+ const context_loader_js_1 = require("../context-loader.js");
14
+ const session_scope_js_1 = require("../session-scope.js");
15
+ const _shared_js_1 = require("../tools/_shared.js");
16
+ const step_injector_js_1 = require("../turn-end/step-injector.js");
17
+ const conversation_flusher_js_1 = require("../turn-end/conversation-flusher.js");
18
+ const tool_schemas_js_1 = require("./tool-schemas.js");
19
+ const construct_tools_js_1 = require("./construct-tools.js");
20
+ function buildTurnEndSubscriber(ctx) {
21
+ return async (event) => {
22
+ if (event.type !== 'turn_end')
23
+ return;
24
+ for (const toolResult of event.toolResults) {
25
+ if (toolResult.isError) {
26
+ const errorText = toolResult.result?.content[0]?.text ?? 'tool error';
27
+ ctx.emitter?.emit({ kind: 'tool_error', sessionId: ctx.sessionId, toolName: toolResult.toolName, error: errorText.slice(0, 200), ...(0, _shared_js_1.withWorkrailSession)(ctx.state.workrailSessionId) });
28
+ }
29
+ }
30
+ ctx.state.turnCount++;
31
+ const signal = (0, index_js_1.evaluateStuckSignals)(ctx.state, ctx.stuckConfig);
32
+ if (signal !== null) {
33
+ if (signal.kind === 'max_turns_exceeded') {
34
+ (0, index_js_1.setTerminalSignal)(ctx.state, { kind: 'timeout', reason: 'max_turns' });
35
+ ctx.emitter?.emit({ kind: 'agent_stuck', sessionId: ctx.sessionId, reason: 'timeout_imminent', detail: 'Max-turn limit reached', ...(0, _shared_js_1.withWorkrailSession)(ctx.state.workrailSessionId) });
36
+ ctx.agent.abort();
37
+ return;
38
+ }
39
+ else if (signal.kind === 'repeated_tool_call') {
40
+ ctx.emitter?.emit({ kind: 'agent_stuck', sessionId: ctx.sessionId, reason: 'repeated_tool_call', detail: `Same tool+args called ${ctx.stuckRepeatThreshold} times: ${signal.toolName}`, toolName: signal.toolName, argsSummary: signal.argsSummary, ...(0, _shared_js_1.withWorkrailSession)(ctx.state.workrailSessionId) });
41
+ void (0, index_js_3.writeStuckOutboxEntry)({ workflowId: ctx.workflowId, reason: 'repeated_tool_call', ...(ctx.state.issueSummaries.length > 0 ? { issueSummaries: [...ctx.state.issueSummaries] } : {}) });
42
+ if (ctx.stuckConfig.stuckAbortPolicy !== 'notify_only') {
43
+ if ((0, index_js_1.setTerminalSignal)(ctx.state, { kind: 'stuck', reason: 'repeated_tool_call' })) {
44
+ ctx.agent.abort();
45
+ return;
46
+ }
47
+ }
48
+ }
49
+ else if (signal.kind === 'no_progress') {
50
+ ctx.emitter?.emit({ kind: 'agent_stuck', sessionId: ctx.sessionId, reason: 'no_progress', detail: `${signal.turnCount} turns used, 0 step advances (${signal.maxTurns} turn limit)`, ...(0, _shared_js_1.withWorkrailSession)(ctx.state.workrailSessionId) });
51
+ if (ctx.stuckConfig.noProgressAbortEnabled) {
52
+ void (0, index_js_3.writeStuckOutboxEntry)({ workflowId: ctx.workflowId, reason: 'no_progress', ...(ctx.state.issueSummaries.length > 0 ? { issueSummaries: [...ctx.state.issueSummaries] } : {}) });
53
+ if (ctx.stuckConfig.stuckAbortPolicy !== 'notify_only') {
54
+ if ((0, index_js_1.setTerminalSignal)(ctx.state, { kind: 'stuck', reason: 'no_progress' })) {
55
+ ctx.agent.abort();
56
+ return;
57
+ }
58
+ }
59
+ }
60
+ }
61
+ else if (signal.kind === 'timeout_imminent') {
62
+ ctx.emitter?.emit({ kind: 'agent_stuck', sessionId: ctx.sessionId, reason: 'timeout_imminent', detail: `${signal.timeoutReason === 'wall_clock' ? 'Wall-clock timeout' : 'Max-turn limit'} reached`, ...(0, _shared_js_1.withWorkrailSession)(ctx.state.workrailSessionId) });
63
+ }
64
+ else {
65
+ (0, assert_never_js_1.assertNever)(signal);
66
+ }
67
+ }
68
+ (0, conversation_flusher_js_1.flushConversation)(ctx.agent.state.messages, ctx.lastFlushedRef, ctx.conversationPath, index_js_3.appendConversationMessages);
69
+ (0, step_injector_js_1.injectPendingSteps)(ctx.state, ctx.agent);
70
+ };
71
+ }
72
+ function buildAgentCallbacks(sessionId, state, modelId, emitter, stuckRepeatThreshold, workflowId) {
73
+ return {
74
+ onLlmTurnStarted: ({ messageCount }) => {
75
+ emitter?.emit({ kind: 'llm_turn_started', sessionId, messageCount, modelId, ...(0, _shared_js_1.withWorkrailSession)(state.workrailSessionId) });
76
+ },
77
+ onLlmTurnCompleted: ({ stopReason, outputTokens, inputTokens, toolNamesRequested }) => {
78
+ emitter?.emit({ kind: 'llm_turn_completed', sessionId, stopReason, outputTokens, inputTokens, toolNamesRequested, ...(0, _shared_js_1.withWorkrailSession)(state.workrailSessionId) });
79
+ },
80
+ onToolCallStarted: ({ toolName, argsSummary }) => {
81
+ emitter?.emit({ kind: 'tool_call_started', sessionId, toolName, argsSummary, ...(0, _shared_js_1.withWorkrailSession)(state.workrailSessionId) });
82
+ (0, index_js_1.recordToolCall)(state, toolName, argsSummary, stuckRepeatThreshold);
83
+ },
84
+ onToolCallCompleted: ({ toolName, durationMs, resultSummary }) => {
85
+ emitter?.emit({ kind: 'tool_call_completed', sessionId, toolName, durationMs, resultSummary, ...(0, _shared_js_1.withWorkrailSession)(state.workrailSessionId) });
86
+ },
87
+ onToolCallFailed: ({ toolName, durationMs, errorMessage }) => {
88
+ emitter?.emit({ kind: 'tool_call_failed', sessionId, toolName, durationMs, errorMessage, ...(0, _shared_js_1.withWorkrailSession)(state.workrailSessionId) });
89
+ },
90
+ onStallDetected: () => {
91
+ (0, index_js_1.setTerminalSignal)(state, { kind: 'stuck', reason: 'stall' });
92
+ emitter?.emit({
93
+ kind: 'agent_stuck',
94
+ sessionId,
95
+ reason: 'stall',
96
+ detail: `No LLM API call started within the stall timeout window. Last tool calls: ${state.lastNToolCalls.map((c) => c.toolName).join(', ') || 'none'}`,
97
+ ...(0, _shared_js_1.withWorkrailSession)(state.workrailSessionId),
98
+ });
99
+ void (0, index_js_3.writeStuckOutboxEntry)({
100
+ workflowId: workflowId ?? sessionId,
101
+ reason: 'stall',
102
+ ...(state.issueSummaries.length > 0 ? { issueSummaries: [...state.issueSummaries] } : {}),
103
+ });
104
+ },
105
+ };
106
+ }
107
+ function buildUserMessage(text) {
108
+ return { role: 'user', content: text, timestamp: Date.now() };
109
+ }
110
+ async function buildAgentReadySession(preAgentSession, trigger, ctx, apiKey, sessionId, emitter, daemonRegistry, activeSessionSet, runWorkflowFn, enricherResult) {
111
+ const { state, firstStepPrompt, sessionWorkspacePath, sessionWorktreePath, agentClient, modelId } = preAgentSession;
112
+ const startContinueToken = preAgentSession.continueToken;
113
+ const handle = preAgentSession.handle;
114
+ const MAX_ISSUE_SUMMARIES = 10;
115
+ const STUCK_REPEAT_THRESHOLD = 3;
116
+ const onAdvance = (stepText, continueToken) => {
117
+ (0, index_js_1.advanceStep)(state, stepText, continueToken);
118
+ if (state.workrailSessionId !== null)
119
+ daemonRegistry?.heartbeat(state.workrailSessionId);
120
+ emitter?.emit({ kind: 'step_advanced', sessionId, ...(0, _shared_js_1.withWorkrailSession)(state.workrailSessionId) });
121
+ };
122
+ const onComplete = (notes, artifacts) => {
123
+ (0, index_js_1.recordCompletion)(state, notes, artifacts);
124
+ };
125
+ const schemas = (0, tool_schemas_js_1.getSchemas)();
126
+ const scope = {
127
+ fileTracker: new session_scope_js_1.DefaultFileStateTracker(preAgentSession.readFileState),
128
+ onAdvance,
129
+ onComplete,
130
+ onTokenUpdate: (t) => { (0, index_js_1.updateToken)(state, t); },
131
+ onIssueReported: (summary) => {
132
+ if (state.issueSummaries.length < MAX_ISSUE_SUMMARIES) {
133
+ state.issueSummaries.push(summary);
134
+ }
135
+ },
136
+ onSteer: (text) => { state.pendingSteerParts.push(text); },
137
+ getCurrentToken: () => state.currentContinueToken,
138
+ sessionWorkspacePath,
139
+ spawnCurrentDepth: preAgentSession.spawnCurrentDepth,
140
+ spawnMaxDepth: preAgentSession.spawnMaxDepth,
141
+ workrailSessionId: state.workrailSessionId,
142
+ emitter,
143
+ sessionId,
144
+ workflowId: trigger.workflowId,
145
+ activeSessionSet,
146
+ };
147
+ const tools = (0, construct_tools_js_1.constructTools)(ctx, apiKey, schemas, scope, runWorkflowFn);
148
+ const contextLoader = new context_loader_js_1.DefaultContextLoader(index_js_2.loadDaemonSoul, index_js_2.loadWorkspaceContext, index_js_2.loadSessionNotes, ctx);
149
+ const baseCtx = await contextLoader.loadBase(trigger);
150
+ const contextBundle = await contextLoader.loadSession(startContinueToken, baseCtx);
151
+ const effectiveWorkspacePath = sessionWorkspacePath;
152
+ const sessionCtx = (0, session_context_js_1.buildSessionContext)(trigger, contextBundle, firstStepPrompt || 'No step content available', effectiveWorkspacePath, enricherResult);
153
+ const agentCallbacks = buildAgentCallbacks(sessionId, state, modelId, emitter, STUCK_REPEAT_THRESHOLD, trigger.workflowId);
154
+ const agent = new agent_loop_js_1.AgentLoop({
155
+ systemPrompt: sessionCtx.systemPrompt,
156
+ modelId,
157
+ tools,
158
+ client: agentClient,
159
+ toolExecution: 'sequential',
160
+ callbacks: agentCallbacks,
161
+ ...(trigger.agentConfig?.maxOutputTokens !== undefined
162
+ ? { maxTokens: trigger.agentConfig.maxOutputTokens }
163
+ : {}),
164
+ stallTimeoutMs: sessionCtx.stallTimeoutMs,
165
+ });
166
+ handle?.setAgent(agent);
167
+ return {
168
+ preAgentSession,
169
+ contextBundle,
170
+ scope,
171
+ tools,
172
+ sessionCtx,
173
+ handle,
174
+ sessionId,
175
+ workflowId: trigger.workflowId,
176
+ worktreePath: sessionWorktreePath,
177
+ agent,
178
+ stuckRepeatThreshold: STUCK_REPEAT_THRESHOLD,
179
+ };
180
+ }
181
+ async function runAgentLoop(session, trigger, conversationPath) {
182
+ const { agent, preAgentSession, sessionCtx, sessionId, handle } = session;
183
+ const { state } = preAgentSession;
184
+ const { emitter } = session.scope;
185
+ const { stuckRepeatThreshold } = session;
186
+ const { sessionTimeoutMs, maxTurns } = sessionCtx;
187
+ const stuckConfig = {
188
+ maxTurns,
189
+ stuckAbortPolicy: trigger.agentConfig?.stuckAbortPolicy ?? 'abort',
190
+ noProgressAbortEnabled: trigger.agentConfig?.noProgressAbortEnabled ?? false,
191
+ stuckRepeatThreshold,
192
+ };
193
+ const lastFlushedRef = { count: 0 };
194
+ const unsubscribe = agent.subscribe(buildTurnEndSubscriber({
195
+ agent,
196
+ state,
197
+ stuckConfig,
198
+ sessionId,
199
+ workflowId: trigger.workflowId,
200
+ emitter,
201
+ conversationPath,
202
+ lastFlushedRef,
203
+ stuckRepeatThreshold,
204
+ }));
205
+ let stopReason = 'stop';
206
+ let errorMessage;
207
+ let timeoutHandle;
208
+ try {
209
+ const timeoutPromise = new Promise((_, reject) => {
210
+ timeoutHandle = setTimeout(() => {
211
+ (0, index_js_1.setTerminalSignal)(state, { kind: 'timeout', reason: 'wall_clock' });
212
+ reject(new Error('Workflow timed out'));
213
+ }, sessionTimeoutMs);
214
+ });
215
+ console.log(`[WorkflowRunner] Agent loop started: sessionId=${sessionId} workflowId=${trigger.workflowId} modelId=${preAgentSession.modelId}`);
216
+ await Promise.race([agent.prompt(buildUserMessage(sessionCtx.initialPrompt)), timeoutPromise])
217
+ .catch((err) => {
218
+ agent.abort();
219
+ throw err;
220
+ });
221
+ const messages = agent.state.messages;
222
+ let lastAssistant;
223
+ for (let i = messages.length - 1; i >= 0; i--) {
224
+ const m = messages[i];
225
+ if ('role' in m && m.role === 'assistant') {
226
+ lastAssistant = m;
227
+ break;
228
+ }
229
+ }
230
+ stopReason = lastAssistant?.stopReason ?? 'stop';
231
+ errorMessage = lastAssistant?.errorMessage;
232
+ }
233
+ catch (err) {
234
+ errorMessage = err instanceof Error ? err.message : String(err);
235
+ stopReason = 'error';
236
+ }
237
+ finally {
238
+ unsubscribe();
239
+ const remainingMessages = agent.state.messages.slice(lastFlushedRef.count);
240
+ void (0, index_js_3.appendConversationMessages)(conversationPath, remainingMessages).catch(() => { });
241
+ if (timeoutHandle !== undefined)
242
+ clearTimeout(timeoutHandle);
243
+ handle?.dispose();
244
+ console.log(`[WorkflowRunner] Agent loop ended: sessionId=${sessionId} stopReason=${stopReason}${errorMessage ? ` error=${errorMessage.slice(0, 120)}` : ''}`);
245
+ }
246
+ if (stopReason === 'error') {
247
+ return { kind: 'aborted', errorMessage };
248
+ }
249
+ return { kind: 'completed', stopReason, errorMessage };
250
+ }
@@ -0,0 +1,5 @@
1
+ import type { AgentTool } from '../agent-loop.js';
2
+ import type { V2ToolContext } from '../../mcp/types.js';
3
+ import type { SessionScope } from '../session-scope.js';
4
+ import type { runWorkflow } from '../workflow-runner.js';
5
+ export declare function constructTools(ctx: V2ToolContext, apiKey: string, schemas: Record<string, any>, scope: SessionScope, runWorkflowFn: typeof runWorkflow): readonly AgentTool[];
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.constructTools = constructTools;
4
+ const index_js_1 = require("../../mcp/handlers/v2-execution/index.js");
5
+ const continue_workflow_js_1 = require("../tools/continue-workflow.js");
6
+ const bash_js_1 = require("../tools/bash.js");
7
+ const file_tools_js_1 = require("../tools/file-tools.js");
8
+ const glob_grep_js_1 = require("../tools/glob-grep.js");
9
+ const spawn_agent_js_1 = require("../tools/spawn-agent.js");
10
+ const report_issue_js_1 = require("../tools/report-issue.js");
11
+ const signal_coordinator_js_1 = require("../tools/signal-coordinator.js");
12
+ function constructTools(ctx, apiKey, schemas, scope, runWorkflowFn) {
13
+ const { fileTracker, onAdvance, onComplete, onTokenUpdate, onIssueReported, getCurrentToken, sessionWorkspacePath, spawnCurrentDepth, spawnMaxDepth, emitter, activeSessionSet, } = scope;
14
+ const sid = scope.sessionId;
15
+ const workrailSid = scope.workrailSessionId;
16
+ const readFileStateMap = fileTracker.toMap();
17
+ return [
18
+ (0, continue_workflow_js_1.makeCompleteStepTool)(sid, ctx, getCurrentToken, onAdvance, onComplete, onTokenUpdate, schemas, index_js_1.executeContinueWorkflow, emitter, workrailSid),
19
+ (0, continue_workflow_js_1.makeContinueWorkflowTool)(sid, ctx, onAdvance, onComplete, schemas, index_js_1.executeContinueWorkflow, emitter, workrailSid),
20
+ (0, bash_js_1.makeBashTool)(sessionWorkspacePath, schemas, sid, emitter, workrailSid),
21
+ (0, file_tools_js_1.makeReadTool)(sessionWorkspacePath, readFileStateMap, schemas, sid, emitter, workrailSid),
22
+ (0, file_tools_js_1.makeWriteTool)(sessionWorkspacePath, readFileStateMap, schemas, sid, emitter, workrailSid),
23
+ (0, glob_grep_js_1.makeGlobTool)(sessionWorkspacePath, schemas, sid, emitter, workrailSid),
24
+ (0, glob_grep_js_1.makeGrepTool)(sessionWorkspacePath, schemas, sid, emitter, workrailSid),
25
+ (0, file_tools_js_1.makeEditTool)(sessionWorkspacePath, readFileStateMap, schemas, sid, emitter, workrailSid),
26
+ (0, report_issue_js_1.makeReportIssueTool)(sid, emitter, workrailSid, undefined, onIssueReported),
27
+ (0, spawn_agent_js_1.makeSpawnAgentTool)(sid, ctx, apiKey, workrailSid ?? '', spawnCurrentDepth, spawnMaxDepth, runWorkflowFn, schemas, emitter, activeSessionSet),
28
+ (0, signal_coordinator_js_1.makeSignalCoordinatorTool)(sid, emitter, workrailSid),
29
+ ];
30
+ }
@@ -0,0 +1,3 @@
1
+ import type { WorkflowRunResult } from '../types.js';
2
+ import type { FinalizationContext } from './runner-types.js';
3
+ export declare function finalizeSession(result: WorkflowRunResult, ctx: FinalizationContext): Promise<void>;