triflux 4.2.8 → 4.2.9
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/hub/team/native.mjs
CHANGED
|
@@ -126,9 +126,10 @@ function getRouteTimeout(role, _mcpProfile) {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
129
|
+
* v3 슬림 래퍼 프롬프트 생성 (async 모드)
|
|
130
|
+
* --async로 즉시 시작 → --job-wait로 내부 폴링 → --job-result로 결과 수집.
|
|
131
|
+
* Claude Code Bash 도구 600초 제한을 우회하여 scientist(24분), scientist-deep(60분) 등
|
|
132
|
+
* 장시간 워커를 안정적으로 실행한다.
|
|
132
133
|
*
|
|
133
134
|
* @param {'codex'|'gemini'} cli — CLI 타입
|
|
134
135
|
* @param {object} opts
|
|
@@ -141,7 +142,7 @@ function getRouteTimeout(role, _mcpProfile) {
|
|
|
141
142
|
* @param {string} [opts.mcp_profile] — MCP 프로필
|
|
142
143
|
* @param {number} [opts.workerIndex] — 검색 힌트 회전에 사용할 워커 인덱스(1-based)
|
|
143
144
|
* @param {string} [opts.searchTool] — 전용 검색 도구 힌트(brave-search|tavily|exa)
|
|
144
|
-
* @param {number} [opts.bashTimeout] —
|
|
145
|
+
* @param {number} [opts.bashTimeout] — (deprecated, async에서는 무시됨)
|
|
145
146
|
* @returns {string} 슬림 래퍼 프롬프트
|
|
146
147
|
*/
|
|
147
148
|
export function buildSlimWrapperPrompt(cli, opts = {}) {
|
|
@@ -156,23 +157,25 @@ export function buildSlimWrapperPrompt(cli, opts = {}) {
|
|
|
156
157
|
workerIndex,
|
|
157
158
|
searchTool = "",
|
|
158
159
|
pipelinePhase = "",
|
|
159
|
-
bashTimeout,
|
|
160
160
|
} = opts;
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
const bashTimeoutMs = bashTimeout ?? (getRouteTimeout(role, mcp_profile) + 60) * 1000;
|
|
164
|
-
|
|
165
|
-
// 셸 이스케이프
|
|
162
|
+
const routeTimeoutSec = getRouteTimeout(role, mcp_profile);
|
|
166
163
|
const escaped = subtask.replace(/'/g, "'\\''");
|
|
167
164
|
const pipelineHint = pipelinePhase
|
|
168
165
|
? `\n파이프라인 단계: ${pipelinePhase}`
|
|
169
166
|
: '';
|
|
170
167
|
const routeEnvPrefix = buildRouteEnvPrefix(agentName, workerIndex, searchTool);
|
|
171
168
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
3
|
|
169
|
+
// Bash 도구 timeout (모두 600초 이내)
|
|
170
|
+
const launchTimeoutMs = 15000; // Step 1: fork + job_id 반환
|
|
171
|
+
const waitTimeoutMs = 570000; // Step 2: 내부 폴링 (540초 대기 + 여유)
|
|
172
|
+
const resultTimeoutMs = 30000; // Step 3: 결과 읽기
|
|
173
|
+
|
|
174
|
+
return `실행 프로토콜 (subagent_type="${SLIM_WRAPPER_SUBAGENT_TYPE}", async):
|
|
175
|
+
1. --async로 백그라운드 시작 → JOB_ID 수신
|
|
176
|
+
2. --job-wait로 완료 대기 (내부 15초 간격 폴링, 최대 540초)
|
|
177
|
+
3. "still_running"이면 Step 2 반복, "done"이면 --job-result로 결과 수집
|
|
178
|
+
4. TaskUpdate + SendMessage → 종료${pipelineHint}
|
|
176
179
|
|
|
177
180
|
[HARD CONSTRAINT] 허용 도구: Bash, TaskUpdate, TaskGet, TaskList, SendMessage만 사용한다.
|
|
178
181
|
Read, Edit, Write, Grep, Glob, Agent, WebSearch, WebFetch 등 다른 모든 도구 사용을 금지한다.
|
|
@@ -182,21 +185,33 @@ Read, Edit, Write, Grep, Glob, Agent, WebSearch, WebFetch 등 다른 모든 도
|
|
|
182
185
|
gemini/codex를 직접 호출하지 마라. 반드시 tfx-route.sh를 거쳐야 한다.
|
|
183
186
|
프롬프트를 파일로 저장하지 마라. tfx-route.sh가 인자로 받는다.
|
|
184
187
|
|
|
185
|
-
Step 1 —
|
|
186
|
-
Bash(command: 'TFX_TEAM_NAME="${teamName}" TFX_TEAM_TASK_ID="${taskId}" TFX_TEAM_AGENT_NAME="${agentName}" TFX_TEAM_LEAD_NAME="${leadName}"${routeEnvPrefix} bash ${ROUTE_SCRIPT} "${role}" '"'"'${escaped}'"'"' ${mcp_profile}', timeout: ${
|
|
188
|
+
Step 1 — Async 시작 (즉시 리턴, <1초):
|
|
189
|
+
Bash(command: 'TFX_TEAM_NAME="${teamName}" TFX_TEAM_TASK_ID="${taskId}" TFX_TEAM_AGENT_NAME="${agentName}" TFX_TEAM_LEAD_NAME="${leadName}"${routeEnvPrefix} bash ${ROUTE_SCRIPT} --async "${role}" '"'"'${escaped}'"'"' ${mcp_profile} ${routeTimeoutSec}', timeout: ${launchTimeoutMs})
|
|
190
|
+
→ 출력 한 줄이 JOB_ID이다. 반드시 기억하라.
|
|
191
|
+
|
|
192
|
+
Step 2 — 완료 대기 (내부 폴링, 최대 540초):
|
|
193
|
+
Bash(command: 'bash ${ROUTE_SCRIPT} --job-wait JOB_ID 540', timeout: ${waitTimeoutMs})
|
|
194
|
+
→ 주기적 "waiting elapsed=Ns progress=NB" 출력 후 최종 상태:
|
|
195
|
+
"done" → Step 3으로
|
|
196
|
+
"timeout" 또는 "failed ..." → Step 4로 (실패 보고)
|
|
197
|
+
"still_running ..." → Step 2 반복 (같은 명령 재실행)
|
|
198
|
+
|
|
199
|
+
Step 3 — 결과 수집:
|
|
200
|
+
Bash(command: 'bash ${ROUTE_SCRIPT} --job-result JOB_ID', timeout: ${resultTimeoutMs})
|
|
201
|
+
→ 출력이 워커 실행 결과이다.
|
|
187
202
|
|
|
188
|
-
Step
|
|
189
|
-
|
|
203
|
+
Step 4 — Claude Code 태스크 동기화 (반드시 실행):
|
|
204
|
+
"done"이면:
|
|
190
205
|
TaskUpdate(taskId: "${taskId}", status: "completed", metadata: {result: "success"})
|
|
191
206
|
SendMessage(type: "message", recipient: "${leadName}", content: "완료: ${agentName}", summary: "task ${taskId} success")
|
|
192
|
-
|
|
193
|
-
TaskUpdate(taskId: "${taskId}", status: "completed", metadata: {result: "failed", error: "
|
|
194
|
-
SendMessage(type: "message", recipient: "${leadName}", content: "실패: ${agentName} (
|
|
207
|
+
"timeout" 또는 "failed"이면:
|
|
208
|
+
TaskUpdate(taskId: "${taskId}", status: "completed", metadata: {result: "failed", error: "상태 메시지"})
|
|
209
|
+
SendMessage(type: "message", recipient: "${leadName}", content: "실패: ${agentName} (상태)", summary: "task ${taskId} failed")
|
|
195
210
|
TFX_NEEDS_FALLBACK 출력 감지 시:
|
|
196
211
|
TaskUpdate(taskId: "${taskId}", status: "completed", metadata: {result: "fallback", reason: "claude-native"})
|
|
197
212
|
SendMessage(type: "message", recipient: "${leadName}", content: "fallback 필요: ${agentName} — claude-native 역할은 Claude Agent로 위임 필요", summary: "task ${taskId} fallback")
|
|
198
213
|
|
|
199
|
-
Step
|
|
214
|
+
Step 5 — TaskUpdate + SendMessage 후 즉시 종료. 추가 도구 호출 금지.`;
|
|
200
215
|
}
|
|
201
216
|
|
|
202
217
|
/**
|
package/package.json
CHANGED
package/scripts/tfx-route.sh
CHANGED
|
@@ -9,17 +9,111 @@
|
|
|
9
9
|
# - Gemini health check 지수 백오프 (30×1s → 5×exp)
|
|
10
10
|
# - 컨텍스트 파일 5번째 인자 지원
|
|
11
11
|
#
|
|
12
|
-
VERSION="2.
|
|
12
|
+
VERSION="2.5"
|
|
13
13
|
#
|
|
14
14
|
# 사용법:
|
|
15
15
|
# tfx-route.sh <agent_type> <prompt> [mcp_profile] [timeout_sec] [context_file]
|
|
16
|
+
# tfx-route.sh --async <agent_type> <prompt> [mcp_profile] [timeout_sec] [context_file]
|
|
17
|
+
# tfx-route.sh --job-status <job_id>
|
|
18
|
+
# tfx-route.sh --job-result <job_id>
|
|
19
|
+
#
|
|
20
|
+
# --async: 백그라운드 실행, 즉시 job_id 반환 (Claude Code Bash 600초 제한 우회)
|
|
21
|
+
# --job-status: running | done | timeout | failed
|
|
22
|
+
# --job-result: 완료된 잡의 전체 출력
|
|
16
23
|
#
|
|
17
24
|
# 예시:
|
|
18
25
|
# tfx-route.sh executor "코드 구현" implement
|
|
19
|
-
# tfx-route.sh
|
|
26
|
+
# tfx-route.sh --async scientist "딥 리서치" auto 1440
|
|
27
|
+
# tfx-route.sh --job-status 1742400000-12345-9876
|
|
28
|
+
# tfx-route.sh --job-result 1742400000-12345-9876
|
|
20
29
|
|
|
21
30
|
set -euo pipefail
|
|
22
31
|
|
|
32
|
+
# ── Async Job 디렉토리 ──
|
|
33
|
+
TFX_JOBS_DIR="${TMPDIR:-/tmp}/tfx-jobs"
|
|
34
|
+
|
|
35
|
+
# ── --job-status / --job-result 핸들러 (인자 파싱 전에 처리) ──
|
|
36
|
+
if [[ "${1:-}" == "--job-status" ]]; then
|
|
37
|
+
job_id="${2:?job_id 필수}"
|
|
38
|
+
job_dir="$TFX_JOBS_DIR/$job_id"
|
|
39
|
+
[[ -d "$job_dir" ]] || { echo "error: job not found"; exit 1; }
|
|
40
|
+
|
|
41
|
+
if [[ -f "$job_dir/done" ]]; then
|
|
42
|
+
exit_code=$(cat "$job_dir/exit_code" 2>/dev/null || echo 1)
|
|
43
|
+
if [[ "$exit_code" -eq 0 ]]; then
|
|
44
|
+
echo "done"
|
|
45
|
+
elif [[ "$exit_code" -eq 124 ]]; then
|
|
46
|
+
echo "timeout"
|
|
47
|
+
else
|
|
48
|
+
echo "failed"
|
|
49
|
+
fi
|
|
50
|
+
elif [[ -f "$job_dir/pid" ]]; then
|
|
51
|
+
pid=$(cat "$job_dir/pid")
|
|
52
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
53
|
+
# 진행 상황 힌트
|
|
54
|
+
local_bytes=$(wc -c < "$job_dir/stdout.log" 2>/dev/null || echo 0)
|
|
55
|
+
elapsed=$(( $(date +%s) - $(cat "$job_dir/start_time" 2>/dev/null || date +%s) ))
|
|
56
|
+
echo "running elapsed=${elapsed}s output=${local_bytes}B"
|
|
57
|
+
else
|
|
58
|
+
# 프로세스 종료됐는데 done 마커 없음 → 비정상 종료
|
|
59
|
+
echo "failed"
|
|
60
|
+
fi
|
|
61
|
+
else
|
|
62
|
+
echo "error: invalid job state"
|
|
63
|
+
exit 1
|
|
64
|
+
fi
|
|
65
|
+
exit 0
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
if [[ "${1:-}" == "--job-result" ]]; then
|
|
69
|
+
job_id="${2:?job_id 필수}"
|
|
70
|
+
job_dir="$TFX_JOBS_DIR/$job_id"
|
|
71
|
+
[[ -d "$job_dir" ]] || { echo "error: job not found"; exit 1; }
|
|
72
|
+
[[ -f "$job_dir/done" ]] || { echo "error: job still running"; exit 1; }
|
|
73
|
+
|
|
74
|
+
cat "$job_dir/result.log" 2>/dev/null
|
|
75
|
+
exit_code=$(cat "$job_dir/exit_code" 2>/dev/null || echo 1)
|
|
76
|
+
exit "$exit_code"
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# ── --job-wait: 내부 폴링으로 완료 대기 (Bash 도구 호출 횟수 최소화) ──
|
|
80
|
+
# 사용법: tfx-route.sh --job-wait <job_id> [max_seconds=540]
|
|
81
|
+
# 출력: 주기적 "waiting elapsed=Ns" + 최종 "done"|"timeout"|"failed"|"still_running"
|
|
82
|
+
if [[ "${1:-}" == "--job-wait" ]]; then
|
|
83
|
+
job_id="${2:?job_id 필수}"
|
|
84
|
+
max_wait="${3:-540}" # 기본 540초 (9분, Bash 도구 600초 제한 이내)
|
|
85
|
+
poll_interval=15
|
|
86
|
+
job_dir="$TFX_JOBS_DIR/$job_id"
|
|
87
|
+
[[ -d "$job_dir" ]] || { echo "error: job not found"; exit 1; }
|
|
88
|
+
|
|
89
|
+
elapsed=0
|
|
90
|
+
while [[ "$elapsed" -lt "$max_wait" ]]; do
|
|
91
|
+
if [[ -f "$job_dir/done" ]]; then
|
|
92
|
+
ec=$(cat "$job_dir/exit_code" 2>/dev/null || echo 1)
|
|
93
|
+
if [[ "$ec" -eq 0 ]]; then echo "done"
|
|
94
|
+
elif [[ "$ec" -eq 124 ]]; then echo "timeout"
|
|
95
|
+
else echo "failed (exit=$ec)"
|
|
96
|
+
fi
|
|
97
|
+
exit 0
|
|
98
|
+
fi
|
|
99
|
+
sleep "$poll_interval"
|
|
100
|
+
elapsed=$((elapsed + poll_interval))
|
|
101
|
+
stderr_bytes=$(wc -c < "$job_dir/stderr.log" 2>/dev/null || echo 0)
|
|
102
|
+
echo "waiting elapsed=${elapsed}s progress=${stderr_bytes}B"
|
|
103
|
+
done
|
|
104
|
+
|
|
105
|
+
# max_wait 도달했지만 아직 실행 중
|
|
106
|
+
echo "still_running elapsed=${elapsed}s"
|
|
107
|
+
exit 0
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
# ── --async 플래그 감지 ──
|
|
111
|
+
TFX_ASYNC_MODE=0
|
|
112
|
+
if [[ "${1:-}" == "--async" ]]; then
|
|
113
|
+
TFX_ASYNC_MODE=1
|
|
114
|
+
shift
|
|
115
|
+
fi
|
|
116
|
+
|
|
23
117
|
# ── 인자 파싱 ──
|
|
24
118
|
AGENT_TYPE="${1:?에이전트 타입 필수 (executor, debugger, designer 등)}"
|
|
25
119
|
PROMPT="${2:?프롬프트 필수}"
|
|
@@ -1518,4 +1612,40 @@ EOF
|
|
|
1518
1612
|
return "$exit_code"
|
|
1519
1613
|
}
|
|
1520
1614
|
|
|
1615
|
+
# ── Async 모드: 백그라운드 실행 + 즉시 job_id 반환 ──
|
|
1616
|
+
if [[ "$TFX_ASYNC_MODE" -eq 1 ]]; then
|
|
1617
|
+
mkdir -p "$TFX_JOBS_DIR"
|
|
1618
|
+
JOB_ID="$TIMESTAMP-$$-${RANDOM}"
|
|
1619
|
+
JOB_DIR="$TFX_JOBS_DIR/$JOB_ID"
|
|
1620
|
+
mkdir -p "$JOB_DIR"
|
|
1621
|
+
echo "$AGENT_TYPE" > "$JOB_DIR/agent_type"
|
|
1622
|
+
date +%s > "$JOB_DIR/start_time"
|
|
1623
|
+
|
|
1624
|
+
# 백그라운드 서브쉘: main 실행 → 결과 저장
|
|
1625
|
+
(
|
|
1626
|
+
set +e # main 내부 에러가 exit_code 기록 전에 서브쉘을 죽이는 것 방지
|
|
1627
|
+
exec > "$JOB_DIR/result.log" 2>"$JOB_DIR/stderr.log"
|
|
1628
|
+
main
|
|
1629
|
+
echo $? > "$JOB_DIR/exit_code"
|
|
1630
|
+
touch "$JOB_DIR/done"
|
|
1631
|
+
) &
|
|
1632
|
+
bg_pid=$!
|
|
1633
|
+
echo "$bg_pid" > "$JOB_DIR/pid"
|
|
1634
|
+
|
|
1635
|
+
# 종료 감지 데몬 (main이 signal/crash로 죽어도 done 마커 생성)
|
|
1636
|
+
(
|
|
1637
|
+
wait "$bg_pid" 2>/dev/null
|
|
1638
|
+
ec=$?
|
|
1639
|
+
if [[ ! -f "$JOB_DIR/done" ]]; then
|
|
1640
|
+
echo "$ec" > "$JOB_DIR/exit_code"
|
|
1641
|
+
touch "$JOB_DIR/done"
|
|
1642
|
+
fi
|
|
1643
|
+
) &
|
|
1644
|
+
disown
|
|
1645
|
+
|
|
1646
|
+
# 즉시 리턴: 1초 이내에 Claude Code Bash 도구 완료
|
|
1647
|
+
echo "$JOB_ID"
|
|
1648
|
+
exit 0
|
|
1649
|
+
fi
|
|
1650
|
+
|
|
1521
1651
|
main
|
|
@@ -170,7 +170,7 @@ status는 "completed"만 사용. 실패 여부는 `metadata.result`로 구분.
|
|
|
170
170
|
|
|
171
171
|
| 항목 | 설명 |
|
|
172
172
|
|------|------|
|
|
173
|
-
| `scripts/tfx-route.sh` | 팀 통합 라우터 |
|
|
173
|
+
| `scripts/tfx-route.sh` | 팀 통합 라우터 (v2.5: `--async`/`--job-wait`/`--job-status`/`--job-result`) |
|
|
174
174
|
| `hub/team/native.mjs` | Native Teams 래퍼 (프롬프트 템플릿) |
|
|
175
175
|
| `hub/pipeline/` | 파이프라인 상태 기계 (`--thorough` 모드) |
|
|
176
176
|
| `tfx-auto` | one-shot 실행 오케스트레이터 |
|
|
@@ -58,12 +58,25 @@ codex-worker는 반드시 tfx-route.sh를 통해 Codex에 위임하고, gemini-w
|
|
|
58
58
|
|
|
59
59
|
리드는 워커의 Step 2, Step 4 시점에 턴 경계를 인식하고, 방향 전환/추가 지시/재실행 요청을 보낼 수 있다.
|
|
60
60
|
|
|
61
|
-
##
|
|
61
|
+
## Async 실행 프로토콜 (v2.5+)
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
Claude Code Bash 도구는 최대 600초(10분) 하드코딩 제한이 있다.
|
|
64
|
+
scientist(24분), scientist-deep(60분) 등 장시간 워커는 이 제한에 걸린다.
|
|
65
|
+
|
|
66
|
+
**해결: `--async` 3단계 패턴**
|
|
67
|
+
|
|
68
|
+
| 단계 | 명령 | Bash timeout | 소요 |
|
|
69
|
+
|------|------|-------------|------|
|
|
70
|
+
| 시작 | `tfx-route.sh --async {role} '{task}' {profile} {timeout}` | 15초 | <1초 |
|
|
71
|
+
| 대기 | `tfx-route.sh --job-wait {job_id} 540` | 570초 | 최대 540초/회 |
|
|
72
|
+
| 결과 | `tfx-route.sh --job-result {job_id}` | 30초 | <1초 |
|
|
73
|
+
|
|
74
|
+
- `--job-wait`는 내부에서 15초 간격으로 폴링하며 `done`/`timeout`/`failed`/`still_running` 반환
|
|
75
|
+
- `still_running` 시 같은 `--job-wait` 명령을 반복 (무한 반복 가능)
|
|
76
|
+
- 실제 워커 timeout은 tfx-route.sh의 `timeout` 명령으로 관리 (Bash 도구와 무관)
|
|
77
|
+
|
|
78
|
+
**이전 방식 (deprecated):**
|
|
79
|
+
Bash timeout을 role/profile별 timeout + 60초로 설정했으나, 600초 초과 시 Bash 도구가 강제 종료했다.
|
|
67
80
|
|
|
68
81
|
## tfx-route.sh 팀 통합 동작
|
|
69
82
|
|