@exaudeus/workrail 3.72.0 → 3.72.1

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 (42) hide show
  1. package/dist/cli-worktrain.js +4 -6
  2. package/dist/console-ui/assets/{index-CTza1zb5.js → index-Yj9NHqbR.js} +1 -1
  3. package/dist/console-ui/index.html +1 -1
  4. package/dist/daemon/active-sessions.d.ts +17 -0
  5. package/dist/daemon/active-sessions.js +55 -0
  6. package/dist/daemon/context-loader.d.ts +32 -0
  7. package/dist/daemon/context-loader.js +34 -0
  8. package/dist/daemon/session-scope.d.ts +3 -2
  9. package/dist/daemon/tools/_shared.d.ts +38 -0
  10. package/dist/daemon/tools/_shared.js +101 -0
  11. package/dist/daemon/tools/bash.d.ts +3 -0
  12. package/dist/daemon/tools/bash.js +57 -0
  13. package/dist/daemon/tools/continue-workflow.d.ts +6 -0
  14. package/dist/daemon/tools/continue-workflow.js +208 -0
  15. package/dist/daemon/tools/file-tools.d.ts +6 -0
  16. package/dist/daemon/tools/file-tools.js +195 -0
  17. package/dist/daemon/tools/glob-grep.d.ts +4 -0
  18. package/dist/daemon/tools/glob-grep.js +172 -0
  19. package/dist/daemon/tools/report-issue.d.ts +3 -0
  20. package/dist/daemon/tools/report-issue.js +129 -0
  21. package/dist/daemon/tools/signal-coordinator.d.ts +4 -0
  22. package/dist/daemon/tools/signal-coordinator.js +105 -0
  23. package/dist/daemon/tools/spawn-agent.d.ts +6 -0
  24. package/dist/daemon/tools/spawn-agent.js +135 -0
  25. package/dist/daemon/workflow-runner.d.ts +54 -29
  26. package/dist/daemon/workflow-runner.js +156 -980
  27. package/dist/infrastructure/storage/workflow-resolution.js +5 -6
  28. package/dist/manifest.json +131 -27
  29. package/dist/mcp/handlers/shared/request-workflow-reader.js +14 -0
  30. package/dist/trigger/coordinator-deps.d.ts +15 -0
  31. package/dist/trigger/coordinator-deps.js +322 -0
  32. package/dist/trigger/delivery-pipeline.d.ts +18 -0
  33. package/dist/trigger/delivery-pipeline.js +148 -0
  34. package/dist/trigger/dispatch-deduplicator.d.ts +6 -0
  35. package/dist/trigger/dispatch-deduplicator.js +24 -0
  36. package/dist/trigger/trigger-listener.d.ts +2 -3
  37. package/dist/trigger/trigger-listener.js +9 -276
  38. package/dist/trigger/trigger-router.d.ts +8 -7
  39. package/dist/trigger/trigger-router.js +19 -97
  40. package/dist/v2/usecases/console-routes.js +10 -2
  41. package/docs/ideas/backlog.md +82 -48
  42. package/package.json +1 -1
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeSpawnAgentTool = makeSpawnAgentTool;
4
+ const start_js_1 = require("../../mcp/handlers/v2-execution/start.js");
5
+ const v2_token_ops_js_1 = require("../../mcp/handlers/v2-token-ops.js");
6
+ const assert_never_js_1 = require("../../runtime/assert-never.js");
7
+ const _shared_js_1 = require("./_shared.js");
8
+ function makeSpawnAgentTool(sessionId, ctx, apiKey, thisWorkrailSessionId, currentDepth, maxDepth, runWorkflowFn, schemas, emitter, activeSessionSet) {
9
+ return {
10
+ name: 'spawn_agent',
11
+ description: 'Spawn a child WorkRail session to handle a delegated sub-task. ' +
12
+ 'Blocks until the child session completes, then returns the child\'s outcome and notes. ' +
13
+ 'Use this when a step requires delegating a well-defined sub-task to a separate workflow. ' +
14
+ 'IMPORTANT: The parent session\'s time limit (maxSessionMinutes) keeps ticking while the child runs. ' +
15
+ 'Configure the parent with enough time to cover both its own work and the child\'s work. ' +
16
+ 'Per-trigger limits (maxOutputTokens, maxTurns, maxSessionMinutes) are NOT inherited by child sessions spawned via spawn_agent -- each child uses its own trigger\'s agentConfig. ' +
17
+ 'Returns: { childSessionId, outcome: "success"|"error"|"timeout", notes: string, artifacts?: readonly unknown[] }. ' +
18
+ 'On success, artifacts contains the child session\'s final step artifacts if any were produced. ' +
19
+ 'Check outcome before using notes -- on error/timeout, notes contains the error message.',
20
+ inputSchema: schemas['SpawnAgentParams'],
21
+ label: 'Spawn Agent',
22
+ execute: async (_toolCallId, params) => {
23
+ if (typeof params.workflowId !== 'string' || !params.workflowId)
24
+ throw new Error('spawn_agent: workflowId must be a non-empty string');
25
+ if (typeof params.goal !== 'string' || !params.goal)
26
+ throw new Error('spawn_agent: goal must be a non-empty string');
27
+ if (typeof params.workspacePath !== 'string' || !params.workspacePath)
28
+ throw new Error('spawn_agent: workspacePath must be a non-empty string');
29
+ console.log(`[WorkflowRunner] Tool: spawn_agent sessionId=${sessionId} workflowId=${String(params.workflowId)} depth=${currentDepth}/${maxDepth}`);
30
+ emitter?.emit({ kind: 'tool_called', sessionId, toolName: 'spawn_agent', summary: `${String(params.workflowId)} depth=${currentDepth}`, ...(0, _shared_js_1.withWorkrailSession)(thisWorkrailSessionId) });
31
+ if (currentDepth >= maxDepth) {
32
+ const limitResult = {
33
+ childSessionId: null,
34
+ outcome: 'error',
35
+ notes: `Max spawn depth exceeded (currentDepth=${currentDepth}, maxDepth=${maxDepth}). ` +
36
+ `Cannot spawn a child session from this depth. ` +
37
+ `Increase agentConfig.maxSubagentDepth if deeper delegation is intentional.`,
38
+ };
39
+ return {
40
+ content: [{ type: 'text', text: JSON.stringify(limitResult) }],
41
+ details: limitResult,
42
+ };
43
+ }
44
+ const startInput = {
45
+ workflowId: String(params.workflowId),
46
+ workspacePath: String(params.workspacePath),
47
+ goal: String(params.goal),
48
+ };
49
+ const startResult = await (0, start_js_1.executeStartWorkflow)(startInput, ctx, { is_autonomous: 'true', workspacePath: String(params.workspacePath), parentSessionId: thisWorkrailSessionId });
50
+ if (startResult.isErr()) {
51
+ const errResult = {
52
+ childSessionId: null,
53
+ outcome: 'error',
54
+ notes: `Failed to start child workflow: ${startResult.error.kind} -- ${JSON.stringify(startResult.error)}`,
55
+ };
56
+ return {
57
+ content: [{ type: 'text', text: JSON.stringify(errResult) }],
58
+ details: errResult,
59
+ };
60
+ }
61
+ let childSessionId = null;
62
+ const childContinueToken = startResult.value.response.continueToken ?? '';
63
+ if (childContinueToken) {
64
+ const decoded = await (0, v2_token_ops_js_1.parseContinueTokenOrFail)(childContinueToken, ctx.v2.tokenCodecPorts, ctx.v2.tokenAliasStore);
65
+ if (decoded.isOk()) {
66
+ childSessionId = decoded.value.sessionId;
67
+ }
68
+ else {
69
+ console.warn(`[WorkflowRunner] spawn_agent: could not decode childSessionId from continueToken -- ` +
70
+ `childSessionId will be null in result. Reason: ${decoded.error.message}`);
71
+ }
72
+ }
73
+ const childTrigger = {
74
+ workflowId: String(params.workflowId),
75
+ goal: String(params.goal),
76
+ workspacePath: String(params.workspacePath),
77
+ context: params.context,
78
+ spawnDepth: currentDepth + 1,
79
+ parentSessionId: thisWorkrailSessionId,
80
+ };
81
+ const r = startResult.value.response;
82
+ const childAllocatedSession = {
83
+ continueToken: r.continueToken ?? '',
84
+ checkpointToken: r.checkpointToken,
85
+ firstStepPrompt: r.pending?.prompt ?? '',
86
+ isComplete: r.isComplete,
87
+ triggerSource: 'daemon',
88
+ };
89
+ const childSource = { kind: 'pre_allocated', trigger: childTrigger, session: childAllocatedSession };
90
+ const childResult = await runWorkflowFn(childTrigger, ctx, apiKey, undefined, emitter, activeSessionSet, undefined, undefined, childSource);
91
+ let resultObj;
92
+ if (childResult._tag === 'success') {
93
+ resultObj = {
94
+ childSessionId,
95
+ outcome: 'success',
96
+ notes: childResult.lastStepNotes ?? '(no notes from child session)',
97
+ ...(childResult.lastStepArtifacts !== undefined ? { artifacts: childResult.lastStepArtifacts } : {}),
98
+ };
99
+ }
100
+ else if (childResult._tag === 'error') {
101
+ resultObj = {
102
+ childSessionId,
103
+ outcome: 'error',
104
+ notes: childResult.message,
105
+ };
106
+ }
107
+ else if (childResult._tag === 'timeout') {
108
+ resultObj = {
109
+ childSessionId,
110
+ outcome: 'timeout',
111
+ notes: childResult.message,
112
+ };
113
+ }
114
+ else if (childResult._tag === 'stuck') {
115
+ resultObj = {
116
+ childSessionId,
117
+ outcome: 'stuck',
118
+ notes: childResult.message,
119
+ ...(childResult.issueSummaries !== undefined
120
+ ? { issueSummaries: childResult.issueSummaries }
121
+ : {}),
122
+ };
123
+ }
124
+ else {
125
+ (0, assert_never_js_1.assertNever)(childResult);
126
+ }
127
+ console.log(`[WorkflowRunner] spawn_agent completed: sessionId=${sessionId} childSessionId=${childSessionId ?? 'null'} outcome=${resultObj.outcome}`);
128
+ emitter?.emit({ kind: 'tool_called', sessionId, toolName: 'spawn_agent_complete', summary: `outcome=${resultObj.outcome} child=${childSessionId ?? 'null'}`, ...(0, _shared_js_1.withWorkrailSession)(thisWorkrailSessionId) });
129
+ return {
130
+ content: [{ type: 'text', text: JSON.stringify(resultObj) }],
131
+ details: resultObj,
132
+ };
133
+ },
134
+ };
135
+ }
@@ -6,15 +6,26 @@ import type { AgentTool, AgentEvent, AgentLoopCallbacks } from "./agent-loop.js"
6
6
  import type { V2ToolContext } from '../mcp/types.js';
7
7
  import { executeContinueWorkflow } from '../mcp/handlers/v2-execution/index.js';
8
8
  import type { DaemonRegistry } from '../v2/infra/in-memory/daemon-registry/index.js';
9
- import type { V2StartWorkflowOutputSchema } from '../mcp/output-schemas.js';
10
9
  import type { ContinueTokenResolved } from '../mcp/handlers/v2-token-ops.js';
11
10
  import type { SessionEventLogReadonlyStorePortV2 } from '../v2/ports/session-event-log-store.port.js';
12
11
  import type { ToolFailure } from '../mcp/handlers/v2-execution-helpers.js';
13
12
  import type { ResultAsync } from 'neverthrow';
14
13
  import type { DaemonEventEmitter } from './daemon-events.js';
14
+ import { type SessionScope } from './session-scope.js';
15
+ import { ActiveSessionSet } from './active-sessions.js';
16
+ import type { SessionHandle } from './active-sessions.js';
17
+ import { makeContinueWorkflowTool, makeCompleteStepTool } from './tools/continue-workflow.js';
18
+ import { makeBashTool } from './tools/bash.js';
19
+ import { makeReadTool, makeWriteTool, makeEditTool } from './tools/file-tools.js';
20
+ import { makeGlobTool, makeGrepTool } from './tools/glob-grep.js';
21
+ import { makeSpawnAgentTool } from './tools/spawn-agent.js';
22
+ import { makeReportIssueTool } from './tools/report-issue.js';
23
+ import { makeSignalCoordinatorTool } from './tools/signal-coordinator.js';
24
+ export { DAEMON_SESSIONS_DIR, type PersistTokensError } from './tools/_shared.js';
25
+ export { DAEMON_SIGNALS_DIR } from './tools/signal-coordinator.js';
26
+ export { makeContinueWorkflowTool, makeCompleteStepTool, makeBashTool, makeReadTool, makeWriteTool, makeEditTool, makeGlobTool, makeGrepTool, makeSpawnAgentTool, makeReportIssueTool, makeSignalCoordinatorTool, };
15
27
  export declare const DEFAULT_SESSION_TIMEOUT_MINUTES = 30;
16
28
  export declare const DEFAULT_MAX_TURNS = 200;
17
- export declare const DAEMON_SESSIONS_DIR: string;
18
29
  export declare const WORKTREES_DIR: string;
19
30
  export { DAEMON_SOUL_DEFAULT, DAEMON_SOUL_TEMPLATE } from './soul-template.js';
20
31
  export type ReadFileState = {
@@ -37,7 +48,6 @@ export interface WorkflowTrigger {
37
48
  readonly stuckAbortPolicy?: 'abort' | 'notify_only';
38
49
  readonly noProgressAbortEnabled?: boolean;
39
50
  };
40
- readonly _preAllocatedStartResponse?: import('zod').infer<typeof V2StartWorkflowOutputSchema>;
41
51
  readonly parentSessionId?: string;
42
52
  readonly spawnDepth?: number;
43
53
  readonly soulFile?: string;
@@ -49,6 +59,21 @@ export interface WorkflowTrigger {
49
59
  readonly baseBranch?: string;
50
60
  readonly branchPrefix?: string;
51
61
  }
62
+ export interface AllocatedSession {
63
+ readonly continueToken: string;
64
+ readonly checkpointToken?: string | null;
65
+ readonly firstStepPrompt: string;
66
+ readonly isComplete: boolean;
67
+ readonly triggerSource: 'daemon' | 'mcp';
68
+ }
69
+ export type SessionSource = {
70
+ readonly kind: 'allocate';
71
+ readonly trigger: WorkflowTrigger;
72
+ } | {
73
+ readonly kind: 'pre_allocated';
74
+ readonly trigger: WorkflowTrigger;
75
+ readonly session: AllocatedSession;
76
+ };
52
77
  export interface WorkflowRunSuccess {
53
78
  readonly _tag: 'success';
54
79
  readonly workflowId: string;
@@ -104,10 +129,6 @@ export interface OrphanedSession {
104
129
  readonly goal?: string;
105
130
  readonly workspacePath?: string;
106
131
  }
107
- export interface PersistTokensError {
108
- readonly code: string;
109
- readonly message: string;
110
- }
111
132
  export declare function readDaemonSessionState(sessionId: string): Promise<{
112
133
  continueToken: string;
113
134
  checkpointToken: string | null;
@@ -122,18 +143,6 @@ export declare function clearQueueIssueSidecars(sessionsDir: string): Promise<vo
122
143
  export declare function stripFrontmatter(content: string): string;
123
144
  export declare function loadWorkspaceContext(workspacePath: string): Promise<string | null>;
124
145
  export declare function loadSessionNotes(continueToken: string, ctx: V2ToolContext): Promise<readonly string[]>;
125
- export declare function makeContinueWorkflowTool(sessionId: string, ctx: V2ToolContext, onAdvance: (nextStepText: string, continueToken: string) => void, onComplete: (notes: string | undefined, artifacts?: readonly unknown[]) => void, schemas: Record<string, any>, _executeContinueWorkflowFn?: typeof executeContinueWorkflow, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
126
- export declare function makeCompleteStepTool(sessionId: string, ctx: V2ToolContext, getCurrentToken: () => string, onAdvance: (nextStepText: string, continueToken: string) => void, onComplete: (notes: string | undefined, artifacts?: readonly unknown[]) => void, onTokenUpdate: (t: string) => void, schemas: Record<string, any>, _executeContinueWorkflowFn?: typeof executeContinueWorkflow, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
127
- export declare function makeBashTool(workspacePath: string, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
128
- export declare function makeReadTool(readFileState: Map<string, ReadFileState>, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
129
- export declare function makeWriteTool(readFileState: Map<string, ReadFileState>, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
130
- export declare function makeGlobTool(workspacePath: string, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
131
- export declare function makeGrepTool(workspacePath: string, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
132
- export declare function makeEditTool(workspacePath: string, readFileState: Map<string, ReadFileState>, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
133
- export declare function makeSpawnAgentTool(sessionId: string, ctx: V2ToolContext, apiKey: string, thisWorkrailSessionId: string, currentDepth: number, maxDepth: number, runWorkflowFn: typeof runWorkflow, schemas: Record<string, any>, emitter?: DaemonEventEmitter, abortRegistry?: AbortRegistry): AgentTool;
134
- export declare function makeReportIssueTool(sessionId: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null, issuesDirOverride?: string, onIssueSummary?: (summary: string) => void): AgentTool;
135
- export declare const DAEMON_SIGNALS_DIR: string;
136
- export declare function makeSignalCoordinatorTool(sessionId: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null, signalsDirOverride?: string): AgentTool;
137
146
  export declare function buildSessionRecap(notes: readonly string[]): string;
138
147
  export declare function buildSystemPrompt(trigger: WorkflowTrigger, sessionState: string, soulContent: string, workspaceContext: string | null): string;
139
148
  export declare function tagToStatsOutcome(tag: WorkflowRunResult['_tag']): 'success' | 'error' | 'timeout' | 'stuck';
@@ -193,7 +202,7 @@ export interface PreAgentSession {
193
202
  readonly checkpointToken: string | null;
194
203
  readonly sessionWorkspacePath: string;
195
204
  readonly sessionWorktreePath: string | undefined;
196
- readonly firstStep: import('zod').infer<typeof V2StartWorkflowOutputSchema>;
205
+ readonly firstStepPrompt: string;
197
206
  readonly state: SessionState;
198
207
  readonly spawnCurrentDepth: number;
199
208
  readonly spawnMaxDepth: number;
@@ -201,6 +210,7 @@ export interface PreAgentSession {
201
210
  readonly agentClient: Anthropic | AnthropicBedrock;
202
211
  readonly modelId: string;
203
212
  readonly startMs: number;
213
+ readonly handle?: SessionHandle;
204
214
  }
205
215
  export type PreAgentSessionResult = {
206
216
  readonly kind: 'ready';
@@ -209,6 +219,27 @@ export type PreAgentSessionResult = {
209
219
  readonly kind: 'complete';
210
220
  readonly result: WorkflowRunResult;
211
221
  };
222
+ export interface AgentReadySession {
223
+ readonly preAgentSession: PreAgentSession;
224
+ readonly contextBundle: import('./context-loader.js').ContextBundle;
225
+ readonly scope: SessionScope;
226
+ readonly tools: readonly AgentTool[];
227
+ readonly sessionCtx: SessionContext;
228
+ readonly handle: import('./active-sessions.js').SessionHandle | undefined;
229
+ readonly sessionId: string;
230
+ readonly workflowId: string;
231
+ readonly worktreePath: string | undefined;
232
+ readonly agent: AgentLoop;
233
+ readonly stuckRepeatThreshold: number;
234
+ }
235
+ export type SessionOutcome = {
236
+ readonly kind: 'completed';
237
+ readonly stopReason: string;
238
+ readonly errorMessage?: string;
239
+ } | {
240
+ readonly kind: 'aborted';
241
+ readonly errorMessage?: string;
242
+ };
212
243
  export interface FinalizationContext {
213
244
  readonly sessionId: string;
214
245
  readonly workrailSessionId: string | null;
@@ -223,20 +254,14 @@ export interface FinalizationContext {
223
254
  readonly workflowId: string;
224
255
  }
225
256
  export declare function finalizeSession(result: WorkflowRunResult, ctx: FinalizationContext): Promise<void>;
226
- export interface SessionContextInputs {
227
- readonly soulContent: string;
228
- readonly workspaceContext: string | null;
229
- readonly sessionNotes: readonly string[];
230
- readonly firstStepPrompt: string;
231
- }
232
257
  export interface SessionContext {
233
258
  readonly systemPrompt: string;
234
259
  readonly initialPrompt: string;
235
260
  readonly sessionTimeoutMs: number;
236
261
  readonly maxTurns: number;
237
262
  }
238
- export declare function buildSessionContext(trigger: WorkflowTrigger, inputs: SessionContextInputs): SessionContext;
239
- export declare function buildPreAgentSession(trigger: WorkflowTrigger, ctx: V2ToolContext, apiKey: string, sessionId: string, startMs: number, statsDir: string, sessionsDir: string, emitter: DaemonEventEmitter | undefined, daemonRegistry: DaemonRegistry | undefined, steerRegistry: SteerRegistry | undefined): Promise<PreAgentSessionResult>;
263
+ export declare function buildSessionContext(trigger: WorkflowTrigger, context: import('./context-loader.js').ContextBundle, firstStepPrompt: string): SessionContext;
264
+ export declare function buildPreAgentSession(trigger: WorkflowTrigger, ctx: V2ToolContext, apiKey: string, sessionId: string, startMs: number, statsDir: string, sessionsDir: string, emitter: DaemonEventEmitter | undefined, daemonRegistry: DaemonRegistry | undefined, activeSessionSet: ActiveSessionSet | undefined, source?: SessionSource): Promise<PreAgentSessionResult>;
240
265
  export interface TurnEndSubscriberContext {
241
266
  readonly agent: AgentLoop;
242
267
  readonly state: SessionState;
@@ -253,4 +278,4 @@ export interface TurnEndSubscriberContext {
253
278
  export declare function buildTurnEndSubscriber(ctx: TurnEndSubscriberContext): (event: AgentEvent) => Promise<void>;
254
279
  export declare function buildAgentCallbacks(sessionId: string, state: SessionState, modelId: string, emitter: DaemonEventEmitter | undefined, stuckRepeatThreshold: number): AgentLoopCallbacks;
255
280
  export declare function buildSessionResult(state: Readonly<SessionState>, stopReason: string, errorMessage: string | undefined, trigger: WorkflowTrigger, sessionId: string, sessionWorktreePath: string | undefined): WorkflowRunResult;
256
- export declare function runWorkflow(trigger: WorkflowTrigger, ctx: V2ToolContext, apiKey: string, daemonRegistry?: DaemonRegistry, emitter?: DaemonEventEmitter, steerRegistry?: SteerRegistry, abortRegistry?: AbortRegistry, _statsDir?: string, _sessionsDir?: string): Promise<WorkflowRunResult>;
281
+ export declare function runWorkflow(trigger: WorkflowTrigger, ctx: V2ToolContext, apiKey: string, daemonRegistry?: DaemonRegistry, emitter?: DaemonEventEmitter, activeSessionSet?: ActiveSessionSet, _statsDir?: string, _sessionsDir?: string, source?: SessionSource): Promise<WorkflowRunResult>;