@triflux/remote 10.86.1 → 10.88.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/hub/team/cli/commands/debug.mjs +11 -4
- package/hub/team/handoff.mjs +100 -0
- package/hub/team/headless.mjs +49 -10
- package/hub/team/lead-launch.mjs +186 -0
- package/hub/team/native-peer.mjs +97 -3
- package/hub/team/orchestrator.mjs +80 -0
- package/hub/team/pane.mjs +99 -0
- package/hub/team/results-index.mjs +35 -0
- package/hub/team/retry-state-machine.mjs +97 -9
- package/hub/team/uds-orchestrator.mjs +30 -8
- package/package.json +1 -1
- package/scripts/lib/agent-route-policy.mjs +39 -28
- package/scripts/lib/codex-profile-config.mjs +40 -59
- package/scripts/lib/doctor-env-checks.mjs +377 -0
- package/scripts/lib/gemini-profiles.mjs +2 -5
- package/scripts/lib/mcp-filter.mjs +86 -3
- package/scripts/lib/model-effort.mjs +134 -0
- package/scripts/lib/model-family-resolver.mjs +73 -8
package/hub/team/pane.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { existsSync, unlinkSync, writeFileSync } from "node:fs";
|
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { judgeComposerState } from "@triflux/core/hub/lib/jev-addon.mjs";
|
|
7
8
|
import { privateTmpDir } from "@triflux/core/hub/lib/private-tmp.mjs";
|
|
8
9
|
import { psmuxExec } from "./psmux.mjs";
|
|
9
10
|
import { detectMultiplexer, tmuxExec } from "./session.mjs";
|
|
@@ -251,6 +252,104 @@ export function submitPromptWithVerification({
|
|
|
251
252
|
}
|
|
252
253
|
}
|
|
253
254
|
|
|
255
|
+
/**
|
|
256
|
+
* 결정론적 컴포저 상태 판정이 모호할 때 Jev로 컴포저 상태를 판별해 Enter 재전송 여부를 결정한다.
|
|
257
|
+
*/
|
|
258
|
+
export async function submitPromptWithJevVerification({
|
|
259
|
+
cli = "claude",
|
|
260
|
+
prompt = "",
|
|
261
|
+
capture,
|
|
262
|
+
sendEnter,
|
|
263
|
+
sleep = sleepMs,
|
|
264
|
+
settleMs = PASTE_SETTLE_MS,
|
|
265
|
+
retryLimit = SUBMIT_RETRY_LIMIT,
|
|
266
|
+
jevOptions = {},
|
|
267
|
+
} = {}) {
|
|
268
|
+
const strictVerification = isAntigravityCli(cli);
|
|
269
|
+
const promptPrefix = promptComposerNeedle(prompt);
|
|
270
|
+
const isConfirmedSubmitted = (text) => {
|
|
271
|
+
if (!promptPrefix) return true;
|
|
272
|
+
if (!text) return !strictVerification;
|
|
273
|
+
if (hasBusyMarkerOutsideComposer(text)) return true;
|
|
274
|
+
const state = composerPromptState(text, promptPrefix);
|
|
275
|
+
if (state === "absent") return true;
|
|
276
|
+
if (state === "unknown" && !strictVerification) return true;
|
|
277
|
+
return false;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
submitPromptWithVerification({
|
|
282
|
+
cli,
|
|
283
|
+
prompt,
|
|
284
|
+
capture,
|
|
285
|
+
sendEnter,
|
|
286
|
+
sleep,
|
|
287
|
+
settleMs,
|
|
288
|
+
retryLimit,
|
|
289
|
+
});
|
|
290
|
+
} catch (err) {
|
|
291
|
+
const capturedText =
|
|
292
|
+
typeof capture === "function" ? String(capture() ?? "") : "";
|
|
293
|
+
const verdict = await judgeComposerState(
|
|
294
|
+
{ capturedText, promptPrefix },
|
|
295
|
+
jevOptions,
|
|
296
|
+
);
|
|
297
|
+
if (verdict?.state === "submitted_running") {
|
|
298
|
+
return { submitted: true, jevState: verdict.state };
|
|
299
|
+
}
|
|
300
|
+
if (
|
|
301
|
+
verdict?.state === "stuck_in_composer" &&
|
|
302
|
+
typeof sendEnter === "function"
|
|
303
|
+
) {
|
|
304
|
+
sendEnter();
|
|
305
|
+
sleep(400);
|
|
306
|
+
const afterText =
|
|
307
|
+
typeof capture === "function" ? String(capture() ?? "") : "";
|
|
308
|
+
if (isConfirmedSubmitted(afterText)) {
|
|
309
|
+
return { submitted: true, jevState: "submitted_running" };
|
|
310
|
+
}
|
|
311
|
+
const postVerdict = await judgeComposerState(
|
|
312
|
+
{ capturedText: afterText, promptPrefix },
|
|
313
|
+
jevOptions,
|
|
314
|
+
);
|
|
315
|
+
if (postVerdict?.state === "submitted_running") {
|
|
316
|
+
return { submitted: true, jevState: postVerdict.state };
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
throw err;
|
|
320
|
+
}
|
|
321
|
+
const capturedText =
|
|
322
|
+
typeof capture === "function" ? String(capture() ?? "") : "";
|
|
323
|
+
const verdict = await judgeComposerState(
|
|
324
|
+
{ capturedText, promptPrefix },
|
|
325
|
+
jevOptions,
|
|
326
|
+
);
|
|
327
|
+
if (
|
|
328
|
+
verdict?.state === "stuck_in_composer" &&
|
|
329
|
+
typeof sendEnter === "function"
|
|
330
|
+
) {
|
|
331
|
+
sendEnter();
|
|
332
|
+
sleep(400);
|
|
333
|
+
const afterText =
|
|
334
|
+
typeof capture === "function" ? String(capture() ?? "") : "";
|
|
335
|
+
if (isConfirmedSubmitted(afterText)) {
|
|
336
|
+
return { submitted: true, jevState: "submitted_running" };
|
|
337
|
+
}
|
|
338
|
+
const postVerdict = await judgeComposerState(
|
|
339
|
+
{ capturedText: afterText, promptPrefix },
|
|
340
|
+
jevOptions,
|
|
341
|
+
);
|
|
342
|
+
return {
|
|
343
|
+
submitted: postVerdict?.state === "submitted_running",
|
|
344
|
+
jevState: postVerdict?.state || verdict.state,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
submitted: verdict?.state !== "modal_blocking",
|
|
349
|
+
jevState: verdict?.state || null,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
254
353
|
function confirmSubmit(target, prompt, cli, sendEnter) {
|
|
255
354
|
submitPromptWithVerification({
|
|
256
355
|
cli,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// headless 세션 결과 색인. 화면의 HEADLESS_COMPLETE 문자열 대신 읽도록 세션당 JSON 하나를 둔다 (ADR-0034).
|
|
2
2
|
import { renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { judgeInstructionAck } from "@triflux/core/hub/lib/jev-addon.mjs";
|
|
4
5
|
|
|
5
6
|
export const RESULTS_INDEX_VERSION = 1;
|
|
6
7
|
|
|
@@ -34,6 +35,9 @@ export function createResultsIndex(path, sessionName, workers, canWrite) {
|
|
|
34
35
|
startedAt: null,
|
|
35
36
|
finishedAt: null,
|
|
36
37
|
resultFile: w.resultFile,
|
|
38
|
+
ackStatus: null,
|
|
39
|
+
semanticState: null,
|
|
40
|
+
ackConfidence: null,
|
|
37
41
|
})),
|
|
38
42
|
};
|
|
39
43
|
const byPane = new Map(index.workers.map((w) => [w.paneName, w]));
|
|
@@ -88,5 +92,36 @@ export function createResultsIndex(path, sessionName, workers, canWrite) {
|
|
|
88
92
|
index.completed = Boolean(completed);
|
|
89
93
|
flush();
|
|
90
94
|
},
|
|
95
|
+
recordInstructionAck(
|
|
96
|
+
paneName,
|
|
97
|
+
{ ackStatus = null, semanticState = null, confidence = null } = {},
|
|
98
|
+
) {
|
|
99
|
+
const w = byPane.get(paneName);
|
|
100
|
+
if (!w) return null;
|
|
101
|
+
w.ackStatus = ackStatus;
|
|
102
|
+
w.semanticState = semanticState;
|
|
103
|
+
w.ackConfidence = confidence;
|
|
104
|
+
flush();
|
|
105
|
+
return w;
|
|
106
|
+
},
|
|
107
|
+
async verifyInstructionAckWithJev(
|
|
108
|
+
paneName,
|
|
109
|
+
{ instruction = "", recentOutput = "" } = {},
|
|
110
|
+
jevOptions = {},
|
|
111
|
+
) {
|
|
112
|
+
const verdict = await judgeInstructionAck(
|
|
113
|
+
{ instruction, recentOutput },
|
|
114
|
+
jevOptions,
|
|
115
|
+
);
|
|
116
|
+
if (!verdict) return null;
|
|
117
|
+
this.recordInstructionAck(paneName, {
|
|
118
|
+
ackStatus: verdict.ackStatus,
|
|
119
|
+
semanticState: verdict.acknowledged
|
|
120
|
+
? "following_instruction"
|
|
121
|
+
: "unverified_instruction",
|
|
122
|
+
confidence: verdict.confidence,
|
|
123
|
+
});
|
|
124
|
+
return verdict;
|
|
125
|
+
},
|
|
91
126
|
};
|
|
92
127
|
}
|
|
@@ -24,7 +24,14 @@ import {
|
|
|
24
24
|
import { dirname, join } from "node:path";
|
|
25
25
|
|
|
26
26
|
import { normalizeCodexProfileName } from "../../scripts/lib/codex-profile-config.mjs";
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
EFFORT_ORDER,
|
|
29
|
+
modelEffortFromProfile,
|
|
30
|
+
profileFromModelEffort,
|
|
31
|
+
resolveCodexModelEffort,
|
|
32
|
+
} from "../../scripts/lib/model-effort.mjs";
|
|
33
|
+
import { AGY_FAMILY_PROFILES } from "../../scripts/lib/model-family-resolver.mjs";
|
|
34
|
+
import { judgeSameFailureCause } from "@triflux/core/hub/lib/jev-addon.mjs";
|
|
28
35
|
|
|
29
36
|
export const STATES = Object.freeze({
|
|
30
37
|
PLANNING: "PLANNING",
|
|
@@ -50,7 +57,8 @@ const DEFAULT_CLAUDE_MODEL = "fable";
|
|
|
50
57
|
const DEFAULT_ESCALATION_CHAIN = Object.freeze([
|
|
51
58
|
Object.freeze({
|
|
52
59
|
cli: "codex",
|
|
53
|
-
|
|
60
|
+
family: "astra",
|
|
61
|
+
effort: "max",
|
|
54
62
|
}),
|
|
55
63
|
Object.freeze({ cli: "claude", model: DEFAULT_CLAUDE_MODEL }),
|
|
56
64
|
]);
|
|
@@ -119,10 +127,14 @@ export function createRetryStateMachine(options = {}) {
|
|
|
119
127
|
return transition(STATES.DONE);
|
|
120
128
|
}
|
|
121
129
|
|
|
122
|
-
function reportVerifyFail(failureReason) {
|
|
130
|
+
function reportVerifyFail(failureReason, failOptions = {}) {
|
|
123
131
|
const reason = String(failureReason || "unknown");
|
|
124
|
-
|
|
132
|
+
const isSameCause =
|
|
133
|
+
reason === state.lastFailureReason ||
|
|
134
|
+
(Boolean(state.lastFailureReason) && failOptions.sameCause === true);
|
|
135
|
+
if (isSameCause) {
|
|
125
136
|
state.stuckCounter += 1;
|
|
137
|
+
state.lastFailureReason = reason;
|
|
126
138
|
} else {
|
|
127
139
|
state.stuckCounter = 1;
|
|
128
140
|
state.lastFailureReason = reason;
|
|
@@ -148,6 +160,20 @@ export function createRetryStateMachine(options = {}) {
|
|
|
148
160
|
return transition(STATES.DIAGNOSING, { reason });
|
|
149
161
|
}
|
|
150
162
|
|
|
163
|
+
async function reportVerifyFailAsync(failureReason, jevOptions = {}) {
|
|
164
|
+
const reason = String(failureReason || "unknown");
|
|
165
|
+
let sameCause = false;
|
|
166
|
+
if (state.lastFailureReason && reason !== state.lastFailureReason) {
|
|
167
|
+
const verdict = await judgeSameFailureCause(
|
|
168
|
+
state.lastFailureReason,
|
|
169
|
+
reason,
|
|
170
|
+
jevOptions,
|
|
171
|
+
);
|
|
172
|
+
sameCause = verdict?.sameCause === true;
|
|
173
|
+
}
|
|
174
|
+
return reportVerifyFail(reason, { sameCause });
|
|
175
|
+
}
|
|
176
|
+
|
|
151
177
|
function escalate() {
|
|
152
178
|
if (state.cliIndex + 1 >= state.cliChain.length) {
|
|
153
179
|
return transition(STATES.BUDGET_EXCEEDED, {
|
|
@@ -155,6 +181,39 @@ export function createRetryStateMachine(options = {}) {
|
|
|
155
181
|
chain: state.cliChain,
|
|
156
182
|
});
|
|
157
183
|
}
|
|
184
|
+
const current = state.cliChain[state.cliIndex];
|
|
185
|
+
const next = state.cliChain[state.cliIndex + 1];
|
|
186
|
+
if (
|
|
187
|
+
current.cli === "codex" &&
|
|
188
|
+
next.cli === "codex" &&
|
|
189
|
+
current.family &&
|
|
190
|
+
next.family
|
|
191
|
+
) {
|
|
192
|
+
try {
|
|
193
|
+
const base = resolveCodexModelEffort(current, options);
|
|
194
|
+
const promoted = resolveCodexModelEffort(next, options);
|
|
195
|
+
if (
|
|
196
|
+
promoted.effort !== next.effort &&
|
|
197
|
+
base.model === promoted.model &&
|
|
198
|
+
base.effort === promoted.effort
|
|
199
|
+
) {
|
|
200
|
+
return transition(STATES.BUDGET_EXCEEDED, {
|
|
201
|
+
reason: "escalation-not-promoted",
|
|
202
|
+
escalated: false,
|
|
203
|
+
model: promoted.model,
|
|
204
|
+
requestedEffort: next.effort,
|
|
205
|
+
effort: promoted.effort,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if (error.code !== "unsupported-effort") throw error;
|
|
210
|
+
return transition(STATES.BUDGET_EXCEEDED, {
|
|
211
|
+
reason: "unsupported-escalation-effort",
|
|
212
|
+
escalated: false,
|
|
213
|
+
error: error.message,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
158
217
|
state.cliIndex += 1;
|
|
159
218
|
state.iterations = 0;
|
|
160
219
|
state.stuckCounter = 0;
|
|
@@ -231,6 +290,7 @@ export function createRetryStateMachine(options = {}) {
|
|
|
231
290
|
startIteration,
|
|
232
291
|
reportVerifySuccess,
|
|
233
292
|
reportVerifyFail,
|
|
293
|
+
reportVerifyFailAsync,
|
|
234
294
|
escalate,
|
|
235
295
|
on,
|
|
236
296
|
serialize,
|
|
@@ -306,11 +366,34 @@ function normalizeEscalationChainEntry(entry, index, source) {
|
|
|
306
366
|
}
|
|
307
367
|
const profile = typeof entry.profile === "string" ? entry.profile.trim() : "";
|
|
308
368
|
const normalizedProfile = normalizeEscalationProfile(profile);
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
369
|
+
const provider = entry.cli === "agy" ? "antigravity" : entry.cli;
|
|
370
|
+
if (provider === "codex" || provider === "antigravity") {
|
|
371
|
+
const explicit = entry.family !== undefined || entry.effort !== undefined;
|
|
372
|
+
let spec = explicit
|
|
373
|
+
? typeof entry.family === "string" && typeof entry.effort === "string"
|
|
374
|
+
? modelEffortFromProfile(provider, entry.family, entry.effort)
|
|
375
|
+
: null
|
|
376
|
+
: modelEffortFromProfile(
|
|
377
|
+
provider,
|
|
378
|
+
provider === "codex" ? normalizedProfile : profile,
|
|
379
|
+
);
|
|
380
|
+
if (explicit && !spec)
|
|
381
|
+
throw new Error(`${source}: chain[${index}] has invalid family/effort`);
|
|
382
|
+
if (spec) {
|
|
383
|
+
if (provider === "codex" && spec.effort === "ultra")
|
|
384
|
+
spec = { family: "astra", effort: "max" };
|
|
385
|
+
const legacy = profileFromModelEffort(provider, spec);
|
|
386
|
+
const normalized = { cli: entry.cli, ...spec };
|
|
387
|
+
if (legacy) normalized.profile = legacy;
|
|
388
|
+
if (provider === "antigravity" && legacy) {
|
|
389
|
+
normalized.model = entry.model || AGY_FAMILY_PROFILES[legacy]?.model;
|
|
390
|
+
if (typeof normalized.model !== "string" || !normalized.model.trim())
|
|
391
|
+
throw new Error(
|
|
392
|
+
`${source}: chain[${index}].model must be a non-empty string`,
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
return normalized;
|
|
396
|
+
}
|
|
314
397
|
}
|
|
315
398
|
if (typeof entry.model !== "string" || entry.model.trim() === "") {
|
|
316
399
|
throw new Error(
|
|
@@ -319,6 +402,11 @@ function normalizeEscalationChainEntry(entry, index, source) {
|
|
|
319
402
|
}
|
|
320
403
|
const normalized = { cli: entry.cli, model: entry.model };
|
|
321
404
|
if (profile) normalized.profile = profile;
|
|
405
|
+
if (entry.effort !== undefined) {
|
|
406
|
+
if (!EFFORT_ORDER.includes(entry.effort))
|
|
407
|
+
throw new Error(`${source}: chain[${index}] has invalid effort`);
|
|
408
|
+
normalized.effort = entry.effort;
|
|
409
|
+
}
|
|
322
410
|
return normalized;
|
|
323
411
|
}
|
|
324
412
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { realpath } from "node:fs/promises";
|
|
2
2
|
import { resolve as pathResolve } from "node:path";
|
|
3
|
+
import { judgeBusyAction } from "@triflux/core/hub/lib/jev-addon.mjs";
|
|
3
4
|
import {
|
|
4
5
|
createActivityLifecycle,
|
|
5
6
|
isActivityLifecycleEnabled,
|
|
@@ -299,12 +300,13 @@ export async function askCodexAppServerThread({
|
|
|
299
300
|
noWait = false,
|
|
300
301
|
warnContextPct,
|
|
301
302
|
maxContextPct,
|
|
303
|
+
jevOptions,
|
|
302
304
|
clientFactory = (opts) => new JsonRpcWsUdsClient(opts),
|
|
303
305
|
} = {}) {
|
|
304
306
|
if (typeof prompt !== "string" || !prompt.trim())
|
|
305
307
|
throw new Error("codex UDS ask requires a non-empty prompt");
|
|
306
308
|
if (!threadId) throw new Error("codex UDS ask requires --thread <id|auto>");
|
|
307
|
-
if (!["wait", "fail", "steer"].includes(ifBusy))
|
|
309
|
+
if (!["wait", "fail", "steer", "auto"].includes(ifBusy))
|
|
308
310
|
throw new Error(`invalid --if-busy: ${ifBusy}`);
|
|
309
311
|
const client = codexClient(
|
|
310
312
|
socketPath,
|
|
@@ -327,11 +329,13 @@ export async function askCodexAppServerThread({
|
|
|
327
329
|
throw new Error(
|
|
328
330
|
"no loaded Codex threads; open the TUI and send its first message before using --thread auto",
|
|
329
331
|
);
|
|
330
|
-
if (candidates.length
|
|
332
|
+
if (candidates.length === 1) {
|
|
333
|
+
selectedId = candidates[0].threadId;
|
|
334
|
+
} else {
|
|
331
335
|
throw new Error(
|
|
332
336
|
`multiple loaded Codex threads; pass --thread ID: ${JSON.stringify(candidates)}`,
|
|
333
337
|
);
|
|
334
|
-
|
|
338
|
+
}
|
|
335
339
|
}
|
|
336
340
|
const busyStarted = Date.now();
|
|
337
341
|
let thread = await readCodexThread(
|
|
@@ -340,14 +344,30 @@ export async function askCodexAppServerThread({
|
|
|
340
344
|
DEFAULT_CODEX_APP_SERVER_UDS_BOOTSTRAP_MS,
|
|
341
345
|
);
|
|
342
346
|
const initiallyBusy = thread.status?.type === "active";
|
|
343
|
-
|
|
347
|
+
let effectiveIfBusy = ifBusy;
|
|
348
|
+
if (initiallyBusy && ifBusy === "auto") {
|
|
349
|
+
const latestTurnSummary =
|
|
350
|
+
thread.turns
|
|
351
|
+
?.slice(-2)
|
|
352
|
+
.map((t) => JSON.stringify(t))
|
|
353
|
+
.join("\n")
|
|
354
|
+
.slice(0, 800) || "";
|
|
355
|
+
const verdict = await judgeBusyAction(
|
|
356
|
+
{ prompt, threadSummary: latestTurnSummary },
|
|
357
|
+
jevOptions,
|
|
358
|
+
);
|
|
359
|
+
effectiveIfBusy = verdict?.action ?? "wait";
|
|
360
|
+
} else if (ifBusy === "auto") {
|
|
361
|
+
effectiveIfBusy = "wait";
|
|
362
|
+
}
|
|
363
|
+
if (thread.status?.type === "active" && effectiveIfBusy === "fail")
|
|
344
364
|
throw new Error(`target busy (--if-busy fail): ${selectedId}`);
|
|
345
365
|
const busyDeadline = busyStarted + busyTimeoutMs;
|
|
346
366
|
const busyError = () =>
|
|
347
367
|
new Error(
|
|
348
368
|
`target busy after ${Math.ceil(busyTimeoutMs / 1000)}s (--if-busy wait)`,
|
|
349
369
|
);
|
|
350
|
-
while (thread.status?.type === "active" &&
|
|
370
|
+
while (thread.status?.type === "active" && effectiveIfBusy === "wait") {
|
|
351
371
|
const waitMs = Math.min(
|
|
352
372
|
pollIntervalMs,
|
|
353
373
|
Math.max(0, busyDeadline - Date.now()),
|
|
@@ -373,7 +393,9 @@ export async function askCodexAppServerThread({
|
|
|
373
393
|
throw busyError();
|
|
374
394
|
}
|
|
375
395
|
const busyWaitedMs =
|
|
376
|
-
|
|
396
|
+
effectiveIfBusy === "wait" && initiallyBusy
|
|
397
|
+
? Date.now() - busyStarted
|
|
398
|
+
: 0;
|
|
377
399
|
const resumed = await client.request(
|
|
378
400
|
"thread/resume",
|
|
379
401
|
{ threadId: selectedId, excludeTurns: true },
|
|
@@ -405,7 +427,7 @@ export async function askCodexAppServerThread({
|
|
|
405
427
|
else if (!resumed?.thread && thread.status?.type === "active")
|
|
406
428
|
activeTurns = thread.turns;
|
|
407
429
|
const expectedTurnId =
|
|
408
|
-
|
|
430
|
+
effectiveIfBusy === "steer"
|
|
409
431
|
? activeTurns?.findLast((turn) => turn.status === "inProgress")?.id
|
|
410
432
|
: undefined;
|
|
411
433
|
const turn = await runCodexAppServerTurn({
|
|
@@ -434,7 +456,7 @@ export async function askCodexAppServerThread({
|
|
|
434
456
|
status: turn.status,
|
|
435
457
|
commentary: turn.commentary,
|
|
436
458
|
meta: { retries: turn.retries },
|
|
437
|
-
ifBusy,
|
|
459
|
+
ifBusy: effectiveIfBusy,
|
|
438
460
|
busyWaitedMs,
|
|
439
461
|
steered: turn.steered,
|
|
440
462
|
socketPath,
|
package/package.json
CHANGED
|
@@ -9,130 +9,134 @@ import {
|
|
|
9
9
|
resolveCodexProfileConfigValues,
|
|
10
10
|
} from "./codex-profile-config.mjs";
|
|
11
11
|
import { resolveGeminiProfileForPurpose } from "./gemini-profiles.mjs";
|
|
12
|
+
import {
|
|
13
|
+
modelEffortFromProfile,
|
|
14
|
+
profileFromModelEffort,
|
|
15
|
+
} from "./model-effort.mjs";
|
|
12
16
|
|
|
13
17
|
const policy = {
|
|
14
18
|
executor: {
|
|
15
|
-
|
|
19
|
+
codex: { family: "sol", effort: "high" },
|
|
16
20
|
timeoutSec: 1080,
|
|
17
21
|
mcpHint: "implement",
|
|
18
22
|
subcommand: "exec",
|
|
19
23
|
},
|
|
20
24
|
codex: {
|
|
21
|
-
|
|
25
|
+
codex: { family: "sol", effort: "high" },
|
|
22
26
|
timeoutSec: 1080,
|
|
23
27
|
mcpHint: "implement",
|
|
24
28
|
subcommand: "exec",
|
|
25
29
|
},
|
|
26
30
|
"build-fixer": {
|
|
27
|
-
|
|
31
|
+
codex: { family: "luna", effort: "high" },
|
|
28
32
|
timeoutSec: 540,
|
|
29
33
|
mcpHint: "implement",
|
|
30
34
|
subcommand: "exec",
|
|
31
35
|
},
|
|
32
36
|
cleanup: {
|
|
33
|
-
|
|
37
|
+
codex: { family: "sol", effort: "medium" },
|
|
34
38
|
timeoutSec: 540,
|
|
35
39
|
mcpHint: "implement",
|
|
36
40
|
subcommand: "exec",
|
|
37
41
|
},
|
|
38
42
|
deslop: {
|
|
39
|
-
|
|
43
|
+
codex: { family: "sol", effort: "medium" },
|
|
40
44
|
timeoutSec: 540,
|
|
41
45
|
mcpHint: "implement",
|
|
42
46
|
subcommand: "exec",
|
|
43
47
|
},
|
|
44
48
|
debugger: {
|
|
45
|
-
|
|
49
|
+
codex: { family: "astra", effort: "xhigh" },
|
|
46
50
|
timeoutSec: 900,
|
|
47
51
|
mcpHint: "implement",
|
|
48
52
|
subcommand: "exec",
|
|
49
53
|
},
|
|
50
54
|
"deep-executor": {
|
|
51
|
-
|
|
55
|
+
codex: { family: "astra", effort: "xhigh" },
|
|
52
56
|
timeoutSec: 3600,
|
|
53
57
|
mcpHint: "implement",
|
|
54
58
|
subcommand: "exec",
|
|
55
59
|
},
|
|
56
60
|
architect: {
|
|
57
|
-
|
|
61
|
+
codex: { family: "astra", effort: "xhigh" },
|
|
58
62
|
timeoutSec: 3600,
|
|
59
63
|
mcpHint: "analyze",
|
|
60
64
|
subcommand: "exec",
|
|
61
65
|
},
|
|
62
66
|
critic: {
|
|
63
|
-
|
|
67
|
+
codex: { family: "astra", effort: "xhigh" },
|
|
64
68
|
timeoutSec: 3600,
|
|
65
69
|
mcpHint: "analyze",
|
|
66
70
|
subcommand: "exec",
|
|
67
71
|
},
|
|
68
72
|
planner: {
|
|
69
|
-
|
|
73
|
+
codex: { family: "astra", effort: "xhigh" },
|
|
70
74
|
timeoutSec: 3600,
|
|
71
75
|
mcpHint: "analyze",
|
|
72
76
|
subcommand: "exec",
|
|
73
77
|
},
|
|
74
78
|
analyst: {
|
|
75
|
-
|
|
79
|
+
codex: { family: "astra", effort: "xhigh" },
|
|
76
80
|
timeoutSec: 3600,
|
|
77
81
|
mcpHint: "analyze",
|
|
78
82
|
subcommand: "exec",
|
|
79
83
|
},
|
|
80
84
|
"code-reviewer": {
|
|
81
|
-
|
|
85
|
+
codex: { family: "sol", effort: "high" },
|
|
82
86
|
timeoutSec: 1800,
|
|
83
87
|
mcpHint: "review",
|
|
84
88
|
subcommand: "review",
|
|
85
89
|
},
|
|
86
90
|
"quality-reviewer": {
|
|
87
|
-
|
|
91
|
+
codex: { family: "sol", effort: "high" },
|
|
88
92
|
timeoutSec: 1800,
|
|
89
93
|
mcpHint: "review",
|
|
90
94
|
subcommand: "review",
|
|
91
95
|
},
|
|
92
96
|
"security-reviewer": {
|
|
93
|
-
|
|
97
|
+
codex: { family: "astra", effort: "xhigh" },
|
|
94
98
|
timeoutSec: 1800,
|
|
95
99
|
mcpHint: "review",
|
|
96
100
|
subcommand: "review",
|
|
97
101
|
},
|
|
98
102
|
scientist: {
|
|
99
|
-
|
|
103
|
+
codex: { family: "sol", effort: "high" },
|
|
100
104
|
timeoutSec: 1440,
|
|
101
105
|
mcpHint: "analyze",
|
|
102
106
|
subcommand: "exec",
|
|
103
107
|
},
|
|
104
108
|
"document-specialist": {
|
|
105
|
-
|
|
109
|
+
codex: { family: "terra", effort: "high" },
|
|
106
110
|
timeoutSec: 1440,
|
|
107
111
|
mcpHint: "analyze",
|
|
108
112
|
subcommand: "exec",
|
|
109
113
|
},
|
|
110
114
|
"scientist-deep": {
|
|
111
|
-
|
|
115
|
+
codex: { family: "astra", effort: "xhigh" },
|
|
112
116
|
timeoutSec: 3600,
|
|
113
117
|
mcpHint: "analyze",
|
|
114
118
|
subcommand: "exec",
|
|
115
119
|
},
|
|
116
120
|
verifier: {
|
|
117
|
-
|
|
121
|
+
codex: { family: "sol", effort: "high" },
|
|
118
122
|
timeoutSec: 1200,
|
|
119
123
|
mcpHint: "review",
|
|
120
124
|
subcommand: "review",
|
|
121
125
|
},
|
|
122
126
|
"test-engineer": {
|
|
123
|
-
|
|
127
|
+
codex: { family: "sol", effort: "high" },
|
|
124
128
|
timeoutSec: 1200,
|
|
125
129
|
mcpHint: "implement",
|
|
126
130
|
subcommand: "exec",
|
|
127
131
|
},
|
|
128
132
|
"qa-tester": {
|
|
129
|
-
|
|
133
|
+
codex: { family: "sol", effort: "high" },
|
|
130
134
|
timeoutSec: 1200,
|
|
131
135
|
mcpHint: "review",
|
|
132
136
|
subcommand: "review",
|
|
133
137
|
},
|
|
134
138
|
spark: {
|
|
135
|
-
|
|
139
|
+
codex: { family: "luna", effort: "low" },
|
|
136
140
|
timeoutSec: 180,
|
|
137
141
|
mcpHint: "implement",
|
|
138
142
|
subcommand: "exec",
|
|
@@ -140,13 +144,13 @@ const policy = {
|
|
|
140
144
|
// These entries are only for explicit direct-Codex overrides. agent-map.json
|
|
141
145
|
// remains the provider-selection authority for their normal execution.
|
|
142
146
|
designer: {
|
|
143
|
-
|
|
147
|
+
codex: { family: "astra", effort: "xhigh" },
|
|
144
148
|
timeoutSec: 900,
|
|
145
149
|
mcpHint: "implement",
|
|
146
150
|
subcommand: "exec",
|
|
147
151
|
},
|
|
148
152
|
writer: {
|
|
149
|
-
|
|
153
|
+
codex: { family: "terra", effort: "high" },
|
|
150
154
|
timeoutSec: 900,
|
|
151
155
|
mcpHint: "implement",
|
|
152
156
|
subcommand: "exec",
|
|
@@ -154,7 +158,11 @@ const policy = {
|
|
|
154
158
|
};
|
|
155
159
|
|
|
156
160
|
function freezePolicy(entry) {
|
|
157
|
-
return Object.freeze({
|
|
161
|
+
return Object.freeze({
|
|
162
|
+
...entry,
|
|
163
|
+
codex: Object.freeze({ ...entry.codex }),
|
|
164
|
+
profile: profileFromModelEffort("codex", entry.codex),
|
|
165
|
+
});
|
|
158
166
|
}
|
|
159
167
|
|
|
160
168
|
const CODEX_AGENT_POLICY = Object.freeze(
|
|
@@ -264,16 +272,16 @@ export function resolveNestedCodexAgentProfile(
|
|
|
264
272
|
: NESTED_GLOBAL_PROFILE_OVERRIDES.has(globalOverride)
|
|
265
273
|
? globalOverride
|
|
266
274
|
: "auto";
|
|
267
|
-
const fallback = resolveCodexAgentProfile(agent);
|
|
268
275
|
const candidate = resolveCodexAgentProfile(agent, {
|
|
269
276
|
profileOverride: effectiveOverride,
|
|
270
277
|
retryProfile,
|
|
271
278
|
nested: true,
|
|
272
279
|
allowCustom: true,
|
|
273
280
|
});
|
|
274
|
-
const { effort } =
|
|
275
|
-
|
|
276
|
-
|
|
281
|
+
const { effort } =
|
|
282
|
+
modelEffortFromProfile("codex", candidate) ||
|
|
283
|
+
resolveCodexProfileConfigValues(candidate, { codexHome });
|
|
284
|
+
return effort?.toLowerCase() === "ultra" ? "astra_max" : candidate;
|
|
277
285
|
}
|
|
278
286
|
|
|
279
287
|
const EXPECTED_DURATION_BY_AGENT = Object.freeze({
|
|
@@ -365,6 +373,7 @@ export function resolveAgentRoute(
|
|
|
365
373
|
);
|
|
366
374
|
}
|
|
367
375
|
}
|
|
376
|
+
const { family, effort } = modelEffortFromProfile("codex", resolvedOverride);
|
|
368
377
|
return [
|
|
369
378
|
provider,
|
|
370
379
|
policy.profile,
|
|
@@ -378,6 +387,8 @@ export function resolveAgentRoute(
|
|
|
378
387
|
NATIVE_CODEX_AGENTS.has(agent),
|
|
379
388
|
byAuthor ? reviewAuthor : "",
|
|
380
389
|
usageSwitch,
|
|
390
|
+
effort,
|
|
391
|
+
family,
|
|
381
392
|
];
|
|
382
393
|
}
|
|
383
394
|
|