triflux 10.3.4 → 10.7.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 (329) hide show
  1. package/LICENSE +21 -21
  2. package/bin/tfx-doctor-tui.mjs +1 -1
  3. package/bin/tfx-doctor.mjs +6 -1
  4. package/bin/tfx-profile.mjs +1 -1
  5. package/bin/tfx-setup-tui.mjs +1 -1
  6. package/bin/tfx-setup.mjs +6 -1
  7. package/bin/triflux.mjs +2396 -1140
  8. package/hooks/agent-route-guard.mjs +12 -8
  9. package/hooks/cross-review-tracker.mjs +21 -8
  10. package/hooks/error-context.mjs +19 -7
  11. package/hooks/hook-adaptive-collector.mjs +18 -16
  12. package/hooks/hook-manager.mjs +93 -32
  13. package/hooks/hook-orchestrator.mjs +108 -24
  14. package/hooks/hook-registry.json +11 -0
  15. package/hooks/keyword-rules.json +6 -10
  16. package/hooks/lib/resolve-root.mjs +1 -1
  17. package/hooks/mcp-config-watcher.mjs +6 -2
  18. package/hooks/pipeline-stop.mjs +3 -6
  19. package/hooks/safety-guard.mjs +99 -28
  20. package/hooks/session-start-fast.mjs +143 -0
  21. package/hooks/subagent-verifier.mjs +5 -4
  22. package/hub/account-broker.mjs +256 -60
  23. package/hub/adaptive-diagnostic.mjs +75 -48
  24. package/hub/adaptive-inject.mjs +95 -57
  25. package/hub/adaptive-memory.mjs +156 -42
  26. package/hub/adaptive.mjs +60 -31
  27. package/hub/assign-callbacks.mjs +67 -30
  28. package/hub/bridge.mjs +0 -1
  29. package/hub/cli-adapter-base.mjs +200 -48
  30. package/hub/codex-adapter.mjs +76 -96
  31. package/hub/codex-compat.mjs +3 -3
  32. package/hub/codex-preflight.mjs +63 -37
  33. package/hub/delegator/contracts.mjs +19 -23
  34. package/hub/delegator/index.mjs +3 -3
  35. package/hub/delegator/service.mjs +88 -64
  36. package/hub/delegator/tool-definitions.mjs +5 -5
  37. package/hub/fullcycle.mjs +33 -17
  38. package/hub/gemini-adapter.mjs +69 -94
  39. package/hub/hitl.mjs +89 -30
  40. package/hub/intent.mjs +161 -38
  41. package/hub/lib/cache-guard.mjs +43 -17
  42. package/hub/lib/mcp-response-cache.mjs +66 -32
  43. package/hub/lib/memory-store.mjs +285 -111
  44. package/hub/lib/path-utils.mjs +35 -37
  45. package/hub/lib/process-utils.mjs +106 -37
  46. package/hub/lib/spawn-trace.mjs +527 -0
  47. package/hub/lib/ssh-command.mjs +34 -4
  48. package/hub/lib/ssh-retry.mjs +5 -1
  49. package/hub/lib/uuidv7.mjs +4 -3
  50. package/hub/memory-doctor.mjs +266 -106
  51. package/hub/middleware/request-logger.mjs +61 -34
  52. package/hub/paths.mjs +9 -9
  53. package/hub/pipeline/gates/confidence.mjs +34 -15
  54. package/hub/pipeline/gates/consensus.mjs +27 -15
  55. package/hub/pipeline/gates/index.mjs +7 -3
  56. package/hub/pipeline/gates/selfcheck.mjs +57 -19
  57. package/hub/pipeline/index.mjs +77 -42
  58. package/hub/pipeline/state.mjs +10 -10
  59. package/hub/pipeline/transitions.mjs +40 -23
  60. package/hub/platform.mjs +57 -48
  61. package/hub/promote-penalties.mjs +25 -7
  62. package/hub/quality/deslop.mjs +70 -49
  63. package/hub/research.mjs +32 -25
  64. package/hub/router.mjs +240 -107
  65. package/hub/routing/complexity.mjs +132 -29
  66. package/hub/routing/index.mjs +17 -12
  67. package/hub/routing/q-learning.mjs +76 -28
  68. package/hub/server.mjs +4 -4
  69. package/hub/session-fingerprint.mjs +126 -60
  70. package/hub/state.mjs +84 -43
  71. package/hub/store-adapter.mjs +59 -26
  72. package/hub/store.mjs +356 -153
  73. package/hub/team/agent-map.json +22 -7
  74. package/hub/team/ansi.mjs +186 -122
  75. package/hub/team/backend.mjs +28 -10
  76. package/hub/team/cli/commands/attach.mjs +29 -9
  77. package/hub/team/cli/commands/control.mjs +29 -8
  78. package/hub/team/cli/commands/debug.mjs +32 -11
  79. package/hub/team/cli/commands/focus.mjs +38 -11
  80. package/hub/team/cli/commands/interrupt.mjs +18 -6
  81. package/hub/team/cli/commands/kill.mjs +16 -5
  82. package/hub/team/cli/commands/list.mjs +11 -4
  83. package/hub/team/cli/commands/send.mjs +19 -6
  84. package/hub/team/cli/commands/start/index.mjs +154 -31
  85. package/hub/team/cli/commands/start/parse-args.mjs +38 -11
  86. package/hub/team/cli/commands/start/start-headless.mjs +112 -36
  87. package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
  88. package/hub/team/cli/commands/start/start-mux.mjs +70 -21
  89. package/hub/team/cli/commands/start/start-wt.mjs +29 -12
  90. package/hub/team/cli/commands/status.mjs +43 -14
  91. package/hub/team/cli/commands/stop.mjs +11 -4
  92. package/hub/team/cli/commands/task.mjs +8 -3
  93. package/hub/team/cli/commands/tasks.mjs +1 -1
  94. package/hub/team/cli/index.mjs +2 -2
  95. package/hub/team/cli/manifest.mjs +38 -8
  96. package/hub/team/cli/render.mjs +30 -8
  97. package/hub/team/cli/services/attach-fallback.mjs +31 -11
  98. package/hub/team/cli/services/hub-client.mjs +42 -14
  99. package/hub/team/cli/services/member-selector.mjs +11 -4
  100. package/hub/team/cli/services/native-control.mjs +48 -21
  101. package/hub/team/cli/services/runtime-mode.mjs +2 -1
  102. package/hub/team/cli/services/state-store.mjs +25 -8
  103. package/hub/team/cli/services/task-model.mjs +16 -6
  104. package/hub/team/conductor-mesh-bridge.mjs +24 -23
  105. package/hub/team/conductor.mjs +8 -4
  106. package/hub/team/dashboard-anchor.mjs +4 -5
  107. package/hub/team/dashboard-layout.mjs +3 -1
  108. package/hub/team/dashboard-open.mjs +41 -21
  109. package/hub/team/dashboard.mjs +76 -28
  110. package/hub/team/event-log.mjs +18 -10
  111. package/hub/team/handoff.mjs +31 -15
  112. package/hub/team/headless.mjs +2 -1
  113. package/hub/team/health-probe.mjs +69 -54
  114. package/hub/team/launcher-template.mjs +16 -13
  115. package/hub/team/native-supervisor.mjs +65 -21
  116. package/hub/team/native.mjs +74 -35
  117. package/hub/team/nativeProxy.mjs +184 -113
  118. package/hub/team/notify.mjs +119 -76
  119. package/hub/team/orchestrator.mjs +9 -4
  120. package/hub/team/pane.mjs +12 -7
  121. package/hub/team/process-cleanup.mjs +25 -16
  122. package/hub/team/psmux.mjs +491 -201
  123. package/hub/team/remote-probe.mjs +68 -52
  124. package/hub/team/remote-session.mjs +117 -59
  125. package/hub/team/remote-watcher.mjs +61 -33
  126. package/hub/team/routing.mjs +51 -25
  127. package/hub/team/runtime-strategy.mjs +3 -1
  128. package/hub/team/session.mjs +98 -34
  129. package/hub/team/staleState.mjs +72 -30
  130. package/hub/team/swarm-locks.mjs +15 -13
  131. package/hub/team/swarm-planner.mjs +32 -21
  132. package/hub/team/swarm-reconciler.mjs +48 -23
  133. package/hub/team/tui-lite.mjs +266 -68
  134. package/hub/team/tui-remote-adapter.mjs +14 -10
  135. package/hub/team/tui-viewer.mjs +99 -43
  136. package/hub/team/tui.mjs +708 -271
  137. package/hub/team/worktree-lifecycle.mjs +152 -58
  138. package/hub/team/wt-manager.mjs +24 -14
  139. package/hub/token-mode.mjs +71 -71
  140. package/hub/tray.mjs +66 -23
  141. package/hub/workers/claude-worker.mjs +162 -118
  142. package/hub/workers/codex-mcp.mjs +192 -141
  143. package/hub/workers/delegator-mcp.mjs +507 -333
  144. package/hub/workers/factory.mjs +8 -8
  145. package/hub/workers/gemini-worker.mjs +115 -84
  146. package/hub/workers/interface.mjs +6 -1
  147. package/hub/workers/worker-utils.mjs +21 -14
  148. package/hud/colors.mjs +27 -9
  149. package/hud/constants.mjs +162 -26
  150. package/hud/context-monitor.mjs +82 -41
  151. package/hud/hud-qos-status.mjs +129 -49
  152. package/hud/mission-board.mjs +6 -3
  153. package/hud/providers/claude.mjs +226 -115
  154. package/hud/providers/codex.mjs +62 -22
  155. package/hud/providers/gemini.mjs +168 -56
  156. package/hud/renderers.mjs +384 -119
  157. package/hud/terminal.mjs +101 -31
  158. package/hud/utils.mjs +78 -38
  159. package/mesh/index.mjs +11 -5
  160. package/mesh/mesh-budget.mjs +18 -9
  161. package/mesh/mesh-heartbeat.mjs +1 -1
  162. package/mesh/mesh-queue.mjs +3 -5
  163. package/mesh/mesh-router.mjs +5 -4
  164. package/package.json +2 -1
  165. package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
  166. package/scripts/__tests__/keyword-detector.test.mjs +77 -28
  167. package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
  168. package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
  169. package/scripts/__tests__/remote-spawn.test.mjs +10 -4
  170. package/scripts/__tests__/session-start-fast.test.mjs +36 -0
  171. package/scripts/__tests__/skill-template.test.mjs +98 -50
  172. package/scripts/__tests__/smoke.test.mjs +1 -1
  173. package/scripts/__tests__/spawn-trace.test.mjs +102 -0
  174. package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
  175. package/scripts/cache-doctor.mjs +11 -4
  176. package/scripts/cache-warmup.mjs +96 -37
  177. package/scripts/claudemd-sync.mjs +27 -17
  178. package/scripts/codex-gateway-preflight.mjs +52 -37
  179. package/scripts/codex-mcp-gateway-sync.mjs +59 -39
  180. package/scripts/completions/tfx.bash +47 -47
  181. package/scripts/completions/tfx.fish +44 -44
  182. package/scripts/completions/tfx.zsh +83 -83
  183. package/scripts/config-audit.mjs +232 -0
  184. package/scripts/convert-to-tmpl.mjs +54 -0
  185. package/scripts/cross-review-gate.mjs +35 -12
  186. package/scripts/cross-review-tracker.mjs +21 -8
  187. package/scripts/demo.mjs +35 -17
  188. package/scripts/doctor-diagnose.mjs +284 -0
  189. package/scripts/gen-skill-docs.mjs +7 -2
  190. package/scripts/gen-skill-manifest.mjs +2 -1
  191. package/scripts/headless-guard.mjs +86 -48
  192. package/scripts/hub-ensure.mjs +45 -26
  193. package/scripts/keyword-detector.mjs +41 -20
  194. package/scripts/keyword-rules-expander.mjs +47 -30
  195. package/scripts/lib/claudemd-scanner.mjs +6 -1
  196. package/scripts/lib/context.mjs +3 -3
  197. package/scripts/lib/cross-review-utils.mjs +6 -3
  198. package/scripts/lib/env-probe.mjs +47 -28
  199. package/scripts/lib/gemini-profiles.mjs +44 -10
  200. package/scripts/lib/handoff.mjs +33 -17
  201. package/scripts/lib/hook-utils.mjs +8 -6
  202. package/scripts/lib/keyword-rules.mjs +43 -19
  203. package/scripts/lib/logger.mjs +24 -24
  204. package/scripts/lib/mcp-filter.mjs +377 -239
  205. package/scripts/lib/mcp-guard-engine.mjs +194 -79
  206. package/scripts/lib/mcp-manifest.mjs +23 -13
  207. package/scripts/lib/mcp-server-catalog.mjs +300 -63
  208. package/scripts/lib/psmux-info.mjs +11 -6
  209. package/scripts/lib/remote-spawn-transfer.mjs +44 -14
  210. package/scripts/lib/skill-template.mjs +30 -7
  211. package/scripts/mcp-check.mjs +58 -39
  212. package/scripts/mcp-gateway-config.mjs +83 -39
  213. package/scripts/mcp-gateway-ensure.mjs +43 -35
  214. package/scripts/mcp-gateway-integration-test.mjs +70 -58
  215. package/scripts/mcp-gateway-start.mjs +126 -60
  216. package/scripts/mcp-gateway-verify.mjs +24 -22
  217. package/scripts/mcp-safety-guard.mjs +44 -11
  218. package/scripts/notion-read.mjs +199 -84
  219. package/scripts/pack.mjs +94 -89
  220. package/scripts/preflight-cache.mjs +27 -10
  221. package/scripts/preinstall.mjs +42 -13
  222. package/scripts/remote-spawn.mjs +309 -94
  223. package/scripts/run.cjs +8 -5
  224. package/scripts/session-spawn-helper.mjs +130 -39
  225. package/scripts/session-stale-cleanup.mjs +123 -0
  226. package/scripts/setup.mjs +941 -492
  227. package/scripts/test-lock.mjs +20 -7
  228. package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
  229. package/scripts/tfx-batch-stats.mjs +32 -11
  230. package/scripts/tfx-gate-activate.mjs +11 -4
  231. package/scripts/tfx-route-post.mjs +87 -20
  232. package/scripts/tfx-route-worker.mjs +57 -51
  233. package/scripts/tfx-route.sh +41 -124
  234. package/scripts/tmp-cleanup.mjs +21 -7
  235. package/scripts/token-snapshot.mjs +204 -85
  236. package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
  237. package/skills/.omc/state/idle-notif-cooldown.json +3 -0
  238. package/skills/.omc/state/last-tool-error.json +7 -0
  239. package/skills/.omc/state/subagent-tracking.json +7 -0
  240. package/skills/_templates/base.md +1 -6
  241. package/skills/merge-worktree/SKILL.md.tmpl +144 -0
  242. package/skills/shared/telemetry-segment.md +6 -0
  243. package/skills/star-prompt/SKILL.md.tmpl +222 -0
  244. package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
  245. package/skills/tfx-analysis/skill.json +1 -6
  246. package/skills/tfx-auto/SKILL.md +1 -0
  247. package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
  248. package/skills/tfx-auto-codex/skill.json +1 -3
  249. package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
  250. package/skills/tfx-autopilot/skill.json +1 -5
  251. package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
  252. package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
  253. package/skills/tfx-autoroute/skill.json +1 -7
  254. package/skills/tfx-codex/SKILL.md +1 -0
  255. package/skills/tfx-codex/skill.json +1 -3
  256. package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
  257. package/skills/tfx-codex-swarm/evals/evals.json +1 -1
  258. package/skills/tfx-codex-swarm/skill.json +1 -4
  259. package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
  260. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
  261. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
  262. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
  263. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
  264. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
  265. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
  266. package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
  267. package/skills/tfx-debate/SKILL.md.tmpl +192 -0
  268. package/skills/tfx-debate/skill.json +1 -7
  269. package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
  270. package/skills/tfx-deep-analysis/skill.json +1 -5
  271. package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
  272. package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
  273. package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
  274. package/skills/tfx-deep-qa/skill.json +1 -6
  275. package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
  276. package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
  277. package/skills/tfx-doctor/SKILL.md +21 -0
  278. package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
  279. package/skills/tfx-doctor/skill.json +1 -3
  280. package/skills/tfx-find/SKILL.md +1 -0
  281. package/skills/tfx-forge/SKILL.md.tmpl +187 -0
  282. package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
  283. package/skills/tfx-fullcycle/skill.json +1 -6
  284. package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
  285. package/skills/tfx-gemini/skill.json +1 -3
  286. package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
  287. package/skills/tfx-hooks/skill.json +1 -3
  288. package/skills/tfx-hub/SKILL.md.tmpl +212 -0
  289. package/skills/tfx-hub/skill.json +1 -3
  290. package/skills/tfx-index/SKILL.md +1 -0
  291. package/skills/tfx-index/skill.json +1 -6
  292. package/skills/tfx-interview/SKILL.md.tmpl +285 -0
  293. package/skills/tfx-multi/SKILL.md.tmpl +183 -0
  294. package/skills/tfx-multi/skill.json +1 -3
  295. package/skills/tfx-panel/SKILL.md.tmpl +189 -0
  296. package/skills/tfx-panel/skill.json +1 -7
  297. package/skills/tfx-persist/SKILL.md.tmpl +270 -0
  298. package/skills/tfx-persist/skill.json +1 -7
  299. package/skills/tfx-plan/SKILL.md +1 -0
  300. package/skills/tfx-plan/skill.json +1 -6
  301. package/skills/tfx-profile/SKILL.md.tmpl +239 -0
  302. package/skills/tfx-profile/skill.json +1 -3
  303. package/skills/tfx-prune/SKILL.md.tmpl +200 -0
  304. package/skills/tfx-prune/skill.json +1 -7
  305. package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
  306. package/skills/tfx-psmux-rules/skill.json +1 -4
  307. package/skills/tfx-qa/SKILL.md +1 -0
  308. package/skills/tfx-qa/skill.json +1 -6
  309. package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
  310. package/skills/tfx-ralph/skill.json +1 -4
  311. package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
  312. package/skills/tfx-remote-setup/skill.json +1 -3
  313. package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
  314. package/skills/tfx-remote-spawn/references/hosts.json +16 -0
  315. package/skills/tfx-remote-spawn/skill.json +1 -4
  316. package/skills/tfx-research/SKILL.md +1 -0
  317. package/skills/tfx-review/SKILL.md +1 -0
  318. package/skills/tfx-review/skill.json +1 -6
  319. package/skills/tfx-setup/SKILL.md.tmpl +504 -0
  320. package/skills/tfx-setup/skill.json +1 -3
  321. package/skills/tfx-swarm/SKILL.md +22 -0
  322. package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
  323. package/tui/codex-profile.mjs +88 -33
  324. package/tui/core.mjs +45 -15
  325. package/tui/doctor.mjs +75 -28
  326. package/tui/gemini-profile.mjs +74 -29
  327. package/tui/monitor-data.mjs +8 -4
  328. package/tui/monitor.mjs +71 -27
  329. package/tui/setup.mjs +133 -42
@@ -1,19 +1,17 @@
1
1
  // hub/workers/claude-worker.mjs — Claude stream-json subprocess 래퍼
2
2
  // ADR-007: --input-format/--output-format stream-json 기반 세션 워커.
3
3
 
4
- import { spawn } from 'node:child_process';
5
- import readline from 'node:readline';
4
+ import { spawn } from "node:child_process";
5
+ import readline from "node:readline";
6
6
 
7
- import { extractText, terminateChild, withRetry } from './worker-utils.mjs';
7
+ import { extractText, terminateChild, withRetry } from "./worker-utils.mjs";
8
8
 
9
9
  const DEFAULT_TIMEOUT_MS = 15 * 60 * 1000;
10
10
  const DEFAULT_KILL_GRACE_MS = 1000;
11
11
 
12
12
  function toStringList(value) {
13
13
  if (!Array.isArray(value)) return [];
14
- return value
15
- .map((item) => String(item ?? '').trim())
16
- .filter(Boolean);
14
+ return value.map((item) => String(item ?? "").trim()).filter(Boolean);
17
15
  }
18
16
 
19
17
  function safeJsonParse(line) {
@@ -25,11 +23,13 @@ function safeJsonParse(line) {
25
23
  }
26
24
 
27
25
  function findSessionId(event) {
28
- return event?.session_id
29
- || event?.sessionId
30
- || event?.message?.session_id
31
- || event?.message?.sessionId
32
- || null;
26
+ return (
27
+ event?.session_id ||
28
+ event?.sessionId ||
29
+ event?.message?.session_id ||
30
+ event?.message?.sessionId ||
31
+ null
32
+ );
33
33
  }
34
34
 
35
35
  function createWorkerError(message, details = {}) {
@@ -39,48 +39,63 @@ function createWorkerError(message, details = {}) {
39
39
  }
40
40
 
41
41
  function normalizeRetryOptions(retryOptions) {
42
- if (!retryOptions || typeof retryOptions !== 'object') {
42
+ if (!retryOptions || typeof retryOptions !== "object") {
43
43
  return Object.freeze({});
44
44
  }
45
45
  return Object.freeze({ ...retryOptions });
46
46
  }
47
47
 
48
48
  function isClaudeRetryable(error) {
49
- return error?.code === 'WORKER_EXIT'
50
- || error?.code === 'ETIMEDOUT'
51
- || error?.code === 'WORKER_STDIN_CLOSED';
49
+ return (
50
+ error?.code === "WORKER_EXIT" ||
51
+ error?.code === "ETIMEDOUT" ||
52
+ error?.code === "WORKER_STDIN_CLOSED"
53
+ );
52
54
  }
53
55
 
54
56
  function detectClaudeCategory(error) {
55
- const combined = `${error?.message || ''}\n${error?.stderr || ''}`.toLowerCase();
56
-
57
- if (/(unauthorized|forbidden|auth|login|token|credential|apikey|api key)/.test(combined)) {
58
- return 'auth';
57
+ const combined =
58
+ `${error?.message || ""}\n${error?.stderr || ""}`.toLowerCase();
59
+
60
+ if (
61
+ /(unauthorized|forbidden|auth|login|token|credential|apikey|api key)/.test(
62
+ combined,
63
+ )
64
+ ) {
65
+ return "auth";
59
66
  }
60
- if (/unknown option|invalid option|config|permission-mode|mcp-config/.test(combined)) {
61
- return 'config';
67
+ if (
68
+ /unknown option|invalid option|config|permission-mode|mcp-config/.test(
69
+ combined,
70
+ )
71
+ ) {
72
+ return "config";
62
73
  }
63
- if (/stdin|prompt|input/.test(combined) && error?.code !== 'WORKER_STDIN_CLOSED') {
64
- return 'input';
74
+ if (
75
+ /stdin|prompt|input/.test(combined) &&
76
+ error?.code !== "WORKER_STDIN_CLOSED"
77
+ ) {
78
+ return "input";
65
79
  }
66
80
 
67
- return 'transient';
81
+ return "transient";
68
82
  }
69
83
 
70
84
  function buildClaudeErrorInfo(error, attempts) {
71
85
  const category = detectClaudeCategory(error);
72
- let recovery = 'Restart the Claude worker session and retry the turn.';
73
-
74
- if (category === 'auth') {
75
- recovery = 'Refresh the Claude authentication state and retry.';
76
- } else if (category === 'config') {
77
- recovery = 'Check the Claude CLI flags, MCP configuration, and permission settings.';
78
- } else if (category === 'input') {
79
- recovery = 'Check the Claude request payload before retrying.';
86
+ let recovery = "Restart the Claude worker session and retry the turn.";
87
+
88
+ if (category === "auth") {
89
+ recovery = "Refresh the Claude authentication state and retry.";
90
+ } else if (category === "config") {
91
+ recovery =
92
+ "Check the Claude CLI flags, MCP configuration, and permission settings.";
93
+ } else if (category === "input") {
94
+ recovery = "Check the Claude request payload before retrying.";
80
95
  }
81
96
 
82
97
  return Object.freeze({
83
- code: error?.code || 'CLAUDE_EXECUTION_ERROR',
98
+ code: error?.code || "CLAUDE_EXECUTION_ERROR",
84
99
  retryable: isClaudeRetryable(error),
85
100
  attempts,
86
101
  category,
@@ -91,22 +106,24 @@ function buildClaudeErrorInfo(error, attempts) {
91
106
  function buildClaudeArgs(worker, options) {
92
107
  const args = [...worker.commandArgs];
93
108
 
94
- args.push('--print');
95
- args.push('--input-format', 'stream-json');
96
- args.push('--output-format', 'stream-json');
109
+ args.push("--print");
110
+ args.push("--input-format", "stream-json");
111
+ args.push("--output-format", "stream-json");
97
112
 
98
- if (options.includePartialMessages) args.push('--include-partial-messages');
99
- if (options.replayUserMessages) args.push('--replay-user-messages');
100
- if (options.model) args.push('--model', options.model);
101
- if (options.allowDangerouslySkipPermissions) args.push('--dangerously-skip-permissions');
102
- if (options.permissionMode) args.push('--permission-mode', options.permissionMode);
113
+ if (options.includePartialMessages) args.push("--include-partial-messages");
114
+ if (options.replayUserMessages) args.push("--replay-user-messages");
115
+ if (options.model) args.push("--model", options.model);
116
+ if (options.allowDangerouslySkipPermissions)
117
+ args.push("--dangerously-skip-permissions");
118
+ if (options.permissionMode)
119
+ args.push("--permission-mode", options.permissionMode);
103
120
 
104
121
  for (const config of toStringList(options.mcpConfig)) {
105
- args.push('--mcp-config', config);
122
+ args.push("--mcp-config", config);
106
123
  }
107
124
 
108
125
  if (worker.resumeSessionId) {
109
- args.push('--resume', worker.resumeSessionId);
126
+ args.push("--resume", worker.resumeSessionId);
110
127
  }
111
128
 
112
129
  args.push(...toStringList(options.extraArgs));
@@ -118,29 +135,38 @@ function buildClaudeArgs(worker, options) {
118
135
  * Claude stream-json 세션 워커
119
136
  */
120
137
  export class ClaudeWorker {
121
- type = 'claude';
138
+ type = "claude";
122
139
 
123
140
  constructor(options = {}) {
124
- this.command = options.command || 'claude';
141
+ this.command = options.command || "claude";
125
142
  this.commandArgs = toStringList(options.commandArgs || options.args);
126
143
  this.cwd = options.cwd || process.cwd();
127
144
  this.env = { ...process.env, ...(options.env || {}) };
128
145
  this.model = options.model || null;
129
146
  this.permissionMode = options.permissionMode || null;
130
- this.allowDangerouslySkipPermissions = options.allowDangerouslySkipPermissions !== false;
147
+ this.allowDangerouslySkipPermissions =
148
+ options.allowDangerouslySkipPermissions !== false;
131
149
  this.includePartialMessages = options.includePartialMessages === true;
132
150
  this.replayUserMessages = options.replayUserMessages !== false;
133
151
  this.mcpConfig = toStringList(options.mcpConfig);
134
152
  this.extraArgs = toStringList(options.extraArgs);
135
- this.timeoutMs = Number(options.timeoutMs) > 0 ? Number(options.timeoutMs) : DEFAULT_TIMEOUT_MS;
136
- this.killGraceMs = Number(options.killGraceMs) > 0 ? Number(options.killGraceMs) : DEFAULT_KILL_GRACE_MS;
153
+ this.timeoutMs =
154
+ Number(options.timeoutMs) > 0
155
+ ? Number(options.timeoutMs)
156
+ : DEFAULT_TIMEOUT_MS;
157
+ this.killGraceMs =
158
+ Number(options.killGraceMs) > 0
159
+ ? Number(options.killGraceMs)
160
+ : DEFAULT_KILL_GRACE_MS;
137
161
  this.retryOptions = normalizeRetryOptions(options.retryOptions);
138
- this.onEvent = typeof options.onEvent === 'function' ? options.onEvent : null;
139
- this.controlRequestHandler = typeof options.controlRequestHandler === 'function'
140
- ? options.controlRequestHandler
141
- : null;
142
-
143
- this.state = 'idle';
162
+ this.onEvent =
163
+ typeof options.onEvent === "function" ? options.onEvent : null;
164
+ this.controlRequestHandler =
165
+ typeof options.controlRequestHandler === "function"
166
+ ? options.controlRequestHandler
167
+ : null;
168
+
169
+ this.state = "idle";
144
170
  this.child = null;
145
171
  this.stdoutReader = null;
146
172
  this.stderrReader = null;
@@ -156,7 +182,7 @@ export class ClaudeWorker {
156
182
 
157
183
  getStatus() {
158
184
  return {
159
- type: 'claude',
185
+ type: "claude",
160
186
  state: this.state,
161
187
  pid: this.child?.pid || null,
162
188
  session_id: this.sessionId,
@@ -167,7 +193,9 @@ export class ClaudeWorker {
167
193
 
168
194
  _writeFrame(frame) {
169
195
  if (!this.child?.stdin?.writable) {
170
- throw createWorkerError('Claude worker stdin is not writable', { code: 'WORKER_STDIN_CLOSED' });
196
+ throw createWorkerError("Claude worker stdin is not writable", {
197
+ code: "WORKER_STDIN_CLOSED",
198
+ });
171
199
  }
172
200
  this.child.stdin.write(`${JSON.stringify(frame)}\n`);
173
201
  }
@@ -185,15 +213,16 @@ export class ClaudeWorker {
185
213
  const requestId = event.request_id || event.requestId;
186
214
  if (!requestId) return;
187
215
 
188
- const successPayload = event.subtype === 'can_use_tool'
189
- ? { decision: 'allow', allowed: true }
190
- : { acknowledged: true, subtype: event.subtype || 'unknown' };
216
+ const successPayload =
217
+ event.subtype === "can_use_tool"
218
+ ? { decision: "allow", allowed: true }
219
+ : { acknowledged: true, subtype: event.subtype || "unknown" };
191
220
 
192
221
  responseFrame = {
193
- type: 'control_response',
222
+ type: "control_response",
194
223
  response: {
195
224
  request_id: requestId,
196
- subtype: 'success',
225
+ subtype: "success",
197
226
  response: successPayload,
198
227
  },
199
228
  };
@@ -211,29 +240,26 @@ export class ClaudeWorker {
211
240
  clearTimeout(turn.timeout);
212
241
  this.pendingTurn = null;
213
242
 
214
- const response = [
215
- ...turn.assistantTexts,
216
- extractText(event),
217
- ]
243
+ const response = [...turn.assistantTexts, extractText(event)]
218
244
  .filter(Boolean)
219
- .join('\n')
245
+ .join("\n")
220
246
  .trim();
221
247
 
222
248
  if (response) {
223
249
  this.history.push({
224
- role: 'assistant',
250
+ role: "assistant",
225
251
  content: response,
226
252
  at_ms: Date.now(),
227
253
  });
228
254
  }
229
255
 
230
256
  const result = {
231
- type: 'claude',
257
+ type: "claude",
232
258
  sessionId: this.sessionId,
233
259
  response,
234
260
  assistantEvents: turn.assistantEvents,
235
261
  resultEvent: event,
236
- stderr: this.stderrLines.join('\n').trim(),
262
+ stderr: this.stderrLines.join("\n").trim(),
237
263
  history: [...this.history],
238
264
  startedAtMs: turn.startedAtMs,
239
265
  finishedAtMs: Date.now(),
@@ -266,15 +292,17 @@ export class ClaudeWorker {
266
292
  }
267
293
 
268
294
  if (this.onEvent) {
269
- try { this.onEvent(event); } catch {}
295
+ try {
296
+ this.onEvent(event);
297
+ } catch {}
270
298
  }
271
299
 
272
- if (event.type === 'control_request') {
300
+ if (event.type === "control_request") {
273
301
  void this._handleControlRequest(event);
274
302
  return;
275
303
  }
276
304
 
277
- if (event.type === 'assistant' || event.type === 'streamlined_text') {
305
+ if (event.type === "assistant" || event.type === "streamlined_text") {
278
306
  const text = extractText(event);
279
307
  if (this.pendingTurn && text) {
280
308
  this.pendingTurn.assistantTexts.push(text);
@@ -283,7 +311,7 @@ export class ClaudeWorker {
283
311
  return;
284
312
  }
285
313
 
286
- if (event.type === 'result' && this.pendingTurn) {
314
+ if (event.type === "result" && this.pendingTurn) {
287
315
  this._finalizePendingTurn(this.pendingTurn, event);
288
316
  }
289
317
  }
@@ -306,12 +334,12 @@ export class ClaudeWorker {
306
334
  const child = spawn(this.command, args, {
307
335
  cwd: this.cwd,
308
336
  env: this.env,
309
- stdio: ['pipe', 'pipe', 'pipe'],
337
+ stdio: ["pipe", "pipe", "pipe"],
310
338
  windowsHide: true,
311
339
  });
312
340
 
313
341
  this.child = child;
314
- this.state = 'ready';
342
+ this.state = "ready";
315
343
  this.stderrLines = [];
316
344
  this.stdoutReader = readline.createInterface({
317
345
  input: child.stdout,
@@ -322,28 +350,33 @@ export class ClaudeWorker {
322
350
  crlfDelay: Infinity,
323
351
  });
324
352
 
325
- this.stdoutReader.on('line', (line) => this._handleStdoutLine(line));
326
- this.stderrReader.on('line', (line) => {
353
+ this.stdoutReader.on("line", (line) => this._handleStdoutLine(line));
354
+ this.stderrReader.on("line", (line) => {
327
355
  if (!line) return;
328
356
  this.stderrLines.push(line);
329
357
  });
330
358
 
331
359
  this._closePromise = new Promise((resolve, reject) => {
332
- child.once('error', reject);
333
- child.once('close', (code, signal) => {
334
- const closeError = code === 0
335
- ? null
336
- : createWorkerError(`Claude worker exited with code ${code}`, {
337
- code: 'WORKER_EXIT',
338
- exitCode: code,
339
- exitSignal: signal,
340
- stderr: this.stderrLines.join('\n').trim(),
341
- });
360
+ child.once("error", reject);
361
+ child.once("close", (code, signal) => {
362
+ const closeError =
363
+ code === 0
364
+ ? null
365
+ : createWorkerError(`Claude worker exited with code ${code}`, {
366
+ code: "WORKER_EXIT",
367
+ exitCode: code,
368
+ exitSignal: signal,
369
+ stderr: this.stderrLines.join("\n").trim(),
370
+ });
342
371
 
343
372
  this.child = null;
344
- this.state = 'idle';
345
- try { this.stdoutReader?.close(); } catch {}
346
- try { this.stderrReader?.close(); } catch {}
373
+ this.state = "idle";
374
+ try {
375
+ this.stdoutReader?.close();
376
+ } catch {}
377
+ try {
378
+ this.stderrReader?.close();
379
+ } catch {}
347
380
  this.stdoutReader = null;
348
381
  this.stderrReader = null;
349
382
  if (closeError) this._rejectPendingTurn(closeError);
@@ -356,7 +389,7 @@ export class ClaudeWorker {
356
389
 
357
390
  async stop() {
358
391
  if (!this.child) {
359
- this.state = 'stopped';
392
+ this.state = "stopped";
360
393
  return this.getStatus();
361
394
  }
362
395
 
@@ -368,13 +401,13 @@ export class ClaudeWorker {
368
401
  ]);
369
402
 
370
403
  this.child = null;
371
- this.state = 'stopped';
404
+ this.state = "stopped";
372
405
  return this.getStatus();
373
406
  }
374
407
 
375
408
  async restart() {
376
409
  await this.stop();
377
- this.state = 'idle';
410
+ this.state = "idle";
378
411
  return this.start();
379
412
  }
380
413
 
@@ -382,25 +415,33 @@ export class ClaudeWorker {
382
415
  await this.start();
383
416
 
384
417
  if (this.pendingTurn) {
385
- throw createWorkerError('ClaudeWorker is already handling another turn', { code: 'WORKER_BUSY' });
418
+ throw createWorkerError("ClaudeWorker is already handling another turn", {
419
+ code: "WORKER_BUSY",
420
+ });
386
421
  }
387
422
 
388
- const timeoutMs = Number(options.timeoutMs) > 0 ? Number(options.timeoutMs) : this.timeoutMs;
389
- const userText = String(prompt ?? '');
423
+ const timeoutMs =
424
+ Number(options.timeoutMs) > 0
425
+ ? Number(options.timeoutMs)
426
+ : this.timeoutMs;
427
+ const userText = String(prompt ?? "");
390
428
  const startedAtMs = Date.now();
391
429
 
392
430
  this.history.push({
393
- role: 'user',
431
+ role: "user",
394
432
  content: userText,
395
433
  at_ms: startedAtMs,
396
434
  });
397
435
 
398
436
  const turnPromise = new Promise((resolve, reject) => {
399
437
  const timeout = setTimeout(() => {
400
- const timeoutError = createWorkerError(`Claude worker timed out after ${timeoutMs}ms`, {
401
- code: 'ETIMEDOUT',
402
- stderr: this.stderrLines.join('\n').trim(),
403
- });
438
+ const timeoutError = createWorkerError(
439
+ `Claude worker timed out after ${timeoutMs}ms`,
440
+ {
441
+ code: "ETIMEDOUT",
442
+ stderr: this.stderrLines.join("\n").trim(),
443
+ },
444
+ );
404
445
  this._rejectPendingTurn(timeoutError);
405
446
  this._terminateChild(this.child);
406
447
  }, timeoutMs);
@@ -417,11 +458,11 @@ export class ClaudeWorker {
417
458
  };
418
459
  });
419
460
 
420
- this.state = 'running';
461
+ this.state = "running";
421
462
  this._writeFrame({
422
- type: 'user',
463
+ type: "user",
423
464
  message: {
424
- role: 'user',
465
+ role: "user",
425
466
  content: userText,
426
467
  },
427
468
  });
@@ -430,29 +471,32 @@ export class ClaudeWorker {
430
471
  return await turnPromise;
431
472
  } finally {
432
473
  if (this.child) {
433
- this.state = 'ready';
474
+ this.state = "ready";
434
475
  }
435
476
  }
436
477
  }
437
478
 
438
479
  isReady() {
439
- return this.state === 'ready' || this.state === 'running';
480
+ return this.state === "ready" || this.state === "running";
440
481
  }
441
482
 
442
483
  async execute(prompt, options = {}) {
443
484
  let attempts = 0;
444
485
 
445
486
  try {
446
- const result = await withRetry(async () => {
447
- attempts += 1;
448
- if (attempts > 1) {
449
- await this.restart();
450
- }
451
- return this.run(prompt, options);
452
- }, {
453
- ...this.retryOptions,
454
- shouldRetry: (error) => isClaudeRetryable(error),
455
- });
487
+ const result = await withRetry(
488
+ async () => {
489
+ attempts += 1;
490
+ if (attempts > 1) {
491
+ await this.restart();
492
+ }
493
+ return this.run(prompt, options);
494
+ },
495
+ {
496
+ ...this.retryOptions,
497
+ shouldRetry: (error) => isClaudeRetryable(error),
498
+ },
499
+ );
456
500
 
457
501
  return {
458
502
  output: result.response,
@@ -463,8 +507,8 @@ export class ClaudeWorker {
463
507
  };
464
508
  } catch (error) {
465
509
  return {
466
- output: error.stderr || error.message || 'Claude worker failed',
467
- exitCode: error.code === 'ETIMEDOUT' ? 124 : 1,
510
+ output: error.stderr || error.message || "Claude worker failed",
511
+ exitCode: error.code === "ETIMEDOUT" ? 124 : 1,
468
512
  threadId: null,
469
513
  sessionKey: options.sessionKey || this.sessionId || null,
470
514
  error: buildClaudeErrorInfo(error, attempts || 1),