triflux 10.45.0 → 10.46.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.
@@ -4,6 +4,11 @@ import { fileURLToPath } from "node:url";
4
4
  import { assertVersionSync, parseArgs, ROOT } from "./lib.mjs";
5
5
 
6
6
  const NPM_VERIFY_TARGETS = ["@triflux/core", "@triflux/remote", "triflux"];
7
+ const NPM_POLL_INTERVAL_MS = 20_000;
8
+
9
+ function sleep(ms) {
10
+ return new Promise((resolve) => setTimeout(resolve, ms));
11
+ }
7
12
 
8
13
  function errorMessage(error) {
9
14
  const stderr = error?.stderr?.toString?.().trim();
@@ -16,7 +21,20 @@ export async function verifyRelease({
16
21
  rootDir = ROOT,
17
22
  dryRun = true,
18
23
  execFileSyncFn = execFileSync,
24
+ npmWaitSeconds = 0,
25
+ npmPendingOk = false,
26
+ nowFn = Date.now,
27
+ sleepFn = sleep,
19
28
  } = {}) {
29
+ const waitSeconds = Number(npmWaitSeconds);
30
+ if (
31
+ typeof npmWaitSeconds === "boolean" ||
32
+ npmWaitSeconds === "" ||
33
+ !Number.isFinite(waitSeconds) ||
34
+ waitSeconds < 0
35
+ ) {
36
+ throw new Error("--npm-wait-seconds must be a non-negative finite number");
37
+ }
20
38
  const sync = assertVersionSync({ rootDir });
21
39
  if (!sync.ok) {
22
40
  throw new Error("Version sync failed. Fix metadata before verify.");
@@ -31,32 +49,61 @@ export async function verifyRelease({
31
49
  ];
32
50
 
33
51
  if (!dryRun) {
34
- for (const packageName of NPM_VERIFY_TARGETS) {
35
- const packageSpec = `${packageName}@${releaseVersion}`;
36
- try {
37
- const npmVersion = execFileSyncFn(
38
- "npm",
39
- ["view", packageSpec, "version"],
40
- {
41
- cwd: rootDir,
42
- encoding: "utf8",
43
- },
44
- ).trim();
45
- checks.push({
46
- name: `npm-view ${packageName}`,
47
- ok: npmVersion === releaseVersion,
48
- detail:
49
- npmVersion === releaseVersion
50
- ? npmVersion
51
- : `expected ${releaseVersion}, got ${npmVersion}`,
52
- });
53
- } catch (error) {
54
- checks.push({
55
- name: `npm-view ${packageName}`,
56
- ok: false,
57
- detail: `npm view failed for ${packageSpec}: ${errorMessage(error)}`,
58
- });
52
+ const deadline = nowFn() + waitSeconds * 1000;
53
+ const lastSeen = new Map();
54
+ while (true) {
55
+ const npmChecks = NPM_VERIFY_TARGETS.map((packageName) => {
56
+ const packageSpec = `${packageName}@${releaseVersion}`;
57
+ try {
58
+ const npmVersion = execFileSyncFn(
59
+ "npm",
60
+ ["view", packageSpec, "version"],
61
+ { cwd: rootDir, encoding: "utf8" },
62
+ ).trim();
63
+ lastSeen.set(packageName, npmVersion);
64
+ return {
65
+ name: `npm-view ${packageName}`,
66
+ ok: npmVersion === releaseVersion,
67
+ detail:
68
+ npmVersion === releaseVersion
69
+ ? npmVersion
70
+ : `expected ${releaseVersion}, got ${npmVersion}`,
71
+ };
72
+ } catch (error) {
73
+ const previous = lastSeen.get(packageName);
74
+ const lastSeenDetail =
75
+ waitSeconds > 0 ? ` (last seen ${previous ?? "unavailable"})` : "";
76
+ return {
77
+ name: `npm-view ${packageName}`,
78
+ ok: false,
79
+ detail: `npm view failed for ${packageSpec}: ${errorMessage(error)}${lastSeenDetail}`,
80
+ };
81
+ }
82
+ });
83
+ const remainingMs = deadline - nowFn();
84
+ if (npmChecks.every((check) => check.ok) || remainingMs <= 0) {
85
+ if (npmPendingOk && remainingMs <= 0) {
86
+ const pending = npmChecks.filter((check) => !check.ok);
87
+ for (const check of pending) {
88
+ const packageName = check.name.slice("npm-view ".length);
89
+ check.ok = null;
90
+ check.status = "pending";
91
+ check.detail = `expected ${releaseVersion}, last seen ${lastSeen.get(packageName) ?? "unavailable"}; npm publish-time scan may still be processing`;
92
+ }
93
+ if (pending.length && process.env.GITHUB_ACTIONS === "true") {
94
+ const packages = pending.map(
95
+ (check) =>
96
+ `${check.name.slice("npm-view ".length)}@${releaseVersion}`,
97
+ );
98
+ console.log(
99
+ `::warning::npm registry pending for ${packages.join(", ")}; npm publish-time scan may still be processing`,
100
+ );
101
+ }
102
+ }
103
+ checks.push(...npmChecks);
104
+ break;
59
105
  }
106
+ await sleepFn(Math.min(NPM_POLL_INTERVAL_MS, remainingMs));
60
107
  }
61
108
 
62
109
  const ghRelease = execFileSyncFn(
@@ -101,6 +148,8 @@ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
101
148
  version: args.version,
102
149
  rootDir: args.root,
103
150
  dryRun: !args.execute,
151
+ npmWaitSeconds: args["npm-wait-seconds"] ?? 0,
152
+ npmPendingOk: args["npm-pending-ok"] === true,
104
153
  });
105
154
  console.log(JSON.stringify(result, null, 2));
106
155
  process.exitCode = result.ok ? 0 : 1;
package/scripts/setup.mjs CHANGED
@@ -437,17 +437,23 @@ const REQUIRED_CODEX_PROFILES = [
437
437
  name: "gpt6_astra_xhigh",
438
438
  lines: ['model = "gpt-6-astra"', 'model_reasoning_effort = "xhigh"'],
439
439
  },
440
+ // GPT-6 Sol: implementation, review, and verification role defaults.
440
441
  {
441
- name: "gpt56_terra_high",
442
- lines: ['model = "gpt-5.6-terra"', 'model_reasoning_effort = "high"'],
442
+ name: "gpt6_sol_high",
443
+ lines: ['model = "gpt-6-sol"', 'model_reasoning_effort = "high"'],
443
444
  },
444
445
  {
445
- name: "gpt56_terra_med",
446
- lines: ['model = "gpt-5.6-terra"', 'model_reasoning_effort = "medium"'],
446
+ name: "gpt6_sol_med",
447
+ lines: ['model = "gpt-6-sol"', 'model_reasoning_effort = "medium"'],
447
448
  },
449
+ // GPT-6 Luna: high for focused lanes, low for latency-first lanes.
448
450
  {
449
- name: "gpt56_luna_low",
450
- lines: ['model = "gpt-5.6-luna"', 'model_reasoning_effort = "low"'],
451
+ name: "gpt6_luna_high",
452
+ lines: ['model = "gpt-6-luna"', 'model_reasoning_effort = "high"'],
453
+ },
454
+ {
455
+ name: "gpt6_luna_low",
456
+ lines: ['model = "gpt-6-luna"', 'model_reasoning_effort = "low"'],
451
457
  },
452
458
  ];
453
459
 
@@ -1391,7 +1397,7 @@ function ensureCodexProfiles() {
1391
1397
  }
1392
1398
  }
1393
1399
  // config.toml 의 모든 inline [profiles.*] 마이그레이션 (Codex 0.134 는 inline 거부).
1394
- // - 관리 gpt56_*: 위에서 canonical 별도 파일을 썼으므로 inline 만 제거.
1400
+ // - 관리 gpt6_*: 위에서 canonical 별도 파일을 썼으므로 inline 만 제거.
1395
1401
  // - 구형 retired (codex53/spark53/gpt54/mini54): inline 제거, 재생성 안 함.
1396
1402
  // - 그 외 사용자 커스텀: 본문을 보존해 별도 파일로 이관한 뒤 inline 제거.
1397
1403
  // (별도 파일이 이미 있으면 덮어쓰지 않는다 — 사용자 수정 보존.)
@@ -1424,7 +1424,7 @@ route_agent() {
1424
1424
  exit 1
1425
1425
  }
1426
1426
  IFS=$'\t' read -r policy_profile policy_timeout policy_mode policy_oversight policy_mcp policy_subcommand <<< "$policy_row"
1427
- case "$policy_profile" in gpt6_astra_xhigh|gpt56_terra_high|gpt56_terra_med|gpt56_luna_low) ;; *) echo "ERROR: invalid Codex profile policy" >&2; exit 1 ;; esac
1427
+ case "$policy_profile" in gpt6_astra_xhigh|gpt6_sol_high|gpt6_sol_med|gpt6_luna_high|gpt6_luna_low) ;; *) echo "ERROR: invalid Codex profile policy" >&2; exit 1 ;; esac
1428
1428
  [[ "$policy_timeout" =~ ^[1-9][0-9]*$ ]] || { echo "ERROR: invalid Codex timeout policy" >&2; exit 1; }
1429
1429
  case "$policy_mode" in fg|bg) ;; *) echo "ERROR: invalid Codex run mode policy" >&2; exit 1 ;; esac
1430
1430
  case "$policy_oversight" in true|false) ;; *) echo "ERROR: invalid Codex oversight policy" >&2; exit 1 ;; esac
@@ -1495,6 +1495,9 @@ normalize_codex_profile_name() {
1495
1495
  gpt56_sol_xhigh) echo "gpt6_astra_xhigh" ;;
1496
1496
  gpt56_sol_max) echo "gpt6_astra_max" ;;
1497
1497
  gpt56_sol_ultra) echo "gpt6_astra_ultra" ;;
1498
+ gpt56_terra_high) echo "gpt6_sol_high" ;;
1499
+ gpt56_terra_med) echo "gpt6_sol_med" ;;
1500
+ gpt56_luna_low) echo "gpt6_luna_low" ;;
1498
1501
  *) echo "$1" ;;
1499
1502
  esac
1500
1503
  }
@@ -1585,9 +1588,9 @@ case "$TFX_CODEX_TRANSPORT" in
1585
1588
  ;;
1586
1589
  esac
1587
1590
  case "$TFX_CODEX_PROFILE" in
1588
- auto|max|ultra|gpt56_luna_low|gpt56_terra_med|gpt56_terra_high|gpt6_astra_xhigh|gpt6_astra_max|gpt6_astra_ultra) ;;
1591
+ auto|max|ultra|gpt6_luna_low|gpt6_luna_high|gpt6_sol_med|gpt6_sol_high|gpt6_astra_xhigh|gpt6_astra_max|gpt6_astra_ultra) ;;
1589
1592
  *)
1590
- echo "ERROR: TFX_CODEX_PROFILE 값은 auto, max, ultra 또는 canonical gpt56_*/gpt6_* profile이어야 합니다. (현재: $TFX_CODEX_PROFILE)" >&2
1593
+ echo "ERROR: TFX_CODEX_PROFILE 값은 auto, max, ultra 또는 canonical gpt6_* profile이어야 합니다. (현재: $TFX_CODEX_PROFILE)" >&2
1591
1594
  exit 1
1592
1595
  ;;
1593
1596
  esac
@@ -1640,9 +1643,9 @@ apply_cli_mode() {
1640
1643
  designer|antigravity|agy|gemini)
1641
1644
  CLI_ARGS="exec --profile gpt6_astra_xhigh ${codex_base}"; CLI_EFFORT="gpt6_astra_xhigh"; DEFAULT_TIMEOUT=3600 ;;
1642
1645
  writer)
1643
- CLI_ARGS="exec --profile gpt56_luna_low ${codex_base}"; CLI_EFFORT="gpt56_luna_low"; DEFAULT_TIMEOUT=180 ;;
1646
+ CLI_ARGS="exec --profile gpt6_luna_high ${codex_base}"; CLI_EFFORT="gpt6_luna_high"; DEFAULT_TIMEOUT=900 ;;
1644
1647
  *)
1645
- CLI_ARGS="exec --profile gpt56_terra_high ${codex_base}"; CLI_EFFORT="gpt56_terra_high"; DEFAULT_TIMEOUT=1080 ;;
1648
+ CLI_ARGS="exec --profile gpt6_sol_high ${codex_base}"; CLI_EFFORT="gpt6_sol_high"; DEFAULT_TIMEOUT=1080 ;;
1646
1649
  esac
1647
1650
  echo "[tfx-route] TFX_CLI_MODE=codex: $AGENT_TYPE → codex($CLI_EFFORT)로 리매핑" >&2
1648
1651
  fi ;;
@@ -1788,10 +1791,10 @@ apply_plan_guard() {
1788
1791
  if [[ "$replacement" == "$CLI_EFFORT" ]]; then
1789
1792
  replacement=""
1790
1793
  case "$CLI_EFFORT" in
1791
- gpt55_low|spark53_low|codex53_low|gpt54_low|mini54_low) replacement="gpt56_luna_low" ;;
1792
- gpt55_med|spark53_med|codex53_med|mini54_med) replacement="gpt56_terra_med" ;;
1794
+ gpt55_low|spark53_low|codex53_low|gpt54_low|mini54_low) replacement="gpt6_luna_low" ;;
1795
+ gpt55_med|spark53_med|codex53_med|mini54_med) replacement="gpt6_sol_med" ;;
1793
1796
  gpt55_xhigh|codex53_xhigh|gpt54_xhigh) replacement="gpt6_astra_xhigh" ;;
1794
- gpt55_high|spark53_*|codex53_*|gpt54_*|mini54_*) replacement="gpt56_terra_high" ;;
1797
+ gpt55_high|spark53_*|codex53_*|gpt54_*|mini54_*) replacement="gpt6_sol_high" ;;
1795
1798
  esac
1796
1799
  fi
1797
1800
  [[ -z "$replacement" ]] && return
@@ -1822,29 +1825,29 @@ apply_no_claude_native_mode() {
1822
1825
 
1823
1826
  case "$AGENT_TYPE" in
1824
1827
  explore)
1825
- CLI_ARGS="exec --profile gpt56_luna_low ${codex_base}"
1826
- CLI_EFFORT="gpt56_luna_low"
1828
+ CLI_ARGS="exec --profile gpt6_luna_low ${codex_base}"
1829
+ CLI_EFFORT="gpt6_luna_low"
1827
1830
  DEFAULT_TIMEOUT=600
1828
1831
  RUN_MODE="fg"
1829
1832
  OPUS_OVERSIGHT="false"
1830
1833
  ;;
1831
1834
  verifier)
1832
- CLI_ARGS="exec --profile gpt56_terra_high ${codex_base} review"
1833
- CLI_EFFORT="gpt56_terra_high"
1835
+ CLI_ARGS="exec --profile gpt6_sol_high ${codex_base} review"
1836
+ CLI_EFFORT="gpt6_sol_high"
1834
1837
  DEFAULT_TIMEOUT=1200
1835
1838
  RUN_MODE="fg"
1836
1839
  OPUS_OVERSIGHT="false"
1837
1840
  ;;
1838
1841
  test-engineer)
1839
- CLI_ARGS="exec --profile gpt56_terra_high ${codex_base}"
1840
- CLI_EFFORT="gpt56_terra_high"
1842
+ CLI_ARGS="exec --profile gpt6_sol_high ${codex_base}"
1843
+ CLI_EFFORT="gpt6_sol_high"
1841
1844
  DEFAULT_TIMEOUT=1200
1842
1845
  RUN_MODE="bg"
1843
1846
  OPUS_OVERSIGHT="false"
1844
1847
  ;;
1845
1848
  qa-tester)
1846
- CLI_ARGS="exec --profile gpt56_terra_high ${codex_base} review"
1847
- CLI_EFFORT="gpt56_terra_high"
1849
+ CLI_ARGS="exec --profile gpt6_sol_high ${codex_base} review"
1850
+ CLI_EFFORT="gpt6_sol_high"
1848
1851
  DEFAULT_TIMEOUT=1200
1849
1852
  RUN_MODE="bg"
1850
1853
  OPUS_OVERSIGHT="false"
@@ -1876,7 +1879,7 @@ resolve_safe_retry_codex_profile() {
1876
1879
  echo "gpt6_astra_max"
1877
1880
  return
1878
1881
  ;;
1879
- gpt56_luna_low|gpt56_terra_med|gpt56_terra_high|gpt6_astra_xhigh|gpt6_astra_max)
1882
+ gpt6_luna_low|gpt6_luna_high|gpt6_sol_med|gpt6_sol_high|gpt6_astra_xhigh|gpt6_astra_max)
1880
1883
  echo "$profile"
1881
1884
  return
1882
1885
  ;;
@@ -1886,7 +1889,7 @@ resolve_safe_retry_codex_profile() {
1886
1889
  codex_home="${CODEX_HOME:-${TFX_CODEX_HOME:-${HOME:-}/.codex}}"
1887
1890
  profile_file="${codex_home}/${profile}.config.toml"
1888
1891
  if [[ ! -f "$profile_file" ]]; then
1889
- echo "gpt56_terra_high"
1892
+ echo "gpt6_sol_high"
1890
1893
  return
1891
1894
  fi
1892
1895
  effort=$(awk -F= '
@@ -1900,7 +1903,7 @@ resolve_safe_retry_codex_profile() {
1900
1903
  ' "$profile_file" 2>/dev/null || true)
1901
1904
  case "$effort" in
1902
1905
  ultra) echo "gpt6_astra_max" ;;
1903
- "") echo "gpt56_terra_high" ;;
1906
+ "") echo "gpt6_sol_high" ;;
1904
1907
  *) echo "$profile" ;;
1905
1908
  esac
1906
1909
  }
@@ -2113,8 +2116,8 @@ apply_dynamic_routing_override() {
2113
2116
  local codex_base
2114
2117
  codex_base="$(build_codex_base)"
2115
2118
  CLI_CMD="codex"
2116
- CLI_ARGS="exec --profile gpt56_terra_high ${codex_base}"
2117
- CLI_EFFORT="gpt56_terra_high"; DEFAULT_TIMEOUT=1080; RUN_MODE="fg"; OPUS_OVERSIGHT="false"
2119
+ CLI_ARGS="exec --profile gpt6_sol_high ${codex_base}"
2120
+ CLI_EFFORT="gpt6_sol_high"; DEFAULT_TIMEOUT=1080; RUN_MODE="fg"; OPUS_OVERSIGHT="false"
2118
2121
  ;;
2119
2122
  antigravity)
2120
2123
  CLI_CMD="agy"
@@ -3071,12 +3074,12 @@ run_codex_exec() {
3071
3074
  _attempt_codex_run
3072
3075
 
3073
3076
  # Tier-fallback: ChatGPT account가 높은 reasoning profile을 거부하면
3074
- # GPT-5.6 family low로만 1회 재시도한다. 구형 gpt-5.4 계열로는 더 이상 다운그레이드하지 않는다.
3077
+ # GPT-6 Luna low로만 1회 재시도한다. 구형 gpt-5.4 계열로는 더 이상 다운그레이드하지 않는다.
3075
3078
  # 출처: Issue #211 — codex CLI tier-aware fallback.
3076
3079
  if [[ "$exit_code_local" -ne 0 ]] && grep -qE "is not supported when using Codex with a ChatGPT account" "$STDERR_LOG" 2>/dev/null; then
3077
3080
  local fallback_profile=""
3078
3081
  case "$CLI_EFFORT" in
3079
- gpt6_astra_ultra|gpt6_astra_max|gpt6_astra_xhigh|gpt56_terra_high|gpt56_terra_med) fallback_profile="gpt56_luna_low" ;;
3082
+ gpt6_astra_ultra|gpt6_astra_max|gpt6_astra_xhigh|gpt6_sol_high|gpt6_sol_med|gpt6_luna_high) fallback_profile="gpt6_luna_low" ;;
3080
3083
  esac
3081
3084
  if [[ -n "$fallback_profile" ]]; then
3082
3085
  echo "[tfx-route] tier fallback: $CLI_EFFORT not supported on ChatGPT account → retry with $fallback_profile" >&2
@@ -1009,10 +1009,11 @@ Agent(subagent_type="oh-my-claudecode:{agent}", model="{model}", prompt="{prompt
1009
1009
 
1010
1010
  | 입력 | CLI | MCP |
1011
1011
  |------|-----|-----|
1012
- | codex / executor / spark | Codex (high; 복잡 구현은 deep-executor/xhigh) | implement |
1013
- | debugger / deep-executor | Codex (xhigh) | implement |
1014
- | build-fixer | Codex (low) | implement |
1015
- | architect / planner / critic / analyst | Codex (xhigh) | analyze |
1012
+ | codex / executor | Codex (`gpt6_sol_high`; 복잡 구현은 deep-executor/xhigh) | implement |
1013
+ | debugger / deep-executor | Codex (`gpt6_astra_xhigh`) | implement |
1014
+ | build-fixer | Codex (`gpt6_luna_high`) | implement |
1015
+ | spark | Codex (`gpt6_luna_low`) | implement |
1016
+ | architect / planner / critic / analyst | Codex (`gpt6_astra_xhigh`) | analyze |
1016
1017
  | scientist / document-specialist | Codex | analyze |
1017
1018
  | code-reviewer / security-reviewer / quality-reviewer | Codex (review) | review |
1018
1019
  | antigravity / designer / writer | Antigravity | docs |
@@ -3,19 +3,20 @@ name: tfx-live
3
3
  description: >
4
4
  Use when Claude, Codex, or a Triflux worker needs live Claude↔Codex orchestration:
5
5
  start/ask/stop, multi-turn, peer relay, daemon UDS attach, or UDS-first with tmux fallback.
6
- argument-hint: "<start|ask|stop|probe|list-sessions|peer|converse|goal-driven> ..."
6
+ argument-hint: "<start|ask|stop|interrupt|probe|list-sessions|peer|converse|goal-driven|orchestrate> ..."
7
7
  ---
8
8
 
9
- # tfx-live — Claude↔Codex live orchestration
9
+ # tfx-live: Claude↔Codex live orchestration
10
10
 
11
11
  `tfx-live` is the Triflux-owned live bridge for Claude Code and Codex TUI sessions.
12
- It runs as a local CLI (`tfx-live`) and supports tmux TUI control plus Claude daemon UDS attach.
12
+ It runs as a local CLI (`tfx-live`) and supports tmux TUI control plus Claude and Codex daemon UDS attach.
13
13
 
14
14
  ## Default transport
15
15
 
16
16
  - Claude targets with `--short` or `--session-id`: **UDS-first `auto` by default**.
17
17
  - If UDS probe/attach fails: write `~/.claude/cache/triflux/tfx-live/bug-reports/uds-fallback-*.json`, then tmux fallback when `--session` is provided.
18
18
  - Codex targets and Claude targets without a daemon ref: tmux by default.
19
+ - Codex existing threads use explicit `--transport uds --thread <id|auto>`; `auto` transport remains Claude-only.
19
20
  - UDS completion contract: `done=true` only when `matchedCompletion===true`; `timedOut` or `closed` is not completion.
20
21
  - Bridge resolution: `--bridge` > `$TFX_BRIDGE` > `$TFX_REPO_ROOT/hub/bridge.mjs` > bundled Triflux `hub/bridge.mjs`.
21
22
 
@@ -58,7 +59,8 @@ tfx-live ask --cli claude --transport uds --short <8hex> --prompt "..." --timeou
58
59
 
59
60
  List the Codex CLI sessions already running in tmux; this does not require a
60
61
  prior `tfx-live start`. The JSON result includes the tmux session name, start
61
- time, current working directory, and attachment state.
62
+ time, current working directory, attachment state, `panes: [{ target, cwd,
63
+ command }]` for Codex panes, and `target` for the first matching pane.
62
64
 
63
65
  ```bash
64
66
  # All locally running Codex tmux sessions
@@ -70,7 +72,73 @@ tfx-live list-sessions --cli codex --cwd ~/Projects/my-worktree
70
72
 
71
73
  `probe` remains the Claude daemon/UDS discovery command. Use
72
74
  `list-sessions --cli codex` when selecting an existing Codex tmux session to
73
- pass to `tfx-live ask --session <name>`.
75
+ pass to `tfx-live ask --session <name:window.pane>`.
76
+ Discovery and peer preflight also inspect pane tty/descendant processes for
77
+ the npm `node .../codex` shim, `codex.js`, and native `codex` executables.
78
+ `codex-code-mode-host` does not count. With `--remote`, the process check runs
79
+ on that host; unavailable `ps` produces a peer `preflightWarning` and allows
80
+ attach to continue.
81
+
82
+ `ask` and tmux `interrupt` accept session names, `name:window`, and
83
+ `name:window.pane`. `start` and `stop` require a session name only; `stop`
84
+ rejects pane targets because it kills the entire session. `--remote HOST`
85
+ preserves the complete target.
86
+ `stop` also rejects pane/window IDs such as `%12` and `@3`, and dotted targets.
87
+
88
+ Before pasting, `ask` checks the visible pane for active work and Codex
89
+ `model: loading`. `--if-busy wait|fail|interrupt` defaults to `wait`.
90
+ `--busy-timeout SECONDS` defaults to `--timeout`; `--poll-interval` is in
91
+ milliseconds. A busy timeout sends no prompt. `interrupt` sends Escape,
92
+ then waits for idle before pasting. Results include `ifBusy` and
93
+ `busyWaitedMs`. `converse`, `goal-driven`, and `peer` accept these flags too.
94
+ Only the last visible `model:` header controls loading detection. Time-only
95
+ lines are removed from the response tail; times inside the answer remain.
96
+
97
+ ## Existing Codex TUI thread over UDS
98
+
99
+ With `daemon_auto_start` enabled, Codex TUI runs on a shared local app-server
100
+ daemon. Its default socket is
101
+ `$CODEX_HOME/app-server-control/app-server-control.sock`, or the same path
102
+ under `~/.codex` when `CODEX_HOME` is unset. `--codex-socket PATH|default`
103
+ selects an existing socket and follows symlinks. If absent, start the daemon
104
+ with `codex app-server daemon start`.
105
+
106
+ ```bash
107
+ tfx-live list-sessions --cli codex --transport uds --cwd ~/Projects/my-worktree
108
+ tfx-live ask --cli codex --transport uds --thread auto \
109
+ --cwd ~/Projects/my-worktree --prompt "현재 변경사항을 요약해줘" \
110
+ --if-busy wait --busy-timeout 120 --timeout 120
111
+ tfx-live ask --cli codex --transport uds --thread <id> \
112
+ --codex-socket default --prompt "추가 확인 사항" --if-busy fail
113
+ ```
114
+
115
+ No `--session`, `--short`, or `--session-id` is required. Discovery returns
116
+ `[{ threadId, cwd, name, status, source }]` for loaded threads. `--thread auto`
117
+ selects exactly one loaded thread after optional realpath-normalized `--cwd`
118
+ filtering. Start the TUI and send its first message if none is loaded. Multiple
119
+ matches produce an error listing candidates; select an explicit thread ID.
120
+
121
+ The client calls `thread/resume` with `excludeTurns: true` before `turn/start`
122
+ to subscribe to response notifications. It does not request historical turns.
123
+ UDS `--if-busy wait|fail|steer` defaults to `wait`.
124
+ `steered` uses the returned turn ID and fresh `turn/started` events after
125
+ subscription, not the earlier busy snapshot; when a current turn ID is
126
+ available, `turn/steer` sends it as `expectedTurnId`.
127
+ The fallback treats no fresh matching `turn/started` before completion as a
128
+ reused turn; it relies on ordered notifications from the resumed connection.
129
+ Completion matches the returned turn ID. Closing the client
130
+ does not archive or delete the thread, or start or stop the shared daemon.
131
+ Responses prefer completed `final_answer` items, joined with blank lines;
132
+ without one, the last agent message is used. Intermediate commentary is
133
+ returned separately. Retryable error notifications keep the turn open and
134
+ increment `meta.retries`.
135
+ `--max-turn SECONDS` sets the Codex UDS activity extension ceiling. Remote
136
+ relay timeouts include busy waiting, the full turn allowance, and a relay margin.
137
+ Codex relay budgets also allow the next lifecycle timer check after that ceiling.
138
+
139
+ Codex UDS `interrupt` is unavailable: `turn/interrupt` requires a turn ID,
140
+ which the supported metadata-only `thread/read` does not provide. Use tmux
141
+ `interrupt --session <target>` when the TUI pane is available.
74
142
 
75
143
  ## Peer relay
76
144
 
@@ -86,9 +154,41 @@ tfx-live peer --cli-a codex --cli-b claude \
86
154
  --session-a cx-peer --session-b cl-fallback --short-b <8hex> \
87
155
  --cwd ~/Projects --mode freeform --seed "서로 응답을 다음 프롬프트로 이어받아라" \
88
156
  --rounds 2 --timeout 180
157
+
158
+ # Attach both existing tmux panes without starting or stopping their sessions
159
+ tfx-live peer --cli-a codex --cli-b claude \
160
+ --session-a work:0.1 --session-b work:0.2 --attach-a --attach-b \
161
+ --mode freeform --seed "변경을 검토해줘" --if-busy wait
162
+
163
+ # Existing Codex daemon thread + existing Claude daemon session
164
+ tfx-live peer --cli-a codex --transport-a uds --thread-a <id|auto> \
165
+ --codex-socket-a default --cli-b claude --transport-b uds --short-b <8hex> \
166
+ --cwd ~/Projects/my-worktree --mode freeform --seed "변경을 검토해줘"
89
167
  ```
90
168
 
91
- In `peer`, every hop sends the previous response as the next prompt to the opposite agent; the final JSON is only the transcript.
169
+ In `peer`, every hop sends the previous response as the next prompt to the opposite agent; the final JSON includes the transcript and lifecycle metadata.
170
+
171
+ `--attach-a` and `--attach-b` preflight existing tmux targets before any hop.
172
+ Codex targets must identify a Codex pane. Attached sessions are preserved on
173
+ success, error, SIGINT, and SIGTERM. UDS sides are always treated as attached.
174
+ The result includes `attachedA` and `attachedB`. Side-specific UDS flags are
175
+ `--thread-a`, `--thread-b`, `--codex-socket-a`, and `--codex-socket-b`.
176
+ `--if-busy-a` and `--if-busy-b` accept `wait|fail|interrupt` for tmux and
177
+ `wait|fail|steer` for Codex UDS. A common `--if-busy` must be valid for both
178
+ sides even when one side overrides it. UDS done-marker lines are removed
179
+ before relaying a response. `stoppedA`/`stoppedB` are true only after an owned
180
+ session was actually stopped; attached sides stay false.
181
+
182
+ ## Known limits
183
+
184
+ - A draft in the TUI composer can merge with the pasted prompt. Changing
185
+ placeholder text prevents reliable detection without terminal styling.
186
+ - `--thread auto` can be ambiguous when an ephemeral orchestrate thread
187
+ remains loaded. The candidate-list error exposes this; choose a thread ID.
188
+ - A TUI's thread stays loaded in the shared daemon after that TUI exits
189
+ (observed with codex-cli 0.156.1: a `tfx-live stop`ped peer session left its
190
+ thread in `thread/loaded/list`). Stale threads in the same cwd make
191
+ `--thread auto` ambiguous, so pin `--thread ID` for long-lived targets.
92
192
 
93
193
  ## Orchestrate (Claude UDS + Codex)
94
194
 
@@ -101,12 +201,17 @@ tfx-live orchestrate --task "이 변경의 위험을 한 줄로" --mode peer
101
201
  # experimental: Codex over a real `codex app-server` WebSocket-over-UDS daemon
102
202
  tfx-live orchestrate --task "이 변경의 위험을 한 줄로" \
103
203
  --mode codex-led --codex-transport app-server-uds --timeout 120
204
+
205
+ # Attach the shared daemon and create a new ephemeral thread for orchestration
206
+ tfx-live orchestrate --task "이 변경의 위험을 한 줄로" \
207
+ --mode codex-led --codex-socket default --timeout 120
104
208
  ```
105
209
 
106
210
  - `--mode` = `peer` (default) | `codex-led` | `claude-led`.
107
211
  - `--codex-transport` = `exec` (default, `codex exec` stdio) | `app-server-uds` (experimental).
108
212
  - `app-server-uds` spawns a private `codex app-server --listen unix://<tmp>`, attaches over WebSocket-over-UDS (RFC 6455, masked client frames), runs `initialize`→`thread/start`→`turn/start`, and resolves on `turn/completed`. Transport proven against codex-cli 0.135.0 (see `experiments/native-bridge-feasibility/codex-app-server-uds-smoke.mjs`).
109
- - It is NOT the default — the existing `codex exec` path is unchanged.
213
+ - `--codex-socket PATH|default` implies `app-server-uds` and conflicts with explicit `--codex-transport exec`. It attaches without spawning or killing the daemon and still creates a new ephemeral thread.
214
+ - Without `--codex-socket`, `app-server-uds` uses the private server path; the default transport remains `exec`.
110
215
  - Requires a live Claude daemon (same as the UDS `ask` path) for the Claude side.
111
216
 
112
217
  ## Useful diagnostics
@@ -45,7 +45,7 @@ options:
45
45
  ```
46
46
  | 프로파일 | 모델 | Effort |
47
47
  |----------|------|--------|
48
- | gpt56_terra_high | 프로필 설정의 현재 모델 | high |
48
+ | gpt6_sol_high | 프로필 설정의 현재 모델 | high |
49
49
  | ... | ... | ... |
50
50
  ```
51
51
 
@@ -75,9 +75,10 @@ options:
75
75
  2. AskUserQuestion으로 프로필/alias 선택:
76
76
  ```
77
77
  options:
78
- - label: "gpt56_terra_high" → 기본 고품질 (Recommended)
79
- - label: "gpt56_terra_med" → 균형형
80
- - label: "gpt56_luna_low" → 빠른 경량
78
+ - label: "gpt6_sol_high" → 구현과 리뷰 기본값 (Recommended)
79
+ - label: "gpt6_sol_med" → 정리와 리팩터 균형형
80
+ - label: "gpt6_luna_high" → 명확한 반복 작업
81
+ - label: "gpt6_luna_low" → 지연 우선 경량
81
82
  - label: "gpt6_astra_xhigh" → 고강도 추론
82
83
  - label: "gpt6_astra_max" → 최난도 단일 작업
83
84
  - label: "gpt6_astra_ultra" → 최상위 자동 위임 (비중첩 전용)
@@ -187,12 +188,13 @@ options:
187
188
 
188
189
  | 프로필 | 용도 |
189
190
  |------|------|
190
- | gpt56_terra_high | 기본 고품질 |
191
+ | gpt6_sol_high | 구현과 리뷰 기본값 |
191
192
  | gpt6_astra_xhigh | 고강도 추론 |
192
193
  | gpt6_astra_max | 최난도 단일 작업 |
193
194
  | gpt6_astra_ultra | 최상위 자동 위임 (비중첩 전용) |
194
- | gpt56_terra_med | 균형형 |
195
- | gpt56_luna_low | 빠른 경량 |
195
+ | gpt6_sol_med | 정리와 리팩터 균형형 |
196
+ | gpt6_luna_high | 명확한 반복 작업 |
197
+ | gpt6_luna_low | 지연 우선 경량 |
196
198
  | custom | 사용자가 관리하는 프로필 |
197
199
 
198
200
  ### Codex Effort 레벨
@@ -112,7 +112,7 @@ Bash("triflux setup")
112
112
  #### 단계 3: Codex 프로파일
113
113
 
114
114
  `~/.codex/config.toml`을 Read 도구로 읽어 필수 프로파일 존재 여부 확인.
115
- 필수: `gpt6_astra_ultra`, `gpt6_astra_max`, `gpt6_astra_xhigh`, `gpt56_terra_high`, `gpt56_terra_med`, `gpt56_luna_low`.
115
+ 필수: `gpt6_astra_ultra`, `gpt6_astra_max`, `gpt6_astra_xhigh`, `gpt6_sol_high`, `gpt6_sol_med`, `gpt6_luna_high`, `gpt6_luna_low`.
116
116
 
117
117
  - 모두 존재 → ✅ 표시
118
118
  - 누락 있으면 → AskUserQuestion: