@triflux/remote 10.42.0 → 10.44.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.
@@ -12,6 +12,12 @@ import { createHash, randomUUID } from "node:crypto";
12
12
  import { isAbsolute } from "node:path";
13
13
  import process from "node:process";
14
14
 
15
+ import {
16
+ createActivityLifecycle,
17
+ isActivityLifecycleEnabled,
18
+ resolveHardCeilingMs,
19
+ resolveStallInterventionMs,
20
+ } from "@triflux/core/hub/lib/worker-lifecycle.mjs";
15
21
  import {
16
22
  JsonRpcProtocolError,
17
23
  JsonRpcStdioClient,
@@ -25,6 +31,7 @@ export const CODEX_APP_SERVER_TRANSPORT_EXIT_CODE = 70;
25
31
  export const CODEX_APP_SERVER_TIMEOUT_EXIT_CODE = 124;
26
32
 
27
33
  export const DEFAULT_CODEX_APP_SERVER_BOOTSTRAP_TIMEOUT_MS = 10_000;
34
+ // Legacy wall-clock default retained for TFX_ACTIVITY_LIFECYCLE=0 compatibility.
28
35
  export const DEFAULT_CODEX_APP_SERVER_EXECUTION_TIMEOUT_MS = 10 * 60 * 1000;
29
36
  export const DEFAULT_UNKNOWN_METHOD_WARN_THRESHOLD = 5;
30
37
  /**
@@ -327,6 +334,7 @@ export class CodexAppServerWorker {
327
334
  * @param {number} [options.unknownMethodWarnThreshold]
328
335
  * @param {(method: string) => void} [options.onUnknownMethod]
329
336
  * @param {(label: string, payload: object) => void} [options.warn]
337
+ * @param {(context: object) => Promise<boolean|string>|boolean|string} [options.interveneFn]
330
338
  */
331
339
  constructor(options = {}) {
332
340
  this.command = options.command || process.env.CODEX_BIN || "codex";
@@ -370,6 +378,8 @@ export class CodexAppServerWorker {
370
378
  typeof options.onUnknownMethod === "function"
371
379
  ? options.onUnknownMethod
372
380
  : null;
381
+ this._interveneFn =
382
+ typeof options.interveneFn === "function" ? options.interveneFn : null;
373
383
 
374
384
  /** @type {import('node:child_process').ChildProcess | null} */
375
385
  this.child = null;
@@ -643,6 +653,8 @@ export class CodexAppServerWorker {
643
653
  }
644
654
 
645
655
  const outputParts = [];
656
+ let lastActivityAt = Date.now();
657
+ let activityLifecycle = null;
646
658
  const unknownMethodsThisTurn = new Set();
647
659
  let threadId =
648
660
  typeof opts.threadId === "string" && opts.threadId ? opts.threadId : null;
@@ -714,6 +726,10 @@ export class CodexAppServerWorker {
714
726
  };
715
727
 
716
728
  const handleNotification = (method, params) => {
729
+ // Any notification, including unknown and opt-out methods, proves the
730
+ // transport is live. Content filtering below remains unchanged.
731
+ lastActivityAt = Date.now();
732
+ activityLifecycle?.observe();
717
733
  // AC5 defense: opt-out should already be filtered server-side.
718
734
  if (OPT_OUT_METHODS.includes(method)) return;
719
735
 
@@ -854,16 +870,40 @@ export class CodexAppServerWorker {
854
870
  };
855
871
  }
856
872
 
857
- // Timeout: SIGTERM + partial WorkerResult (AC8)
873
+ // In activity mode timeoutMs is the inactivity window. Notifications
874
+ // re-arm the lifecycle; hardCeilingMs remains the leak-prevention bound.
875
+ const activityEnabled = isActivityLifecycleEnabled();
858
876
  const timeoutMs = Number.isFinite(opts.timeoutMs)
859
877
  ? opts.timeoutMs
860
- : DEFAULT_CODEX_APP_SERVER_EXECUTION_TIMEOUT_MS;
878
+ : activityEnabled
879
+ ? resolveStallInterventionMs()
880
+ : DEFAULT_CODEX_APP_SERVER_EXECUTION_TIMEOUT_MS;
881
+ const hardCeilingMs = Number.isFinite(opts.hardCeilingMs)
882
+ ? opts.hardCeilingMs
883
+ : resolveHardCeilingMs();
861
884
  // Capture current child so a stale timer (post-result) cannot SIGTERM a
862
885
  // later reused worker's child process.
863
886
  const capturedChild = this.child;
887
+ activityLifecycle = createActivityLifecycle({
888
+ enabled: activityEnabled,
889
+ interventionMs: timeoutMs,
890
+ hardCeilingMs,
891
+ onIntervene: async (context) => {
892
+ if (this._interveneFn) {
893
+ const result = await this._interveneFn({
894
+ threadId,
895
+ client,
896
+ lastActivityAt,
897
+ ...context,
898
+ });
899
+ return result === true || result === "resolved";
900
+ }
901
+ return false;
902
+ },
903
+ });
864
904
  const timeoutPromise = new Promise((resolve) => {
865
- timer = setTimeout(() => {
866
- this._warn("execution timeout", { timeoutMs });
905
+ const fire = (reason) => {
906
+ this._warn("execution timeout", { timeoutMs, reason });
867
907
  try {
868
908
  capturedChild?.kill?.("SIGTERM");
869
909
  } catch {}
@@ -878,7 +918,16 @@ export class CodexAppServerWorker {
878
918
  ),
879
919
  raw: null,
880
920
  });
881
- }, timeoutMs);
921
+ };
922
+ const arm = () => {
923
+ timer = setTimeout(async () => {
924
+ if (!activityEnabled) return fire("timeout");
925
+ const reason = await activityLifecycle.check({ threadId, client });
926
+ if (reason) return fire(reason);
927
+ arm();
928
+ }, timeoutMs);
929
+ };
930
+ arm();
882
931
  });
883
932
 
884
933
  const result = await Promise.race([resultPromise, timeoutPromise]);
@@ -119,7 +119,7 @@ function readTomlScalar(raw, key) {
119
119
  }
120
120
 
121
121
  /**
122
- * Resolve a Codex effort profile name (e.g. "gpt55_high") to its concrete
122
+ * Resolve a Codex effort profile name (e.g. "gpt56_terra_high") to its concrete
123
123
  * `model` + reasoning effort. SSOT = the per-profile file written by
124
124
  * `tfx setup` at `~/.codex/<profile>.config.toml`. Falls back to deriving the
125
125
  * effort from the `<...>_<effort>` naming convention when the file is absent.
@@ -146,9 +146,11 @@ export function resolveCodexProfileConfig(profileName) {
146
146
  } catch {
147
147
  // Profile file absent (non-file alias). Derive effort from the naming
148
148
  // convention; leave model unset so codex uses its config.toml default.
149
- const suffix = /_(xhigh|high|med|medium|low)$/.exec(profileName);
149
+ const suffix = /_(ultra|max|xhigh|high|med|medium|low)$/.exec(profileName);
150
150
  if (suffix) {
151
151
  const map = {
152
+ ultra: "ultra",
153
+ max: "max",
152
154
  xhigh: "xhigh",
153
155
  high: "high",
154
156
  med: "medium",
@@ -193,16 +195,27 @@ export function buildCodexArguments(prompt, opts = {}) {
193
195
  // profile effort merges on top of any opts.config.
194
196
  if (typeof opts.profile === "string" && opts.profile) {
195
197
  const resolved = resolveCodexProfileConfig(opts.profile);
196
- if (resolved.model && typeof args.model !== "string") {
197
- args.model = resolved.model;
198
+ const profileDerivedUltra =
199
+ resolved.reasoningEffort?.toLowerCase() === "ultra";
200
+ if (typeof args.model !== "string") {
201
+ if (profileDerivedUltra) args.model = "gpt-5.6-sol";
202
+ else if (resolved.model) args.model = resolved.model;
198
203
  }
199
204
  if (resolved.reasoningEffort) {
200
205
  args.config = {
201
206
  ...(args.config || {}),
202
- model_reasoning_effort: resolved.reasoningEffort,
207
+ model_reasoning_effort: profileDerivedUltra
208
+ ? "max"
209
+ : resolved.reasoningEffort,
203
210
  };
204
211
  }
205
212
  }
213
+ if (typeof opts.reasoningEffort === "string" && opts.reasoningEffort) {
214
+ args.config = {
215
+ ...(args.config || {}),
216
+ model_reasoning_effort: opts.reasoningEffort,
217
+ };
218
+ }
206
219
 
207
220
  return args;
208
221
  }
@@ -694,6 +707,9 @@ function parseCliArgs(argv) {
694
707
  case "--model":
695
708
  options.model = next();
696
709
  break;
710
+ case "--reasoning-effort":
711
+ options.reasoningEffort = next();
712
+ break;
697
713
  case "--approval-policy":
698
714
  options.approvalPolicy = next();
699
715
  break;
@@ -11,6 +11,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
11
11
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
12
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
13
  import * as z from "zod";
14
+ import { resolveCodexAgentPolicy } from "@triflux/core/scripts/lib/agent-route-policy.mjs";
14
15
  import { resolveBashExecutable } from "@triflux/core/hub/lib/bash-path.mjs";
15
16
  import { runHeadlessWithCleanup } from "../team/headless.mjs";
16
17
  import { buildWorkerSandboxEnv } from "../team/worker-sandbox.mjs";
@@ -33,57 +34,11 @@ const { buildPromptHint, getCodexMcpConfig, SUPPORTED_MCP_PROFILES } =
33
34
  await import(pathToFileURL(mcpFilterPath).href);
34
35
  const SERVER_INFO = { name: "triflux-delegator", version: "1.0.0" };
35
36
  const DEFAULT_CONTEXT_BYTES = 32 * 1024;
36
- const DEFAULT_ROUTE_TIMEOUT_SEC = 120;
37
37
  const DIRECT_PROGRESS_START = 5;
38
38
  const DIRECT_PROGRESS_RUNNING = 60;
39
39
  const DIRECT_PROGRESS_DONE = 100;
40
40
  const SEARCH_ENGINE_CACHE_PATH = [".omc", "state", "search-engines.json"];
41
41
 
42
- const AGENT_TIMEOUT_SEC = Object.freeze({
43
- executor: 1080,
44
- "build-fixer": 540,
45
- debugger: 900,
46
- "deep-executor": 3600,
47
- architect: 3600,
48
- planner: 3600,
49
- critic: 3600,
50
- analyst: 3600,
51
- "code-reviewer": 1800,
52
- "security-reviewer": 1800,
53
- "quality-reviewer": 1800,
54
- scientist: 1440,
55
- "scientist-deep": 3600,
56
- "document-specialist": 1440,
57
- designer: 900,
58
- writer: 900,
59
- explore: 300,
60
- verifier: 1200,
61
- "test-engineer": 300,
62
- "qa-tester": 300,
63
- spark: 180,
64
- });
65
-
66
- const CODEX_PROFILE_BY_AGENT = Object.freeze({
67
- executor: "gpt55_high",
68
- "build-fixer": "gpt55_low",
69
- debugger: "gpt55_xhigh",
70
- "deep-executor": "gpt55_xhigh",
71
- architect: "gpt55_xhigh",
72
- planner: "gpt55_xhigh",
73
- critic: "gpt55_xhigh",
74
- analyst: "gpt55_xhigh",
75
- "code-reviewer": "gpt55_high",
76
- "security-reviewer": "gpt55_xhigh",
77
- "quality-reviewer": "gpt55_high",
78
- scientist: "gpt55_high",
79
- "scientist-deep": "gpt55_xhigh",
80
- "document-specialist": "gpt55_high",
81
- verifier: "gpt55_high",
82
- designer: "gpt55_xhigh",
83
- writer: "gpt55_low",
84
- spark: "gpt55_low",
85
- });
86
-
87
42
  const REVIEW_INSTRUCTION_BY_AGENT = Object.freeze({
88
43
  "code-reviewer":
89
44
  "코드 리뷰 모드로 동작하라. 버그, 리스크, 회귀, 테스트 누락을 우선 식별하라.",
@@ -137,7 +92,7 @@ function resolveRouteScript(explicitPath, cwd = process.cwd()) {
137
92
  }
138
93
 
139
94
  function resolveCodexProfile(agentType) {
140
- return CODEX_PROFILE_BY_AGENT[agentType] || "high";
95
+ return resolveCodexAgentPolicy(agentType).profile;
141
96
  }
142
97
 
143
98
  function normalizeProvider(provider = "auto") {
@@ -154,8 +109,7 @@ function resolveTimeoutMs(agentType, timeoutMs) {
154
109
  return Math.trunc(timeoutMs);
155
110
  }
156
111
 
157
- const timeoutSec = AGENT_TIMEOUT_SEC[agentType] || DEFAULT_ROUTE_TIMEOUT_SEC;
158
- return timeoutSec * 1000;
112
+ return resolveCodexAgentPolicy(agentType).timeoutSec * 1000;
159
113
  }
160
114
 
161
115
  function resolveTimeoutSec(agentType, timeoutMs) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@triflux/remote",
3
- "version": "10.42.0",
3
+ "version": "10.44.0",
4
4
  "description": "triflux remote — team mode, psmux, MCP workers, SQLite store.",
5
5
  "type": "module",
6
6
  "main": "hub/index.mjs",
@@ -0,0 +1,307 @@
1
+ // Canonical Codex role policy. Concrete model IDs stay in profile config files.
2
+
3
+ import { resolveCodexProfileConfigValues } from "./codex-profile-config.mjs";
4
+
5
+ const policy = {
6
+ executor: {
7
+ profile: "gpt56_terra_high",
8
+ timeoutSec: 1080,
9
+ runMode: "fg",
10
+ opusOversight: false,
11
+ mcpHint: "implement",
12
+ subcommand: "exec",
13
+ },
14
+ codex: {
15
+ profile: "gpt56_terra_high",
16
+ timeoutSec: 1080,
17
+ runMode: "fg",
18
+ opusOversight: false,
19
+ mcpHint: "implement",
20
+ subcommand: "exec",
21
+ },
22
+ "build-fixer": {
23
+ profile: "gpt56_luna_low",
24
+ timeoutSec: 540,
25
+ runMode: "fg",
26
+ opusOversight: false,
27
+ mcpHint: "implement",
28
+ subcommand: "exec",
29
+ },
30
+ cleanup: {
31
+ profile: "gpt56_terra_med",
32
+ timeoutSec: 540,
33
+ runMode: "fg",
34
+ opusOversight: false,
35
+ mcpHint: "implement",
36
+ subcommand: "exec",
37
+ },
38
+ deslop: {
39
+ profile: "gpt56_terra_med",
40
+ timeoutSec: 540,
41
+ runMode: "fg",
42
+ opusOversight: false,
43
+ mcpHint: "implement",
44
+ subcommand: "exec",
45
+ },
46
+ debugger: {
47
+ profile: "gpt56_sol_xhigh",
48
+ timeoutSec: 900,
49
+ runMode: "bg",
50
+ opusOversight: false,
51
+ mcpHint: "implement",
52
+ subcommand: "exec",
53
+ },
54
+ "deep-executor": {
55
+ profile: "gpt56_sol_xhigh",
56
+ timeoutSec: 3600,
57
+ runMode: "bg",
58
+ opusOversight: true,
59
+ mcpHint: "implement",
60
+ subcommand: "exec",
61
+ },
62
+ architect: {
63
+ profile: "gpt56_sol_xhigh",
64
+ timeoutSec: 3600,
65
+ runMode: "bg",
66
+ opusOversight: true,
67
+ mcpHint: "analyze",
68
+ subcommand: "exec",
69
+ },
70
+ critic: {
71
+ profile: "gpt56_sol_xhigh",
72
+ timeoutSec: 3600,
73
+ runMode: "bg",
74
+ opusOversight: true,
75
+ mcpHint: "analyze",
76
+ subcommand: "exec",
77
+ },
78
+ planner: {
79
+ profile: "gpt56_sol_xhigh",
80
+ timeoutSec: 3600,
81
+ runMode: "fg",
82
+ opusOversight: true,
83
+ mcpHint: "analyze",
84
+ subcommand: "exec",
85
+ },
86
+ analyst: {
87
+ profile: "gpt56_sol_xhigh",
88
+ timeoutSec: 3600,
89
+ runMode: "fg",
90
+ opusOversight: true,
91
+ mcpHint: "analyze",
92
+ subcommand: "exec",
93
+ },
94
+ "code-reviewer": {
95
+ profile: "gpt56_terra_high",
96
+ timeoutSec: 1800,
97
+ runMode: "bg",
98
+ opusOversight: false,
99
+ mcpHint: "review",
100
+ subcommand: "review",
101
+ },
102
+ "quality-reviewer": {
103
+ profile: "gpt56_terra_high",
104
+ timeoutSec: 1800,
105
+ runMode: "bg",
106
+ opusOversight: false,
107
+ mcpHint: "review",
108
+ subcommand: "review",
109
+ },
110
+ "security-reviewer": {
111
+ profile: "gpt56_sol_xhigh",
112
+ timeoutSec: 1800,
113
+ runMode: "bg",
114
+ opusOversight: true,
115
+ mcpHint: "review",
116
+ subcommand: "review",
117
+ },
118
+ scientist: {
119
+ profile: "gpt56_terra_high",
120
+ timeoutSec: 1440,
121
+ runMode: "bg",
122
+ opusOversight: false,
123
+ mcpHint: "analyze",
124
+ subcommand: "exec",
125
+ },
126
+ "document-specialist": {
127
+ profile: "gpt56_terra_high",
128
+ timeoutSec: 1440,
129
+ runMode: "bg",
130
+ opusOversight: false,
131
+ mcpHint: "analyze",
132
+ subcommand: "exec",
133
+ },
134
+ "scientist-deep": {
135
+ profile: "gpt56_sol_xhigh",
136
+ timeoutSec: 3600,
137
+ runMode: "bg",
138
+ opusOversight: false,
139
+ mcpHint: "analyze",
140
+ subcommand: "exec",
141
+ },
142
+ verifier: {
143
+ profile: "gpt56_terra_high",
144
+ timeoutSec: 1200,
145
+ runMode: "fg",
146
+ opusOversight: false,
147
+ mcpHint: "review",
148
+ subcommand: "review",
149
+ },
150
+ "test-engineer": {
151
+ profile: "gpt56_terra_high",
152
+ timeoutSec: 1200,
153
+ runMode: "bg",
154
+ opusOversight: false,
155
+ mcpHint: "implement",
156
+ subcommand: "exec",
157
+ },
158
+ "qa-tester": {
159
+ profile: "gpt56_terra_high",
160
+ timeoutSec: 1200,
161
+ runMode: "bg",
162
+ opusOversight: false,
163
+ mcpHint: "review",
164
+ subcommand: "review",
165
+ },
166
+ spark: {
167
+ profile: "gpt56_luna_low",
168
+ timeoutSec: 180,
169
+ runMode: "fg",
170
+ opusOversight: false,
171
+ mcpHint: "implement",
172
+ subcommand: "exec",
173
+ },
174
+ // These entries are only for explicit direct-Codex overrides. agent-map.json
175
+ // remains the provider-selection authority for their normal execution.
176
+ designer: {
177
+ profile: "gpt56_sol_xhigh",
178
+ timeoutSec: 900,
179
+ runMode: "bg",
180
+ opusOversight: false,
181
+ mcpHint: "implement",
182
+ subcommand: "exec",
183
+ },
184
+ writer: {
185
+ profile: "gpt56_luna_low",
186
+ timeoutSec: 900,
187
+ runMode: "bg",
188
+ opusOversight: false,
189
+ mcpHint: "implement",
190
+ subcommand: "exec",
191
+ },
192
+ };
193
+
194
+ function freezePolicy(entry) {
195
+ return Object.freeze({ ...entry });
196
+ }
197
+
198
+ export const CODEX_AGENT_POLICY = Object.freeze(
199
+ Object.fromEntries(
200
+ Object.entries(policy).map(([agent, entry]) => [
201
+ agent,
202
+ freezePolicy(entry),
203
+ ]),
204
+ ),
205
+ );
206
+ export const DEFAULT_CODEX_AGENT = "executor";
207
+
208
+ export const CANONICAL_CODEX_PROFILE_OVERRIDES = Object.freeze(
209
+ new Set([
210
+ "gpt56_luna_low", "gpt56_terra_med", "gpt56_terra_high", "gpt56_sol_xhigh",
211
+ "gpt56_sol_max", "gpt56_sol_ultra",
212
+ ]),
213
+ );
214
+ export const ULTRA_ELIGIBLE_CODEX_AGENTS = Object.freeze(
215
+ new Set(["deep-executor", "scientist-deep"]),
216
+ );
217
+ const NESTED_GLOBAL_PROFILE_OVERRIDES = new Set([
218
+ "max", "ultra", "gpt56_sol_max", "gpt56_sol_ultra",
219
+ ]);
220
+
221
+ export function resolveCodexAgentPolicy(agent) {
222
+ return freezePolicy(
223
+ CODEX_AGENT_POLICY[agent] ?? CODEX_AGENT_POLICY[DEFAULT_CODEX_AGENT],
224
+ );
225
+ }
226
+
227
+ export function resolveCodexAgentProfile(
228
+ agent,
229
+ { profileOverride = "auto", retryProfile = null, nested = false, allowCustom = false } = {},
230
+ ) {
231
+ const defaultProfile = resolveCodexAgentPolicy(agent).profile;
232
+ const retryRequested = String(retryProfile ?? "").trim();
233
+ const requested = retryRequested || String(profileOverride || "auto").trim();
234
+ if (!requested || requested === "auto") return defaultProfile;
235
+ const canonical = requested === "max" ? "gpt56_sol_max" : requested === "ultra" ? "gpt56_sol_ultra" : requested;
236
+ if (!CANONICAL_CODEX_PROFILE_OVERRIDES.has(canonical)) {
237
+ if (allowCustom) return requested;
238
+ throw new Error(`[agent-route-policy] unsupported profile override: ${requested}`);
239
+ }
240
+ if (canonical !== "gpt56_sol_ultra") return canonical;
241
+ return nested || !ULTRA_ELIGIBLE_CODEX_AGENTS.has(agent) ? "gpt56_sol_max" : canonical;
242
+ }
243
+
244
+ export function resolveNestedCodexAgentProfile(
245
+ agent,
246
+ { profileOverride = "auto", globalProfile = "auto", retryProfile = null, codexHome } = {},
247
+ ) {
248
+ const explicitProfile = String(profileOverride || "auto").trim();
249
+ const globalOverride = String(globalProfile || "auto").trim();
250
+ const effectiveOverride = explicitProfile !== "auto" ? explicitProfile : NESTED_GLOBAL_PROFILE_OVERRIDES.has(globalOverride) ? globalOverride : "auto";
251
+ const fallback = resolveCodexAgentProfile(agent);
252
+ const candidate = resolveCodexAgentProfile(agent, { profileOverride: effectiveOverride, retryProfile, nested: true, allowCustom: true });
253
+ const { effort } = resolveCodexProfileConfigValues(candidate, { codexHome });
254
+ if (!effort) return fallback;
255
+ return effort.toLowerCase() === "ultra" ? "gpt56_sol_max" : candidate;
256
+ }
257
+
258
+ export function describeCodexAgentPolicy() {
259
+ return {
260
+ defaultAgent: DEFAULT_CODEX_AGENT,
261
+ agents: Object.keys(CODEX_AGENT_POLICY),
262
+ };
263
+ }
264
+
265
+ function parseCliArgs(argv) {
266
+ const args = [...argv];
267
+ let format = "json";
268
+ let agent = DEFAULT_CODEX_AGENT;
269
+ while (args.length > 0) {
270
+ const flag = args.shift();
271
+ if (flag === "--format") format = args.shift() || "";
272
+ else if (flag === "--agent") agent = args.shift() || "";
273
+ else throw new Error(`unknown argument: ${flag}`);
274
+ }
275
+ if (!new Set(["json", "tsv"]).has(format)) {
276
+ throw new Error(`unsupported format: ${format}`);
277
+ }
278
+ return { format, agent };
279
+ }
280
+
281
+ function runCli() {
282
+ const { format, agent } = parseCliArgs(process.argv.slice(2));
283
+ const resolved = resolveCodexAgentPolicy(agent);
284
+ if (format === "tsv") {
285
+ process.stdout.write(
286
+ [
287
+ resolved.profile,
288
+ resolved.timeoutSec,
289
+ resolved.runMode,
290
+ resolved.opusOversight,
291
+ resolved.mcpHint,
292
+ resolved.subcommand,
293
+ ].join("\t"),
294
+ );
295
+ return;
296
+ }
297
+ process.stdout.write(`${JSON.stringify(resolved)}\n`);
298
+ }
299
+
300
+ if (import.meta.url === new URL(process.argv[1], "file:").href) {
301
+ try {
302
+ runCli();
303
+ } catch (error) {
304
+ process.stderr.write(`[agent-route-policy] ${error.message}\n`);
305
+ process.exitCode = 1;
306
+ }
307
+ }
@@ -5,6 +5,7 @@
5
5
  //
6
6
  // 출처: scripts/tfx-route.sh route_agent() L1052-L1059, run_codex_exec() L2036-L2046.
7
7
  // .claude/rules/tfx-update-logic.md Antigravity CLI 1.0.0 정밀 sanity matrix.
8
+ // timeoutSec은 lane 예상 소요 시간(advisory)이다. hard ceiling 집행은 Bash route가 소유한다.
8
9
 
9
10
  export const id = "agy";
10
11
  export const cliType = "antigravity";
@@ -5,6 +5,7 @@
5
5
  //
6
6
  // 출처: scripts/tfx-route.sh route_agent() L1062-L1063 (explore|claude),
7
7
  // L1196-L1203 (TFX_CLI_MODE=gemini fallback), TFX_VERIFIER_OVERRIDE=claude.
8
+ // timeoutSec은 lane 예상 소요 시간(advisory)이다. hard ceiling 집행은 Bash route가 소유한다.
8
9
 
9
10
  export const id = "claude";
10
11
  export const cliType = "claude-native";