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.
- package/LICENSE +21 -21
- package/bin/tfx-doctor-tui.mjs +1 -1
- package/bin/tfx-doctor.mjs +6 -1
- package/bin/tfx-profile.mjs +1 -1
- package/bin/tfx-setup-tui.mjs +1 -1
- package/bin/tfx-setup.mjs +6 -1
- package/bin/triflux.mjs +2396 -1140
- package/hooks/agent-route-guard.mjs +12 -8
- package/hooks/cross-review-tracker.mjs +21 -8
- package/hooks/error-context.mjs +19 -7
- package/hooks/hook-adaptive-collector.mjs +18 -16
- package/hooks/hook-manager.mjs +93 -32
- package/hooks/hook-orchestrator.mjs +108 -24
- package/hooks/hook-registry.json +11 -0
- package/hooks/keyword-rules.json +6 -10
- package/hooks/lib/resolve-root.mjs +1 -1
- package/hooks/mcp-config-watcher.mjs +6 -2
- package/hooks/pipeline-stop.mjs +3 -6
- package/hooks/safety-guard.mjs +99 -28
- package/hooks/session-start-fast.mjs +143 -0
- package/hooks/subagent-verifier.mjs +5 -4
- package/hub/account-broker.mjs +256 -60
- package/hub/adaptive-diagnostic.mjs +75 -48
- package/hub/adaptive-inject.mjs +95 -57
- package/hub/adaptive-memory.mjs +156 -42
- package/hub/adaptive.mjs +60 -31
- package/hub/assign-callbacks.mjs +67 -30
- package/hub/bridge.mjs +0 -1
- package/hub/cli-adapter-base.mjs +200 -48
- package/hub/codex-adapter.mjs +76 -96
- package/hub/codex-compat.mjs +3 -3
- package/hub/codex-preflight.mjs +63 -37
- package/hub/delegator/contracts.mjs +19 -23
- package/hub/delegator/index.mjs +3 -3
- package/hub/delegator/service.mjs +88 -64
- package/hub/delegator/tool-definitions.mjs +5 -5
- package/hub/fullcycle.mjs +33 -17
- package/hub/gemini-adapter.mjs +69 -94
- package/hub/hitl.mjs +89 -30
- package/hub/intent.mjs +161 -38
- package/hub/lib/cache-guard.mjs +43 -17
- package/hub/lib/mcp-response-cache.mjs +66 -32
- package/hub/lib/memory-store.mjs +285 -111
- package/hub/lib/path-utils.mjs +35 -37
- package/hub/lib/process-utils.mjs +106 -37
- package/hub/lib/spawn-trace.mjs +527 -0
- package/hub/lib/ssh-command.mjs +34 -4
- package/hub/lib/ssh-retry.mjs +5 -1
- package/hub/lib/uuidv7.mjs +4 -3
- package/hub/memory-doctor.mjs +266 -106
- package/hub/middleware/request-logger.mjs +61 -34
- package/hub/paths.mjs +9 -9
- package/hub/pipeline/gates/confidence.mjs +34 -15
- package/hub/pipeline/gates/consensus.mjs +27 -15
- package/hub/pipeline/gates/index.mjs +7 -3
- package/hub/pipeline/gates/selfcheck.mjs +57 -19
- package/hub/pipeline/index.mjs +77 -42
- package/hub/pipeline/state.mjs +10 -10
- package/hub/pipeline/transitions.mjs +40 -23
- package/hub/platform.mjs +57 -48
- package/hub/promote-penalties.mjs +25 -7
- package/hub/quality/deslop.mjs +70 -49
- package/hub/research.mjs +32 -25
- package/hub/router.mjs +240 -107
- package/hub/routing/complexity.mjs +132 -29
- package/hub/routing/index.mjs +17 -12
- package/hub/routing/q-learning.mjs +76 -28
- package/hub/server.mjs +4 -4
- package/hub/session-fingerprint.mjs +126 -60
- package/hub/state.mjs +84 -43
- package/hub/store-adapter.mjs +59 -26
- package/hub/store.mjs +356 -153
- package/hub/team/agent-map.json +22 -7
- package/hub/team/ansi.mjs +186 -122
- package/hub/team/backend.mjs +28 -10
- package/hub/team/cli/commands/attach.mjs +29 -9
- package/hub/team/cli/commands/control.mjs +29 -8
- package/hub/team/cli/commands/debug.mjs +32 -11
- package/hub/team/cli/commands/focus.mjs +38 -11
- package/hub/team/cli/commands/interrupt.mjs +18 -6
- package/hub/team/cli/commands/kill.mjs +16 -5
- package/hub/team/cli/commands/list.mjs +11 -4
- package/hub/team/cli/commands/send.mjs +19 -6
- package/hub/team/cli/commands/start/index.mjs +154 -31
- package/hub/team/cli/commands/start/parse-args.mjs +38 -11
- package/hub/team/cli/commands/start/start-headless.mjs +112 -36
- package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
- package/hub/team/cli/commands/start/start-mux.mjs +70 -21
- package/hub/team/cli/commands/start/start-wt.mjs +29 -12
- package/hub/team/cli/commands/status.mjs +43 -14
- package/hub/team/cli/commands/stop.mjs +11 -4
- package/hub/team/cli/commands/task.mjs +8 -3
- package/hub/team/cli/commands/tasks.mjs +1 -1
- package/hub/team/cli/index.mjs +2 -2
- package/hub/team/cli/manifest.mjs +38 -8
- package/hub/team/cli/render.mjs +30 -8
- package/hub/team/cli/services/attach-fallback.mjs +31 -11
- package/hub/team/cli/services/hub-client.mjs +42 -14
- package/hub/team/cli/services/member-selector.mjs +11 -4
- package/hub/team/cli/services/native-control.mjs +48 -21
- package/hub/team/cli/services/runtime-mode.mjs +2 -1
- package/hub/team/cli/services/state-store.mjs +25 -8
- package/hub/team/cli/services/task-model.mjs +16 -6
- package/hub/team/conductor-mesh-bridge.mjs +24 -23
- package/hub/team/conductor.mjs +8 -4
- package/hub/team/dashboard-anchor.mjs +4 -5
- package/hub/team/dashboard-layout.mjs +3 -1
- package/hub/team/dashboard-open.mjs +41 -21
- package/hub/team/dashboard.mjs +76 -28
- package/hub/team/event-log.mjs +18 -10
- package/hub/team/handoff.mjs +31 -15
- package/hub/team/headless.mjs +2 -1
- package/hub/team/health-probe.mjs +69 -54
- package/hub/team/launcher-template.mjs +16 -13
- package/hub/team/native-supervisor.mjs +65 -21
- package/hub/team/native.mjs +74 -35
- package/hub/team/nativeProxy.mjs +184 -113
- package/hub/team/notify.mjs +119 -76
- package/hub/team/orchestrator.mjs +9 -4
- package/hub/team/pane.mjs +12 -7
- package/hub/team/process-cleanup.mjs +25 -16
- package/hub/team/psmux.mjs +491 -201
- package/hub/team/remote-probe.mjs +68 -52
- package/hub/team/remote-session.mjs +117 -59
- package/hub/team/remote-watcher.mjs +61 -33
- package/hub/team/routing.mjs +51 -25
- package/hub/team/runtime-strategy.mjs +3 -1
- package/hub/team/session.mjs +98 -34
- package/hub/team/staleState.mjs +72 -30
- package/hub/team/swarm-locks.mjs +15 -13
- package/hub/team/swarm-planner.mjs +32 -21
- package/hub/team/swarm-reconciler.mjs +48 -23
- package/hub/team/tui-lite.mjs +266 -68
- package/hub/team/tui-remote-adapter.mjs +14 -10
- package/hub/team/tui-viewer.mjs +99 -43
- package/hub/team/tui.mjs +708 -271
- package/hub/team/worktree-lifecycle.mjs +152 -58
- package/hub/team/wt-manager.mjs +24 -14
- package/hub/token-mode.mjs +71 -71
- package/hub/tray.mjs +66 -23
- package/hub/workers/claude-worker.mjs +162 -118
- package/hub/workers/codex-mcp.mjs +192 -141
- package/hub/workers/delegator-mcp.mjs +507 -333
- package/hub/workers/factory.mjs +8 -8
- package/hub/workers/gemini-worker.mjs +115 -84
- package/hub/workers/interface.mjs +6 -1
- package/hub/workers/worker-utils.mjs +21 -14
- package/hud/colors.mjs +27 -9
- package/hud/constants.mjs +162 -26
- package/hud/context-monitor.mjs +82 -41
- package/hud/hud-qos-status.mjs +129 -49
- package/hud/mission-board.mjs +6 -3
- package/hud/providers/claude.mjs +226 -115
- package/hud/providers/codex.mjs +62 -22
- package/hud/providers/gemini.mjs +168 -56
- package/hud/renderers.mjs +384 -119
- package/hud/terminal.mjs +101 -31
- package/hud/utils.mjs +78 -38
- package/mesh/index.mjs +11 -5
- package/mesh/mesh-budget.mjs +18 -9
- package/mesh/mesh-heartbeat.mjs +1 -1
- package/mesh/mesh-queue.mjs +3 -5
- package/mesh/mesh-router.mjs +5 -4
- package/package.json +2 -1
- package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
- package/scripts/__tests__/keyword-detector.test.mjs +77 -28
- package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
- package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
- package/scripts/__tests__/remote-spawn.test.mjs +10 -4
- package/scripts/__tests__/session-start-fast.test.mjs +36 -0
- package/scripts/__tests__/skill-template.test.mjs +98 -50
- package/scripts/__tests__/smoke.test.mjs +1 -1
- package/scripts/__tests__/spawn-trace.test.mjs +102 -0
- package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
- package/scripts/cache-doctor.mjs +11 -4
- package/scripts/cache-warmup.mjs +96 -37
- package/scripts/claudemd-sync.mjs +27 -17
- package/scripts/codex-gateway-preflight.mjs +52 -37
- package/scripts/codex-mcp-gateway-sync.mjs +59 -39
- package/scripts/completions/tfx.bash +47 -47
- package/scripts/completions/tfx.fish +44 -44
- package/scripts/completions/tfx.zsh +83 -83
- package/scripts/config-audit.mjs +232 -0
- package/scripts/convert-to-tmpl.mjs +54 -0
- package/scripts/cross-review-gate.mjs +35 -12
- package/scripts/cross-review-tracker.mjs +21 -8
- package/scripts/demo.mjs +35 -17
- package/scripts/doctor-diagnose.mjs +284 -0
- package/scripts/gen-skill-docs.mjs +7 -2
- package/scripts/gen-skill-manifest.mjs +2 -1
- package/scripts/headless-guard.mjs +86 -48
- package/scripts/hub-ensure.mjs +45 -26
- package/scripts/keyword-detector.mjs +41 -20
- package/scripts/keyword-rules-expander.mjs +47 -30
- package/scripts/lib/claudemd-scanner.mjs +6 -1
- package/scripts/lib/context.mjs +3 -3
- package/scripts/lib/cross-review-utils.mjs +6 -3
- package/scripts/lib/env-probe.mjs +47 -28
- package/scripts/lib/gemini-profiles.mjs +44 -10
- package/scripts/lib/handoff.mjs +33 -17
- package/scripts/lib/hook-utils.mjs +8 -6
- package/scripts/lib/keyword-rules.mjs +43 -19
- package/scripts/lib/logger.mjs +24 -24
- package/scripts/lib/mcp-filter.mjs +377 -239
- package/scripts/lib/mcp-guard-engine.mjs +194 -79
- package/scripts/lib/mcp-manifest.mjs +23 -13
- package/scripts/lib/mcp-server-catalog.mjs +300 -63
- package/scripts/lib/psmux-info.mjs +11 -6
- package/scripts/lib/remote-spawn-transfer.mjs +44 -14
- package/scripts/lib/skill-template.mjs +30 -7
- package/scripts/mcp-check.mjs +58 -39
- package/scripts/mcp-gateway-config.mjs +83 -39
- package/scripts/mcp-gateway-ensure.mjs +43 -35
- package/scripts/mcp-gateway-integration-test.mjs +70 -58
- package/scripts/mcp-gateway-start.mjs +126 -60
- package/scripts/mcp-gateway-verify.mjs +24 -22
- package/scripts/mcp-safety-guard.mjs +44 -11
- package/scripts/notion-read.mjs +199 -84
- package/scripts/pack.mjs +94 -89
- package/scripts/preflight-cache.mjs +27 -10
- package/scripts/preinstall.mjs +42 -13
- package/scripts/remote-spawn.mjs +309 -94
- package/scripts/run.cjs +8 -5
- package/scripts/session-spawn-helper.mjs +130 -39
- package/scripts/session-stale-cleanup.mjs +123 -0
- package/scripts/setup.mjs +941 -492
- package/scripts/test-lock.mjs +20 -7
- package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
- package/scripts/tfx-batch-stats.mjs +32 -11
- package/scripts/tfx-gate-activate.mjs +11 -4
- package/scripts/tfx-route-post.mjs +87 -20
- package/scripts/tfx-route-worker.mjs +57 -51
- package/scripts/tfx-route.sh +41 -124
- package/scripts/tmp-cleanup.mjs +21 -7
- package/scripts/token-snapshot.mjs +204 -85
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
- package/skills/.omc/state/idle-notif-cooldown.json +3 -0
- package/skills/.omc/state/last-tool-error.json +7 -0
- package/skills/.omc/state/subagent-tracking.json +7 -0
- package/skills/_templates/base.md +1 -6
- package/skills/merge-worktree/SKILL.md.tmpl +144 -0
- package/skills/shared/telemetry-segment.md +6 -0
- package/skills/star-prompt/SKILL.md.tmpl +222 -0
- package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
- package/skills/tfx-analysis/skill.json +1 -6
- package/skills/tfx-auto/SKILL.md +1 -0
- package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
- package/skills/tfx-auto-codex/skill.json +1 -3
- package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
- package/skills/tfx-autopilot/skill.json +1 -5
- package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
- package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
- package/skills/tfx-autoroute/skill.json +1 -7
- package/skills/tfx-codex/SKILL.md +1 -0
- package/skills/tfx-codex/skill.json +1 -3
- package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
- package/skills/tfx-codex-swarm/evals/evals.json +1 -1
- package/skills/tfx-codex-swarm/skill.json +1 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
- package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
- package/skills/tfx-debate/SKILL.md.tmpl +192 -0
- package/skills/tfx-debate/skill.json +1 -7
- package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
- package/skills/tfx-deep-analysis/skill.json +1 -5
- package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
- package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
- package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
- package/skills/tfx-deep-qa/skill.json +1 -6
- package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
- package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
- package/skills/tfx-doctor/SKILL.md +21 -0
- package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
- package/skills/tfx-doctor/skill.json +1 -3
- package/skills/tfx-find/SKILL.md +1 -0
- package/skills/tfx-forge/SKILL.md.tmpl +187 -0
- package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
- package/skills/tfx-fullcycle/skill.json +1 -6
- package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
- package/skills/tfx-gemini/skill.json +1 -3
- package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
- package/skills/tfx-hooks/skill.json +1 -3
- package/skills/tfx-hub/SKILL.md.tmpl +212 -0
- package/skills/tfx-hub/skill.json +1 -3
- package/skills/tfx-index/SKILL.md +1 -0
- package/skills/tfx-index/skill.json +1 -6
- package/skills/tfx-interview/SKILL.md.tmpl +285 -0
- package/skills/tfx-multi/SKILL.md.tmpl +183 -0
- package/skills/tfx-multi/skill.json +1 -3
- package/skills/tfx-panel/SKILL.md.tmpl +189 -0
- package/skills/tfx-panel/skill.json +1 -7
- package/skills/tfx-persist/SKILL.md.tmpl +270 -0
- package/skills/tfx-persist/skill.json +1 -7
- package/skills/tfx-plan/SKILL.md +1 -0
- package/skills/tfx-plan/skill.json +1 -6
- package/skills/tfx-profile/SKILL.md.tmpl +239 -0
- package/skills/tfx-profile/skill.json +1 -3
- package/skills/tfx-prune/SKILL.md.tmpl +200 -0
- package/skills/tfx-prune/skill.json +1 -7
- package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
- package/skills/tfx-psmux-rules/skill.json +1 -4
- package/skills/tfx-qa/SKILL.md +1 -0
- package/skills/tfx-qa/skill.json +1 -6
- package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
- package/skills/tfx-ralph/skill.json +1 -4
- package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
- package/skills/tfx-remote-setup/skill.json +1 -3
- package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
- package/skills/tfx-remote-spawn/references/hosts.json +16 -0
- package/skills/tfx-remote-spawn/skill.json +1 -4
- package/skills/tfx-research/SKILL.md +1 -0
- package/skills/tfx-review/SKILL.md +1 -0
- package/skills/tfx-review/skill.json +1 -6
- package/skills/tfx-setup/SKILL.md.tmpl +504 -0
- package/skills/tfx-setup/skill.json +1 -3
- package/skills/tfx-swarm/SKILL.md +22 -0
- package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
- package/tui/codex-profile.mjs +88 -33
- package/tui/core.mjs +45 -15
- package/tui/doctor.mjs +75 -28
- package/tui/gemini-profile.mjs +74 -29
- package/tui/monitor-data.mjs +8 -4
- package/tui/monitor.mjs +71 -27
- 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
|
|
5
|
-
import readline from
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import readline from "node:readline";
|
|
6
6
|
|
|
7
|
-
import { extractText, terminateChild, withRetry } from
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
||
|
|
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 !==
|
|
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
|
|
50
|
-
|
|
51
|
-
|
|
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 =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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 (
|
|
61
|
-
|
|
67
|
+
if (
|
|
68
|
+
/unknown option|invalid option|config|permission-mode|mcp-config/.test(
|
|
69
|
+
combined,
|
|
70
|
+
)
|
|
71
|
+
) {
|
|
72
|
+
return "config";
|
|
62
73
|
}
|
|
63
|
-
if (
|
|
64
|
-
|
|
74
|
+
if (
|
|
75
|
+
/stdin|prompt|input/.test(combined) &&
|
|
76
|
+
error?.code !== "WORKER_STDIN_CLOSED"
|
|
77
|
+
) {
|
|
78
|
+
return "input";
|
|
65
79
|
}
|
|
66
80
|
|
|
67
|
-
return
|
|
81
|
+
return "transient";
|
|
68
82
|
}
|
|
69
83
|
|
|
70
84
|
function buildClaudeErrorInfo(error, attempts) {
|
|
71
85
|
const category = detectClaudeCategory(error);
|
|
72
|
-
let recovery =
|
|
73
|
-
|
|
74
|
-
if (category ===
|
|
75
|
-
recovery =
|
|
76
|
-
} else if (category ===
|
|
77
|
-
recovery =
|
|
78
|
-
|
|
79
|
-
|
|
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 ||
|
|
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(
|
|
95
|
-
args.push(
|
|
96
|
-
args.push(
|
|
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(
|
|
99
|
-
if (options.replayUserMessages) args.push(
|
|
100
|
-
if (options.model) args.push(
|
|
101
|
-
if (options.allowDangerouslySkipPermissions)
|
|
102
|
-
|
|
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(
|
|
122
|
+
args.push("--mcp-config", config);
|
|
106
123
|
}
|
|
107
124
|
|
|
108
125
|
if (worker.resumeSessionId) {
|
|
109
|
-
args.push(
|
|
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 =
|
|
138
|
+
type = "claude";
|
|
122
139
|
|
|
123
140
|
constructor(options = {}) {
|
|
124
|
-
this.command = options.command ||
|
|
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 =
|
|
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 =
|
|
136
|
-
|
|
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 =
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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:
|
|
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(
|
|
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 =
|
|
189
|
-
|
|
190
|
-
|
|
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:
|
|
222
|
+
type: "control_response",
|
|
194
223
|
response: {
|
|
195
224
|
request_id: requestId,
|
|
196
|
-
subtype:
|
|
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(
|
|
245
|
+
.join("\n")
|
|
220
246
|
.trim();
|
|
221
247
|
|
|
222
248
|
if (response) {
|
|
223
249
|
this.history.push({
|
|
224
|
-
role:
|
|
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:
|
|
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(
|
|
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 {
|
|
295
|
+
try {
|
|
296
|
+
this.onEvent(event);
|
|
297
|
+
} catch {}
|
|
270
298
|
}
|
|
271
299
|
|
|
272
|
-
if (event.type ===
|
|
300
|
+
if (event.type === "control_request") {
|
|
273
301
|
void this._handleControlRequest(event);
|
|
274
302
|
return;
|
|
275
303
|
}
|
|
276
304
|
|
|
277
|
-
if (event.type ===
|
|
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 ===
|
|
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: [
|
|
337
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
310
338
|
windowsHide: true,
|
|
311
339
|
});
|
|
312
340
|
|
|
313
341
|
this.child = child;
|
|
314
|
-
this.state =
|
|
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(
|
|
326
|
-
this.stderrReader.on(
|
|
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(
|
|
333
|
-
child.once(
|
|
334
|
-
const closeError =
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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 =
|
|
345
|
-
try {
|
|
346
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
418
|
+
throw createWorkerError("ClaudeWorker is already handling another turn", {
|
|
419
|
+
code: "WORKER_BUSY",
|
|
420
|
+
});
|
|
386
421
|
}
|
|
387
422
|
|
|
388
|
-
const timeoutMs =
|
|
389
|
-
|
|
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:
|
|
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(
|
|
401
|
-
|
|
402
|
-
|
|
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 =
|
|
461
|
+
this.state = "running";
|
|
421
462
|
this._writeFrame({
|
|
422
|
-
type:
|
|
463
|
+
type: "user",
|
|
423
464
|
message: {
|
|
424
|
-
role:
|
|
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 =
|
|
474
|
+
this.state = "ready";
|
|
434
475
|
}
|
|
435
476
|
}
|
|
436
477
|
}
|
|
437
478
|
|
|
438
479
|
isReady() {
|
|
439
|
-
return this.state ===
|
|
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(
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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 ||
|
|
467
|
-
exitCode: error.code ===
|
|
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),
|