autodev-cli 1.4.185 → 1.4.187
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/out/dispatcher.js +6 -1
- package/out/dispatcher.js.map +1 -1
- package/out/messageBuilder.d.ts +41 -1
- package/out/messageBuilder.js +102 -5
- package/out/messageBuilder.js.map +1 -1
- package/out/prompt.d.ts +3 -1
- package/out/prompt.js +4 -2
- package/out/prompt.js.map +1 -1
- package/out/providers/grok/tui.d.ts +45 -13
- package/out/providers/grok/tui.js +168 -40
- package/out/providers/grok/tui.js.map +1 -1
- package/out/taskLoop.js +11 -1
- package/out/taskLoop.js.map +1 -1
- package/package.json +2 -2
|
@@ -74,6 +74,7 @@ exports.isGrokTuiBusy = isGrokTuiBusy;
|
|
|
74
74
|
exports.getGrokTuiLastActivity = getGrokTuiLastActivity;
|
|
75
75
|
exports.forceIdleGrokTui = forceIdleGrokTui;
|
|
76
76
|
exports.tmuxAvailable = tmuxAvailable;
|
|
77
|
+
exports.grokSessionArgsFor = grokSessionArgsFor;
|
|
77
78
|
exports.sendGrokPrompt = sendGrokPrompt;
|
|
78
79
|
exports.sendGrokCliPrompt = sendGrokCliPrompt;
|
|
79
80
|
exports.decideGrokRunMode = decideGrokRunMode;
|
|
@@ -1510,6 +1511,29 @@ function runGrokTmuxTurn(root, promptFilePath, resolvedSessionId, stdoutFile, ex
|
|
|
1510
1511
|
}
|
|
1511
1512
|
})();
|
|
1512
1513
|
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Pure builder for the grok session CLI flags, extracted so the mutually-exclusive
|
|
1516
|
+
* invariants are unit-testable without spawning grok:
|
|
1517
|
+
* - continueSession → `['--continue']` ONLY. NEVER with `--session-id` (grok rejects
|
|
1518
|
+
* that combo unless `--fork-session`, which we never pass) and never an explicit id.
|
|
1519
|
+
* - sessionId → `['--resume', <id>]` (legacy explicit-id resume; not used by the
|
|
1520
|
+
* headless path anymore, kept for callers that pass a concrete id).
|
|
1521
|
+
* - newSessionId → `['--session-id', <uuid>]` (bootstrap a brand-new named session).
|
|
1522
|
+
* - none → `[]` (stateless: grok mints its own throwaway id).
|
|
1523
|
+
* continueSession takes precedence, so `--continue` can never be emitted alongside an id.
|
|
1524
|
+
*/
|
|
1525
|
+
function grokSessionArgsFor(args) {
|
|
1526
|
+
if (args.continueSession) {
|
|
1527
|
+
return ['--continue'];
|
|
1528
|
+
}
|
|
1529
|
+
if (args.sessionId) {
|
|
1530
|
+
return ['--resume', args.sessionId];
|
|
1531
|
+
}
|
|
1532
|
+
if (args.newSessionId) {
|
|
1533
|
+
return ['--session-id', args.newSessionId];
|
|
1534
|
+
}
|
|
1535
|
+
return [];
|
|
1536
|
+
}
|
|
1513
1537
|
function sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, opts = {}) {
|
|
1514
1538
|
opts.showOutput?.();
|
|
1515
1539
|
const modelArgs = opts.model ? ['-m', opts.model] : [];
|
|
@@ -1525,10 +1549,34 @@ function sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, opts =
|
|
|
1525
1549
|
// read from the start = an instant false failure). This is the primary fix for
|
|
1526
1550
|
// the 1.4.181 forced-resume regression.
|
|
1527
1551
|
let _launchSid;
|
|
1552
|
+
// For the `--continue` path the concrete id is not known up front — grok appends to
|
|
1553
|
+
// the cwd's most-recent session dir — so we pin the tailer to that EXISTING newest
|
|
1554
|
+
// dir (resolved at launch, before grok appends). Kept separate from _launchSid (which
|
|
1555
|
+
// means "we minted/passed this exact id"), since --continue passes no id at all.
|
|
1556
|
+
let _continueDir;
|
|
1528
1557
|
if (opts.persist) {
|
|
1529
1558
|
const providerId = opts.providerId ?? 'grok-tui';
|
|
1530
|
-
if (opts.
|
|
1531
|
-
|
|
1559
|
+
if (opts.continueSession) {
|
|
1560
|
+
// `grok --continue`: resume the folder's most-recent session. No id, and NO
|
|
1561
|
+
// --session-id (grok rejects that combo unless --fork-session, which we never
|
|
1562
|
+
// want). Robust to the stale-id / cwd-tracking-drift that made `--resume <id>`
|
|
1563
|
+
// error in ~1s on Windows and forced every turn stateless.
|
|
1564
|
+
sessionArgs.push(...grokSessionArgsFor({ continueSession: true }));
|
|
1565
|
+
_continueDir = (0, sessions_1.newestGrokSessionDir)(path.resolve(root));
|
|
1566
|
+
// Best-effort: stamp the resolved id into session-state.json so the sessions UI
|
|
1567
|
+
// / resume-aware profile reminder can still name the session. The continue
|
|
1568
|
+
// DECISION no longer depends on a stored id (it keys off folder-session presence).
|
|
1569
|
+
try {
|
|
1570
|
+
const resumedId = _continueDir ? path.basename(_continueDir) : undefined;
|
|
1571
|
+
if (resumedId) {
|
|
1572
|
+
(0, sessionState_1.saveSessionId)(root, providerId, resumedId);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
catch { /* best effort */ }
|
|
1576
|
+
log(`Grok: continuing most-recent session for cwd (--continue) (model=${modelLabel})`);
|
|
1577
|
+
}
|
|
1578
|
+
else if (opts.sessionId) {
|
|
1579
|
+
sessionArgs.push(...grokSessionArgsFor({ sessionId: opts.sessionId }));
|
|
1532
1580
|
_launchSid = opts.sessionId;
|
|
1533
1581
|
// Persist the id we're resuming so the NEXT turn reads it from
|
|
1534
1582
|
// session-state.json directly — this also stamps a session that was only
|
|
@@ -1542,7 +1590,7 @@ function sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, opts =
|
|
|
1542
1590
|
}
|
|
1543
1591
|
else {
|
|
1544
1592
|
const sid = (0, crypto_1.randomUUID)();
|
|
1545
|
-
sessionArgs.push(
|
|
1593
|
+
sessionArgs.push(...grokSessionArgsFor({ newSessionId: sid }));
|
|
1546
1594
|
_launchSid = sid;
|
|
1547
1595
|
try {
|
|
1548
1596
|
(0, sessionState_1.saveSessionId)(root, providerId, sid);
|
|
@@ -1666,6 +1714,22 @@ function sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, opts =
|
|
|
1666
1714
|
let _endSeen = false;
|
|
1667
1715
|
let _forcedExit = null; // set by a session-log turn_ended finish
|
|
1668
1716
|
let _grokFilesActive = false; // once true, session logs own narration
|
|
1717
|
+
// Best diagnostic grok gave us THIS turn, so a fast-failing resume/continue is no
|
|
1718
|
+
// longer a blind guess (the user's Windows `--resume <id>` errors in ~1s but our log
|
|
1719
|
+
// never showed grok's real reason). Populated from three channels: an authoritative
|
|
1720
|
+
// streaming-json {type:"error"} record or an error turn_ended detail (_lastGrokErr),
|
|
1721
|
+
// and, as a fallback, the most recent stderr line (_lastGrokStderr). Surfaced right
|
|
1722
|
+
// before the stateless fallback in finishFromTurnEnd, redacted + truncated.
|
|
1723
|
+
let _lastGrokErr = ''; // authoritative: json error record / error turn_ended detail
|
|
1724
|
+
let _lastGrokStderr = ''; // fallback: latest non-empty stderr line
|
|
1725
|
+
const grokFailDiag = () => {
|
|
1726
|
+
const raw = _lastGrokErr || _lastGrokStderr;
|
|
1727
|
+
if (!raw) {
|
|
1728
|
+
return '';
|
|
1729
|
+
}
|
|
1730
|
+
const t = (0, redactSecrets_1.redactSecrets)(String(raw)).replace(/\s+/g, ' ').trim();
|
|
1731
|
+
return t.length > 300 ? t.slice(0, 297) + '…' : t;
|
|
1732
|
+
};
|
|
1669
1733
|
// Headless resume hang-probe state (see resumeProbeMs / onResumeHang below). The
|
|
1670
1734
|
// probe window is the caller-supplied value (sendGrokTuiPrompt reads the env);
|
|
1671
1735
|
// 0 (all stateless / non-probed calls) leaves the probe disarmed.
|
|
@@ -1700,7 +1764,10 @@ function sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, opts =
|
|
|
1700
1764
|
// the tailer pins THIS dir and never "newest-by-mtime" (which a newer unrelated
|
|
1701
1765
|
// session dir could hijack). undefined only in the stateless mode, where grok
|
|
1702
1766
|
// mints an unknown id and newest-by-mtime is the only way to find it.
|
|
1703
|
-
|
|
1767
|
+
// --continue → the existing newest dir grok appends to (resolved above); resume /
|
|
1768
|
+
// new-persistent → the fixed dir for the known id; stateless → undefined (unknown id).
|
|
1769
|
+
const _fixedDir = _continueDir
|
|
1770
|
+
?? (_launchSid ? (0, sessions_1.grokSessionDirFor)(path.resolve(root), _launchSid) : undefined);
|
|
1704
1771
|
// Snapshot the pre-turn session so the tailer forwards ONLY this turn's records:
|
|
1705
1772
|
// a RESUMED session's dir already holds backlog → seed at its current end; a
|
|
1706
1773
|
// FRESH session mints a NEW dir that appears later → read that dir from the start
|
|
@@ -1787,8 +1854,16 @@ function sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, opts =
|
|
|
1787
1854
|
// re-attempts `--resume <storedId>` — continuity resumes as soon as the transient
|
|
1788
1855
|
// condition clears. Only for a launched RESUME (opts.sessionId present, probe
|
|
1789
1856
|
// armed); a new-persistent/stateless error falls through to the normal report.
|
|
1790
|
-
if (!clean && !_sawFirstToken && opts.sessionId && opts.onResumeHang && !_resumeAbandoned) {
|
|
1857
|
+
if (!clean && !_sawFirstToken && (opts.sessionId || opts.continueSession) && opts.onResumeHang && !_resumeAbandoned) {
|
|
1791
1858
|
_resumeAbandoned = true; // close skips the exit file — the relaunch owns the turn
|
|
1859
|
+
const how = opts.continueSession ? '--continue' : '--resume';
|
|
1860
|
+
const diag = grokFailDiag();
|
|
1861
|
+
if (diag) {
|
|
1862
|
+
log(`Grok: ${how} failed fast — grok said: "${diag}"`);
|
|
1863
|
+
}
|
|
1864
|
+
else {
|
|
1865
|
+
log(`Grok: ${how} failed fast — grok emitted no diagnostic (no stderr / error record captured this turn)`);
|
|
1866
|
+
}
|
|
1792
1867
|
log('Grok: resume errored before any output (no first_token) — marking session non-resumable this run, falling back to stateless for this turn');
|
|
1793
1868
|
if (grokFilePoll) {
|
|
1794
1869
|
clearInterval(grokFilePoll);
|
|
@@ -1889,7 +1964,7 @@ function sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, opts =
|
|
|
1889
1964
|
// prior transient hiccup stops biasing later turns.
|
|
1890
1965
|
if (!_sawFirstToken && (0, grokEvents_1.scanGrokFirstToken)(ev.lines)) {
|
|
1891
1966
|
_sawFirstToken = true;
|
|
1892
|
-
if (opts.sessionId && opts.onResumeOk) {
|
|
1967
|
+
if ((opts.sessionId || opts.continueSession) && opts.onResumeOk) {
|
|
1893
1968
|
try {
|
|
1894
1969
|
opts.onResumeOk();
|
|
1895
1970
|
}
|
|
@@ -1927,6 +2002,28 @@ function sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, opts =
|
|
|
1927
2002
|
// instead of hanging to GROK_MAX_RUN_MS. An error/aborted outcome ends it too
|
|
1928
2003
|
// (retryable), honoring scanGrokTurnEnd's classification.
|
|
1929
2004
|
const te = (0, grokEvents_1.scanGrokTurnEnd)(ev.lines);
|
|
2005
|
+
// Capture grok's own error detail from an error turn_ended so the fast-fail
|
|
2006
|
+
// fallback can name WHY (session-not-found, in-use, cwd mismatch, auth, …). Only
|
|
2007
|
+
// when we don't already have an authoritative streaming-json error record.
|
|
2008
|
+
if (te === 'error' && !_lastGrokErr) {
|
|
2009
|
+
for (const l of ev.lines) {
|
|
2010
|
+
let o;
|
|
2011
|
+
try {
|
|
2012
|
+
o = typeof l === 'string' ? JSON.parse(l) : l;
|
|
2013
|
+
}
|
|
2014
|
+
catch {
|
|
2015
|
+
continue;
|
|
2016
|
+
}
|
|
2017
|
+
if (!o || o.type !== 'turn_ended') {
|
|
2018
|
+
continue;
|
|
2019
|
+
}
|
|
2020
|
+
const d = o.error?.message ?? o.error ?? o.detail ?? o.message ?? o.reason ?? o.outcome_detail;
|
|
2021
|
+
if (d) {
|
|
2022
|
+
_lastGrokErr = typeof d === 'string' ? d : JSON.stringify(d);
|
|
2023
|
+
break;
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
1930
2027
|
// Safety net against any residual offset skew: only honor a turn_ended once
|
|
1931
2028
|
// THIS turn has demonstrably started in the tailed window. A turn_ended with no
|
|
1932
2029
|
// preceding current-turn start is backlog (a prior errored turn read from a
|
|
@@ -2004,7 +2101,12 @@ function sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, opts =
|
|
|
2004
2101
|
// counts a TRANSIENT failure (bounded counter); it does NOT permanently disable
|
|
2005
2102
|
// resume — the next turn re-attempts `--resume`.
|
|
2006
2103
|
_resumeAbandoned = true;
|
|
2007
|
-
|
|
2104
|
+
const how = opts.continueSession ? '--continue' : '--resume';
|
|
2105
|
+
const diag = grokFailDiag();
|
|
2106
|
+
if (diag) {
|
|
2107
|
+
log(`Grok: ${how} failed — grok said: "${diag}"`);
|
|
2108
|
+
}
|
|
2109
|
+
log(`Grok: headless ${how} produced no output in ${Math.round(_probeWaitedMs / 1000)}s (${_probeRound} extension${_probeRound === 1 ? '' : 's'}) — stateless for this turn only (stored session id kept; resume retried next turn)`);
|
|
2008
2110
|
if (grokFilePoll) {
|
|
2009
2111
|
clearInterval(grokFilePoll);
|
|
2010
2112
|
}
|
|
@@ -2060,6 +2162,7 @@ function sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, opts =
|
|
|
2060
2162
|
}
|
|
2061
2163
|
else if (type === 'error') {
|
|
2062
2164
|
const errMsg = msg.message ?? JSON.stringify(msg);
|
|
2165
|
+
_lastGrokErr = errMsg; // authoritative diagnostic for a fast resume/continue failure
|
|
2063
2166
|
log(`Grok error: ${errMsg}`);
|
|
2064
2167
|
try {
|
|
2065
2168
|
fs.appendFileSync(stdoutFile, `\n[Grok error: ${errMsg}]\n`, 'utf8');
|
|
@@ -2080,6 +2183,7 @@ function sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, opts =
|
|
|
2080
2183
|
if (!text) {
|
|
2081
2184
|
return;
|
|
2082
2185
|
}
|
|
2186
|
+
_lastGrokStderr = text; // fallback diagnostic for a fast resume/continue failure
|
|
2083
2187
|
log(`Grok stderr: ${text}`);
|
|
2084
2188
|
if (GROK_MAX_TOOL_ERRORS > 0 && text.includes('tool_output_error')) {
|
|
2085
2189
|
toolErrorCount++;
|
|
@@ -2122,6 +2226,17 @@ function sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, opts =
|
|
|
2122
2226
|
// A session-log turn_ended already reported the true outcome via _forcedExit;
|
|
2123
2227
|
// don't let the SIGKILL exit code (1) override a clean finish.
|
|
2124
2228
|
const exitCode = _forcedExit ?? code ?? 1;
|
|
2229
|
+
// A resume/continue that FAILED FAST by exiting non-zero directly (grok printed to
|
|
2230
|
+
// stderr and quit, e.g. "Session <id> not found locally … 404 Not Found") never
|
|
2231
|
+
// wrote a session-log turn_ended, so it bypassed finishFromTurnEnd's diagnostic.
|
|
2232
|
+
// Surface grok's captured reason here too so this failure shape is not a blind guess.
|
|
2233
|
+
if (exitCode !== 0 && !_sawFirstToken && (opts.sessionId || opts.continueSession)) {
|
|
2234
|
+
const how = opts.continueSession ? '--continue' : '--resume';
|
|
2235
|
+
const diag = grokFailDiag();
|
|
2236
|
+
if (diag) {
|
|
2237
|
+
log(`Grok: ${how} failed fast (exit ${exitCode}) — grok said: "${diag}"`);
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2125
2240
|
log(`Grok: exited (code=${exitCode})`);
|
|
2126
2241
|
if (!_endSeen) {
|
|
2127
2242
|
_emitGrokHook(root, exitCode === 0 ? 'Stop' : 'StopFailure', { exit_code: exitCode });
|
|
@@ -2156,7 +2271,7 @@ function sendGrokCliPrompt(root, promptFilePath, resolvedSessionId, stdoutFile,
|
|
|
2156
2271
|
*
|
|
2157
2272
|
* - grok-tui + tmux → useTmux: true (persistent live pane; context accretes)
|
|
2158
2273
|
* - grok-tui + no tmux → useTmux: false (HEADLESS path — persistence handled there
|
|
2159
|
-
* via `grok --
|
|
2274
|
+
* via `grok --continue` behind the first_token hang-probe; see decideHeadlessGrokMode)
|
|
2160
2275
|
* - grok-cli → useTmux: false always (never uses tmux)
|
|
2161
2276
|
*/
|
|
2162
2277
|
function decideGrokRunMode(opts) {
|
|
@@ -2168,27 +2283,32 @@ function decideGrokRunMode(opts) {
|
|
|
2168
2283
|
* unit-testable. The `maxConsecutiveFailures` guard REPLACES the old permanent
|
|
2169
2284
|
* "resumeBroken" flag that doomed a whole run to statelessness after one hiccup.
|
|
2170
2285
|
*
|
|
2171
|
-
* - no
|
|
2172
|
-
*
|
|
2173
|
-
* (`--session-id`, mints + persists a UUID; NOT `--
|
|
2174
|
-
*
|
|
2175
|
-
* - a
|
|
2176
|
-
* `grok --
|
|
2177
|
-
*
|
|
2178
|
-
* -
|
|
2286
|
+
* - no folder session yet → 'new-session': there is no
|
|
2287
|
+
* session on disk for this cwd to continue, so bootstrap persistence with a fresh
|
|
2288
|
+
* named session (`--session-id`, mints + persists a UUID; NOT `--continue`, which
|
|
2289
|
+
* would have nothing to continue), behind the probe as a safety net.
|
|
2290
|
+
* - a folder session exists, failures < max → 'continue': resume the cwd's
|
|
2291
|
+
* most-recent session with `grok --continue` — robust to the stale-id / cwd-tracking
|
|
2292
|
+
* drift that made an explicit `--resume <id>` error in ~1s on Windows. Gated by the
|
|
2293
|
+
* first_token hang-probe. This is the STEADY STATE — every turn continues.
|
|
2294
|
+
* - a folder session, failures >= max (sustained hang) → 'stateless' for exactly ONE
|
|
2179
2295
|
* breather turn (skip the probe cost once); the caller resets the counter so the
|
|
2180
|
-
* NEXT turn returns to '
|
|
2181
|
-
*
|
|
2296
|
+
* NEXT turn returns to 'continue'. The folder session is untouched, so continuity is
|
|
2297
|
+
* NEVER permanently abandoned — persistence self-heals.
|
|
2298
|
+
*
|
|
2299
|
+
* NOTE: the decision keys off FOLDER-SESSION PRESENCE (newestGrokSessionDir exists),
|
|
2300
|
+
* NOT a stored id — because `--continue` needs no id, and keying off a stored id is
|
|
2301
|
+
* exactly what let a stale/mismatched id drive a doomed `--resume`.
|
|
2182
2302
|
*/
|
|
2183
2303
|
function decideHeadlessGrokMode(args) {
|
|
2184
|
-
if (!args.
|
|
2185
|
-
return 'new-
|
|
2304
|
+
if (!args.hasFolderSession) {
|
|
2305
|
+
return 'new-session';
|
|
2186
2306
|
}
|
|
2187
2307
|
const max = args.maxConsecutiveFailures ?? RESUME_MAX_CONSEC_FAILURES;
|
|
2188
2308
|
if (args.consecutiveResumeFailures >= max) {
|
|
2189
2309
|
return 'stateless';
|
|
2190
2310
|
}
|
|
2191
|
-
return '
|
|
2311
|
+
return 'continue';
|
|
2192
2312
|
}
|
|
2193
2313
|
/**
|
|
2194
2314
|
* Pure verdict for the headless resume hang-probe (multi-round exponential, 1.4.185).
|
|
@@ -2247,14 +2367,16 @@ function sendGrokTuiPrompt(root, promptFilePath, resolvedSessionId, stdoutFile,
|
|
|
2247
2367
|
}
|
|
2248
2368
|
/**
|
|
2249
2369
|
* The persistent HEADLESS grok launch, shared by grok-cli and headless grok-tui.
|
|
2250
|
-
* We want PERSISTENCE across turns, so
|
|
2251
|
-
*
|
|
2252
|
-
*
|
|
2253
|
-
*
|
|
2254
|
-
*
|
|
2255
|
-
*
|
|
2256
|
-
*
|
|
2257
|
-
*
|
|
2370
|
+
* We want PERSISTENCE across turns, so when the cwd already has a grok session on disk
|
|
2371
|
+
* we resume the MOST RECENT one with `grok --continue` (robust to the stale-id /
|
|
2372
|
+
* cwd-tracking drift that made an explicit `--resume <id>` error in ~1s on Windows and
|
|
2373
|
+
* forced every turn stateless). grok's headless resume can also hang a resumed turn
|
|
2374
|
+
* (loaded-but-never-answers) → the watchdog, so every persistent launch is gated behind
|
|
2375
|
+
* a FAST first_token hang-probe: if grok begins answering within
|
|
2376
|
+
* AUTODEV_GROK_RESUME_PROBE_MS the turn runs normally (persistent, context intact); if
|
|
2377
|
+
* not, sendGrokPrompt kills it and calls onResumeHang to relaunch THIS turn stateless. A
|
|
2378
|
+
* failure is COUNTED (bounded, NOT a permanent poison): the folder session is preserved
|
|
2379
|
+
* and the NEXT turn re-attempts `--continue`, so a
|
|
2258
2380
|
* transient hiccup can't strand the run in per-turn statelessness (the pre-1.4.184 bug).
|
|
2259
2381
|
* Only after RESUME_MAX_CONSEC_FAILURES straight failures does a single breather turn
|
|
2260
2382
|
* skip resume, after which resume is tried again. The stateless path stays intact as the
|
|
@@ -2264,6 +2386,11 @@ function sendGrokTuiPrompt(root, promptFilePath, resolvedSessionId, stdoutFile,
|
|
|
2264
2386
|
function sendGrokPersistentHeadless(root, promptFilePath, resolvedSessionId, stdoutFile, exitFile, log, providerId, model, showOutput) {
|
|
2265
2387
|
const label = providerId === 'grok-cli' ? 'Grok CLI' : 'Grok TUI';
|
|
2266
2388
|
const PROBE_MS = envNum('AUTODEV_GROK_RESUME_PROBE_MS', 90_000);
|
|
2389
|
+
// Continuity is decided by whether grok has a session on disk for THIS cwd — the same
|
|
2390
|
+
// signal `grok --continue` itself uses — NOT a stored id (a stale/mismatched id was the
|
|
2391
|
+
// root cause of the instant `--resume <id>` error). resolvedSessionId/getSessionId are
|
|
2392
|
+
// kept only for logging + best-effort id display.
|
|
2393
|
+
const hasFolderSession = !!(0, sessions_1.newestGrokSessionDir)(path.resolve(root));
|
|
2267
2394
|
const persistedSid = resolvedSessionId || (0, sessionState_1.getSessionId)(root, providerId);
|
|
2268
2395
|
// Stateless relaunch: grok mints a throwaway id for THIS turn. persist:false means
|
|
2269
2396
|
// it never overwrites the STORED session id in session-state.json → the next turn
|
|
@@ -2285,27 +2412,28 @@ function sendGrokPersistentHeadless(root, promptFilePath, resolvedSessionId, std
|
|
|
2285
2412
|
// Clear the failure counter the instant a resumed turn starts answering.
|
|
2286
2413
|
const onResumeOk = () => { _noteResumeSuccess(root); };
|
|
2287
2414
|
const decision = decideHeadlessGrokMode({
|
|
2288
|
-
|
|
2415
|
+
hasFolderSession, consecutiveResumeFailures: _resumeFailCount(root),
|
|
2289
2416
|
});
|
|
2290
2417
|
if (decision === 'stateless') {
|
|
2291
|
-
// Sustained
|
|
2292
|
-
// counter resets so the NEXT turn
|
|
2293
|
-
log(`${label}: --
|
|
2294
|
-
_noteResumeSuccess(root); // reset so the next turn returns to '
|
|
2418
|
+
// Sustained continue failures → ONE breather turn (skip the probe cost), then the
|
|
2419
|
+
// counter resets so the NEXT turn continues again. Persistence is never abandoned.
|
|
2420
|
+
log(`${label}: --continue failed ${_resumeFailCount(root)}× in a row — one stateless breather turn, will retry --continue next turn`);
|
|
2421
|
+
_noteResumeSuccess(root); // reset so the next turn returns to 'continue'
|
|
2295
2422
|
runStateless();
|
|
2296
2423
|
return;
|
|
2297
2424
|
}
|
|
2298
|
-
if (decision === '
|
|
2299
|
-
|
|
2425
|
+
if (decision === 'continue') {
|
|
2426
|
+
const idNote = persistedSid ? ` (${persistedSid.slice(0, 8)}…)` : '';
|
|
2427
|
+
log(`${label}: continuing most-recent session for cwd${idNote} (headless --continue)`);
|
|
2300
2428
|
sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, {
|
|
2301
|
-
model, showOutput, persist: true,
|
|
2429
|
+
model, showOutput, persist: true, continueSession: true, providerId,
|
|
2302
2430
|
resumeProbeMs: PROBE_MS, onResumeHang, onResumeOk,
|
|
2303
2431
|
});
|
|
2304
2432
|
return;
|
|
2305
2433
|
}
|
|
2306
|
-
// new-
|
|
2307
|
-
// have
|
|
2308
|
-
//
|
|
2434
|
+
// new-session — no folder session yet: bootstrap a fresh named session so later turns
|
|
2435
|
+
// have one to `--continue`. A fresh `--session-id` isn't a resume, but keep the probe
|
|
2436
|
+
// as a belt-and-braces safety net (and it can never exceed the watchdog).
|
|
2309
2437
|
log(`${label}: starting a new persistent session (headless)`);
|
|
2310
2438
|
sendGrokPrompt(root, promptFilePath, stdoutFile, exitFile, log, {
|
|
2311
2439
|
model, showOutput, persist: true, providerId,
|