@exaudeus/workrail 3.72.3 → 3.73.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.
@@ -137,7 +137,7 @@ export interface SignalEmittedEvent {
137
137
  export interface AgentStuckEvent {
138
138
  readonly kind: 'agent_stuck';
139
139
  readonly sessionId: string;
140
- readonly reason: 'repeated_tool_call' | 'no_progress' | 'timeout_imminent';
140
+ readonly reason: 'repeated_tool_call' | 'no_progress' | 'timeout_imminent' | 'stall';
141
141
  readonly detail: string;
142
142
  readonly toolName?: string;
143
143
  readonly argsSummary?: string;
@@ -1,6 +1,6 @@
1
1
  import type { AgentTool } from '../agent-loop.js';
2
2
  import type { DaemonEventEmitter } from '../daemon-events.js';
3
3
  import type { ReadFileState } from '../workflow-runner.js';
4
- export declare function makeReadTool(readFileState: Map<string, ReadFileState>, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
5
- export declare function makeWriteTool(readFileState: Map<string, ReadFileState>, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
4
+ export declare function makeReadTool(workspacePath: string, readFileState: Map<string, ReadFileState>, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
5
+ export declare function makeWriteTool(workspacePath: string, readFileState: Map<string, ReadFileState>, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
6
6
  export declare function makeEditTool(workspacePath: string, readFileState: Map<string, ReadFileState>, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
@@ -39,7 +39,16 @@ exports.makeEditTool = makeEditTool;
39
39
  const fs = __importStar(require("node:fs/promises"));
40
40
  const path = __importStar(require("node:path"));
41
41
  const _shared_js_1 = require("./_shared.js");
42
- function makeReadTool(readFileState, schemas, sessionId, emitter, workrailSessionId) {
42
+ function resolveWithinWorkspace(filePath, workspacePath, toolName) {
43
+ const absolute = path.isAbsolute(filePath) ? filePath : path.join(workspacePath, filePath);
44
+ const normalizedWorkspace = path.normalize(workspacePath) + path.sep;
45
+ const normalizedTarget = path.normalize(absolute);
46
+ if (normalizedTarget !== path.normalize(workspacePath) && !normalizedTarget.startsWith(normalizedWorkspace)) {
47
+ throw new Error(`${toolName} target is outside the workspace: ${filePath}`);
48
+ }
49
+ return normalizedTarget;
50
+ }
51
+ function makeReadTool(workspacePath, readFileState, schemas, sessionId, emitter, workrailSessionId) {
43
52
  return {
44
53
  name: 'Read',
45
54
  description: 'Read the contents of a file at the given absolute path. ' +
@@ -50,7 +59,7 @@ function makeReadTool(readFileState, schemas, sessionId, emitter, workrailSessio
50
59
  execute: async (_toolCallId, params) => {
51
60
  if (typeof params.filePath !== 'string' || !params.filePath)
52
61
  throw new Error('Read: filePath must be a non-empty string');
53
- const filePath = params.filePath;
62
+ const filePath = resolveWithinWorkspace(params.filePath, workspacePath, 'Read');
54
63
  if (sessionId)
55
64
  emitter?.emit({ kind: 'tool_called', sessionId, toolName: 'Read', summary: filePath.slice(0, 80), ...(0, _shared_js_1.withWorkrailSession)(workrailSessionId) });
56
65
  const devPaths = ['/dev/stdin', '/dev/tty', '/dev/zero', '/dev/random', '/dev/full', '/dev/urandom'];
@@ -79,7 +88,7 @@ function makeReadTool(readFileState, schemas, sessionId, emitter, workrailSessio
79
88
  },
80
89
  };
81
90
  }
82
- function makeWriteTool(readFileState, schemas, sessionId, emitter, workrailSessionId) {
91
+ function makeWriteTool(workspacePath, readFileState, schemas, sessionId, emitter, workrailSessionId) {
83
92
  return {
84
93
  name: 'Write',
85
94
  description: 'Write content to a file at the given absolute path. Creates parent directories if needed. ' +
@@ -92,7 +101,7 @@ function makeWriteTool(readFileState, schemas, sessionId, emitter, workrailSessi
92
101
  throw new Error('Write: filePath must be a non-empty string');
93
102
  if (typeof params.content !== 'string')
94
103
  throw new Error('Write: content must be a string');
95
- const filePath = params.filePath;
104
+ const filePath = resolveWithinWorkspace(params.filePath, workspacePath, 'Write');
96
105
  if (sessionId)
97
106
  emitter?.emit({ kind: 'tool_called', sessionId, toolName: 'Write', summary: filePath.slice(0, 80), ...(0, _shared_js_1.withWorkrailSession)(workrailSessionId) });
98
107
  let existsOnDisk = false;
@@ -139,14 +148,7 @@ function makeEditTool(workspacePath, readFileState, schemas, sessionId, emitter,
139
148
  throw new Error('Edit: old_string must be a string');
140
149
  if (typeof params.new_string !== 'string')
141
150
  throw new Error('Edit: new_string must be a string');
142
- const rawFilePath = params.file_path;
143
- const absoluteFilePath = path.isAbsolute(rawFilePath)
144
- ? rawFilePath
145
- : path.join(workspacePath, rawFilePath);
146
- if (!absoluteFilePath.startsWith(workspacePath)) {
147
- throw new Error(`Edit target is outside the workspace: ${rawFilePath}`);
148
- }
149
- const filePath = absoluteFilePath;
151
+ const filePath = resolveWithinWorkspace(params.file_path, workspacePath, 'Edit');
150
152
  const oldString = params.old_string;
151
153
  const newString = params.new_string;
152
154
  const replaceAll = params.replace_all ?? false;
@@ -46,7 +46,7 @@ function makeSpawnAgentTool(sessionId, ctx, apiKey, thisWorkrailSessionId, curre
46
46
  workspacePath: String(params.workspacePath),
47
47
  goal: String(params.goal),
48
48
  };
49
- const startResult = await (0, start_js_1.executeStartWorkflow)(startInput, ctx, { is_autonomous: 'true', workspacePath: String(params.workspacePath), parentSessionId: thisWorkrailSessionId });
49
+ const startResult = await (0, start_js_1.executeStartWorkflow)(startInput, ctx, { is_autonomous: 'true', workspacePath: String(params.workspacePath), parentSessionId: thisWorkrailSessionId, triggerSource: 'daemon' });
50
50
  if (startResult.isErr()) {
51
51
  const errResult = {
52
52
  childSessionId: null,
@@ -26,6 +26,7 @@ export { DAEMON_SIGNALS_DIR } from './tools/signal-coordinator.js';
26
26
  export { makeContinueWorkflowTool, makeCompleteStepTool, makeBashTool, makeReadTool, makeWriteTool, makeEditTool, makeGlobTool, makeGrepTool, makeSpawnAgentTool, makeReportIssueTool, makeSignalCoordinatorTool, };
27
27
  export declare const DEFAULT_SESSION_TIMEOUT_MINUTES = 30;
28
28
  export declare const DEFAULT_MAX_TURNS = 200;
29
+ export declare const DEFAULT_STALL_TIMEOUT_SECONDS = 120;
29
30
  export declare const WORKTREES_DIR: string;
30
31
  export { DAEMON_SOUL_DEFAULT, DAEMON_SOUL_TEMPLATE } from './soul-template.js';
31
32
  export type ReadFileState = {
@@ -47,6 +48,7 @@ export interface WorkflowTrigger {
47
48
  readonly maxSubagentDepth?: number;
48
49
  readonly stuckAbortPolicy?: 'abort' | 'notify_only';
49
50
  readonly noProgressAbortEnabled?: boolean;
51
+ readonly stallTimeoutSeconds?: number;
50
52
  };
51
53
  readonly parentSessionId?: string;
52
54
  readonly spawnDepth?: number;
@@ -105,7 +107,7 @@ export interface WorkflowRunTimeout {
105
107
  export interface WorkflowRunStuck {
106
108
  readonly _tag: 'stuck';
107
109
  readonly workflowId: string;
108
- readonly reason: 'repeated_tool_call' | 'no_progress';
110
+ readonly reason: 'repeated_tool_call' | 'no_progress' | 'stall';
109
111
  readonly message: string;
110
112
  readonly stopReason: string;
111
113
  readonly issueSummaries?: readonly string[];
@@ -170,7 +172,7 @@ export interface SessionState {
170
172
  }>;
171
173
  issueSummaries: string[];
172
174
  pendingSteerParts: string[];
173
- stuckReason: 'repeated_tool_call' | 'no_progress' | null;
175
+ stuckReason: 'repeated_tool_call' | 'no_progress' | 'stall' | null;
174
176
  timeoutReason: 'wall_clock' | 'max_turns' | null;
175
177
  turnCount: number;
176
178
  }
@@ -260,6 +262,7 @@ export interface SessionContext {
260
262
  readonly initialPrompt: string;
261
263
  readonly sessionTimeoutMs: number;
262
264
  readonly maxTurns: number;
265
+ readonly stallTimeoutMs: number;
263
266
  }
264
267
  export declare function buildSessionContext(trigger: WorkflowTrigger, context: import('./context-loader.js').ContextBundle, firstStepPrompt: string, effectiveWorkspacePath: string): SessionContext;
265
268
  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>;
@@ -277,6 +280,6 @@ export interface TurnEndSubscriberContext {
277
280
  readonly stuckRepeatThreshold: number;
278
281
  }
279
282
  export declare function buildTurnEndSubscriber(ctx: TurnEndSubscriberContext): (event: AgentEvent) => Promise<void>;
280
- export declare function buildAgentCallbacks(sessionId: string, state: SessionState, modelId: string, emitter: DaemonEventEmitter | undefined, stuckRepeatThreshold: number): AgentLoopCallbacks;
283
+ export declare function buildAgentCallbacks(sessionId: string, state: SessionState, modelId: string, emitter: DaemonEventEmitter | undefined, stuckRepeatThreshold: number, workflowId?: string): AgentLoopCallbacks;
281
284
  export declare function buildSessionResult(state: Readonly<SessionState>, stopReason: string, errorMessage: string | undefined, trigger: WorkflowTrigger, sessionId: string, sessionWorktreePath: string | undefined): WorkflowRunResult;
282
285
  export declare function runWorkflow(trigger: WorkflowTrigger, ctx: V2ToolContext, apiKey: string, daemonRegistry?: DaemonRegistry, emitter?: DaemonEventEmitter, activeSessionSet?: ActiveSessionSet, _statsDir?: string, _sessionsDir?: string, source?: SessionSource): Promise<WorkflowRunResult>;
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.DAEMON_SOUL_TEMPLATE = exports.DAEMON_SOUL_DEFAULT = exports.WORKTREES_DIR = exports.DEFAULT_MAX_TURNS = exports.DEFAULT_SESSION_TIMEOUT_MINUTES = exports.makeSignalCoordinatorTool = exports.makeReportIssueTool = exports.makeSpawnAgentTool = exports.makeGrepTool = exports.makeGlobTool = exports.makeEditTool = exports.makeWriteTool = exports.makeReadTool = exports.makeBashTool = exports.makeCompleteStepTool = exports.makeContinueWorkflowTool = exports.DAEMON_SIGNALS_DIR = exports.DAEMON_SESSIONS_DIR = void 0;
39
+ exports.DAEMON_SOUL_TEMPLATE = exports.DAEMON_SOUL_DEFAULT = exports.WORKTREES_DIR = exports.DEFAULT_STALL_TIMEOUT_SECONDS = exports.DEFAULT_MAX_TURNS = exports.DEFAULT_SESSION_TIMEOUT_MINUTES = exports.makeSignalCoordinatorTool = exports.makeReportIssueTool = exports.makeSpawnAgentTool = exports.makeGrepTool = exports.makeGlobTool = exports.makeEditTool = exports.makeWriteTool = exports.makeReadTool = exports.makeBashTool = exports.makeCompleteStepTool = exports.makeContinueWorkflowTool = exports.DAEMON_SIGNALS_DIR = exports.DAEMON_SESSIONS_DIR = void 0;
40
40
  exports.readDaemonSessionState = readDaemonSessionState;
41
41
  exports.readAllDaemonSessions = readAllDaemonSessions;
42
42
  exports.runStartupRecovery = runStartupRecovery;
@@ -110,6 +110,7 @@ const MAX_SESSION_RECAP_NOTES = 3;
110
110
  const MAX_SESSION_NOTE_CHARS = 800;
111
111
  exports.DEFAULT_SESSION_TIMEOUT_MINUTES = 30;
112
112
  exports.DEFAULT_MAX_TURNS = 200;
113
+ exports.DEFAULT_STALL_TIMEOUT_SECONDS = 120;
113
114
  const MAX_ORPHAN_AGE_MS = 2 * 60 * 60 * 1000;
114
115
  const MAX_WORKTREE_ORPHAN_AGE_MS = 24 * 60 * 60 * 1000;
115
116
  const WORKRAIL_DIR = path.join(os.homedir(), '.workrail');
@@ -980,7 +981,8 @@ function buildSessionContext(trigger, context, firstStepPrompt, effectiveWorkspa
980
981
  '\n\nComplete all step work, then call complete_step with your notes to advance.';
981
982
  const sessionTimeoutMs = (trigger.agentConfig?.maxSessionMinutes ?? exports.DEFAULT_SESSION_TIMEOUT_MINUTES) * 60 * 1000;
982
983
  const maxTurns = trigger.agentConfig?.maxTurns ?? exports.DEFAULT_MAX_TURNS;
983
- return { systemPrompt, initialPrompt, sessionTimeoutMs, maxTurns };
984
+ const stallTimeoutMs = (trigger.agentConfig?.stallTimeoutSeconds ?? exports.DEFAULT_STALL_TIMEOUT_SECONDS) * 1000;
985
+ return { systemPrompt, initialPrompt, sessionTimeoutMs, maxTurns, stallTimeoutMs };
984
986
  }
985
987
  async function buildPreAgentSession(trigger, ctx, apiKey, sessionId, startMs, statsDir, sessionsDir, emitter, daemonRegistry, activeSessionSet, source) {
986
988
  let agentClient;
@@ -1019,7 +1021,7 @@ async function buildPreAgentSession(trigger, ctx, apiKey, sessionId, startMs, st
1019
1021
  isComplete = s.isComplete;
1020
1022
  }
1021
1023
  else {
1022
- const startResult = await (0, start_js_1.executeStartWorkflow)({ workflowId: trigger.workflowId, workspacePath: trigger.workspacePath, goal: trigger.goal }, ctx, { is_autonomous: 'true', workspacePath: trigger.workspacePath });
1024
+ const startResult = await (0, start_js_1.executeStartWorkflow)({ workflowId: trigger.workflowId, workspacePath: trigger.workspacePath, goal: trigger.goal }, ctx, { is_autonomous: 'true', workspacePath: trigger.workspacePath, triggerSource: 'daemon' });
1023
1025
  if (startResult.isErr()) {
1024
1026
  writeExecutionStats(statsDir, sessionId, trigger.workflowId, startMs, 'error', 0);
1025
1027
  return {
@@ -1177,8 +1179,8 @@ function constructTools(session, ctx, apiKey, schemas, scope) {
1177
1179
  (0, continue_workflow_js_1.makeCompleteStepTool)(sid, ctx, () => state.currentContinueToken, onAdvance, onComplete, (t) => { state.currentContinueToken = t; }, schemas, index_js_1.executeContinueWorkflow, emitter, workrailSid),
1178
1180
  (0, continue_workflow_js_1.makeContinueWorkflowTool)(sid, ctx, onAdvance, onComplete, schemas, index_js_1.executeContinueWorkflow, emitter, workrailSid),
1179
1181
  (0, bash_js_1.makeBashTool)(sessionWorkspacePath, schemas, sid, emitter, workrailSid),
1180
- (0, file_tools_js_1.makeReadTool)(readFileStateMap, schemas, sid, emitter, workrailSid),
1181
- (0, file_tools_js_1.makeWriteTool)(readFileStateMap, schemas, sid, emitter, workrailSid),
1182
+ (0, file_tools_js_1.makeReadTool)(sessionWorkspacePath, readFileStateMap, schemas, sid, emitter, workrailSid),
1183
+ (0, file_tools_js_1.makeWriteTool)(sessionWorkspacePath, readFileStateMap, schemas, sid, emitter, workrailSid),
1182
1184
  (0, glob_grep_js_1.makeGlobTool)(sessionWorkspacePath, schemas, sid, emitter, workrailSid),
1183
1185
  (0, glob_grep_js_1.makeGrepTool)(sessionWorkspacePath, schemas, sid, emitter, workrailSid),
1184
1186
  (0, file_tools_js_1.makeEditTool)(sessionWorkspacePath, readFileStateMap, schemas, sid, emitter, workrailSid),
@@ -1241,7 +1243,7 @@ function buildTurnEndSubscriber(ctx) {
1241
1243
  (0, step_injector_js_1.injectPendingSteps)(ctx.state, ctx.agent);
1242
1244
  };
1243
1245
  }
1244
- function buildAgentCallbacks(sessionId, state, modelId, emitter, stuckRepeatThreshold) {
1246
+ function buildAgentCallbacks(sessionId, state, modelId, emitter, stuckRepeatThreshold, workflowId) {
1245
1247
  return {
1246
1248
  onLlmTurnStarted: ({ messageCount }) => {
1247
1249
  emitter?.emit({ kind: 'llm_turn_started', sessionId, messageCount, modelId, ...(0, _shared_js_1.withWorkrailSession)(state.workrailSessionId) });
@@ -1261,6 +1263,21 @@ function buildAgentCallbacks(sessionId, state, modelId, emitter, stuckRepeatThre
1261
1263
  onToolCallFailed: ({ toolName, durationMs, errorMessage }) => {
1262
1264
  emitter?.emit({ kind: 'tool_call_failed', sessionId, toolName, durationMs, errorMessage, ...(0, _shared_js_1.withWorkrailSession)(state.workrailSessionId) });
1263
1265
  },
1266
+ onStallDetected: () => {
1267
+ state.stuckReason = 'stall';
1268
+ emitter?.emit({
1269
+ kind: 'agent_stuck',
1270
+ sessionId,
1271
+ reason: 'stall',
1272
+ detail: `No LLM API call started within the stall timeout window. Last tool calls: ${state.lastNToolCalls.map((c) => c.toolName).join(', ') || 'none'}`,
1273
+ ...(0, _shared_js_1.withWorkrailSession)(state.workrailSessionId),
1274
+ });
1275
+ void writeStuckOutboxEntry({
1276
+ workflowId: workflowId ?? sessionId,
1277
+ reason: 'stall',
1278
+ ...(state.issueSummaries.length > 0 ? { issueSummaries: [...state.issueSummaries] } : {}),
1279
+ });
1280
+ },
1264
1281
  };
1265
1282
  }
1266
1283
  function buildSessionResult(state, stopReason, errorMessage, trigger, sessionId, sessionWorktreePath) {
@@ -1355,7 +1372,7 @@ async function buildAgentReadySession(preAgentSession, trigger, ctx, apiKey, ses
1355
1372
  const contextBundle = await contextLoader.loadSession(startContinueToken, baseCtx);
1356
1373
  const effectiveWorkspacePath = sessionWorkspacePath ?? trigger.workspacePath;
1357
1374
  const sessionCtx = buildSessionContext(trigger, contextBundle, firstStepPrompt || 'No step content available', effectiveWorkspacePath);
1358
- const agentCallbacks = buildAgentCallbacks(sessionId, state, modelId, emitter, STUCK_REPEAT_THRESHOLD);
1375
+ const agentCallbacks = buildAgentCallbacks(sessionId, state, modelId, emitter, STUCK_REPEAT_THRESHOLD, trigger.workflowId);
1359
1376
  const agent = new agent_loop_js_1.AgentLoop({
1360
1377
  systemPrompt: sessionCtx.systemPrompt,
1361
1378
  modelId,
@@ -1366,6 +1383,7 @@ async function buildAgentReadySession(preAgentSession, trigger, ctx, apiKey, ses
1366
1383
  ...(trigger.agentConfig?.maxOutputTokens !== undefined
1367
1384
  ? { maxTokens: trigger.agentConfig.maxOutputTokens }
1368
1385
  : {}),
1386
+ stallTimeoutMs: sessionCtx.stallTimeoutMs,
1369
1387
  });
1370
1388
  handle?.setAgent(agent);
1371
1389
  return {
@@ -473,16 +473,16 @@
473
473
  "sha256": "5fe866e54f796975dec5d8ba9983aefd86074db212d3fccd64eed04bc9f0b3da",
474
474
  "bytes": 8011
475
475
  },
476
- "console-ui/assets/index-BUyvn3iN.js": {
477
- "sha256": "24da815a28eafc4cfcb6e10a688bf283a27b700d0e5eb1518e8ca9a8fd8de8e8",
478
- "bytes": 768234
479
- },
480
476
  "console-ui/assets/index-DHrKiMCf.css": {
481
477
  "sha256": "40290b50e21ee7e82433efe13b1aa31c1ea608bd057a5c4e324982f284bc928b",
482
478
  "bytes": 60673
483
479
  },
480
+ "console-ui/assets/index-HnM-KywY.js": {
481
+ "sha256": "dceb8f4d616f8c18643b6705b96f0749436f32c5c39fb71df7cadd9cb8def8a0",
482
+ "bytes": 768234
483
+ },
484
484
  "console-ui/index.html": {
485
- "sha256": "04f18ffc27f4f69a55a55deb11a2ace71b757b6c0320340daff8fb9f62e71a76",
485
+ "sha256": "73bcca58451f05a95e6b4cbad9ce993f2bbbc5bddb4d92ebbef8969fb3422bec",
486
486
  "bytes": 417
487
487
  },
488
488
  "console/standalone-console.d.ts": {
@@ -606,12 +606,12 @@
606
606
  "bytes": 1330
607
607
  },
608
608
  "daemon/agent-loop.d.ts": {
609
- "sha256": "a05b27f2cdc7bacd35bb41b9b271367ba9d1f550c9cbf873f1a9b4fa092e8bbb",
610
- "bytes": 3779
609
+ "sha256": "01e2a49307b62fb035861bb50b4022f2d76f5207151f4faabc194e116a1f9d44",
610
+ "bytes": 3891
611
611
  },
612
612
  "daemon/agent-loop.js": {
613
- "sha256": "aa47bea99cf9a5ce35d2bc375b6dd51a9fbb68d36f2b185ee0a98176f67d647d",
614
- "bytes": 9803
613
+ "sha256": "fb154e46790a89fe4a92cfa5e3c37361b1d1a41c2443437105cfda9d6c78a981",
614
+ "bytes": 10607
615
615
  },
616
616
  "daemon/context-loader.d.ts": {
617
617
  "sha256": "db099ae68c8f0fd69503afd49daaf2f84fa0ada9791dc3c5e3278382506b13c5",
@@ -630,8 +630,8 @@
630
630
  "bytes": 1216
631
631
  },
632
632
  "daemon/daemon-events.d.ts": {
633
- "sha256": "469cc9b6954e19a5eb87c3fac42f96fd66cd31321b33ce741fb9c1aa64cb2b80",
634
- "bytes": 5369
633
+ "sha256": "465a4e3e034c11cbe5eb05693e6bbed9eb4c1d9a892b72249b6d5f8adb7b0339",
634
+ "bytes": 5379
635
635
  },
636
636
  "daemon/daemon-events.js": {
637
637
  "sha256": "b6841eef4634bb266faf81961c1e387b535dd64a74d58582f3f2bad8c3469d95",
@@ -702,12 +702,12 @@
702
702
  "bytes": 11377
703
703
  },
704
704
  "daemon/tools/file-tools.d.ts": {
705
- "sha256": "a05940e571d7e353007132b4264eb79eb37f1b68fc403e0fc1d4ed6125b54163",
706
- "bytes": 819
705
+ "sha256": "a758cb2bc9f657110e2a18d31db761d375eef4034aa8016102ae68ef3753c7bc",
706
+ "bytes": 865
707
707
  },
708
708
  "daemon/tools/file-tools.js": {
709
- "sha256": "c47cfc384850bc4f823450fa44bebd507a608f848de7539c1bda537e4c805f68",
710
- "bytes": 10714
709
+ "sha256": "289a440a337f11b04192884278d85f8a9a0b3240f3b53583a0c3fd0d78eda0d5",
710
+ "bytes": 11048
711
711
  },
712
712
  "daemon/tools/glob-grep.d.ts": {
713
713
  "sha256": "13b79eae6df2fa4b736543b821d868399a1659f3b05c60e5efac63fc5ea41b46",
@@ -738,8 +738,8 @@
738
738
  "bytes": 605
739
739
  },
740
740
  "daemon/tools/spawn-agent.js": {
741
- "sha256": "f740326855d0b051a4d99ce000046df2bc01d446c057980bfb0fe4539605738a",
742
- "bytes": 7847
741
+ "sha256": "59a552a553a48921cc6e0b4720de5006a8c871637f2d6f3b78d0fed1adaece4d",
742
+ "bytes": 7872
743
743
  },
744
744
  "daemon/turn-end/conversation-flusher.d.ts": {
745
745
  "sha256": "5a8c1666ad18c31f49cb34ad2f4b5f134437bfb902dc5f89ea3db568ca44976d",
@@ -766,12 +766,12 @@
766
766
  "bytes": 429
767
767
  },
768
768
  "daemon/workflow-runner.d.ts": {
769
- "sha256": "cdea9422bff42b6c5833b343c100d947e164eb015a2a2777c89ab32fca70c426",
770
- "bytes": 13073
769
+ "sha256": "757ac2f51eb800f1b7d714fdf86695fac10322829f334166f0a083abdabe0cb1",
770
+ "bytes": 13256
771
771
  },
772
772
  "daemon/workflow-runner.js": {
773
- "sha256": "aabe365f2250461c2762e87eeff1b8742e48a80813cf49c6c1d0bac7c72d2242",
774
- "bytes": 78676
773
+ "sha256": "9d25e56934be6a7cfdfc54086faacee6e3119e7b7d01240cb958749ea33f8bcd",
774
+ "bytes": 79776
775
775
  },
776
776
  "di/container.d.ts": {
777
777
  "sha256": "003bb7fb7478d627524b9b1e76bd0a963a243794a687ff233b96dc0e33a06d9f",
@@ -1274,8 +1274,8 @@
1274
1274
  "bytes": 1437
1275
1275
  },
1276
1276
  "mcp/handlers/v2-execution/index.js": {
1277
- "sha256": "dff4cf30eee4efc6f37257995e9e9bee6824d8a3eeaa14bfcc63abf452e0e057",
1278
- "bytes": 7731
1277
+ "sha256": "ab5ca7e41236fcbcbb0116b3a11bd931478f8ec4be1073884c33d6aee86cc22e",
1278
+ "bytes": 7757
1279
1279
  },
1280
1280
  "mcp/handlers/v2-execution/replay.d.ts": {
1281
1281
  "sha256": "4f1601755e98c336f7d98c096753c38559d76ab5ebbacb1aed679983d46cb6b7",
@@ -1290,8 +1290,8 @@
1290
1290
  "bytes": 3558
1291
1291
  },
1292
1292
  "mcp/handlers/v2-execution/start.js": {
1293
- "sha256": "be45c65e839a4dc6ab41fe3562fa16059e3719d82ea38007edeb570b642987d0",
1294
- "bytes": 21576
1293
+ "sha256": "18e0998fad89943f975b7a59eaecf976871076c2952772cd141952d5dbbd2169",
1294
+ "bytes": 21784
1295
1295
  },
1296
1296
  "mcp/handlers/v2-execution/workflow-object-cache.d.ts": {
1297
1297
  "sha256": "7e58a2a020fd8443821dbe4e6a2702a9882c517f032a340c1b393cdebf4af907",
@@ -1786,8 +1786,8 @@
1786
1786
  "bytes": 854
1787
1787
  },
1788
1788
  "trigger/coordinator-deps.js": {
1789
- "sha256": "a1f112821bde6c31037442bb3679a3e8b5e9103e6801764cdba6ac9ab191d2bd",
1790
- "bytes": 15023
1789
+ "sha256": "ade7810cf782d88e58074e75a937de08b350ff2ffae533b563fb37753c7ef49c",
1790
+ "bytes": 15048
1791
1791
  },
1792
1792
  "trigger/delivery-action.d.ts": {
1793
1793
  "sha256": "559e2b2645aa60528f73de351cd35ebf45c5b82f47797aa15ddd681319315d39",
@@ -1854,12 +1854,12 @@
1854
1854
  "bytes": 6968
1855
1855
  },
1856
1856
  "trigger/polling-scheduler.d.ts": {
1857
- "sha256": "c8bcd28794a23906feabe276725e63fe7af79749e0517a46c6e0ed41da0cabb2",
1858
- "bytes": 1152
1857
+ "sha256": "0f3fe224d8aa4449831a46037a84b68a5cf5fe03b82379141b6b1cddac420e12",
1858
+ "bytes": 1241
1859
1859
  },
1860
1860
  "trigger/polling-scheduler.js": {
1861
- "sha256": "f920526c08cbed491ede6215e227eb3af7c57e46006e5587bb072cc1987e4081",
1862
- "bytes": 27720
1861
+ "sha256": "49c2c436476d3bebb753cace02be650d318f3c2bfb223dc40a4d36e0d11b156d",
1862
+ "bytes": 28320
1863
1863
  },
1864
1864
  "trigger/trigger-listener.d.ts": {
1865
1865
  "sha256": "57192a2e0e01d472985acd6d17be41930d2c189e029a11137661f990e1446ed3",
@@ -1882,12 +1882,12 @@
1882
1882
  "bytes": 1830
1883
1883
  },
1884
1884
  "trigger/trigger-store.js": {
1885
- "sha256": "9c8ef1c06464e325cc904e215a0cd370a49631db371c06f5f3e64510d200bc70",
1886
- "bytes": 49788
1885
+ "sha256": "79e7fe516eede2008fc3a32c439c2840a4895b265b13dba8977a76a51d0903e4",
1886
+ "bytes": 50609
1887
1887
  },
1888
1888
  "trigger/types.d.ts": {
1889
- "sha256": "0061e573b1ed3cc5217d6b88636a3c5bde6773b692f3c3ad9a7d3f115ee34c8b",
1890
- "bytes": 4443
1889
+ "sha256": "68887851741b85262c438d602f1892cd1a4c8ce8adf9f3aeb483965b3011aecf",
1890
+ "bytes": 4490
1891
1891
  },
1892
1892
  "trigger/types.js": {
1893
1893
  "sha256": "45b4e4f23a6d1a2b07350196871b0c53840e5d8142b47f7acedd2f40ae7a6b73",
@@ -2478,8 +2478,8 @@
2478
2478
  "bytes": 3397
2479
2479
  },
2480
2480
  "v2/durable-core/schemas/export-bundle/index.d.ts": {
2481
- "sha256": "823a96b41bc27defef5ba7fc2eb34ab0cf09db2f2d71dd9820d02564b1cce788",
2482
- "bytes": 551200
2481
+ "sha256": "ae48214ae394702c323a6a4ccd0498ba54c3abcc3104ea9396f5f226b633423e",
2482
+ "bytes": 552192
2483
2483
  },
2484
2484
  "v2/durable-core/schemas/export-bundle/index.js": {
2485
2485
  "sha256": "6e3566b2d05ea6302bbf4d311b8ec3e94725a8523834efe7670a79e7bd7dc40d",
@@ -2534,12 +2534,12 @@
2534
2534
  "bytes": 2138
2535
2535
  },
2536
2536
  "v2/durable-core/schemas/session/events.d.ts": {
2537
- "sha256": "c5117fba94923bbc61dfcf705c96bde3d1ee6f0e27dd558ad728cb851ed29a50",
2538
- "bytes": 84590
2537
+ "sha256": "878c97d2442d782d202316606b46ad8bf51ba98b0f8b892fd1f6a1c1a6e8eb70",
2538
+ "bytes": 84874
2539
2539
  },
2540
2540
  "v2/durable-core/schemas/session/events.js": {
2541
- "sha256": "6b1f80f3d9d7329a59b4afd433e939c2c0e22da5b1e9b0cb1586e124522ed31f",
2542
- "bytes": 13831
2541
+ "sha256": "23651dfa8219a75318474629c45e37bdad2f91f48d1555dfbed649c5c2ed830a",
2542
+ "bytes": 13894
2543
2543
  },
2544
2544
  "v2/durable-core/schemas/session/gaps.d.ts": {
2545
2545
  "sha256": "85d17b865a1ebe9deaa0c99d69039c514b217362715c6697b0bc5908cbf9fff0",
@@ -3218,20 +3218,20 @@
3218
3218
  "bytes": 572
3219
3219
  },
3220
3220
  "v2/usecases/console-routes.js": {
3221
- "sha256": "02e3be4472172bf03efb573376885fdb893af9b81b157370e25597750f77b03e",
3222
- "bytes": 31729
3221
+ "sha256": "a99eabcb4c1e6ed5d3c9e06e4475e28a2e6dba62b74997ac08503f24f1a78f54",
3222
+ "bytes": 31754
3223
3223
  },
3224
3224
  "v2/usecases/console-service.d.ts": {
3225
3225
  "sha256": "fc8fe65427fa9f4f3535344b385b36f66ca06b7e3bfaea708931817a3edcad2b",
3226
3226
  "bytes": 1701
3227
3227
  },
3228
3228
  "v2/usecases/console-service.js": {
3229
- "sha256": "0d0296877eeb561603d55d5e48e31e69a8e338995cebacdecff20f887012c4c8",
3230
- "bytes": 39213
3229
+ "sha256": "46c126cc1edee0e672629ec546bc80dcd0700044d89867277dfb285277fff206",
3230
+ "bytes": 40380
3231
3231
  },
3232
3232
  "v2/usecases/console-types.d.ts": {
3233
- "sha256": "a45892442f1581feb00e2ad9bbe172d34703d03855801288cf9269ad35eaa6aa",
3234
- "bytes": 8002
3233
+ "sha256": "854975a0735590ac2981c1c4b5b93a0518eb97493567e88af4ffa101e596195f",
3234
+ "bytes": 8094
3235
3235
  },
3236
3236
  "v2/usecases/console-types.js": {
3237
3237
  "sha256": "d43aa81f5bc89faa359e0f97c814ba25155591ff078fbb9bfd40f8c7c9683230",
@@ -35,7 +35,7 @@ async function handleV2StartWorkflow(input, ctx) {
35
35
  const rememberedRootFailure = await (0, remembered_roots_js_1.rememberExplicitWorkspaceRoot)(input.workspacePath, guard.ctx.v2.rememberedRootsStore);
36
36
  if (rememberedRootFailure)
37
37
  return rememberedRootFailure;
38
- return (0, start_js_1.executeStartWorkflow)(input, guard.ctx).match((result) => (0, types_js_1.success)((0, render_envelope_js_1.attachV2ExecutionRenderMetadata)({
38
+ return (0, start_js_1.executeStartWorkflow)(input, guard.ctx, { triggerSource: 'mcp' }).match((result) => (0, types_js_1.success)((0, render_envelope_js_1.attachV2ExecutionRenderMetadata)({
39
39
  response: result.response,
40
40
  lifecycle: 'start',
41
41
  contentEnvelope: result.contentEnvelope,
@@ -140,6 +140,9 @@ function buildInitialEvents(args) {
140
140
  workflowHash,
141
141
  workflowSourceKind,
142
142
  workflowSourceRef,
143
+ ...(extraContext?.['triggerSource'] === 'daemon' || extraContext?.['triggerSource'] === 'mcp'
144
+ ? { triggerSource: extraContext['triggerSource'] }
145
+ : {}),
143
146
  },
144
147
  },
145
148
  {
@@ -57,7 +57,7 @@ function createCoordinatorDeps(deps) {
57
57
  if (dispatch === null) {
58
58
  return { kind: 'err', error: 'in-process router not initialized -- coordinator deps not ready' };
59
59
  }
60
- const startResult = await (0, start_js_1.executeStartWorkflow)({ workflowId, workspacePath: workspace, goal }, ctx, { is_autonomous: 'true', workspacePath: workspace });
60
+ const startResult = await (0, start_js_1.executeStartWorkflow)({ workflowId, workspacePath: workspace, goal }, ctx, { is_autonomous: 'true', workspacePath: workspace, triggerSource: 'daemon' });
61
61
  if (startResult.isErr()) {
62
62
  const detail = `${startResult.error.kind}${'message' in startResult.error ? ': ' + startResult.error.message : ''}`;
63
63
  return { kind: 'err', error: `Session creation failed: ${detail}` };
@@ -31,3 +31,4 @@ export declare class PollingScheduler {
31
31
  private dispatchAndRecord;
32
32
  private doPollGitHubQueue;
33
33
  }
34
+ export declare function rotateLogFile(logPath: string, maxBytes: number): Promise<void>;
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.PollingScheduler = void 0;
37
+ exports.rotateLogFile = rotateLogFile;
37
38
  const gitlab_poller_js_1 = require("./adapters/gitlab-poller.js");
38
39
  const github_poller_js_1 = require("./adapters/github-poller.js");
39
40
  const github_queue_poller_js_1 = require("./adapters/github-queue-poller.js");
@@ -457,17 +458,35 @@ async function countActiveSessions(sessionsDir) {
457
458
  }
458
459
  }
459
460
  const MAX_QUEUE_POLL_FILE_SIZE = 10 * 1024 * 1024;
460
- async function appendQueuePollLog(entry) {
461
- const logPath = path.join(os.homedir(), '.workrail', 'queue-poll.jsonl');
461
+ async function rotateLogFile(logPath, maxBytes) {
462
462
  try {
463
+ const stat = await fs.stat(logPath);
464
+ if (stat.size < maxBytes)
465
+ return;
466
+ const backup2 = logPath + '.2';
463
467
  try {
464
- const stat = await fs.stat(logPath);
465
- if (stat.size >= MAX_QUEUE_POLL_FILE_SIZE) {
466
- await fs.rename(logPath, logPath + '.1');
467
- }
468
+ await fs.unlink(backup2);
469
+ }
470
+ catch {
471
+ }
472
+ const backup1 = logPath + '.1';
473
+ try {
474
+ await fs.rename(backup1, backup2);
468
475
  }
469
476
  catch {
470
477
  }
478
+ await fs.rename(logPath, backup1);
479
+ }
480
+ catch (e) {
481
+ const isEnoent = e instanceof Error && 'code' in e && e.code === 'ENOENT';
482
+ if (!isEnoent) {
483
+ console.warn(`[rotateLogFile] Failed to rotate ${logPath}: ${e instanceof Error ? e.message : String(e)}`);
484
+ }
485
+ }
486
+ }
487
+ async function appendQueuePollLog(entry, logPath = path.join(os.homedir(), '.workrail', 'queue-poll.jsonl')) {
488
+ try {
489
+ await rotateLogFile(logPath, MAX_QUEUE_POLL_FILE_SIZE);
471
490
  await fs.appendFile(logPath, JSON.stringify(entry) + '\n', 'utf8');
472
491
  }
473
492
  catch (e) {
@@ -221,6 +221,8 @@ function parseTriggersYaml(content) {
221
221
  agentConfig.maxOutputTokens = acValueResult.value;
222
222
  else if (acKey === 'stuckAbortPolicy')
223
223
  agentConfig.stuckAbortPolicy = acValueResult.value;
224
+ else if (acKey === 'stallTimeoutSeconds')
225
+ agentConfig.stallTimeoutSeconds = acValueResult.value;
224
226
  }
225
227
  lineIndex++;
226
228
  }
@@ -667,13 +669,26 @@ function validateAndResolveTrigger(raw, env, workspaces = {}) {
667
669
  }
668
670
  stuckAbortPolicy = rawSap;
669
671
  }
670
- if (model !== undefined || maxSessionMinutes !== undefined || maxTurns !== undefined || maxOutputTokens !== undefined || stuckAbortPolicy !== undefined) {
672
+ let stallTimeoutSeconds;
673
+ if (raw.agentConfig.stallTimeoutSeconds !== undefined) {
674
+ const asNumber = Number(raw.agentConfig.stallTimeoutSeconds);
675
+ if (!Number.isInteger(asNumber) || asNumber <= 0) {
676
+ return (0, result_js_1.err)({
677
+ kind: 'invalid_field_value',
678
+ field: 'agentConfig.stallTimeoutSeconds (must be a positive integer)',
679
+ triggerId: rawId,
680
+ });
681
+ }
682
+ stallTimeoutSeconds = asNumber;
683
+ }
684
+ if (model !== undefined || maxSessionMinutes !== undefined || maxTurns !== undefined || maxOutputTokens !== undefined || stuckAbortPolicy !== undefined || stallTimeoutSeconds !== undefined) {
671
685
  agentConfig = {
672
686
  ...(model !== undefined ? { model } : {}),
673
687
  ...(maxSessionMinutes !== undefined ? { maxSessionMinutes } : {}),
674
688
  ...(maxTurns !== undefined ? { maxTurns } : {}),
675
689
  ...(maxOutputTokens !== undefined ? { maxOutputTokens } : {}),
676
690
  ...(stuckAbortPolicy !== undefined ? { stuckAbortPolicy } : {}),
691
+ ...(stallTimeoutSeconds !== undefined ? { stallTimeoutSeconds } : {}),
677
692
  };
678
693
  }
679
694
  }
@@ -80,6 +80,7 @@ export interface TriggerDefinition {
80
80
  readonly maxOutputTokens?: number;
81
81
  readonly stuckAbortPolicy?: 'abort' | 'notify_only';
82
82
  readonly noProgressAbortEnabled?: boolean;
83
+ readonly stallTimeoutSeconds?: number;
83
84
  };
84
85
  readonly concurrencyMode: 'serial' | 'parallel';
85
86
  readonly maxQueueDepth?: number;