triflux 2.4.6 → 2.5.1

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # cli-route.sh v1.6 — CLI 라우팅 래퍼 (ai-scaffold 템플릿)
2
+ # cli-route.sh v1.7 — CLI 라우팅 래퍼 (ai-scaffold 템플릿)
3
3
  # v1.0: 기본 라우팅 (Codex/Gemini/Claude 분기)
4
4
  # v1.1: stderr 분리, 출력 필터링, 타임아웃, MCP 프로필 지원
5
5
  # v1.2: effort 동적 라우팅, bg/fg 모드, Opus 직접 수행, Gemini 모델 분기, 실행 로그
@@ -7,7 +7,8 @@
7
7
  # v1.4: TFX_CLI_MODE 지원 (codex-only/gemini-only), CLI 미설치 자동 fallback
8
8
  # v1.5: MCP 인벤토리 캐싱 — 실제 서버 가용성 기반 동적 힌트 생성
9
9
  # v1.6: 토큰 사용량 추출 + sv-accumulator.json 누적
10
- VERSION="1.6"
10
+ # v1.7: 배치 AIMD 전략 — 성공 시 +1, 실패/타임아웃 시 ×0.5, 수렴 감지
11
+ VERSION="1.7"
11
12
  #
12
13
  # 설치: cp scripts/cli-route.sh ~/.claude/scripts/cli-route.sh
13
14
  #
@@ -35,8 +36,207 @@ GEMINI_BIN="${GEMINI_BIN:-$(command -v gemini 2>/dev/null || echo gemini)}"
35
36
  # ── 상수 ──
36
37
  MAX_STDOUT_BYTES=51200 # 50KB — Claude 컨텍스트 절약
37
38
  TIMESTAMP=$(date +%s)
38
- STDERR_LOG="/tmp/omc-route-${AGENT_TYPE}-${TIMESTAMP}-stderr.log"
39
- STDOUT_LOG="/tmp/omc-route-${AGENT_TYPE}-${TIMESTAMP}-stdout.log"
39
+ STDERR_LOG="/tmp/tfx-route-${AGENT_TYPE}-${TIMESTAMP}-stderr.log"
40
+ STDOUT_LOG="/tmp/tfx-route-${AGENT_TYPE}-${TIMESTAMP}-stdout.log"
41
+
42
+ # fallback 시 원래 에이전트/CLI 인자 보존용 (수정 3: review fallback 프로필 유실 방지)
43
+ ORIGINAL_AGENT=""
44
+ ORIGINAL_CLI_ARGS=""
45
+
46
+ # ── 크로스 세션 활성 에이전트 추적 ──
47
+ # 활성 에이전트 레지스트리 경로
48
+ ACTIVE_AGENTS_FILE="${HOME}/.claude/cache/active-agents.json"
49
+
50
+ # 죽은 PID 및 좀비 정리
51
+ cleanup_stale_agents() {
52
+ [[ ! -f "$ACTIVE_AGENTS_FILE" ]] && return
53
+ local now
54
+ now=$(date +%s)
55
+ local tmp="${ACTIVE_AGENTS_FILE}.tmp"
56
+ # jq가 있으면 사용, 없으면 건너뜀
57
+ if command -v jq &>/dev/null; then
58
+ jq --argjson now "$now" '
59
+ .agents |= map(select(
60
+ # PID 생존 확인은 셸에서 하므로 여기선 타임아웃만 체크
61
+ (.started + 1200) > $now
62
+ ))
63
+ ' "$ACTIVE_AGENTS_FILE" > "$tmp" 2>/dev/null && mv "$tmp" "$ACTIVE_AGENTS_FILE"
64
+ # 추가로 kill -0으로 죽은 PID 제거
65
+ local pids
66
+ pids=$(jq -r '.agents[].pid' "$ACTIVE_AGENTS_FILE" 2>/dev/null)
67
+ local pid
68
+ for pid in $pids; do
69
+ if ! kill -0 "$pid" 2>/dev/null; then
70
+ jq --argjson pid "$pid" '.agents |= map(select(.pid != $pid))' "$ACTIVE_AGENTS_FILE" > "$tmp" 2>/dev/null && mv "$tmp" "$ACTIVE_AGENTS_FILE"
71
+ fi
72
+ done
73
+ fi
74
+ }
75
+
76
+ # 에이전트 등록
77
+ register_agent() {
78
+ local pid="$1" cli="$2" agent="$3"
79
+ local now
80
+ now=$(date +%s)
81
+ cleanup_stale_agents
82
+ if command -v jq &>/dev/null; then
83
+ # 캐시 디렉토리가 없으면 생성
84
+ mkdir -p "$(dirname "$ACTIVE_AGENTS_FILE")"
85
+ if [[ -f "$ACTIVE_AGENTS_FILE" ]]; then
86
+ jq --argjson pid "$pid" --arg cli "$cli" --arg agent "$agent" --argjson started "$now" \
87
+ '.agents += [{"pid": $pid, "cli": $cli, "agent": $agent, "started": $started}]' \
88
+ "$ACTIVE_AGENTS_FILE" > "${ACTIVE_AGENTS_FILE}.tmp" && mv "${ACTIVE_AGENTS_FILE}.tmp" "$ACTIVE_AGENTS_FILE"
89
+ else
90
+ echo "{\"agents\":[{\"pid\":$pid,\"cli\":\"$cli\",\"agent\":\"$agent\",\"started\":$now}]}" > "$ACTIVE_AGENTS_FILE"
91
+ fi
92
+ fi
93
+ }
94
+
95
+ # 에이전트 등록 해제
96
+ deregister_agent() {
97
+ local pid="$1"
98
+ if command -v jq &>/dev/null && [[ -f "$ACTIVE_AGENTS_FILE" ]]; then
99
+ jq --argjson pid "$pid" '.agents |= map(select(.pid != $pid))' \
100
+ "$ACTIVE_AGENTS_FILE" > "${ACTIVE_AGENTS_FILE}.tmp" && mv "${ACTIVE_AGENTS_FILE}.tmp" "$ACTIVE_AGENTS_FILE"
101
+ fi
102
+ }
103
+
104
+ # ── 배치 AIMD 전략 ──
105
+ # 배치 설정 파일: ~/.claude/cache/batch-config.json
106
+ # 초기 batch_size=2, 성공→+1 (AI), 실패/타임아웃→×0.5 (MD), 상한=8, 수렴=3연속 동일
107
+ BATCH_CONFIG_FILE="${HOME}/.claude/cache/batch-config.json"
108
+
109
+ # 현재 batch_size 반환 (파일 없으면 기본값 2)
110
+ get_batch_size() {
111
+ if ! command -v jq &>/dev/null; then
112
+ echo "2"
113
+ return
114
+ fi
115
+ if [[ -f "$BATCH_CONFIG_FILE" ]]; then
116
+ local size
117
+ size=$(jq -r '.batch_size // 2' "$BATCH_CONFIG_FILE" 2>/dev/null)
118
+ # 숫자가 아니거나 비어 있으면 기본값
119
+ if [[ "$size" =~ ^[0-9]+$ ]]; then
120
+ echo "$size"
121
+ else
122
+ echo "2"
123
+ fi
124
+ else
125
+ echo "2"
126
+ fi
127
+ }
128
+
129
+ # 현재 활성 에이전트 수 반환 (active-agents.json 기반)
130
+ get_active_agent_count() {
131
+ if ! command -v jq &>/dev/null; then
132
+ echo "0"
133
+ return
134
+ fi
135
+ if [[ -f "$ACTIVE_AGENTS_FILE" ]]; then
136
+ local count
137
+ count=$(jq '.agents | length' "$ACTIVE_AGENTS_FILE" 2>/dev/null)
138
+ echo "${count:-0}"
139
+ else
140
+ echo "0"
141
+ fi
142
+ }
143
+
144
+ # AIMD 결과 기록 및 batch_size 업데이트
145
+ # 인자: result (success/failed/timeout), agent
146
+ update_batch_result() {
147
+ local result="$1"
148
+ local agent="${2:-unknown}"
149
+
150
+ if ! command -v jq &>/dev/null; then
151
+ return
152
+ fi
153
+
154
+ mkdir -p "$(dirname "$BATCH_CONFIG_FILE")"
155
+
156
+ # 현재 설정 읽기 (없으면 초기값)
157
+ local current_size consecutive_same converged
158
+ if [[ -f "$BATCH_CONFIG_FILE" ]]; then
159
+ current_size=$(jq -r '.batch_size // 2' "$BATCH_CONFIG_FILE" 2>/dev/null)
160
+ consecutive_same=$(jq -r '.consecutive_same // 0' "$BATCH_CONFIG_FILE" 2>/dev/null)
161
+ converged=$(jq -r '.converged // false' "$BATCH_CONFIG_FILE" 2>/dev/null)
162
+ else
163
+ current_size=2
164
+ consecutive_same=0
165
+ converged="false"
166
+ fi
167
+
168
+ # 숫자 검증
169
+ [[ "$current_size" =~ ^[0-9]+$ ]] || current_size=2
170
+ [[ "$consecutive_same" =~ ^[0-9]+$ ]] || consecutive_same=0
171
+
172
+ # 수렴 상태면 batch_size 고정 (업데이트만 기록)
173
+ local new_size="$current_size"
174
+ if [[ "$converged" != "true" ]]; then
175
+ case "$result" in
176
+ success)
177
+ # Additive Increase: +1, 상한 8
178
+ new_size=$((current_size + 1))
179
+ if [[ $new_size -gt 8 ]]; then new_size=8; fi
180
+ ;;
181
+ failed|timeout)
182
+ # Multiplicative Decrease: ×0.5, 하한 1
183
+ new_size=$((current_size / 2))
184
+ if [[ $new_size -lt 1 ]]; then new_size=1; fi
185
+ ;;
186
+ esac
187
+ fi
188
+
189
+ # 수렴 판단: 3연속 동일하면 converged=true
190
+ if [[ $new_size -eq $current_size ]]; then
191
+ consecutive_same=$((consecutive_same + 1))
192
+ else
193
+ consecutive_same=0
194
+ fi
195
+
196
+ local new_converged="false"
197
+ if [[ $consecutive_same -ge 3 ]]; then
198
+ new_converged="true"
199
+ fi
200
+
201
+ local now
202
+ now=$(date +%s)
203
+
204
+ # history에 추가 (최대 50건 유지) 후 batch_size 업데이트
205
+ local tmp="${BATCH_CONFIG_FILE}.tmp"
206
+ if [[ -f "$BATCH_CONFIG_FILE" ]]; then
207
+ jq --argjson now "$now" \
208
+ --arg agent "$agent" \
209
+ --arg result "$result" \
210
+ --argjson batch_at_time "$current_size" \
211
+ --argjson new_size "$new_size" \
212
+ --argjson consecutive_same "$consecutive_same" \
213
+ --argjson converged "$new_converged" \
214
+ '
215
+ .history += [{"timestamp": $now, "agent": $agent, "result": $result, "batch_at_time": $batch_at_time}] |
216
+ .history = (.history | if length > 50 then .[-50:] else . end) |
217
+ .batch_size = $new_size |
218
+ .consecutive_same = $consecutive_same |
219
+ .converged = $converged
220
+ ' "$BATCH_CONFIG_FILE" > "$tmp" 2>/dev/null && mv "$tmp" "$BATCH_CONFIG_FILE"
221
+ else
222
+ # 파일 신규 생성
223
+ jq -n \
224
+ --argjson now "$now" \
225
+ --arg agent "$agent" \
226
+ --arg result "$result" \
227
+ --argjson new_size "$new_size" \
228
+ --argjson consecutive_same "$consecutive_same" \
229
+ --argjson converged "$new_converged" \
230
+ '{
231
+ batch_size: $new_size,
232
+ history: [{"timestamp": $now, "agent": $agent, "result": $result, "batch_at_time": 2}],
233
+ consecutive_same: $consecutive_same,
234
+ converged: $converged
235
+ }' > "$BATCH_CONFIG_FILE" 2>/dev/null || true
236
+ fi
237
+
238
+ echo "[cli-route] AIMD: $result → batch_size $current_size→$new_size (consecutive_same=$consecutive_same, converged=$new_converged)" >&2
239
+ }
40
240
 
41
241
  # ── 라우팅 테이블 ──
42
242
  # 반환: CLI_CMD, CLI_ARGS, CLI_TYPE, CLI_EFFORT, DEFAULT_TIMEOUT, RUN_MODE, OPUS_OVERSIGHT
@@ -373,10 +573,13 @@ apply_cli_mode() {
373
573
  apply_cli_mode
374
574
  return
375
575
  else
576
+ # 원래 에이전트 및 MCP 프로필 정보 보존
577
+ ORIGINAL_AGENT="${AGENT_TYPE}"
578
+ ORIGINAL_CLI_ARGS="$CLI_ARGS"
376
579
  CLI_TYPE="claude-native"
377
580
  CLI_CMD=""
378
581
  CLI_ARGS=""
379
- echo "[cli-route] codex/gemini 모두 미설치: $AGENT_TYPE → claude-native fallback" >&2
582
+ echo "[cli-route] codex/gemini 모두 미설치: $AGENT_TYPE → claude-native fallback (원래 프로필: $MCP_PROFILE)" >&2
380
583
  fi
381
584
  elif [[ "$CLI_TYPE" == "gemini" ]] && ! command -v "$GEMINI_BIN" &>/dev/null; then
382
585
  if command -v "$CODEX_BIN" &>/dev/null; then
@@ -384,10 +587,13 @@ apply_cli_mode() {
384
587
  apply_cli_mode
385
588
  return
386
589
  else
590
+ # 원래 에이전트 및 MCP 프로필 정보 보존
591
+ ORIGINAL_AGENT="${AGENT_TYPE}"
592
+ ORIGINAL_CLI_ARGS="$CLI_ARGS"
387
593
  CLI_TYPE="claude-native"
388
594
  CLI_CMD=""
389
595
  CLI_ARGS=""
390
- echo "[cli-route] codex/gemini 모두 미설치: $AGENT_TYPE → claude-native fallback" >&2
596
+ echo "[cli-route] codex/gemini 모두 미설치: $AGENT_TYPE → claude-native fallback (원래 프로필: $MCP_PROFILE)" >&2
391
597
  fi
392
598
  fi
393
599
  ;;
@@ -764,6 +970,9 @@ truncate_output() {
764
970
 
765
971
  # ── 메인 실행 ──
766
972
  main() {
973
+ # 종료 시 활성 에이전트 레지스트리에서 자동 제거
974
+ trap 'deregister_agent $$' EXIT
975
+
767
976
  route_agent "$AGENT_TYPE"
768
977
 
769
978
  # CLI 모드 오버라이드 적용 (tfx-codex/tfx-gemini 또는 auto-fallback)
@@ -782,12 +991,12 @@ main() {
782
991
  TIMEOUT_SEC="$DEFAULT_TIMEOUT"
783
992
  fi
784
993
 
785
- # kteam 안정화: Gemini 에이전트 기본 타임아웃 축소 (사용자 미지정 시만)
994
+ # kteam 안정화: Gemini 에이전트 기본 타임아웃 하한 적용 (사용자 미지정 시만)
786
995
  if [[ -z "$USER_TIMEOUT" ]]; then
787
996
  case "$AGENT_TYPE" in
788
997
  designer|writer)
789
- if [[ "$DEFAULT_TIMEOUT" -gt 60 ]]; then
790
- TIMEOUT_SEC=60
998
+ if [[ "$DEFAULT_TIMEOUT" -gt 300 ]]; then
999
+ TIMEOUT_SEC=300
791
1000
  fi
792
1001
  ;;
793
1002
  esac
@@ -808,6 +1017,12 @@ main() {
808
1017
  echo "RUN_MODE=$RUN_MODE"
809
1018
  echo "OPUS_OVERSIGHT=$OPUS_OVERSIGHT"
810
1019
  echo "TIMEOUT=$TIMEOUT_SEC"
1020
+ echo "MCP_PROFILE=$MCP_PROFILE"
1021
+ # fallback 시 원래 에이전트/MCP 프로필 정보를 함께 출력 (수정 3)
1022
+ if [[ -n "$ORIGINAL_AGENT" ]]; then
1023
+ echo "ORIGINAL_AGENT=$ORIGINAL_AGENT"
1024
+ echo "ORIGINAL_CLI_ARGS=$ORIGINAL_CLI_ARGS"
1025
+ fi
811
1026
  echo "PROMPT=$PROMPT"
812
1027
  echo "--- Claude Task($model) 에이전트로 위임하세요 ---"
813
1028
  exit 0
@@ -825,6 +1040,9 @@ main() {
825
1040
  echo "[cli-route] type=$CLI_TYPE agent=$AGENT_TYPE effort=$CLI_EFFORT mode=$RUN_MODE timeout=${TIMEOUT_SEC}s" >&2
826
1041
  echo "[cli-route] opus_oversight=$OPUS_OVERSIGHT mcp_profile=$MCP_PROFILE stderr_log=$STDERR_LOG" >&2
827
1042
 
1043
+ # 크로스 세션 활성 에이전트 레지스트리에 등록 (수정 4)
1044
+ register_agent $$ "$CLI_TYPE" "$AGENT_TYPE"
1045
+
828
1046
  # CLI 실행 (stderr 분리 + 타임아웃 + 소요시간 측정)
829
1047
  local exit_code=0
830
1048
  local raw_output=""
@@ -906,6 +1124,15 @@ main() {
906
1124
  accumulate_tokens "$CLI_TYPE" "$input_tokens" "$output_tokens" || true
907
1125
  fi
908
1126
 
1127
+ # AIMD 배치 크기 업데이트 (exit code 기반)
1128
+ if [[ $exit_code -eq 0 ]]; then
1129
+ update_batch_result "success" "$AGENT_TYPE" || true
1130
+ elif [[ $exit_code -eq 124 ]]; then
1131
+ update_batch_result "timeout" "$AGENT_TYPE" || true
1132
+ else
1133
+ update_batch_result "failed" "$AGENT_TYPE" || true
1134
+ fi
1135
+
909
1136
  # CLI 이슈 자동 수집
910
1137
  local _stderr_for_track=""
911
1138
  [[ -f "$STDERR_LOG" ]] && _stderr_for_track=$(cat "$STDERR_LOG" 2>/dev/null || echo "")