triflux 10.49.0 → 10.50.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/CLAUDE.md +1 -2
- package/README.ko.md +4 -7
- package/README.md +4 -7
- package/adapters/codex/skills/tfx-live/SKILL.md +129 -0
- package/bin/tfx-live.mjs +446 -21
- package/bin/triflux.mjs +181 -1507
- package/config/mcp-registry.json +10 -8
- package/hooks/agy-session-hook.mjs +1 -1
- package/hooks/claude-cwd-projection-refresh.mjs +7 -79
- package/hooks/codex-session-hook.mjs +3 -8
- package/hooks/hook-orchestrator.mjs +8 -648
- package/hooks/lib/resolve-root.mjs +1 -1
- package/hub/bridge.mjs +290 -51
- package/hub/team/claude-daemon-control.mjs +54 -6
- package/hub/team/claude-transcript.mjs +240 -0
- package/hub/team/cli/commands/start/index.mjs +17 -3
- package/hub/team/cli/commands/start/parse-args.mjs +1 -0
- package/hub/team/session-context.mjs +203 -0
- package/hub/team/uds-orchestrator.mjs +63 -1
- package/hub/team/worktree-lifecycle.mjs +0 -1
- package/hud/constants.mjs +10 -0
- package/hud/hud-qos-status.mjs +23 -4
- package/hud/providers/gemini.mjs +4 -1
- package/hud/renderers.mjs +10 -6
- package/package.json +5 -2
- package/references/codex-plugin-cc-analysis.md +5 -10
- package/references/codex-plugin-cc-code-patterns.md +3 -3
- package/scripts/__tests__/mcp-guard-engine-policy-sync.test.mjs +4 -19
- package/scripts/__tests__/mcp-guard-engine-proactive-sync.test.mjs +8 -34
- package/scripts/__tests__/mcp-guard-engine-stdio-sync.test.mjs +24 -9
- package/scripts/__tests__/mcp-guard-engine-sync-http-headers.test.mjs +0 -2
- package/scripts/__tests__/mcp-guard-engine.test.mjs +1 -58
- package/scripts/__tests__/setup-cleanup-stale-skills.test.mjs +68 -212
- package/scripts/__tests__/skill-surface.test.mjs +2 -53
- package/scripts/__tests__/smoke.test.mjs +0 -8
- package/scripts/completions/tfx.bash +1 -7
- package/scripts/completions/tfx.fish +1 -4
- package/scripts/completions/tfx.zsh +1 -10
- package/scripts/config-audit.mjs +0 -55
- package/scripts/doctor-diagnose.mjs +1 -1
- package/scripts/install-commit-hook.mjs +56 -0
- package/scripts/lib/doctor-env-checks.mjs +0 -85
- package/scripts/lib/legacy-hook-cleanup.mjs +230 -0
- package/scripts/lib/legacy-mcp-cleanup.mjs +772 -0
- package/scripts/lib/mcp-guard-engine.mjs +44 -271
- package/scripts/lib/mcp-manifest.mjs +2 -2
- package/scripts/lib/session-presence.mjs +249 -0
- package/scripts/pack.mjs +4 -0
- package/scripts/preflight-cache.mjs +19 -6
- package/scripts/release/check-packages-mirror.mjs +8 -0
- package/scripts/remote-spawn.mjs +56 -1
- package/scripts/run.cjs +1 -1
- package/scripts/session-stale-cleanup.mjs +2 -3
- package/scripts/setup.mjs +320 -713
- package/scripts/sync-hub-mcp-settings.mjs +0 -280
- package/scripts/tfx-route.sh +8 -0
- package/skills/tfx-doctor/SKILL.md +14 -199
- package/skills/tfx-live/SKILL.md +73 -206
- package/skills/tfx-remote/SKILL.md +20 -10
- package/skills/tfx-research/SKILL.md +5 -2
- package/skills/tfx-setup/SKILL.md +14 -574
- package/skills/tfx-setup/skill.json +2 -2
- package/skills/tfx-wt/SKILL.md +6 -8
- package/skills/tfx-wt/skill.json +1 -1
- package/tui/setup.mjs +0 -130
- package/hooks/agent-route-guard.mjs +0 -113
- package/hooks/cross-review-tracker.mjs +0 -135
- package/hooks/cto-north-star-brief.mjs +0 -133
- package/hooks/error-context.mjs +0 -185
- package/hooks/hook-adaptive-collector.mjs +0 -88
- package/hooks/hook-manager.mjs +0 -426
- package/hooks/hook-registry.json +0 -367
- package/hooks/hooks.json +0 -149
- package/hooks/keyword-rules.json +0 -676
- package/hooks/mcp-config-watcher.mjs +0 -84
- package/hooks/permission-safe-allow.mjs +0 -267
- package/hooks/pipeline-stop.mjs +0 -435
- package/hooks/post-tool-tips.mjs +0 -306
- package/hooks/pre-compact-snapshot.mjs +0 -181
- package/hooks/safety-guard.mjs +0 -466
- package/hooks/session-end-cleanup.mjs +0 -360
- package/hooks/session-start-fast.mjs +0 -579
- package/hooks/session-start-lake.mjs +0 -81
- package/hooks/subagent-tracker.mjs +0 -281
- package/hooks/subagent-verifier.mjs +0 -85
- package/scripts/__tests__/install-mcp-gateway-startup.test.mjs +0 -367
- package/scripts/__tests__/keyword-detector.test.mjs +0 -368
- package/scripts/__tests__/mcp-gateway-health-check.test.mjs +0 -261
- package/scripts/__tests__/mcp-gateway-wrapper-check.test.mjs +0 -54
- package/scripts/__tests__/mcp-guard-engine-watch-http-headers.test.mjs +0 -137
- package/scripts/__tests__/session-start-fast.test.mjs +0 -36
- package/scripts/codex-gateway-preflight.mjs +0 -208
- package/scripts/codex-mcp-gateway-sync.mjs +0 -275
- package/scripts/cross-review-gate.mjs +0 -162
- package/scripts/cross-review-tracker.mjs +0 -251
- package/scripts/install-mcp-gateway-startup.mjs +0 -503
- package/scripts/keyword-detector.mjs +0 -485
- package/scripts/keyword-rules-expander.mjs +0 -538
- package/scripts/lib/cross-review-utils.mjs +0 -59
- package/scripts/lib/hook-utils.mjs +0 -16
- package/scripts/lib/keyword-rules.mjs +0 -296
- package/scripts/lib/mcp-gateway-health-check.mjs +0 -224
- package/scripts/lib/mcp-gateway-servers.mjs +0 -70
- package/scripts/lib/mcp-gateway-wrapper-check.mjs +0 -35
- package/scripts/mcp-gateway-config.mjs +0 -222
- package/scripts/mcp-gateway-ensure.mjs +0 -135
- package/scripts/mcp-gateway-integration-test.mjs +0 -262
- package/scripts/mcp-gateway-start.mjs +0 -304
- package/scripts/mcp-gateway-start.ps1 +0 -140
- package/scripts/mcp-gateway-verify.mjs +0 -79
- package/scripts/mcp-safety-guard.mjs +0 -97
- package/scripts/tfx-gate-activate.mjs +0 -98
- package/skills/merge-worktree/SKILL.md +0 -144
- package/skills/merge-worktree/skill.json +0 -5
- package/skills/star-prompt/SKILL.md +0 -220
- package/skills/star-prompt/skill.json +0 -4
- package/skills/tfx-interview/SKILL.md +0 -371
- package/skills/tfx-interview/skill.json +0 -6
- package/skills/tfx-plan/SKILL.md +0 -197
- package/skills/tfx-plan/skill.json +0 -6
- package/skills/tfx-profile/SKILL.md +0 -249
- package/skills/tfx-profile/skill.json +0 -5
package/bin/tfx-live.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
3
4
|
import { mkdirSync, realpathSync, statSync, writeFileSync } from "node:fs";
|
|
4
5
|
import {
|
|
5
6
|
mkdir,
|
|
@@ -21,6 +22,10 @@ import {
|
|
|
21
22
|
registryDir,
|
|
22
23
|
} from "../hub/lib/codex-session-registry.mjs";
|
|
23
24
|
import { resolveHardCeilingMs } from "../hub/lib/worker-lifecycle.mjs";
|
|
25
|
+
import {
|
|
26
|
+
findClaudeTranscript,
|
|
27
|
+
readClaudeTranscript,
|
|
28
|
+
} from "../hub/team/claude-transcript.mjs";
|
|
24
29
|
import { createNotifier } from "../hub/team/notify.mjs";
|
|
25
30
|
import {
|
|
26
31
|
escapePwshSingleQuoted as escapeRemotePwshSingleQuoted,
|
|
@@ -28,6 +33,12 @@ import {
|
|
|
28
33
|
shellQuote as remoteShellQuote,
|
|
29
34
|
validateHost as validateRemoteHost,
|
|
30
35
|
} from "../hub/team/remote-session.mjs";
|
|
36
|
+
import {
|
|
37
|
+
CONTEXT_THRESHOLDS,
|
|
38
|
+
contextGuard,
|
|
39
|
+
modelContext,
|
|
40
|
+
readCodexContext,
|
|
41
|
+
} from "../hub/team/session-context.mjs";
|
|
31
42
|
|
|
32
43
|
const execFileAsync = promisify(execFile);
|
|
33
44
|
|
|
@@ -51,7 +62,13 @@ const BRIDGE_TIMEOUT_BUFFER_MS = 15_000;
|
|
|
51
62
|
// spill the JSON to a temp file and pass --payload-file instead.
|
|
52
63
|
const PAYLOAD_FILE_THRESHOLD = 96 * 1024;
|
|
53
64
|
const VALID_TRANSPORTS = ["tmux", "uds", "auto"];
|
|
54
|
-
const BOOLEAN_FLAGS = new Set([
|
|
65
|
+
const BOOLEAN_FLAGS = new Set([
|
|
66
|
+
"json",
|
|
67
|
+
"attach-a",
|
|
68
|
+
"attach-b",
|
|
69
|
+
"no-wait",
|
|
70
|
+
"no-relay-tag",
|
|
71
|
+
]);
|
|
55
72
|
const PEER_HOP_DONE_MARKER = "<<<TFX_PEER_HOP_DONE>>>";
|
|
56
73
|
// uds-fallback diagnostics land here, written async so a failed daemon attach
|
|
57
74
|
// never blocks the tmux fallback path (see writeUdsBugReport / doAskAuto).
|
|
@@ -59,16 +76,21 @@ const BUG_REPORT_DIR =
|
|
|
59
76
|
process.env.TFX_LIVE_BUG_REPORT_DIR ??
|
|
60
77
|
pathJoin(homedir(), ".claude", "cache", "triflux", "tfx-live", "bug-reports");
|
|
61
78
|
|
|
62
|
-
function usage() {
|
|
63
|
-
|
|
79
|
+
function usage(command) {
|
|
80
|
+
const lines = [
|
|
64
81
|
"Usage:",
|
|
65
|
-
" tfx-live start --session NAME [--cli codex|claude] [--model ID] [--effort TIER] [--cwd DIR] [--remote HOST] [--resume ID] [--resume-last 1] [--ready-timeout 30] [--poll-interval 1500]",
|
|
82
|
+
" tfx-live start --session NAME [--name NAME] [--cli codex|claude] [--model ID] [--effort TIER] [--cwd DIR] [--remote HOST] [--resume ID] [--resume-last 1] [--ready-timeout 30] [--poll-interval 1500]",
|
|
66
83
|
" tfx-live ask --session NAME[:WINDOW.PANE] --prompt TEXT [--cli codex|claude] [--if-busy wait|fail|interrupt] [--busy-timeout 60] [--timeout 60] [--remote HOST] [--settle 1500] [--poll-interval 1500]",
|
|
67
84
|
" tfx-live ask --cli codex --transport uds --thread ID|auto --prompt TEXT [--codex-socket PATH|default] [--cwd DIR] [--if-busy wait|fail|steer] [--busy-timeout 60] [--timeout 60] [--max-turn SECONDS]",
|
|
68
85
|
" tfx-live ask --transport uds|auto (--short SHORT | --session-id ID) --prompt TEXT [--config-dir DIR] [--bridge ABS] [--session NAME (auto fallback)] [--timeout 60]",
|
|
86
|
+
" ask options: --no-wait --no-relay-tag --warn-context-pct N --max-context-pct N (0 disables; Claude 60/90, Codex 15/22).",
|
|
87
|
+
" tfx-live compact --cli claude --session NAME [--instructions TEXT] [--if-busy fail|wait] [--timeout 60]",
|
|
88
|
+
" tfx-live wait --cli claude (--short SHORT | --session-id ID) [--request-id ID] [--config-dir DIR] [--bridge ABS] [--timeout 60] [--poll-interval 1500]",
|
|
89
|
+
" tfx-live rename --cli codex --transport uds --thread ID --name NAME [--codex-socket PATH|default]",
|
|
69
90
|
" transport: auto is the default for Claude when --short/--session-id is present; otherwise tmux. bridge path: --bridge > $TFX_BRIDGE > $TFX_REPO_ROOT/hub/bridge.mjs > bundled Triflux hub/bridge.mjs.",
|
|
70
91
|
" tfx-live interrupt --session NAME [--cli codex|claude] [--transport tmux|uds|auto] [--short SHORT | --session-id ID] [--config-dir DIR] [--bridge ABS] [--timeout 5]",
|
|
71
92
|
" tfx-live stop --session NAME [--cli codex|claude] [--remote HOST]",
|
|
93
|
+
" tfx-live stop --cli claude (--short SHORT | --session-id ID) [--config-dir DIR]",
|
|
72
94
|
" tfx-live probe [--short SHORT] [--session-id ID] [--config-dir DIR] [--bridge ABS] [--timeout 10]",
|
|
73
95
|
" tfx-live list-sessions --cli codex|claude [--transport tmux|uds] [--codex-socket PATH|default] [--cwd DIR] [--remote HOST (codex tmux only)]",
|
|
74
96
|
" Claude list-sessions supports local tmux only; --transport uds requires --cli codex.",
|
|
@@ -80,10 +102,22 @@ function usage() {
|
|
|
80
102
|
" Runs the Claude(UDS)+Codex orchestration engine. --codex-transport app-server-uds drives a real `codex app-server` over WebSocket-over-UDS (experimental); default exec keeps the codex stdio one-shot path.",
|
|
81
103
|
" tfx-live cto-hygiene-notify --root DIR --state-file PATH [--json]",
|
|
82
104
|
" One-shot CTO hygiene dry-run notification: sends only when actionable hygiene state changes; no polling or apply/steward lock.",
|
|
83
|
-
]
|
|
105
|
+
];
|
|
106
|
+
if (!command) return lines.join("\n");
|
|
107
|
+
const selected = lines.filter(
|
|
108
|
+
(line) =>
|
|
109
|
+
line.startsWith(` tfx-live ${command} `) ||
|
|
110
|
+
(command === "ask" && line.startsWith(" ask options:")),
|
|
111
|
+
);
|
|
112
|
+
return selected.length
|
|
113
|
+
? ["Usage:", ...selected].join("\n")
|
|
114
|
+
: lines.join("\n");
|
|
84
115
|
}
|
|
85
116
|
|
|
86
117
|
function parseCli(argv) {
|
|
118
|
+
if (argv.some((arg) => arg === "--help" || arg === "-h")) {
|
|
119
|
+
return { command: "help", flags: { subcommand: argv[0] } };
|
|
120
|
+
}
|
|
87
121
|
const [command, ...rest] = argv;
|
|
88
122
|
if (!command || command === "--help" || command === "-h") {
|
|
89
123
|
return { command: "help", flags: {} };
|
|
@@ -158,6 +192,28 @@ function integerFlag(flags, name, defaultValue) {
|
|
|
158
192
|
return value;
|
|
159
193
|
}
|
|
160
194
|
|
|
195
|
+
function contextPctFlag(flags, name, defaultValue) {
|
|
196
|
+
const value = flags[name] === undefined ? defaultValue : Number(flags[name]);
|
|
197
|
+
if (!Number.isFinite(value) || value < 0 || value > 100)
|
|
198
|
+
throw new Error(`--${name} must be between 0 and 100`);
|
|
199
|
+
return value;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function contextOpts(flags, cli) {
|
|
203
|
+
return {
|
|
204
|
+
warnContextPct: contextPctFlag(
|
|
205
|
+
flags,
|
|
206
|
+
"warn-context-pct",
|
|
207
|
+
CONTEXT_THRESHOLDS[cli].warnContextPct,
|
|
208
|
+
),
|
|
209
|
+
maxContextPct: contextPctFlag(
|
|
210
|
+
flags,
|
|
211
|
+
"max-context-pct",
|
|
212
|
+
CONTEXT_THRESHOLDS[cli].maxContextPct,
|
|
213
|
+
),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
161
217
|
function selectAdapter(flags) {
|
|
162
218
|
const cli = flags.cli ?? "codex";
|
|
163
219
|
const adapter = ADAPTERS[cli];
|
|
@@ -233,7 +289,16 @@ function buildRemoteLiveArgv(verb, opts) {
|
|
|
233
289
|
if (opts.busyTimeoutMs)
|
|
234
290
|
args.push("--busy-timeout", String(opts.busyTimeoutMs / 1000));
|
|
235
291
|
if (opts.maxTurnMs) args.push("--max-turn", String(opts.maxTurnMs / 1000));
|
|
236
|
-
if (verb === "ask")
|
|
292
|
+
if (verb === "ask") {
|
|
293
|
+
args.push("--prompt", opts.prompt ?? "");
|
|
294
|
+
if (opts.noWait) args.push("--no-wait");
|
|
295
|
+
if (opts.noRelayTag) args.push("--no-relay-tag");
|
|
296
|
+
if (opts.requestId) args.push("--request-id", opts.requestId);
|
|
297
|
+
if (opts.maxContextPct !== undefined)
|
|
298
|
+
args.push("--max-context-pct", String(opts.maxContextPct));
|
|
299
|
+
if (opts.warnContextPct !== undefined)
|
|
300
|
+
args.push("--warn-context-pct", String(opts.warnContextPct));
|
|
301
|
+
}
|
|
237
302
|
args.push("--timeout", timeoutSeconds(opts.timeoutMs));
|
|
238
303
|
if (verb === "ask" && opts.settleMs) {
|
|
239
304
|
args.push("--settle", String(opts.settleMs));
|
|
@@ -1665,10 +1730,10 @@ const ADAPTERS = {
|
|
|
1665
1730
|
},
|
|
1666
1731
|
};
|
|
1667
1732
|
|
|
1668
|
-
function buildLaunchKeys(adapter, { model, effort } = {}) {
|
|
1733
|
+
function buildLaunchKeys(adapter, { model, effort, name } = {}) {
|
|
1669
1734
|
const hasModel = model !== undefined;
|
|
1670
1735
|
const hasEffort = effort !== undefined;
|
|
1671
|
-
if (!hasModel && !hasEffort) {
|
|
1736
|
+
if (!hasModel && !hasEffort && !(adapter.cli === "claude" && name)) {
|
|
1672
1737
|
return adapter.launchKeys;
|
|
1673
1738
|
}
|
|
1674
1739
|
|
|
@@ -1684,6 +1749,7 @@ function buildLaunchKeys(adapter, { model, effort } = {}) {
|
|
|
1684
1749
|
);
|
|
1685
1750
|
}
|
|
1686
1751
|
} else if (adapter.cli === "claude") {
|
|
1752
|
+
if (name) overrideArgs.push("-n", name);
|
|
1687
1753
|
if (hasModel) {
|
|
1688
1754
|
overrideArgs.push("--model", model);
|
|
1689
1755
|
}
|
|
@@ -1971,12 +2037,23 @@ async function doStart(adapter, opts) {
|
|
|
1971
2037
|
} = opts;
|
|
1972
2038
|
if (session.includes(":"))
|
|
1973
2039
|
throw new Error("start --session accepts only a session name without ':'");
|
|
2040
|
+
const now = new Date();
|
|
2041
|
+
const resumed = Boolean(resume || resumeLast);
|
|
2042
|
+
const name =
|
|
2043
|
+
opts.name ??
|
|
2044
|
+
(resumed ? null : `${now.getMonth() + 1}.${now.getDate()} ${session}`);
|
|
2045
|
+
if (
|
|
2046
|
+
name !== null &&
|
|
2047
|
+
(!/^\d{1,2}\.\d{1,2} \S/u.test(name) || /[\r\n]/u.test(name))
|
|
2048
|
+
)
|
|
2049
|
+
throw new Error("--name must use '<month>.<day> <topic>' on one line");
|
|
1974
2050
|
const launchKeys = resume
|
|
1975
2051
|
? [adapter.resumeById(resume), "Enter"]
|
|
1976
2052
|
: resumeLast
|
|
1977
2053
|
? [adapter.resumeLast(), "Enter"]
|
|
1978
|
-
: buildLaunchKeys(adapter, { model, effort });
|
|
1979
|
-
|
|
2054
|
+
: buildLaunchKeys(adapter, { model, effort, name });
|
|
2055
|
+
if (resumed && name && adapter.cli === "claude")
|
|
2056
|
+
launchKeys[0] += ` ${shellQuote(["-n", name])}`;
|
|
1980
2057
|
const resumeTarget = resume ?? (resumeLast ? "last" : null);
|
|
1981
2058
|
|
|
1982
2059
|
await runTmux(remote, [
|
|
@@ -2006,6 +2083,34 @@ async function doStart(adapter, opts) {
|
|
|
2006
2083
|
readyTimeoutMs,
|
|
2007
2084
|
pollIntervalMs,
|
|
2008
2085
|
);
|
|
2086
|
+
let nameApplied = Boolean(name) && adapter.cli === "claude" && ready;
|
|
2087
|
+
if (name && adapter.cli === "codex" && ready) {
|
|
2088
|
+
try {
|
|
2089
|
+
await runTmux(remote, [
|
|
2090
|
+
"send-keys",
|
|
2091
|
+
"-t",
|
|
2092
|
+
session,
|
|
2093
|
+
"-l",
|
|
2094
|
+
"--",
|
|
2095
|
+
`/rename ${name}`,
|
|
2096
|
+
]);
|
|
2097
|
+
await runTmux(remote, ["send-keys", "-t", session, "Enter"]);
|
|
2098
|
+
const deadline = Date.now() + Math.min(readyTimeoutMs, 2000);
|
|
2099
|
+
do {
|
|
2100
|
+
const discovery = await discoverCodexTmuxSessions({ remote });
|
|
2101
|
+
nameApplied =
|
|
2102
|
+
discovery.sessions?.some(
|
|
2103
|
+
(entry) =>
|
|
2104
|
+
entry.session === session &&
|
|
2105
|
+
entry.panes.some((pane) => pane.name === name),
|
|
2106
|
+
) === true;
|
|
2107
|
+
if (nameApplied || remote) break;
|
|
2108
|
+
await sleep(Math.min(pollIntervalMs, 200));
|
|
2109
|
+
} while (Date.now() < deadline);
|
|
2110
|
+
} catch {
|
|
2111
|
+
nameApplied = false;
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2009
2114
|
return addLoginGuard(adapter, raw, {
|
|
2010
2115
|
cli: adapter.cli,
|
|
2011
2116
|
session,
|
|
@@ -2013,6 +2118,15 @@ async function doStart(adapter, opts) {
|
|
|
2013
2118
|
resumed,
|
|
2014
2119
|
resumeTarget,
|
|
2015
2120
|
ready,
|
|
2121
|
+
name,
|
|
2122
|
+
nameGenerated: !resumed && opts.name === undefined,
|
|
2123
|
+
nameApplied,
|
|
2124
|
+
...(name && adapter.cli === "codex" && !nameApplied
|
|
2125
|
+
? {
|
|
2126
|
+
nameWarning:
|
|
2127
|
+
"name update could not be confirmed from the Codex session registry",
|
|
2128
|
+
}
|
|
2129
|
+
: {}),
|
|
2016
2130
|
raw,
|
|
2017
2131
|
});
|
|
2018
2132
|
}
|
|
@@ -2068,6 +2182,19 @@ async function doAskViaTmux(adapter, opts) {
|
|
|
2068
2182
|
});
|
|
2069
2183
|
const beforeRaw = await capturePane(remote, session);
|
|
2070
2184
|
const contextPctBefore = adapter.contextPct(beforeRaw);
|
|
2185
|
+
const context = opts.skipContextGuard ? {} : await tmuxContext(adapter, opts);
|
|
2186
|
+
const guard = opts.skipContextGuard
|
|
2187
|
+
? {}
|
|
2188
|
+
: contextGuard(adapter.cli, context, opts);
|
|
2189
|
+
if (guard.ok === false)
|
|
2190
|
+
return {
|
|
2191
|
+
...guard,
|
|
2192
|
+
status: "failed",
|
|
2193
|
+
done: false,
|
|
2194
|
+
cli: adapter.cli,
|
|
2195
|
+
session,
|
|
2196
|
+
transport: "tmux",
|
|
2197
|
+
};
|
|
2071
2198
|
|
|
2072
2199
|
const bufferName = tmuxBufferName(session);
|
|
2073
2200
|
await runTmux(remote, ["set-buffer", "-b", bufferName, "--", prompt]);
|
|
@@ -2076,13 +2203,14 @@ async function doAskViaTmux(adapter, opts) {
|
|
|
2076
2203
|
"-b",
|
|
2077
2204
|
bufferName,
|
|
2078
2205
|
"-d",
|
|
2206
|
+
"-p",
|
|
2079
2207
|
"-t",
|
|
2080
2208
|
session,
|
|
2081
2209
|
]);
|
|
2082
2210
|
await sleep(settleMs);
|
|
2083
2211
|
|
|
2084
2212
|
let promptSubmitted = !doneMarker;
|
|
2085
|
-
if (doneMarker) {
|
|
2213
|
+
if (doneMarker || opts.noWait) {
|
|
2086
2214
|
for (let attempt = 1; attempt <= PROMPT_SUBMIT_MAX_ATTEMPTS; attempt += 1) {
|
|
2087
2215
|
await runTmux(remote, ["send-keys", "-t", session, "Enter"]);
|
|
2088
2216
|
await sleep(PROMPT_SUBMIT_RETRY_DELAY_MS);
|
|
@@ -2101,6 +2229,25 @@ async function doAskViaTmux(adapter, opts) {
|
|
|
2101
2229
|
await runTmux(remote, ["send-keys", "-t", session, "Enter"]);
|
|
2102
2230
|
}
|
|
2103
2231
|
|
|
2232
|
+
if (opts.noWait) {
|
|
2233
|
+
return {
|
|
2234
|
+
...context,
|
|
2235
|
+
...guard,
|
|
2236
|
+
ok: true,
|
|
2237
|
+
cli: adapter.cli,
|
|
2238
|
+
transport: "tmux",
|
|
2239
|
+
session,
|
|
2240
|
+
remote: remote ?? null,
|
|
2241
|
+
status: "submitted",
|
|
2242
|
+
inputSent: true,
|
|
2243
|
+
done: false,
|
|
2244
|
+
submittedAt: new Date().toISOString(),
|
|
2245
|
+
target: session,
|
|
2246
|
+
ifBusy,
|
|
2247
|
+
busyWaitedMs,
|
|
2248
|
+
};
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2104
2251
|
const startedAt = Date.now();
|
|
2105
2252
|
let raw = "";
|
|
2106
2253
|
let visible = "";
|
|
@@ -2184,6 +2331,8 @@ async function doAskViaTmux(adapter, opts) {
|
|
|
2184
2331
|
response,
|
|
2185
2332
|
contextPctBefore,
|
|
2186
2333
|
contextPctAfter,
|
|
2334
|
+
...context,
|
|
2335
|
+
...guard,
|
|
2187
2336
|
matchedCompletion: done,
|
|
2188
2337
|
done,
|
|
2189
2338
|
raw,
|
|
@@ -2191,6 +2340,24 @@ async function doAskViaTmux(adapter, opts) {
|
|
|
2191
2340
|
}
|
|
2192
2341
|
|
|
2193
2342
|
async function doAsk(adapter, opts) {
|
|
2343
|
+
const requestId = opts.requestId ?? randomBytes(6).toString("hex");
|
|
2344
|
+
const tag = `[tfx-live req=${requestId}]`;
|
|
2345
|
+
const prompt =
|
|
2346
|
+
opts.noRelayTag || opts.prompt.startsWith(`${tag}\n`)
|
|
2347
|
+
? opts.prompt
|
|
2348
|
+
: `${tag}\n${opts.prompt}`;
|
|
2349
|
+
try {
|
|
2350
|
+
return {
|
|
2351
|
+
...(await dispatchAsk(adapter, { ...opts, prompt, requestId })),
|
|
2352
|
+
requestId,
|
|
2353
|
+
};
|
|
2354
|
+
} catch (error) {
|
|
2355
|
+
error.requestId = requestId;
|
|
2356
|
+
throw error;
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
async function dispatchAsk(adapter, opts) {
|
|
2194
2361
|
const transport = opts.transport ?? "tmux";
|
|
2195
2362
|
if (transport === "tmux") {
|
|
2196
2363
|
return doAskViaTmux(adapter, opts);
|
|
@@ -2211,6 +2378,9 @@ async function doAsk(adapter, opts) {
|
|
|
2211
2378
|
ifBusy: opts.ifBusy,
|
|
2212
2379
|
busyTimeoutMs: opts.busyTimeoutMs,
|
|
2213
2380
|
pollIntervalMs: opts.pollIntervalMs,
|
|
2381
|
+
noWait: opts.noWait,
|
|
2382
|
+
maxContextPct: opts.maxContextPct,
|
|
2383
|
+
warnContextPct: opts.warnContextPct,
|
|
2214
2384
|
});
|
|
2215
2385
|
}
|
|
2216
2386
|
|
|
@@ -2262,6 +2432,11 @@ async function doAskViaDaemon(opts, meta = {}) {
|
|
|
2262
2432
|
if (short) payload.short = short;
|
|
2263
2433
|
if (sessionId) payload.sessionId = sessionId;
|
|
2264
2434
|
if (configDir) payload.configDir = configDir;
|
|
2435
|
+
if (opts.noWait) payload.noWait = true;
|
|
2436
|
+
if (opts.maxContextPct !== undefined)
|
|
2437
|
+
payload.maxContextPct = opts.maxContextPct;
|
|
2438
|
+
if (opts.warnContextPct !== undefined)
|
|
2439
|
+
payload.warnContextPct = opts.warnContextPct;
|
|
2265
2440
|
|
|
2266
2441
|
const result = await callBridgeVerb(
|
|
2267
2442
|
bridgePath,
|
|
@@ -2273,6 +2448,14 @@ async function doAskViaDaemon(opts, meta = {}) {
|
|
|
2273
2448
|
// are surfaced but never counted as done.
|
|
2274
2449
|
const matchedCompletion = result?.matchedCompletion === true;
|
|
2275
2450
|
return {
|
|
2451
|
+
...result,
|
|
2452
|
+
status:
|
|
2453
|
+
result?.status ??
|
|
2454
|
+
(matchedCompletion
|
|
2455
|
+
? "completed"
|
|
2456
|
+
: result?.inputSent === false
|
|
2457
|
+
? "failed"
|
|
2458
|
+
: "unknown"),
|
|
2276
2459
|
cli: "claude",
|
|
2277
2460
|
transport: "uds",
|
|
2278
2461
|
short: short ?? null,
|
|
@@ -2280,9 +2463,11 @@ async function doAskViaDaemon(opts, meta = {}) {
|
|
|
2280
2463
|
response: result?.text ?? "",
|
|
2281
2464
|
raw: result?.raw ?? result?.responseRaw ?? "",
|
|
2282
2465
|
matchedCompletion,
|
|
2283
|
-
|
|
2466
|
+
...(result?.status === "submitted"
|
|
2467
|
+
? {}
|
|
2468
|
+
: { timedOut: result?.timedOut === true }),
|
|
2284
2469
|
closed: result?.closed === true,
|
|
2285
|
-
inputSent: result?.inputSent
|
|
2470
|
+
inputSent: result?.inputSent ?? null,
|
|
2286
2471
|
daemon: result?.daemon ?? null,
|
|
2287
2472
|
daemons: result?.daemons ?? [],
|
|
2288
2473
|
matches: result?.matches ?? [],
|
|
@@ -2431,11 +2616,21 @@ async function doAskAuto(adapter, opts) {
|
|
|
2431
2616
|
{
|
|
2432
2617
|
transportSelected: "uds",
|
|
2433
2618
|
transportProbe: resolution.transportProbe,
|
|
2619
|
+
...(probe?.raw?.recoveredFrom
|
|
2620
|
+
? { recoveredFrom: probe.raw.recoveredFrom }
|
|
2621
|
+
: {}),
|
|
2434
2622
|
},
|
|
2435
2623
|
);
|
|
2436
|
-
if (
|
|
2624
|
+
if (
|
|
2625
|
+
udsResult.matchedCompletion ||
|
|
2626
|
+
udsResult.status === "submitted" ||
|
|
2627
|
+
udsResult.errorCode === "context-limit"
|
|
2628
|
+
) {
|
|
2437
2629
|
return udsResult;
|
|
2438
2630
|
}
|
|
2631
|
+
if (udsResult.inputSent !== false) {
|
|
2632
|
+
return { ...udsResult, status: "unknown", done: false };
|
|
2633
|
+
}
|
|
2439
2634
|
// Daemon was reachable but the attach round did not complete (error,
|
|
2440
2635
|
// timeout, or socket close). File a report async, then keep the user moving
|
|
2441
2636
|
// on tmux instead of returning a dead uds result.
|
|
@@ -2497,6 +2692,34 @@ async function doAskAuto(adapter, opts) {
|
|
|
2497
2692
|
}
|
|
2498
2693
|
|
|
2499
2694
|
async function doStop(adapter, opts) {
|
|
2695
|
+
if (opts.short || opts.sessionId) {
|
|
2696
|
+
let sessionId = opts.sessionId;
|
|
2697
|
+
let configDir = opts.configDir;
|
|
2698
|
+
if (!sessionId) {
|
|
2699
|
+
const probe = await callBridgeVerb(
|
|
2700
|
+
opts.bridgePath,
|
|
2701
|
+
"daemon-probe",
|
|
2702
|
+
{
|
|
2703
|
+
short: opts.short,
|
|
2704
|
+
configDir: opts.configDir,
|
|
2705
|
+
},
|
|
2706
|
+
10_000,
|
|
2707
|
+
);
|
|
2708
|
+
if (!probe.ok || !probe.target?.sessionId)
|
|
2709
|
+
throw new Error(
|
|
2710
|
+
probe.error || probe.reason || "Claude session not found",
|
|
2711
|
+
);
|
|
2712
|
+
sessionId = probe.target.sessionId;
|
|
2713
|
+
configDir = probe.daemon?.configDir ?? configDir;
|
|
2714
|
+
}
|
|
2715
|
+
await execFileAsync("claude", ["stop", sessionId], {
|
|
2716
|
+
timeout: 15_000,
|
|
2717
|
+
env: configDir
|
|
2718
|
+
? { ...process.env, CLAUDE_CONFIG_DIR: configDir }
|
|
2719
|
+
: process.env,
|
|
2720
|
+
});
|
|
2721
|
+
return { cli: "claude", sessionId, stopped: true, conversationKept: true };
|
|
2722
|
+
}
|
|
2500
2723
|
const { session, remote } = opts;
|
|
2501
2724
|
if (/[:.]|^[%@]\d+$/.test(session))
|
|
2502
2725
|
throw new Error(
|
|
@@ -2630,6 +2853,7 @@ function startOpts(flags) {
|
|
|
2630
2853
|
resumeLast: Object.hasOwn(flags, "resume-last"),
|
|
2631
2854
|
model: flags.model,
|
|
2632
2855
|
effort: flags.effort,
|
|
2856
|
+
name: flags.name,
|
|
2633
2857
|
readyTimeoutMs: secondsFlag(
|
|
2634
2858
|
flags,
|
|
2635
2859
|
"ready-timeout",
|
|
@@ -2701,6 +2925,10 @@ function askOpts(flags, adapter) {
|
|
|
2701
2925
|
),
|
|
2702
2926
|
bridgePath: resolveBridgePath(flags),
|
|
2703
2927
|
prompt: requireFlag(flags, "prompt"),
|
|
2928
|
+
requestId: flags["request-id"],
|
|
2929
|
+
noWait: Object.hasOwn(flags, "no-wait"),
|
|
2930
|
+
noRelayTag: Object.hasOwn(flags, "no-relay-tag"),
|
|
2931
|
+
...contextOpts(flags, adapter.cli),
|
|
2704
2932
|
remote: flags.remote,
|
|
2705
2933
|
timeoutMs: secondsFlag(flags, "timeout", DEFAULT_ANSWER_TIMEOUT_MS),
|
|
2706
2934
|
maxTurnMs: codexUds
|
|
@@ -2716,8 +2944,22 @@ function askOpts(flags, adapter) {
|
|
|
2716
2944
|
}
|
|
2717
2945
|
|
|
2718
2946
|
function stopOpts(flags) {
|
|
2947
|
+
const targetCount = [flags.session, flags.short, flags["session-id"]].filter(
|
|
2948
|
+
Boolean,
|
|
2949
|
+
).length;
|
|
2950
|
+
if (targetCount !== 1)
|
|
2951
|
+
throw new Error("stop requires one of --session, --short, or --session-id");
|
|
2952
|
+
if (
|
|
2953
|
+
(flags.short || flags["session-id"]) &&
|
|
2954
|
+
(flags.remote || (flags.cli && flags.cli !== "claude"))
|
|
2955
|
+
)
|
|
2956
|
+
throw new Error("stop --short/--session-id supports local Claude only");
|
|
2719
2957
|
return {
|
|
2720
|
-
session:
|
|
2958
|
+
session: flags.session,
|
|
2959
|
+
short: flags.short,
|
|
2960
|
+
sessionId: flags["session-id"],
|
|
2961
|
+
configDir: flags["config-dir"],
|
|
2962
|
+
bridgePath: flags.short ? resolveBridgePath(flags) : undefined,
|
|
2721
2963
|
remote: flags.remote,
|
|
2722
2964
|
};
|
|
2723
2965
|
}
|
|
@@ -2755,18 +2997,186 @@ async function ask(flags) {
|
|
|
2755
2997
|
printJson(await doAsk(adapter, askOpts(flags, adapter)));
|
|
2756
2998
|
}
|
|
2757
2999
|
|
|
3000
|
+
async function wait(flags) {
|
|
3001
|
+
if ((flags.cli ?? "claude") !== "claude")
|
|
3002
|
+
throw new Error(
|
|
3003
|
+
"wait supports Claude UDS only; Codex UDS wait is unavailable",
|
|
3004
|
+
);
|
|
3005
|
+
if (!flags.short && !flags["session-id"])
|
|
3006
|
+
throw new Error("wait requires --short or --session-id");
|
|
3007
|
+
if (flags.remote || (flags.transport && flags.transport !== "uds"))
|
|
3008
|
+
throw new Error("wait supports local Claude UDS only");
|
|
3009
|
+
const timeoutMs = secondsFlag(flags, "timeout", DEFAULT_ANSWER_TIMEOUT_MS);
|
|
3010
|
+
printJson(
|
|
3011
|
+
await callBridgeVerb(
|
|
3012
|
+
resolveBridgePath(flags),
|
|
3013
|
+
"daemon-wait",
|
|
3014
|
+
{
|
|
3015
|
+
short: flags.short,
|
|
3016
|
+
sessionId: flags["session-id"],
|
|
3017
|
+
configDir: flags["config-dir"],
|
|
3018
|
+
requestId: flags["request-id"],
|
|
3019
|
+
timeoutMs,
|
|
3020
|
+
pollIntervalMs: msFlag(
|
|
3021
|
+
flags,
|
|
3022
|
+
"poll-interval",
|
|
3023
|
+
DEFAULT_POLL_INTERVAL_MS,
|
|
3024
|
+
),
|
|
3025
|
+
},
|
|
3026
|
+
timeoutMs,
|
|
3027
|
+
),
|
|
3028
|
+
);
|
|
3029
|
+
}
|
|
3030
|
+
|
|
3031
|
+
async function rename(flags) {
|
|
3032
|
+
if (flags.cli !== "codex" || flags.transport !== "uds" || flags.remote)
|
|
3033
|
+
throw new Error("rename requires local --cli codex --transport uds");
|
|
3034
|
+
const name = requireFlag(flags, "name").trim();
|
|
3035
|
+
if (!name) throw new Error("--name must not be blank");
|
|
3036
|
+
const threadId = requireFlag(flags, "thread");
|
|
3037
|
+
if (threadId === "auto")
|
|
3038
|
+
throw new Error("rename requires an explicit --thread ID");
|
|
3039
|
+
const { renameCodexAppServerThread } = await import(
|
|
3040
|
+
"../hub/team/uds-orchestrator.mjs"
|
|
3041
|
+
);
|
|
3042
|
+
printJson(
|
|
3043
|
+
await renameCodexAppServerThread({
|
|
3044
|
+
socketPath: resolveCodexDaemonSocket(flags["codex-socket"] ?? "default"),
|
|
3045
|
+
threadId,
|
|
3046
|
+
name,
|
|
3047
|
+
}),
|
|
3048
|
+
);
|
|
3049
|
+
}
|
|
3050
|
+
|
|
2758
3051
|
async function stop(flags) {
|
|
2759
|
-
const adapter = selectAdapter(
|
|
3052
|
+
const adapter = selectAdapter({
|
|
3053
|
+
...flags,
|
|
3054
|
+
cli: flags.cli ?? (flags.short || flags["session-id"] ? "claude" : "codex"),
|
|
3055
|
+
});
|
|
2760
3056
|
printJson(await doStop(adapter, stopOpts(flags)));
|
|
2761
3057
|
}
|
|
2762
3058
|
|
|
3059
|
+
async function claudeTmuxTranscript(session, configDir) {
|
|
3060
|
+
const { stdout: paneId } = await runTmux(null, [
|
|
3061
|
+
"display-message",
|
|
3062
|
+
"-p",
|
|
3063
|
+
"-t",
|
|
3064
|
+
session,
|
|
3065
|
+
"#{pane_id}",
|
|
3066
|
+
]);
|
|
3067
|
+
const discovery = await discoverClaudeTmuxSessions();
|
|
3068
|
+
const target = discovery.sessions.find(
|
|
3069
|
+
(entry) => entry.paneId === paneId.trim(),
|
|
3070
|
+
);
|
|
3071
|
+
return findClaudeTranscript({
|
|
3072
|
+
configDir:
|
|
3073
|
+
configDir ||
|
|
3074
|
+
process.env.CLAUDE_CONFIG_DIR ||
|
|
3075
|
+
pathJoin(homedir(), ".claude"),
|
|
3076
|
+
sessionId: target?.sessionId,
|
|
3077
|
+
cwd: target?.cwd,
|
|
3078
|
+
});
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
async function tmuxContext(adapter, opts) {
|
|
3082
|
+
const unknown = modelContext(adapter.cli, null);
|
|
3083
|
+
if (opts.remote) return unknown;
|
|
3084
|
+
try {
|
|
3085
|
+
if (adapter.cli === "claude") {
|
|
3086
|
+
const transcript = await claudeTmuxTranscript(
|
|
3087
|
+
opts.session,
|
|
3088
|
+
opts.configDir,
|
|
3089
|
+
);
|
|
3090
|
+
return (await readClaudeTranscript(transcript))?.context ?? unknown;
|
|
3091
|
+
}
|
|
3092
|
+
const { stdout: target } = await runTmux(null, [
|
|
3093
|
+
"display-message",
|
|
3094
|
+
"-p",
|
|
3095
|
+
"-t",
|
|
3096
|
+
opts.session,
|
|
3097
|
+
"#{session_name}:#{window_index}.#{pane_index}",
|
|
3098
|
+
]);
|
|
3099
|
+
const discovery = await discoverCodexTmuxSessions();
|
|
3100
|
+
const pane = discovery.sessions
|
|
3101
|
+
.flatMap((entry) => entry.panes)
|
|
3102
|
+
.find((entry) => entry.target === target.trim());
|
|
3103
|
+
return await readCodexContext(null, pane?.threadId);
|
|
3104
|
+
} catch {
|
|
3105
|
+
return unknown;
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
async function compact(flags) {
|
|
3110
|
+
if (
|
|
3111
|
+
(flags.cli ?? "claude") !== "claude" ||
|
|
3112
|
+
flags.remote ||
|
|
3113
|
+
flags.short ||
|
|
3114
|
+
flags["session-id"] ||
|
|
3115
|
+
(flags.transport && flags.transport !== "tmux")
|
|
3116
|
+
)
|
|
3117
|
+
throw new Error(
|
|
3118
|
+
"compact supports local --cli claude --session NAME only; UDS slash execution is unverified",
|
|
3119
|
+
);
|
|
3120
|
+
const ifBusy = flags["if-busy"] ?? "fail";
|
|
3121
|
+
if (!["fail", "wait"].includes(ifBusy))
|
|
3122
|
+
throw new Error("compact --if-busy must be fail or wait");
|
|
3123
|
+
const opts = askOpts(
|
|
3124
|
+
{
|
|
3125
|
+
...flags,
|
|
3126
|
+
cli: "claude",
|
|
3127
|
+
transport: "tmux",
|
|
3128
|
+
"if-busy": ifBusy,
|
|
3129
|
+
"no-wait": true,
|
|
3130
|
+
prompt: `/compact${flags.instructions ? ` ${flags.instructions}` : ""}`,
|
|
3131
|
+
},
|
|
3132
|
+
ADAPTERS.claude,
|
|
3133
|
+
);
|
|
3134
|
+
if (ifBusy === "wait") await waitForTmuxIdle(ADAPTERS.claude, opts);
|
|
3135
|
+
const transcriptPath = await claudeTmuxTranscript(
|
|
3136
|
+
opts.session,
|
|
3137
|
+
opts.configDir,
|
|
3138
|
+
);
|
|
3139
|
+
const before = await readClaudeTranscript(transcriptPath);
|
|
3140
|
+
if (!before)
|
|
3141
|
+
throw new Error(
|
|
3142
|
+
"compact requires a discoverable Claude transcript for this tmux pane",
|
|
3143
|
+
);
|
|
3144
|
+
await doAskViaTmux(ADAPTERS.claude, {
|
|
3145
|
+
...opts,
|
|
3146
|
+
ifBusy: "fail",
|
|
3147
|
+
skipContextGuard: true,
|
|
3148
|
+
});
|
|
3149
|
+
const deadline = Date.now() + opts.timeoutMs;
|
|
3150
|
+
do {
|
|
3151
|
+
const snapshot = await readClaudeTranscript(transcriptPath);
|
|
3152
|
+
if (snapshot?.compactCount > before.compactCount) {
|
|
3153
|
+
printJson({
|
|
3154
|
+
cli: "claude",
|
|
3155
|
+
session: opts.session,
|
|
3156
|
+
compacted: true,
|
|
3157
|
+
...snapshot.compact,
|
|
3158
|
+
});
|
|
3159
|
+
return;
|
|
3160
|
+
}
|
|
3161
|
+
await sleep(
|
|
3162
|
+
Math.min(opts.pollIntervalMs, Math.max(0, deadline - Date.now())),
|
|
3163
|
+
);
|
|
3164
|
+
} while (Date.now() < deadline);
|
|
3165
|
+
printJson({
|
|
3166
|
+
cli: "claude",
|
|
3167
|
+
session: opts.session,
|
|
3168
|
+
compacted: false,
|
|
3169
|
+
timedOut: true,
|
|
3170
|
+
});
|
|
3171
|
+
}
|
|
3172
|
+
|
|
2763
3173
|
async function interrupt(flags) {
|
|
2764
3174
|
const adapter = selectAdapter(flags);
|
|
2765
3175
|
printJson(await doInterrupt(adapter, interruptOpts(flags, adapter)));
|
|
2766
3176
|
}
|
|
2767
3177
|
|
|
2768
3178
|
async function probe(flags) {
|
|
2769
|
-
const payload = {};
|
|
3179
|
+
const payload = { includeContext: true };
|
|
2770
3180
|
if (flags.short) payload.short = flags.short;
|
|
2771
3181
|
if (flags["session-id"]) payload.sessionId = flags["session-id"];
|
|
2772
3182
|
if (flags["config-dir"]) payload.configDir = flags["config-dir"];
|
|
@@ -2822,6 +3232,7 @@ async function listSessions(flags) {
|
|
|
2822
3232
|
function startOptsForSession(flags, session, side) {
|
|
2823
3233
|
return {
|
|
2824
3234
|
session,
|
|
3235
|
+
...(!side ? { name: flags.name } : {}),
|
|
2825
3236
|
remote: flags.remote,
|
|
2826
3237
|
cwd: flags.cwd,
|
|
2827
3238
|
...(side
|
|
@@ -2839,10 +3250,12 @@ function startOptsForSession(flags, session, side) {
|
|
|
2839
3250
|
};
|
|
2840
3251
|
}
|
|
2841
3252
|
|
|
2842
|
-
function askOptsForSession(flags, session, prompt) {
|
|
3253
|
+
function askOptsForSession(flags, session, prompt, cli) {
|
|
2843
3254
|
return {
|
|
2844
3255
|
session,
|
|
2845
3256
|
prompt,
|
|
3257
|
+
...contextOpts(flags, cli),
|
|
3258
|
+
noRelayTag: Object.hasOwn(flags, "no-relay-tag"),
|
|
2846
3259
|
remote: flags.remote,
|
|
2847
3260
|
timeoutMs: secondsFlag(flags, "timeout", DEFAULT_ANSWER_TIMEOUT_MS),
|
|
2848
3261
|
settleMs: msFlag(flags, "settle", DEFAULT_SETTLE_MS),
|
|
@@ -2894,6 +3307,7 @@ async function stopAfterLifecycle(stopSpecs, primaryError = null) {
|
|
|
2894
3307
|
|
|
2895
3308
|
function turnFromAsk(prompt, result) {
|
|
2896
3309
|
return {
|
|
3310
|
+
requestId: result.requestId,
|
|
2897
3311
|
prompt,
|
|
2898
3312
|
response: result.response,
|
|
2899
3313
|
done: result.done,
|
|
@@ -2924,7 +3338,7 @@ async function converse(flags) {
|
|
|
2924
3338
|
for (const prompt of prompts) {
|
|
2925
3339
|
const result = await doAsk(
|
|
2926
3340
|
adapter,
|
|
2927
|
-
askOptsForSession(flags, session, prompt),
|
|
3341
|
+
askOptsForSession(flags, session, prompt, adapter.cli),
|
|
2928
3342
|
);
|
|
2929
3343
|
turns.push(turnFromAsk(prompt, result));
|
|
2930
3344
|
}
|
|
@@ -2965,7 +3379,7 @@ async function goalDriven(flags) {
|
|
|
2965
3379
|
: `Continue. Reply with only ${doneToken} on its own line when fully complete.`;
|
|
2966
3380
|
const result = await doAsk(
|
|
2967
3381
|
adapter,
|
|
2968
|
-
askOptsForSession(flags, session, prompt),
|
|
3382
|
+
askOptsForSession(flags, session, prompt, adapter.cli),
|
|
2969
3383
|
);
|
|
2970
3384
|
turns.push(turnFromAsk(prompt, result));
|
|
2971
3385
|
if (hasDoneToken(result.response, doneToken)) {
|
|
@@ -3344,6 +3758,8 @@ function peerSideBaseOpts(flags, side, adapter, session) {
|
|
|
3344
3758
|
}
|
|
3345
3759
|
return {
|
|
3346
3760
|
session,
|
|
3761
|
+
noRelayTag: Object.hasOwn(flags, "no-relay-tag"),
|
|
3762
|
+
...contextOpts(flags, adapter.cli),
|
|
3347
3763
|
short,
|
|
3348
3764
|
sessionId,
|
|
3349
3765
|
threadId,
|
|
@@ -3592,6 +4008,7 @@ async function peer(flags) {
|
|
|
3592
4008
|
|
|
3593
4009
|
hops.push({
|
|
3594
4010
|
hop: hopIndex + 1,
|
|
4011
|
+
requestId: result.requestId,
|
|
3595
4012
|
from: isA ? "a" : "b",
|
|
3596
4013
|
cli: adapter.cli,
|
|
3597
4014
|
transport: result.transport ?? base.transport,
|
|
@@ -3767,7 +4184,7 @@ async function main() {
|
|
|
3767
4184
|
const { command, flags } = parseCli(process.argv.slice(2));
|
|
3768
4185
|
|
|
3769
4186
|
if (command === "help") {
|
|
3770
|
-
process.stdout.write(`${usage()}\n`);
|
|
4187
|
+
process.stdout.write(`${usage(flags.subcommand)}\n`);
|
|
3771
4188
|
return;
|
|
3772
4189
|
}
|
|
3773
4190
|
|
|
@@ -3775,6 +4192,12 @@ async function main() {
|
|
|
3775
4192
|
await start(flags);
|
|
3776
4193
|
} else if (command === "ask") {
|
|
3777
4194
|
await ask(flags);
|
|
4195
|
+
} else if (command === "wait") {
|
|
4196
|
+
await wait(flags);
|
|
4197
|
+
} else if (command === "compact") {
|
|
4198
|
+
await compact(flags);
|
|
4199
|
+
} else if (command === "rename") {
|
|
4200
|
+
await rename(flags);
|
|
3778
4201
|
} else if (command === "stop") {
|
|
3779
4202
|
await stop(flags);
|
|
3780
4203
|
} else if (command === "interrupt") {
|
|
@@ -3825,6 +4248,7 @@ export {
|
|
|
3825
4248
|
resolveAskTransport,
|
|
3826
4249
|
resolveCodexDaemonSocket,
|
|
3827
4250
|
splitTmuxTarget,
|
|
4251
|
+
stopOpts,
|
|
3828
4252
|
tmuxBufferName,
|
|
3829
4253
|
verifyAttachedPeerSide,
|
|
3830
4254
|
};
|
|
@@ -3846,6 +4270,7 @@ if (isMainModule()) {
|
|
|
3846
4270
|
printJson({
|
|
3847
4271
|
ok: false,
|
|
3848
4272
|
error: error.message,
|
|
4273
|
+
...(error.requestId ? { requestId: error.requestId } : {}),
|
|
3849
4274
|
});
|
|
3850
4275
|
process.exitCode = 1;
|
|
3851
4276
|
});
|