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.
@@ -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.sessionId) {
1531
- sessionArgs.push('--resume', opts.sessionId);
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('--session-id', sid);
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
- const _fixedDir = _launchSid ? (0, sessions_1.grokSessionDirFor)(path.resolve(root), _launchSid) : undefined;
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
- log(`Grok: headless --resume 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)`);
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 --resume` behind the first_token hang-probe; see decideHeadlessGrokMode)
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 stored session id → 'new-persistent': there is
2172
- * no id to reuse, so bootstrap persistence with a fresh named session
2173
- * (`--session-id`, mints + persists a UUID; NOT `--resume`, so it can't hit a
2174
- * resume-hang), behind the probe as a safety net.
2175
- * - a stored id, failures < max → 'resume': attempt persistent
2176
- * `grok --resume <id>` (the reliable, near-instant path on grok 1.0.13), gated by
2177
- * the first_token hang-probe. This is the STEADY STATE — every turn resumes.
2178
- * - a stored id, failures >= max (sustained resume hang) → 'stateless' for exactly ONE
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 'resume'. The stored id is preserved throughout, so resume
2181
- * is NEVER permanently abandoned — persistence self-heals.
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.hasSessionId) {
2185
- return 'new-persistent';
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 'resume';
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 attempt grok's persistent session
2251
- * (`--resume` when a session id exists). grok 1.0.41's headless `--resume` can hang
2252
- * the resumed turn (loaded-but-never-answers) → the 10-min watchdog, so every
2253
- * persistent launch is gated behind a FAST first_token hang-probe: if grok begins
2254
- * answering within AUTODEV_GROK_RESUME_PROBE_MS the turn runs normally (persistent,
2255
- * context intact); if not, sendGrokPrompt kills it and calls onResumeHang to relaunch
2256
- * THIS turn stateless. A resume failure is COUNTED (bounded, NOT a permanent poison):
2257
- * the STORED session id is preserved and the NEXT turn re-attempts `--resume`, so a
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
- hasSessionId: !!persistedSid, consecutiveResumeFailures: _resumeFailCount(root),
2415
+ hasFolderSession, consecutiveResumeFailures: _resumeFailCount(root),
2289
2416
  });
2290
2417
  if (decision === 'stateless') {
2291
- // Sustained resume failures → ONE breather turn (skip the probe cost), then the
2292
- // counter resets so the NEXT turn resumes again. Persistence is never abandoned.
2293
- log(`${label}: --resume failed ${_resumeFailCount(root)}× in a row — one stateless breather turn, will retry --resume next turn`);
2294
- _noteResumeSuccess(root); // reset so the next turn returns to 'resume'
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 === 'resume') {
2299
- log(`${label}: resuming session ${persistedSid.slice(0, 8)}… (headless)`);
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, sessionId: persistedSid, providerId,
2429
+ model, showOutput, persist: true, continueSession: true, providerId,
2302
2430
  resumeProbeMs: PROBE_MS, onResumeHang, onResumeOk,
2303
2431
  });
2304
2432
  return;
2305
2433
  }
2306
- // new-persistent — no stored id yet: bootstrap a fresh named session so later turns
2307
- // have an id to resume. A fresh `--session-id` isn't a `--resume`, but keep the
2308
- // probe as a belt-and-braces safety net (and it can never exceed the watchdog).
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,