@drisp/cli 0.5.23 → 0.5.24

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.
@@ -8,7 +8,7 @@ import {
8
8
  installWorkflowPlugins,
9
9
  resolveWorkflow,
10
10
  writeGlobalConfig
11
- } from "./chunk-JHSADKDJ.js";
11
+ } from "./chunk-7UUPLAP4.js";
12
12
 
13
13
  // src/setup/steps/WorkflowInstallWizard.tsx
14
14
  import { useState, useEffect, useCallback, useRef } from "react";
@@ -92,4 +92,4 @@ function WorkflowInstallWizard({ source, onDone }) {
92
92
  export {
93
93
  WorkflowInstallWizard as default
94
94
  };
95
- //# sourceMappingURL=WorkflowInstallWizard-CCCUR6KA.js.map
95
+ //# sourceMappingURL=WorkflowInstallWizard-IMU47QWB.js.map
@@ -3,13 +3,14 @@ import {
3
3
  loadOrCreateToken,
4
4
  requireTokenForBind,
5
5
  timingSafeTokenEqual
6
- } from "./chunk-MRAM6EYI.js";
6
+ } from "./chunk-7GEQJQMR.js";
7
7
  import {
8
8
  CHANNEL_REQUEST_ID_REGEX,
9
9
  createUdsServerTransport,
10
10
  generateChannelRequestId,
11
11
  isLoopbackHost,
12
12
  isValidChannelRequestId,
13
+ openVersionedDb,
13
14
  refreshDashboardAccessToken,
14
15
  resolveGatewayPaths,
15
16
  resolveListenSpec,
@@ -19,7 +20,7 @@ import {
19
20
  trackGatewayTransportConnect,
20
21
  trackGatewayTransportDisconnect,
21
22
  writeGatewayTrace
22
- } from "./chunk-BTY7MYYT.js";
23
+ } from "./chunk-YU2WMPRC.js";
23
24
 
24
25
  // src/gateway/daemon.ts
25
26
  import fs4 from "fs";
@@ -2440,7 +2441,7 @@ var cachedVersion = null;
2440
2441
  function readVersion() {
2441
2442
  if (cachedVersion !== null) return cachedVersion;
2442
2443
  try {
2443
- const injected = "0.5.23";
2444
+ const injected = "0.5.24";
2444
2445
  if (typeof injected === "string" && injected.length > 0) {
2445
2446
  cachedVersion = injected;
2446
2447
  return cachedVersion;
@@ -3758,31 +3759,9 @@ function questionFingerprint(req) {
3758
3759
 
3759
3760
  // src/gateway/state/db.ts
3760
3761
  import fs3 from "fs";
3761
- import path3 from "path";
3762
- import Database from "better-sqlite3";
3763
3762
  var GATEWAY_STATE_VERSION = 2;
3764
- function openGatewayState(dbPath) {
3765
- if (dbPath !== ":memory:") {
3766
- fs3.mkdirSync(path3.dirname(dbPath), { recursive: true, mode: 448 });
3767
- }
3768
- const db = new Database(dbPath);
3769
- db.exec("PRAGMA journal_mode = WAL");
3770
- db.exec("PRAGMA foreign_keys = ON");
3771
- initGatewayStateSchema(db);
3772
- if (dbPath !== ":memory:" && process.platform !== "win32") {
3773
- try {
3774
- fs3.chmodSync(dbPath, 384);
3775
- } catch {
3776
- }
3777
- }
3778
- return db;
3779
- }
3780
- function initGatewayStateSchema(db) {
3763
+ var migrateGatewayState = (db, fromVersion) => {
3781
3764
  db.exec(`
3782
- CREATE TABLE IF NOT EXISTS schema_version (
3783
- version INTEGER NOT NULL
3784
- );
3785
-
3786
3765
  -- Inbound chat messages parked while no runtime is registered. Drained
3787
3766
  -- in FIFO id order on session.register. Idempotency key prevents the
3788
3767
  -- same provider message from being parked twice if an adapter retries.
@@ -3814,27 +3793,35 @@ function initGatewayStateSchema(db) {
3814
3793
  CREATE INDEX IF NOT EXISTS ix_channel_outbox_due
3815
3794
  ON channel_outbox(next_attempt_at);
3816
3795
  `);
3817
- const existing = db.prepare("SELECT version FROM schema_version").get();
3818
- if (existing && existing.version > GATEWAY_STATE_VERSION) {
3819
- throw new Error(
3820
- `Gateway state DB has newer schema version ${existing.version} (expected <= ${GATEWAY_STATE_VERSION}). Update athena-cli.`
3821
- );
3822
- }
3823
- if (!existing) {
3824
- db.prepare("INSERT INTO schema_version (version) VALUES (?)").run(
3825
- GATEWAY_STATE_VERSION
3826
- );
3827
- return;
3828
- }
3796
+ if (fromVersion === void 0) return;
3829
3797
  const inboundColumns = db.prepare("PRAGMA table_info(inbound_queue)").all();
3830
3798
  if (!inboundColumns.some((column) => column.name === "attachment_id")) {
3831
3799
  db.prepare("ALTER TABLE inbound_queue ADD COLUMN attachment_id TEXT").run();
3832
3800
  }
3833
- if (existing.version < GATEWAY_STATE_VERSION) {
3801
+ if (fromVersion < GATEWAY_STATE_VERSION) {
3834
3802
  db.prepare("UPDATE schema_version SET version = ?").run(
3835
3803
  GATEWAY_STATE_VERSION
3836
3804
  );
3837
3805
  }
3806
+ };
3807
+ function openGatewayState(dbPath) {
3808
+ const db = openVersionedDb(dbPath, {
3809
+ version: GATEWAY_STATE_VERSION,
3810
+ migrate: migrateGatewayState,
3811
+ foreignKeys: true,
3812
+ ensureDir: true,
3813
+ dirMode: 448,
3814
+ onNewerVersion: (found, expected) => new Error(
3815
+ `Gateway state DB has newer schema version ${found} (expected <= ${expected}). Update athena-cli.`
3816
+ )
3817
+ });
3818
+ if (dbPath !== ":memory:" && process.platform !== "win32") {
3819
+ try {
3820
+ fs3.chmodSync(dbPath, 384);
3821
+ } catch {
3822
+ }
3823
+ }
3824
+ return db;
3838
3825
  }
3839
3826
 
3840
3827
  // src/gateway/transport/tlsWs.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isLoopbackHost
3
- } from "./chunk-BTY7MYYT.js";
3
+ } from "./chunk-YU2WMPRC.js";
4
4
 
5
5
  // src/gateway/auth.ts
6
6
  import crypto from "crypto";
@@ -73,4 +73,4 @@ export {
73
73
  timingSafeTokenEqual,
74
74
  requireTokenForBind
75
75
  };
76
- //# sourceMappingURL=chunk-MRAM6EYI.js.map
76
+ //# sourceMappingURL=chunk-7GEQJQMR.js.map
@@ -1138,7 +1138,7 @@ import fs7 from "fs";
1138
1138
  import os4 from "os";
1139
1139
  import path5 from "path";
1140
1140
 
1141
- // src/core/workflows/loopManager.ts
1141
+ // src/core/workflows/trackerReader.ts
1142
1142
  import fs6 from "fs";
1143
1143
 
1144
1144
  // src/core/workflows/templateVars.ts
@@ -1157,84 +1157,60 @@ function substituteVariables(text, ctx) {
1157
1157
  return result;
1158
1158
  }
1159
1159
 
1160
- // src/core/workflows/loopManager.ts
1160
+ // src/core/workflows/trackerReader.ts
1161
1161
  var DEFAULT_COMPLETION_MARKER = "<!-- WORKFLOW_COMPLETE -->";
1162
1162
  var DEFAULT_BLOCKED_MARKER = "<!-- WORKFLOW_BLOCKED";
1163
1163
  var DEFAULT_TRACKER_PATH = ".athena/{sessionId}/tracker.md";
1164
1164
  var TRACKER_SKELETON_MARKER = "<!-- TRACKER_SKELETON -->";
1165
1165
  var DEFAULT_CONTINUE_PROMPT = "Continue the task. Read the tracker at {trackerPath} for current progress. If the work is complete or blocked, the terminal marker must be the final non-empty line of the tracker; do not write any prose after it.";
1166
- function createLoopManager(trackerPath, config) {
1167
- let iteration = 0;
1168
- let active = true;
1169
- const completionMarker = config.completionMarker ?? DEFAULT_COMPLETION_MARKER;
1170
- const blockedMarker = config.blockedMarker ?? DEFAULT_BLOCKED_MARKER;
1171
- function readTracker() {
1172
- try {
1173
- return fs6.readFileSync(trackerPath, "utf-8");
1174
- } catch {
1175
- return "";
1176
- }
1177
- }
1178
- function getNonEmptyLines(content) {
1179
- return content.trimEnd().split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
1180
- }
1181
- function isBlockedLine(line) {
1182
- return line === `${blockedMarker} -->` || line.startsWith(`${blockedMarker}:`);
1183
- }
1184
- function isTerminalMarkerLine(line) {
1185
- return line === completionMarker || isBlockedLine(line);
1186
- }
1187
- function getTerminalLine(content) {
1188
- const lines = getNonEmptyLines(content);
1189
- return lines.at(-1);
1190
- }
1191
- function getMisplacedTerminalMarker(content) {
1192
- const lines = getNonEmptyLines(content);
1193
- if (lines.length < 2) return void 0;
1194
- const terminalLine = lines.at(-1);
1195
- if (terminalLine && isTerminalMarkerLine(terminalLine)) return void 0;
1196
- return lines.slice(0, -1).find(isTerminalMarkerLine);
1197
- }
1198
- function extractBlockedReason(line) {
1199
- if (!line.startsWith(blockedMarker)) return void 0;
1200
- const afterMarker = line.slice(blockedMarker.length);
1201
- const match = afterMarker.match(/^:\s*(.+?)(?:\s*-->|$)/);
1202
- return match?.[1]?.trim();
1203
- }
1204
- function getState() {
1205
- const content = readTracker();
1206
- const terminalLine = getTerminalLine(content);
1207
- const completed = terminalLine === completionMarker;
1208
- const blocked = terminalLine !== void 0 && isBlockedLine(terminalLine);
1209
- const blockedReason = blocked && terminalLine ? extractBlockedReason(terminalLine) : void 0;
1210
- const misplacedTerminalMarker = getMisplacedTerminalMarker(content);
1211
- const reachedLimit = iteration >= config.maxIterations;
1212
- const skeletonNotReplaced = content.includes(TRACKER_SKELETON_MARKER);
1213
- return {
1214
- active,
1215
- iteration,
1216
- maxIterations: config.maxIterations,
1217
- completionMarker,
1218
- blockedMarker,
1219
- completed,
1220
- blocked,
1221
- blockedReason,
1222
- misplacedTerminalMarker,
1223
- reachedLimit,
1224
- skeletonNotReplaced
1225
- };
1226
- }
1227
- function incrementIteration() {
1228
- iteration++;
1166
+ function readTracker(trackerPath) {
1167
+ try {
1168
+ return fs6.readFileSync(trackerPath, "utf-8");
1169
+ } catch {
1170
+ return "";
1229
1171
  }
1230
- function deactivate() {
1231
- active = false;
1172
+ }
1173
+ function getNonEmptyLines(content) {
1174
+ return content.trimEnd().split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
1175
+ }
1176
+ function isBlockedLine(line, blockedMarker) {
1177
+ return line === `${blockedMarker} -->` || line.startsWith(`${blockedMarker}:`);
1178
+ }
1179
+ function isTerminalMarkerLine(line, completionMarker, blockedMarker) {
1180
+ return line === completionMarker || isBlockedLine(line, blockedMarker);
1181
+ }
1182
+ function getMisplacedTerminalMarker(lines, completionMarker, blockedMarker) {
1183
+ if (lines.length < 2) return void 0;
1184
+ const terminalLine = lines.at(-1);
1185
+ if (terminalLine && isTerminalMarkerLine(terminalLine, completionMarker, blockedMarker)) {
1186
+ return void 0;
1232
1187
  }
1188
+ return lines.slice(0, -1).find((line) => isTerminalMarkerLine(line, completionMarker, blockedMarker));
1189
+ }
1190
+ function extractBlockedReason(line, blockedMarker) {
1191
+ if (!line.startsWith(blockedMarker)) return void 0;
1192
+ const afterMarker = line.slice(blockedMarker.length);
1193
+ const match = afterMarker.match(/^:\s*(.+?)(?:\s*-->|$)/);
1194
+ return match?.[1]?.trim();
1195
+ }
1196
+ function parseTrackerState(content, markers = {}) {
1197
+ const completionMarker = markers.completionMarker ?? DEFAULT_COMPLETION_MARKER;
1198
+ const blockedMarker = markers.blockedMarker ?? DEFAULT_BLOCKED_MARKER;
1199
+ const lines = getNonEmptyLines(content);
1200
+ const terminalLine = lines.at(-1);
1201
+ const completed = terminalLine === completionMarker;
1202
+ const blocked = terminalLine !== void 0 && isBlockedLine(terminalLine, blockedMarker);
1203
+ const blockedReason = blocked && terminalLine ? extractBlockedReason(terminalLine, blockedMarker) : void 0;
1233
1204
  return {
1234
- getState,
1235
- incrementIteration,
1236
- deactivate,
1237
- trackerPath
1205
+ completed,
1206
+ blocked,
1207
+ blockedReason,
1208
+ misplacedTerminalMarker: getMisplacedTerminalMarker(
1209
+ lines,
1210
+ completionMarker,
1211
+ blockedMarker
1212
+ ),
1213
+ skeletonNotReplaced: content.includes(TRACKER_SKELETON_MARKER)
1238
1214
  };
1239
1215
  }
1240
1216
  function buildContinuePrompt(loop) {
@@ -1808,7 +1784,6 @@ function resolveTrackerPath(input) {
1808
1784
  function createWorkflowRunState(input) {
1809
1785
  const { projectDir, sessionId, workflow, harness } = input;
1810
1786
  const trackerResolved = resolveTrackerPath({ projectDir, sessionId, workflow });
1811
- const loopManager = workflow?.loop?.enabled === true && trackerResolved ? createLoopManager(trackerResolved.absolutePath, workflow.loop) : null;
1812
1787
  const { workflowOverride, warnings } = readWorkflowOverride(
1813
1788
  projectDir,
1814
1789
  workflow,
@@ -1818,15 +1793,16 @@ function createWorkflowRunState(input) {
1818
1793
  );
1819
1794
  return {
1820
1795
  workflow,
1821
- loopManager,
1822
1796
  trackerPathForPrompt: trackerResolved?.promptPath,
1823
1797
  workflowOverride,
1824
1798
  warnings
1825
1799
  };
1826
1800
  }
1827
1801
  function prepareWorkflowTurn(state, input) {
1828
- const { workflow, loopManager } = state;
1829
- const prompt = workflow && loopManager && loopManager.getState().iteration > 0 ? buildContinuePrompt({
1802
+ const { workflow } = state;
1803
+ const iteration = input.iteration ?? 1;
1804
+ const isContinuation = workflow?.loop?.enabled === true && iteration > 1;
1805
+ const prompt = isContinuation ? buildContinuePrompt({
1830
1806
  ...workflow.loop,
1831
1807
  trackerPath: state.trackerPathForPrompt ?? workflow.loop?.trackerPath
1832
1808
  }) : workflow ? applyPromptTemplate(workflow.promptTemplate, input.prompt) : input.prompt;
@@ -1839,67 +1815,57 @@ function prepareWorkflowTurn(state, input) {
1839
1815
  warnings: state.warnings
1840
1816
  };
1841
1817
  }
1842
- function shouldContinueWorkflowRun(state) {
1843
- const { workflow, loopManager } = state;
1844
- if (!workflow?.loop?.enabled || !loopManager) {
1845
- return null;
1846
- }
1847
- const loopState = loopManager.getState();
1848
- if (!fs10.existsSync(loopManager.trackerPath)) {
1849
- cleanupWorkflowRun(state);
1818
+
1819
+ // src/core/workflows/useWorkflowSessionController.ts
1820
+ import { useCallback, useEffect, useRef, useState } from "react";
1821
+
1822
+ // src/core/workflows/workflowRunner.ts
1823
+ import crypto from "crypto";
1824
+ import fs12 from "fs";
1825
+ import path9 from "path";
1826
+
1827
+ // src/core/workflows/terminalOutcome.ts
1828
+ import fs11 from "fs";
1829
+ var MISSING_TRACKER_MESSAGE = "the tracker file went missing during the run \u2014 the workflow can no longer verify progress";
1830
+ var SKELETON_NOT_REPLACED_MESSAGE = "tracker skeleton was never replaced \u2014 Claude did not bootstrap the tracker";
1831
+ var MISPLACED_TERMINAL_MARKER_MESSAGE = "terminal workflow marker is not the final non-empty line of the tracker; move all summary text above the marker";
1832
+ function resolveTurnOutcome(input) {
1833
+ const { trackerPath, loop, iteration } = input;
1834
+ if (!fs11.existsSync(trackerPath)) {
1850
1835
  return {
1851
- reason: "missing_tracker",
1852
- maxIterations: loopState.maxIterations
1836
+ kind: "stop",
1837
+ status: "failed",
1838
+ stopReason: MISSING_TRACKER_MESSAGE
1853
1839
  };
1854
1840
  }
1855
- if (loopState.skeletonNotReplaced) {
1856
- cleanupWorkflowRun(state);
1841
+ const tracker = parseTrackerState(readTracker(trackerPath), loop);
1842
+ if (tracker.skeletonNotReplaced) {
1857
1843
  return {
1858
- reason: "skeleton_not_replaced",
1859
- maxIterations: loopState.maxIterations
1844
+ kind: "stop",
1845
+ status: "failed",
1846
+ stopReason: SKELETON_NOT_REPLACED_MESSAGE
1860
1847
  };
1861
1848
  }
1862
- if (loopState.misplacedTerminalMarker) {
1863
- cleanupWorkflowRun(state);
1849
+ if (tracker.misplacedTerminalMarker) {
1864
1850
  return {
1865
- reason: "misplaced_terminal_marker",
1866
- maxIterations: loopState.maxIterations
1851
+ kind: "stop",
1852
+ status: "failed",
1853
+ stopReason: MISPLACED_TERMINAL_MARKER_MESSAGE
1867
1854
  };
1868
1855
  }
1869
- let reason;
1870
- if (loopState.completed) {
1871
- reason = "completed";
1872
- } else if (loopState.blocked) {
1873
- reason = "blocked";
1874
- } else if (loopState.iteration + 1 >= loopState.maxIterations) {
1875
- reason = "max_iterations";
1856
+ if (tracker.completed) {
1857
+ return { kind: "stop", status: "completed" };
1876
1858
  }
1877
- if (reason) {
1878
- cleanupWorkflowRun(state);
1879
- return {
1880
- reason,
1881
- blockedReason: loopState.blockedReason,
1882
- maxIterations: loopState.maxIterations
1883
- };
1859
+ if (tracker.blocked) {
1860
+ return { kind: "stop", status: "blocked", stopReason: tracker.blockedReason };
1884
1861
  }
1885
- loopManager.incrementIteration();
1886
- return null;
1887
- }
1888
- function cleanupWorkflowRun(state) {
1889
- if (!state.loopManager) {
1890
- return;
1862
+ if (iteration >= loop.maxIterations) {
1863
+ return { kind: "stop", status: "exhausted" };
1891
1864
  }
1892
- state.loopManager.deactivate();
1893
- state.loopManager = null;
1865
+ return { kind: "continue" };
1894
1866
  }
1895
1867
 
1896
- // src/core/workflows/useWorkflowSessionController.ts
1897
- import { useCallback, useEffect, useRef, useState } from "react";
1898
-
1899
1868
  // src/core/workflows/workflowRunner.ts
1900
- import crypto from "crypto";
1901
- import fs11 from "fs";
1902
- import path9 from "path";
1903
1869
  var NULL_TOKENS = {
1904
1870
  input: null,
1905
1871
  output: null,
@@ -1956,9 +1922,9 @@ function mergeTokens(base, next) {
1956
1922
  };
1957
1923
  }
1958
1924
  function defaultCreateTracker(trackerPath, content) {
1959
- fs11.mkdirSync(path9.dirname(trackerPath), { recursive: true });
1925
+ fs12.mkdirSync(path9.dirname(trackerPath), { recursive: true });
1960
1926
  try {
1961
- fs11.writeFileSync(trackerPath, content, { encoding: "utf-8", flag: "wx" });
1927
+ fs12.writeFileSync(trackerPath, content, { encoding: "utf-8", flag: "wx" });
1962
1928
  } catch (e) {
1963
1929
  if (e.code !== "EEXIST") throw e;
1964
1930
  }
@@ -2016,83 +1982,72 @@ function createWorkflowRunner(input) {
2016
1982
  let nextContinuation = input.initialContinuation ?? {
2017
1983
  mode: "fresh"
2018
1984
  };
2019
- try {
2020
- while (!cancelled) {
2021
- iterations++;
2022
- const prepared = prepareWorkflowTurn(workflowState, {
2023
- prompt: input.prompt,
2024
- configOverride: void 0
2025
- });
2026
- const turnResult = await input.startTurn({
2027
- prompt: prepared.prompt,
2028
- continuation: nextContinuation,
2029
- configOverride: prepared.configOverride
2030
- });
2031
- if (cancelled) {
2032
- status = "cancelled";
2033
- persist();
2034
- break;
2035
- }
2036
- cumulativeTokens = mergeTokens(cumulativeTokens, turnResult.tokens);
2037
- if (turnResult.error || turnResult.exitCode !== null && turnResult.exitCode !== 0) {
2038
- status = "failed";
2039
- const parts = [];
2040
- if (turnResult.error?.message) {
2041
- parts.push(turnResult.error.message);
2042
- } else if (turnResult.exitCode !== null) {
2043
- parts.push(`Process exited with code ${turnResult.exitCode}`);
2044
- }
2045
- if (turnResult.lastStderr) {
2046
- parts.push(turnResult.lastStderr);
2047
- }
2048
- stopReason = parts.join(": ") || "Turn failed";
2049
- persist();
2050
- break;
2051
- }
2052
- const transport = turnResult.diagnostics?.transport;
2053
- if (transport && transport.streamToolUses > 0 && transport.preToolUseEvents === 0) {
2054
- status = "failed";
2055
- stopReason = `Hook transport broken: observed ${transport.streamToolUses} tool use(s) in Claude stream but received no PreToolUse events.`;
2056
- persist();
2057
- break;
2058
- }
2059
- if (!input.workflow?.loop?.enabled) {
2060
- status = "completed";
2061
- persist();
2062
- break;
1985
+ const loop = input.workflow?.loop;
1986
+ while (!cancelled) {
1987
+ iterations++;
1988
+ const prepared = prepareWorkflowTurn(workflowState, {
1989
+ prompt: input.prompt,
1990
+ iteration: iterations,
1991
+ configOverride: void 0
1992
+ });
1993
+ const turnResult = await input.startTurn({
1994
+ prompt: prepared.prompt,
1995
+ continuation: nextContinuation,
1996
+ configOverride: prepared.configOverride
1997
+ });
1998
+ if (cancelled) {
1999
+ status = "cancelled";
2000
+ persist();
2001
+ break;
2002
+ }
2003
+ cumulativeTokens = mergeTokens(cumulativeTokens, turnResult.tokens);
2004
+ if (turnResult.error || turnResult.exitCode !== null && turnResult.exitCode !== 0) {
2005
+ status = "failed";
2006
+ const parts = [];
2007
+ if (turnResult.error?.message) {
2008
+ parts.push(turnResult.error.message);
2009
+ } else if (turnResult.exitCode !== null) {
2010
+ parts.push(`Process exited with code ${turnResult.exitCode}`);
2063
2011
  }
2064
- const loopStop = shouldContinueWorkflowRun(workflowState);
2065
- if (loopStop) {
2066
- if (loopStop.reason === "completed") {
2067
- status = "completed";
2068
- } else if (loopStop.reason === "blocked") {
2069
- status = "blocked";
2070
- stopReason = loopStop.blockedReason;
2071
- } else if (loopStop.reason === "max_iterations") {
2072
- status = "exhausted";
2073
- } else if (loopStop.reason === "skeleton_not_replaced") {
2074
- status = "failed";
2075
- stopReason = "tracker skeleton was never replaced \u2014 Claude did not bootstrap the tracker";
2076
- } else if (loopStop.reason === "misplaced_terminal_marker") {
2077
- status = "failed";
2078
- stopReason = "terminal workflow marker is not the final non-empty line of the tracker; move all summary text above the marker";
2079
- } else {
2080
- status = "failed";
2081
- stopReason = `Loop stopped: ${loopStop.reason}`;
2082
- }
2083
- persist();
2084
- break;
2012
+ if (turnResult.lastStderr) {
2013
+ parts.push(turnResult.lastStderr);
2085
2014
  }
2015
+ stopReason = parts.join(": ") || "Turn failed";
2086
2016
  persist();
2087
- input.onIterationComplete?.(snapshot());
2088
- nextContinuation = { mode: "fresh" };
2017
+ break;
2089
2018
  }
2090
- if (cancelled && status === "running") {
2091
- status = "cancelled";
2019
+ const transport = turnResult.diagnostics?.transport;
2020
+ if (transport && transport.streamToolUses > 0 && transport.preToolUseEvents === 0) {
2021
+ status = "failed";
2022
+ stopReason = `Hook transport broken: observed ${transport.streamToolUses} tool use(s) in Claude stream but received no PreToolUse events.`;
2092
2023
  persist();
2024
+ break;
2093
2025
  }
2094
- } finally {
2095
- cleanupWorkflowRun(workflowState);
2026
+ if (!loop?.enabled) {
2027
+ status = "completed";
2028
+ persist();
2029
+ break;
2030
+ }
2031
+ if (trackerAbsPath) {
2032
+ const outcome = resolveTurnOutcome({
2033
+ trackerPath: trackerAbsPath,
2034
+ loop,
2035
+ iteration: iterations
2036
+ });
2037
+ if (outcome.kind === "stop") {
2038
+ status = outcome.status;
2039
+ stopReason = outcome.stopReason;
2040
+ persist();
2041
+ break;
2042
+ }
2043
+ }
2044
+ persist();
2045
+ input.onIterationComplete?.(snapshot());
2046
+ nextContinuation = { mode: "fresh" };
2047
+ }
2048
+ if (cancelled && status === "running") {
2049
+ status = "cancelled";
2050
+ persist();
2096
2051
  }
2097
2052
  return {
2098
2053
  runId,
@@ -2219,17 +2174,17 @@ function useWorkflowSessionController(base, input) {
2219
2174
  }
2220
2175
 
2221
2176
  // src/infra/plugins/mcpOptions.ts
2222
- import fs12 from "fs";
2177
+ import fs13 from "fs";
2223
2178
  import path10 from "path";
2224
2179
  function collectMcpServersWithOptions(pluginDirs) {
2225
2180
  const result = [];
2226
2181
  const seen = /* @__PURE__ */ new Set();
2227
2182
  for (const dir of pluginDirs) {
2228
2183
  const mcpPath = path10.join(dir, ".mcp.json");
2229
- if (!fs12.existsSync(mcpPath)) {
2184
+ if (!fs13.existsSync(mcpPath)) {
2230
2185
  continue;
2231
2186
  }
2232
- const config = JSON.parse(fs12.readFileSync(mcpPath, "utf-8"));
2187
+ const config = JSON.parse(fs13.readFileSync(mcpPath, "utf-8"));
2233
2188
  for (const [serverName, serverConfig] of Object.entries(
2234
2189
  config.mcpServers ?? {}
2235
2190
  )) {
@@ -2274,4 +2229,4 @@ export {
2274
2229
  compileWorkflowPlan,
2275
2230
  collectMcpServersWithOptions
2276
2231
  };
2277
- //# sourceMappingURL=chunk-JHSADKDJ.js.map
2232
+ //# sourceMappingURL=chunk-7UUPLAP4.js.map