mixdog 0.9.38 → 0.9.39
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/package.json +9 -4
- package/scripts/abort-recovery-test.mjs +43 -2
- package/scripts/agent-tag-reuse-smoke.mjs +146 -6
- package/scripts/agent-terminal-reap-test.mjs +127 -0
- package/scripts/agent-trace-io-test.mjs +69 -0
- package/scripts/code-graph-disk-hit-test.mjs +224 -0
- package/scripts/execution-completion-dedup-test.mjs +157 -0
- package/scripts/execution-pending-resume-kick-test.mjs +57 -2
- package/scripts/execution-resume-esc-integration-test.mjs +174 -0
- package/scripts/explore-bench.mjs +38 -2
- package/scripts/explore-prompt-policy-test.mjs +152 -11
- package/scripts/find-fuzzy-hidden-test.mjs +122 -0
- package/scripts/internal-comms-bench-test.mjs +226 -0
- package/scripts/internal-comms-bench.mjs +185 -58
- package/scripts/internal-comms-smoke.mjs +171 -23
- package/scripts/live-worker-smoke.mjs +38 -2
- package/scripts/memory-cycle-routing-test.mjs +111 -0
- package/scripts/memory-rule-contract-test.mjs +93 -0
- package/scripts/output-style-smoke.mjs +2 -2
- package/scripts/rg-runner-test.mjs +240 -0
- package/scripts/routing-corpus-test.mjs +349 -0
- package/scripts/routing-corpus.mjs +211 -32
- package/scripts/session-orphan-sweep-test.mjs +83 -0
- package/scripts/steering-drain-buckets-test.mjs +179 -0
- package/scripts/tool-smoke.mjs +21 -13
- package/scripts/tool-tui-presentation-test.mjs +202 -0
- package/src/agents/heavy-worker/AGENT.md +10 -7
- package/src/agents/reviewer/AGENT.md +6 -4
- package/src/agents/worker/AGENT.md +7 -5
- package/src/rules/agent/00-common.md +4 -4
- package/src/rules/agent/00-core.md +11 -14
- package/src/rules/agent/20-skip-protocol.md +3 -3
- package/src/rules/agent/30-explorer.md +50 -60
- package/src/rules/agent/40-cycle1-agent.md +15 -24
- package/src/rules/agent/41-cycle2-agent.md +33 -57
- package/src/rules/agent/42-cycle3-agent.md +28 -42
- package/src/rules/lead/01-general.md +7 -10
- package/src/rules/lead/lead-brief.md +11 -14
- package/src/rules/lead/lead-tool.md +6 -5
- package/src/rules/shared/01-tool.md +44 -45
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +37 -1
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +20 -5
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +21 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +17 -38
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +17 -8
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +270 -78
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +123 -0
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +15 -2
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +41 -2
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store.mjs +223 -42
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +25 -20
- package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +118 -53
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +106 -29
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +8 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +33 -2
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +151 -64
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +37 -13
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +84 -49
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +172 -23
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +3 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +68 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +17 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +24 -10
- package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +4 -7
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +1 -0
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +4 -4
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +4 -3
- package/src/runtime/memory/lib/memory-cycle3.mjs +12 -2
- package/src/runtime/shared/tool-primitives.mjs +4 -1
- package/src/runtime/shared/tool-status.mjs +27 -0
- package/src/runtime/shared/tool-surface.mjs +6 -3
- package/src/session-runtime/config-helpers.mjs +14 -0
- package/src/session-runtime/context-status.mjs +1 -0
- package/src/session-runtime/effort.mjs +6 -2
- package/src/session-runtime/model-recency.mjs +5 -2
- package/src/session-runtime/runtime-core.mjs +35 -2
- package/src/session-runtime/tool-catalog.mjs +34 -0
- package/src/standalone/agent-tool/notify.mjs +13 -0
- package/src/standalone/agent-tool.mjs +45 -69
- package/src/standalone/explore-tool.mjs +6 -7
- package/src/tui/App.jsx +31 -0
- package/src/tui/app/model-options.mjs +5 -3
- package/src/tui/app/model-picker.mjs +12 -24
- package/src/tui/app/transcript-window.mjs +1 -0
- package/src/tui/components/ToolExecution.jsx +11 -6
- package/src/tui/components/TranscriptItem.jsx +1 -1
- package/src/tui/components/tool-execution/surface-detail.mjs +24 -10
- package/src/tui/components/tool-execution/text-format.mjs +10 -19
- package/src/tui/dist/index.mjs +517 -142
- package/src/tui/engine/agent-job-feed.mjs +144 -17
- package/src/tui/engine/agent-response-tail.mjs +68 -0
- package/src/tui/engine/notification-plan.mjs +16 -0
- package/src/tui/engine/session-api.mjs +8 -2
- package/src/tui/engine/session-flow.mjs +19 -1
- package/src/tui/engine/tool-card-results.mjs +54 -32
- package/src/tui/engine/tool-result-status.mjs +75 -21
- package/src/tui/engine/turn.mjs +77 -42
- package/src/tui/engine.mjs +63 -2
- package/src/workflows/bench/WORKFLOW.md +25 -35
- package/src/workflows/default/WORKFLOW.md +38 -32
- package/src/workflows/solo/WORKFLOW.md +19 -22
- package/scripts/_jitter-fuzz.mjs +0 -44410
- package/scripts/_jitter-fuzz2.mjs +0 -44400
- package/scripts/_jitter-probe.mjs +0 -44397
- package/scripts/_jp2.mjs +0 -45614
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
2
|
import { countJsonNextCalls } from './tools/next-call-utils.mjs';
|
|
3
|
+
import { splitGrepLinePrefix } from './tools/builtin/grep-formatting.mjs';
|
|
3
4
|
import {
|
|
4
5
|
appendAgentTrace,
|
|
5
6
|
normalizeSessionId,
|
|
@@ -108,7 +109,7 @@ const TOOL_ARG_KEYS = {
|
|
|
108
109
|
recall: ['query', 'limit', 'session_id', 'cwd'],
|
|
109
110
|
search: ['query', 'limit', 'cwd'],
|
|
110
111
|
explore: ['query', 'queries', 'limit', 'cwd'],
|
|
111
|
-
code_graph: ['mode', 'file', 'symbol', 'symbols', 'body', 'language', 'limit', 'depth', 'page', 'cwd'],
|
|
112
|
+
code_graph: ['mode', 'file', 'files', 'symbol', 'symbols', 'body', 'language', 'limit', 'depth', 'page', 'cwd'],
|
|
112
113
|
shell: ['command', 'cwd', 'timeout', 'mode', 'run_in_background', 'persistent', 'session_id'],
|
|
113
114
|
task: ['task_id', 'action', 'timeout_ms', 'poll_ms'],
|
|
114
115
|
edit: ['path', 'replace_all', 'edits'],
|
|
@@ -211,6 +212,38 @@ function _redactLogText(text) {
|
|
|
211
212
|
return out;
|
|
212
213
|
}
|
|
213
214
|
|
|
215
|
+
const GREP_COVERAGE_MAX = 512;
|
|
216
|
+
export function parseGrepCoverage(resultText, toolName, toolArgs, resultKind) {
|
|
217
|
+
if (toolName !== 'grep' || resultKind === 'error' || (toolArgs?.output_mode && toolArgs.output_mode !== 'content_with_context')) return null;
|
|
218
|
+
const out = [];
|
|
219
|
+
const seen = new Set();
|
|
220
|
+
let sectionPath = null;
|
|
221
|
+
for (const line of String(resultText ?? '').split(/\r?\n/)) {
|
|
222
|
+
const section = line.match(/^# grep (.+)$/);
|
|
223
|
+
if (section) {
|
|
224
|
+
if (!section[1].startsWith('pattern:')) sectionPath = section[1];
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const split = splitGrepLinePrefix(line);
|
|
228
|
+
const omitted = !split && typeof toolArgs?.path === 'string'
|
|
229
|
+
? String(line).match(/^(\d+)(?::|-)/)
|
|
230
|
+
: null;
|
|
231
|
+
const sectionOmitted = !split && sectionPath
|
|
232
|
+
? String(line).match(/^(\d+)(?::|-)/)
|
|
233
|
+
: null;
|
|
234
|
+
const path = split?.path || (omitted ? toolArgs.path : null) || (sectionOmitted ? sectionPath : null);
|
|
235
|
+
const lineNo = split?.lineNo || (omitted ? Number(omitted[1]) : null)
|
|
236
|
+
|| (sectionOmitted ? Number(sectionOmitted[1]) : null);
|
|
237
|
+
if (!path || !Number.isInteger(lineNo) || lineNo < 1) continue;
|
|
238
|
+
const key = `${path}\0${lineNo}`;
|
|
239
|
+
if (seen.has(key)) continue;
|
|
240
|
+
seen.add(key);
|
|
241
|
+
out.push({ path: String(path).replace(/\\/g, '/'), line: lineNo });
|
|
242
|
+
if (out.length >= GREP_COVERAGE_MAX) break;
|
|
243
|
+
}
|
|
244
|
+
return out.length ? out : null;
|
|
245
|
+
}
|
|
246
|
+
|
|
214
247
|
function classifyToolFailure(resultText, toolName) {
|
|
215
248
|
const text = String(resultText ?? '').toLowerCase();
|
|
216
249
|
if (/\[shell-tool-failed\]/i.test(String(resultText ?? ''))) return 'tool-call/failure';
|
|
@@ -264,6 +297,7 @@ function traceAgentTool({ sessionId, iteration, toolName, toolKind, toolMs, tool
|
|
|
264
297
|
const resultLinesEst = typeof resultText === 'string' && resultText.length > 0 ? resultText.split('\n').length : 0;
|
|
265
298
|
const numericToolMs = Number(toolMs);
|
|
266
299
|
const summarizedArgs = summarizeToolArgs(toolName, toolArgs);
|
|
300
|
+
const grepCoverage = parseGrepCoverage(resultText, toolName, toolArgs, resultKind);
|
|
267
301
|
// Hash the FULL args, not the summary: summaries drop payload fields
|
|
268
302
|
// (e.g. apply_patch keeps only base_path), which made every patch in a
|
|
269
303
|
// session collide to one hash and broke duplicate/retry detection.
|
|
@@ -297,6 +331,8 @@ function traceAgentTool({ sessionId, iteration, toolName, toolKind, toolMs, tool
|
|
|
297
331
|
result_next_call_count: nextCallCount,
|
|
298
332
|
result_bytes_est: resultBytesEst,
|
|
299
333
|
result_lines_est: resultLinesEst,
|
|
334
|
+
grep_coverage: grepCoverage,
|
|
335
|
+
cwd: cwd || null,
|
|
300
336
|
});
|
|
301
337
|
if (
|
|
302
338
|
Number.isFinite(numericToolMs)
|
|
@@ -26,6 +26,7 @@ let _localTracePath = null;
|
|
|
26
26
|
let _localTraceBuffer = [];
|
|
27
27
|
let _localTraceTimer = null;
|
|
28
28
|
let _localTraceFlushInFlight = false;
|
|
29
|
+
let _localTraceFlushPromise = null;
|
|
29
30
|
let _toolFailurePath = null;
|
|
30
31
|
let _toolFailureBuffer = [];
|
|
31
32
|
let _toolFailureTimer = null;
|
|
@@ -132,12 +133,10 @@ function _flushLocalTrace() {
|
|
|
132
133
|
clearTimeout(_localTraceTimer);
|
|
133
134
|
_localTraceTimer = null;
|
|
134
135
|
}
|
|
135
|
-
if (_localTraceBuffer.length === 0) return;
|
|
136
136
|
if (_localTraceFlushInFlight) {
|
|
137
|
-
|
|
138
|
-
_localTraceTimer.unref?.();
|
|
139
|
-
return;
|
|
137
|
+
return _localTraceFlushPromise;
|
|
140
138
|
}
|
|
139
|
+
if (_localTraceBuffer.length === 0) return null;
|
|
141
140
|
const path = _resolveLocalTracePath();
|
|
142
141
|
if (!path) return;
|
|
143
142
|
const chunk = _localTraceBuffer.join('');
|
|
@@ -157,17 +156,20 @@ function _flushLocalTrace() {
|
|
|
157
156
|
// mode only applies on file creation; existing files keep their mode.
|
|
158
157
|
// Windows ignores POSIX bits — ACL governs there.
|
|
159
158
|
_localTraceFlushInFlight = true;
|
|
160
|
-
appendFile(path, chunk, { encoding: 'utf8', mode: 0o600 })
|
|
159
|
+
const pending = appendFile(path, chunk, { encoding: 'utf8', mode: 0o600 })
|
|
161
160
|
.catch((err) => {
|
|
162
161
|
warnAgentOnce('agent-trace:local-spool', `[agent-trace] local spool failed (${err?.message})`);
|
|
163
162
|
})
|
|
164
163
|
.finally(() => {
|
|
165
164
|
_localTraceFlushInFlight = false;
|
|
165
|
+
_localTraceFlushPromise = null;
|
|
166
166
|
if (_localTraceBuffer.length > 0) {
|
|
167
167
|
_localTraceTimer = setTimeout(_flushLocalTrace, 0);
|
|
168
168
|
_localTraceTimer.unref?.();
|
|
169
169
|
}
|
|
170
170
|
});
|
|
171
|
+
_localTraceFlushPromise = pending;
|
|
172
|
+
return pending;
|
|
171
173
|
}
|
|
172
174
|
|
|
173
175
|
function _flushLocalTraceSync() {
|
|
@@ -294,7 +296,20 @@ function _scheduleFlush(immediate = false) {
|
|
|
294
296
|
}
|
|
295
297
|
}
|
|
296
298
|
|
|
299
|
+
async function _drainLocalTrace() {
|
|
300
|
+
if (!_resolveLocalTracePath()) return;
|
|
301
|
+
if (_localTraceTimer) {
|
|
302
|
+
clearTimeout(_localTraceTimer);
|
|
303
|
+
_localTraceTimer = null;
|
|
304
|
+
}
|
|
305
|
+
while (_localTraceBuffer.length > 0 || _localTraceFlushInFlight) {
|
|
306
|
+
const pending = _flushLocalTrace();
|
|
307
|
+
if (pending) await pending;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
297
311
|
async function drainAgentTrace() {
|
|
312
|
+
await _drainLocalTrace();
|
|
298
313
|
if (!_resolveServiceUrl()) return;
|
|
299
314
|
if (_flushTimer) { clearTimeout(_flushTimer); _flushTimer = null; }
|
|
300
315
|
for (let i = 0; i < 10 && _buffer.length > 0; i++) {
|
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
import os from 'node:os';
|
|
17
17
|
|
|
18
18
|
// Offline fallback only; live value refreshes from npm (24h TTL, in-process).
|
|
19
|
-
|
|
19
|
+
// The backend gates model exposure AND per-request model access on the client
|
|
20
|
+
// version (gpt-5.6-* require >= 0.144.0 per codex models-manager/models.json,
|
|
21
|
+
// verified 2026-07-09), so keep this at the current release when bumping.
|
|
22
|
+
export const CODEX_CLIENT_VERSION_FLOOR = '0.144.1';
|
|
20
23
|
const VERSION_TTL_MS = 24 * 60 * 60_000;
|
|
21
24
|
let _cache = { value: null, fetchedAt: 0 };
|
|
22
25
|
let _refreshInFlight = null;
|
|
@@ -52,6 +55,23 @@ export function codexClientVersionSync() {
|
|
|
52
55
|
return _cache.value || CODEX_CLIENT_VERSION_FLOOR;
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Awaitable warmup for cold-start paths: resolves the live npm version (or
|
|
60
|
+
* floor on failure) and fills the shared cache so codexClientVersionSync()
|
|
61
|
+
* and codexVersionHeader() stop reporting the floor. Never rejects; dedupes
|
|
62
|
+
* with the in-flight background refresh. First turns await this so the
|
|
63
|
+
* backend's minimal_client_version gate never sees a stale floor.
|
|
64
|
+
*/
|
|
65
|
+
export function warmCodexClientVersion() {
|
|
66
|
+
if (_cache.value && Date.now() - _cache.fetchedAt < VERSION_TTL_MS) {
|
|
67
|
+
return Promise.resolve(_cache.value);
|
|
68
|
+
}
|
|
69
|
+
if (!_refreshInFlight) {
|
|
70
|
+
_refreshInFlight = _refresh().finally(() => { _refreshInFlight = null; });
|
|
71
|
+
}
|
|
72
|
+
return _refreshInFlight;
|
|
73
|
+
}
|
|
74
|
+
|
|
55
75
|
function _osType() {
|
|
56
76
|
// codex os_info reports "Windows"/"Mac OS"/"Linux"; node os.type() gives
|
|
57
77
|
// Windows_NT/Darwin/Linux. Map to codex's vocabulary.
|
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
_shouldUseOpenAIHttpFallback,
|
|
60
60
|
} from './openai-oauth-http-sse.mjs';
|
|
61
61
|
import { createOpenAIOAuthLogin } from './openai-oauth-login.mjs';
|
|
62
|
+
import { warmCodexClientVersion } from './codex-client-meta.mjs';
|
|
62
63
|
import {
|
|
63
64
|
_displayCodexModel,
|
|
64
65
|
_codexFamily,
|
|
@@ -77,42 +78,13 @@ const CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';
|
|
|
77
78
|
export const CODEX_OAUTH_ORIGINATOR = 'codex_cli_rs';
|
|
78
79
|
const TOKEN_URL = 'https://auth.openai.com/oauth/token';
|
|
79
80
|
export const CODEX_RESPONSES_URL = 'https://chatgpt.com/backend-api/codex/responses';
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
// gpt-5.
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
// Offline fallback only — _resolveCodexClientVersion() fetches the live
|
|
88
|
-
// @openai/codex latest from npm first. Bumped to the current release
|
|
89
|
-
// (0.142.5, verified 2026-07-03) so the offline path stays close to what the
|
|
90
|
-
// backend expects for client-version gating.
|
|
91
|
-
const CODEX_CLIENT_VERSION_FLOOR = '0.142.5';
|
|
92
|
-
const CODEX_VERSION_CACHE_TTL_MS = 24 * 60 * 60_000;
|
|
93
|
-
let _codexVersionCache = { value: null, fetchedAt: 0 };
|
|
94
|
-
|
|
95
|
-
async function _resolveCodexClientVersion() {
|
|
96
|
-
const now = Date.now();
|
|
97
|
-
if (_codexVersionCache.value && now - _codexVersionCache.fetchedAt < CODEX_VERSION_CACHE_TTL_MS) {
|
|
98
|
-
return _codexVersionCache.value;
|
|
99
|
-
}
|
|
100
|
-
try {
|
|
101
|
-
const res = await fetch('https://registry.npmjs.org/@openai/codex/latest', {
|
|
102
|
-
signal: AbortSignal.timeout(5_000),
|
|
103
|
-
});
|
|
104
|
-
if (res.ok) {
|
|
105
|
-
const j = await res.json();
|
|
106
|
-
const v = String(j?.version || '').trim();
|
|
107
|
-
if (/^\d+\.\d+\.\d+/.test(v)) {
|
|
108
|
-
_codexVersionCache = { value: v, fetchedAt: now };
|
|
109
|
-
return v;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
} catch { /* network down / npm rejects — use floor */ }
|
|
113
|
-
_codexVersionCache = { value: CODEX_CLIENT_VERSION_FLOOR, fetchedAt: now };
|
|
114
|
-
return CODEX_CLIENT_VERSION_FLOOR;
|
|
115
|
-
}
|
|
81
|
+
// Client version for the models endpoint query and the `version`/User-Agent
|
|
82
|
+
// request headers — the OAuth backend rejects requests without it, gates new
|
|
83
|
+
// model exposures on it (gpt-5.6-* require >= 0.144.0), and rejects turns on
|
|
84
|
+
// gated models when the reported version is below the model's
|
|
85
|
+
// minimal_client_version. Resolution is unified in codex-client-meta.mjs
|
|
86
|
+
// (live npm @openai/codex latest, 24h in-process cache, offline floor) so the
|
|
87
|
+
// catalog query and the transport headers can never disagree.
|
|
116
88
|
const CODEX_MODEL_CACHE_TTL_MS = 24 * 60 * 60_000;
|
|
117
89
|
const CODEX_MODEL_CACHE_SCHEMA_VERSION = 3;
|
|
118
90
|
const TOKEN_REFRESH_SKEW_MS = 5 * 60_000;
|
|
@@ -784,7 +756,14 @@ export class OpenAIOAuthProvider {
|
|
|
784
756
|
? Promise.resolve(opts._prebuiltBody)
|
|
785
757
|
: Promise.resolve().then(() => buildRequestBody(messages, useModel, tools, bodyOpts));
|
|
786
758
|
const _authP = this.ensureAuth();
|
|
759
|
+
// Cold-start guard: the WS/SSE transports read the client version via
|
|
760
|
+
// the SYNC accessor for the `version` header + User-Agent. Await the
|
|
761
|
+
// shared resolver (parallel with auth; never rejects; no-op once
|
|
762
|
+
// cached) so the first turn after boot doesn't report the offline
|
|
763
|
+
// floor and trip the backend's minimal_client_version gate.
|
|
764
|
+
const _verP = warmCodexClientVersion();
|
|
787
765
|
let auth = await _authP;
|
|
766
|
+
await _verP;
|
|
788
767
|
const body = await _bodyP;
|
|
789
768
|
// poolKey != cacheKey by design (see openai-oauth-ws.mjs header note).
|
|
790
769
|
// poolKey is per-session so parallel reviewer/worker callers each get
|
|
@@ -1038,7 +1017,7 @@ export class OpenAIOAuthProvider {
|
|
|
1038
1017
|
}
|
|
1039
1018
|
try {
|
|
1040
1019
|
const auth = await this.ensureAuth();
|
|
1041
|
-
const clientVersion = await
|
|
1020
|
+
const clientVersion = await warmCodexClientVersion();
|
|
1042
1021
|
const url = `https://chatgpt.com/backend-api/codex/models?client_version=${clientVersion}`;
|
|
1043
1022
|
const res = await fetch(url, {
|
|
1044
1023
|
signal: AbortSignal.timeout(10_000),
|
|
@@ -1076,7 +1055,7 @@ export class OpenAIOAuthProvider {
|
|
|
1076
1055
|
_codexRefreshInFlight = (async () => {
|
|
1077
1056
|
try {
|
|
1078
1057
|
const auth = await this.ensureAuth();
|
|
1079
|
-
const clientVersion = await
|
|
1058
|
+
const clientVersion = await warmCodexClientVersion();
|
|
1080
1059
|
const url = `https://chatgpt.com/backend-api/codex/models?client_version=${clientVersion}`;
|
|
1081
1060
|
const res = await fetch(url, {
|
|
1082
1061
|
signal: AbortSignal.timeout(10_000),
|
|
@@ -305,11 +305,18 @@ export async function agentLoop(provider, messages, model, tools, onToolCall, cw
|
|
|
305
305
|
// cut off early. Other runaway protection is behavior-based (steering
|
|
306
306
|
// ladder hints, REPEAT_FAIL_LIMIT), never a lower iteration count.
|
|
307
307
|
let _iterWarnStage = 0;
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
308
|
+
// Tiny-cap loops (e.g. explorer cap=3) can't afford staged 50/75/90%
|
|
309
|
+
// steers — the 50% stage lands on iteration 1 in every session, spamming
|
|
310
|
+
// the normal batch→answer path. For caps < 10 emit ONE wrap-up warning at
|
|
311
|
+
// the penultimate iteration instead; caps >= 10 keep staged behavior.
|
|
312
|
+
const _singleWarn = maxLoopIterations < 10;
|
|
313
|
+
const _iterWarnAt = _singleWarn
|
|
314
|
+
? [Math.max(1, maxLoopIterations - 1)]
|
|
315
|
+
: [
|
|
316
|
+
Math.floor(maxLoopIterations * 0.5),
|
|
317
|
+
Math.floor(maxLoopIterations * 0.75),
|
|
318
|
+
Math.floor(maxLoopIterations * 0.9),
|
|
319
|
+
];
|
|
313
320
|
while (true) {
|
|
314
321
|
throwIfAborted();
|
|
315
322
|
if (iterations >= maxLoopIterations) {
|
|
@@ -341,9 +348,11 @@ export async function agentLoop(provider, messages, model, tools, onToolCall, cw
|
|
|
341
348
|
if (_iterWarnStage < _iterWarnAt.length && iterations >= _iterWarnAt[_iterWarnStage]) {
|
|
342
349
|
_iterWarnStage += 1;
|
|
343
350
|
const warnAt = _iterWarnAt[_iterWarnStage - 1];
|
|
344
|
-
const stageMsg =
|
|
345
|
-
? `Iteration budget
|
|
346
|
-
:
|
|
351
|
+
const stageMsg = _singleWarn
|
|
352
|
+
? `Iteration budget nearly spent: ${warnAt} of ${maxLoopIterations} iterations used — answer NOW with the best anchors you already hold.`
|
|
353
|
+
: _iterWarnStage === 1
|
|
354
|
+
? `Iteration budget notice: ${warnAt} of ${maxLoopIterations} iterations used. Converge on a conclusion: prefer finishing the current objective over opening new exploration.`
|
|
355
|
+
: `Iteration budget warning (stage ${_iterWarnStage}): ${warnAt} of ${maxLoopIterations} iterations used — the loop hard-stops at ${maxLoopIterations}. Wrap up now: summarize progress, state what remains, and finish with your best current result.`;
|
|
347
356
|
messages.push({ role: 'user', content: `<system-reminder>\n${stageMsg}\n</system-reminder>`, meta: 'hook' });
|
|
348
357
|
process.stderr.write(`[loop] iteration warning stage ${_iterWarnStage} at ${iterations} (sess=${sessionId || 'unknown'}); continuing with steer.\n`);
|
|
349
358
|
try {
|