autodev-cli 1.4.192 → 1.4.194

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.
Files changed (36) hide show
  1. package/out/cli.js +4 -0
  2. package/out/cli.js.map +1 -1
  3. package/out/commands/grokKeepalive.d.ts +10 -0
  4. package/out/commands/grokKeepalive.js +27 -0
  5. package/out/commands/grokKeepalive.js.map +1 -0
  6. package/out/commands/leaderStop.d.ts +10 -0
  7. package/out/commands/leaderStop.js +63 -0
  8. package/out/commands/leaderStop.js.map +1 -0
  9. package/out/messageBuilder.js +5 -3
  10. package/out/messageBuilder.js.map +1 -1
  11. package/out/profileBuilder.js +1 -0
  12. package/out/profileBuilder.js.map +1 -1
  13. package/out/providers/acp/AcpSession.d.ts +30 -0
  14. package/out/providers/acp/AcpSession.js +86 -1
  15. package/out/providers/acp/AcpSession.js.map +1 -1
  16. package/out/providers/grok/GrokAcpSession.d.ts +14 -1
  17. package/out/providers/grok/GrokAcpSession.js +29 -2
  18. package/out/providers/grok/GrokAcpSession.js.map +1 -1
  19. package/out/providers/grok/keepalive.d.ts +15 -0
  20. package/out/providers/grok/keepalive.js +141 -0
  21. package/out/providers/grok/keepalive.js.map +1 -0
  22. package/out/providers/grok/keepaliveMain.d.ts +1 -0
  23. package/out/providers/grok/keepaliveMain.js +22 -0
  24. package/out/providers/grok/keepaliveMain.js.map +1 -0
  25. package/out/providers/grok/leader.d.ts +66 -0
  26. package/out/providers/grok/leader.js +390 -0
  27. package/out/providers/grok/leader.js.map +1 -0
  28. package/out/providers/grok/tui.js +130 -29
  29. package/out/providers/grok/tui.js.map +1 -1
  30. package/out/providers/opencode/sdk.d.ts +11 -0
  31. package/out/providers/opencode/sdk.js +110 -10
  32. package/out/providers/opencode/sdk.js.map +1 -1
  33. package/out/taskLoop.d.ts +64 -0
  34. package/out/taskLoop.js +270 -9
  35. package/out/taskLoop.js.map +1 -1
  36. package/package.json +2 -2
@@ -0,0 +1,390 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // grok LEADER daemon — "always open, reconnect-never-reload" (Layer B, 1.4.193).
4
+ //
5
+ // GOAL: the grok backend OUTLIVES loop/app restarts so a restarted loop
6
+ // RECONNECTS to the live in-memory session with NO `session/load` replay (the
7
+ // office-feed spam Layer A only quieted). GATED behind env AUTODEV_GROK_LEADER,
8
+ // DEFAULT OFF — when off, the existing non-leader ACP path (1.4.188/1.4.191) is
9
+ // used UNCHANGED.
10
+ //
11
+ // ARCHITECTURE (verified live on grok 1.0.41; see memory `grok-leader-always-open`
12
+ // + the 1.4.193 re-spike):
13
+ // • A per-agent grok LEADER daemon (`grok agent --always-approve leader
14
+ // --no-exit-on-disconnect --relay-on-demand --no-auto-update --leader-socket
15
+ // <per-agent .sock>`) is a shared backend that survives client disconnects.
16
+ // • CRITICAL grok-1.0.41 fact: the leader EVICTS a session's in-memory context
17
+ // when its LAST client disconnects (the session persists on disk, but a fresh
18
+ // client then gets "unknown session id" and must `session/load` = the replay).
19
+ // So a bare leader is NOT enough for attach-without-load across a loop restart.
20
+ // • Fix: a detached KEEPALIVE client holds the session warm in the leader's
21
+ // memory forever. Loop clients then attach as a SECOND concurrent client and
22
+ // `session/prompt {sessionId}` DIRECTLY — context intact, NO session/load —
23
+ // across any number of loop-process restarts (proven: codeword recalled,
24
+ // zero session/load).
25
+ // • The ONE `session/load` (cold recovery from disk) happens inside the
26
+ // keepalive at cold start when it is seeded with the persisted id; every
27
+ // subsequent loop restart is a warm attach. No live keepalive ⇒ degrade to
28
+ // Layer A (session/load), never data-loss.
29
+ //
30
+ // REAP BY PID (critical): `grok leader list/kill` CANNOT see a per-socket
31
+ // `--relay-on-demand` leader (verified: list→[], "No leader candidates found").
32
+ // So we track the leader + keepalive PIDs in `.autodev/leader.json` and reap the
33
+ // pid-trees ourselves. `autodev leader-stop <root>` (hidden) is what the app calls
34
+ // on office stop/remove/quit. The loop's own SIGINT/exit reaper kills ONLY the
35
+ // thin client (leader/keepalive are DETACHED, so they survive a loop restart).
36
+ // ---------------------------------------------------------------------------
37
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
38
+ if (k2 === undefined) k2 = k;
39
+ var desc = Object.getOwnPropertyDescriptor(m, k);
40
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
41
+ desc = { enumerable: true, get: function() { return m[k]; } };
42
+ }
43
+ Object.defineProperty(o, k2, desc);
44
+ }) : (function(o, m, k, k2) {
45
+ if (k2 === undefined) k2 = k;
46
+ o[k2] = m[k];
47
+ }));
48
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
49
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
50
+ }) : function(o, v) {
51
+ o["default"] = v;
52
+ });
53
+ var __importStar = (this && this.__importStar) || (function () {
54
+ var ownKeys = function(o) {
55
+ ownKeys = Object.getOwnPropertyNames || function (o) {
56
+ var ar = [];
57
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
58
+ return ar;
59
+ };
60
+ return ownKeys(o);
61
+ };
62
+ return function (mod) {
63
+ if (mod && mod.__esModule) return mod;
64
+ var result = {};
65
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
66
+ __setModuleDefault(result, mod);
67
+ return result;
68
+ };
69
+ })();
70
+ Object.defineProperty(exports, "__esModule", { value: true });
71
+ exports.runKeepalive = void 0;
72
+ exports.leaderModeEnabled = leaderModeEnabled;
73
+ exports.leaderSocketPath = leaderSocketPath;
74
+ exports.leaderJsonPath = leaderJsonPath;
75
+ exports.leaderSidPath = leaderSidPath;
76
+ exports.readLeaderInfo = readLeaderInfo;
77
+ exports.writeLeaderInfo = writeLeaderInfo;
78
+ exports.removeLeaderInfo = removeLeaderInfo;
79
+ exports.isPidAlive = isPidAlive;
80
+ exports.decideLeaderAction = decideLeaderAction;
81
+ exports.leaderHealthy = leaderHealthy;
82
+ exports.keepaliveEntryPath = keepaliveEntryPath;
83
+ exports.ensureLeader = ensureLeader;
84
+ exports.stopLeader = stopLeader;
85
+ const fs = __importStar(require("fs"));
86
+ const os = __importStar(require("os"));
87
+ const path = __importStar(require("path"));
88
+ const crypto = __importStar(require("crypto"));
89
+ const child_process = __importStar(require("child_process"));
90
+ const killProcessTree_1 = require("../../core/killProcessTree");
91
+ const sessionState_1 = require("../../sessionState");
92
+ // Re-export the keepalive runner + its options so existing importers keep working.
93
+ var keepalive_1 = require("./keepalive");
94
+ Object.defineProperty(exports, "runKeepalive", { enumerable: true, get: function () { return keepalive_1.runKeepalive; } });
95
+ /** Env gate — leader mode is OPT-IN and DEFAULT OFF. Pure (env injectable). */
96
+ function leaderModeEnabled(env = process.env) {
97
+ const raw = String(env['AUTODEV_GROK_LEADER'] ?? '').toLowerCase().trim();
98
+ return raw === '1' || raw === 'true' || raw === 'on' || raw === 'yes';
99
+ }
100
+ /** Per-agent AF_UNIX socket. Prefer `.autodev/grok-leader.sock`; fall back to a
101
+ * hashed path under tmp when the workspace path would blow the ~108-byte sun_path
102
+ * limit (a deterministic function of the root either way). */
103
+ function leaderSocketPath(root) {
104
+ const inAutodev = path.join(root, '.autodev', 'grok-leader.sock');
105
+ if (Buffer.byteLength(inAutodev) <= 100) {
106
+ return inAutodev;
107
+ }
108
+ const h = crypto.createHash('sha1').update(path.resolve(root)).digest('hex').slice(0, 16);
109
+ return path.join(os.tmpdir(), `autodev-grok-${h}.sock`);
110
+ }
111
+ function leaderJsonPath(root) { return path.join(root, '.autodev', 'leader.json'); }
112
+ function leaderSidPath(root) { return path.join(root, '.autodev', 'grok-leader.sid'); }
113
+ function readLeaderInfo(root) {
114
+ try {
115
+ const raw = fs.readFileSync(leaderJsonPath(root), 'utf8');
116
+ const info = JSON.parse(raw);
117
+ if (info && typeof info.pid === 'number' && typeof info.socket === 'string') {
118
+ return info;
119
+ }
120
+ }
121
+ catch { /* absent / malformed */ }
122
+ return null;
123
+ }
124
+ function writeLeaderInfo(root, info) {
125
+ try {
126
+ (0, sessionState_1.autodevDir)(root);
127
+ fs.writeFileSync(leaderJsonPath(root), JSON.stringify(info, null, 2), 'utf8');
128
+ }
129
+ catch { /* best effort */ }
130
+ }
131
+ function removeLeaderInfo(root) {
132
+ try {
133
+ fs.unlinkSync(leaderJsonPath(root));
134
+ }
135
+ catch { /* already gone */ }
136
+ }
137
+ /** `process.kill(pid, 0)` liveness probe — true iff the pid exists (and is ours
138
+ * or EPERM, which still means alive). */
139
+ function isPidAlive(pid) {
140
+ if (!pid || !Number.isInteger(pid) || pid <= 1) {
141
+ return false;
142
+ }
143
+ try {
144
+ process.kill(pid, 0);
145
+ return true;
146
+ }
147
+ catch (err) {
148
+ return err?.code === 'EPERM';
149
+ }
150
+ }
151
+ /**
152
+ * PURE orphan-sweep decision. Given the persisted info and the observed liveness
153
+ * of its pieces, decide whether to reuse a still-live leader ('adopt'), reap a
154
+ * stale/dead one ('reap-stale'), or spawn fresh ('spawn'). Unit-tested.
155
+ */
156
+ function decideLeaderAction(info, alive) {
157
+ if (!info) {
158
+ return 'spawn';
159
+ }
160
+ const keepaliveOk = info.keepalivePid ? alive.keepalive : true;
161
+ if (alive.leader && keepaliveOk && alive.socket) {
162
+ return 'adopt';
163
+ }
164
+ return 'reap-stale';
165
+ }
166
+ /** Is the persisted leader fully healthy right now (adoptable)? */
167
+ function leaderHealthy(info) {
168
+ if (!info) {
169
+ return false;
170
+ }
171
+ const alive = { leader: isPidAlive(info.pid), keepalive: isPidAlive(info.keepalivePid), socket: (() => { try {
172
+ return fs.existsSync(info.socket);
173
+ }
174
+ catch {
175
+ return false;
176
+ } })() };
177
+ return decideLeaderAction(info, alive) === 'adopt';
178
+ }
179
+ function readSidFile(sidFile) {
180
+ try {
181
+ const s = fs.readFileSync(sidFile, 'utf8').trim();
182
+ return s || null;
183
+ }
184
+ catch {
185
+ return null;
186
+ }
187
+ }
188
+ async function waitUntil(pred, ms, stepMs = 100) {
189
+ const end = Date.now() + ms;
190
+ while (Date.now() < end) {
191
+ if (pred()) {
192
+ return true;
193
+ }
194
+ await new Promise((r) => setTimeout(r, stepMs));
195
+ }
196
+ return pred();
197
+ }
198
+ function openLogFd(file) {
199
+ try {
200
+ return fs.openSync(file, 'a');
201
+ }
202
+ catch {
203
+ return 'ignore';
204
+ }
205
+ }
206
+ /** Resolve the minimal keepalive entry (out/providers/grok/keepaliveMain.js) so a
207
+ * detached keepalive loads with NO commander/CLI graph. Overridable for odd
208
+ * packagings via AUTODEV_KEEPALIVE_ENTRY. */
209
+ function keepaliveEntryPath() {
210
+ const override = process.env['AUTODEV_KEEPALIVE_ENTRY'];
211
+ if (override && fs.existsSync(override)) {
212
+ return override;
213
+ }
214
+ return path.join(__dirname, 'keepaliveMain.js');
215
+ }
216
+ /** Reap the leader + keepalive pid-trees named in `info` (best-effort). */
217
+ function reapLeaderProcs(info) {
218
+ if (info.keepalivePid) {
219
+ try {
220
+ (0, killProcessTree_1.killProcessTree)(info.keepalivePid);
221
+ }
222
+ catch { /* ignore */ }
223
+ }
224
+ if (info.pid) {
225
+ try {
226
+ (0, killProcessTree_1.killProcessTree)(info.pid);
227
+ }
228
+ catch { /* ignore */ }
229
+ }
230
+ }
231
+ /**
232
+ * ORPHAN-SWEEP + ensure a live leader+keepalive for this root. Adopts a still-live
233
+ * one (no second leader), reaps a stale/dead one, else spawns both DETACHED so they
234
+ * survive the loop process exiting. Returns the LeaderInfo (with the warm session
235
+ * id) or null when the backend could not be established (caller falls back to the
236
+ * non-leader ACP path). Never throws.
237
+ */
238
+ async function ensureLeader(opts) {
239
+ const { root, bin, providerId, model, log } = opts;
240
+ const existing = readLeaderInfo(root);
241
+ if (existing && leaderHealthy(existing)) {
242
+ log(`grok leader: adopting live backend (leader=${existing.pid} keepalive=${existing.keepalivePid ?? '?'} sid=${(existing.sessionId ?? '').slice(0, 8)}…)`);
243
+ return existing;
244
+ }
245
+ if (existing) {
246
+ log(`grok leader: reaping stale backend (leader=${existing.pid} keepalive=${existing.keepalivePid ?? '?'})`);
247
+ reapLeaderProcs(existing);
248
+ removeLeaderInfo(existing.root ?? root);
249
+ }
250
+ const socket = leaderSocketPath(root);
251
+ try {
252
+ fs.unlinkSync(socket);
253
+ }
254
+ catch { /* no stale socket */ }
255
+ (0, sessionState_1.autodevDir)(root);
256
+ // 1) Spawn the LEADER daemon, detached.
257
+ const leaderArgs = ['agent', '--always-approve', 'leader', '--no-exit-on-disconnect', '--relay-on-demand', '--no-auto-update', '--leader-socket', socket];
258
+ const leaderErrFd = openLogFd(path.join(root, '.autodev', 'grok-leader.err'));
259
+ let leaderChild;
260
+ try {
261
+ leaderChild = child_process.spawn(bin, leaderArgs, {
262
+ cwd: root, detached: true,
263
+ stdio: ['ignore', 'ignore', leaderErrFd],
264
+ env: { ...process.env, TERM: process.env['TERM'] || 'xterm-256color' },
265
+ });
266
+ }
267
+ catch (err) {
268
+ log(`grok leader: spawn failed (${err?.message ?? err})`);
269
+ return null;
270
+ }
271
+ leaderChild.unref();
272
+ const leaderPid = leaderChild.pid;
273
+ if (!leaderPid) {
274
+ log('grok leader: no pid after spawn');
275
+ return null;
276
+ }
277
+ const sockOk = await waitUntil(() => { try {
278
+ return fs.existsSync(socket);
279
+ }
280
+ catch {
281
+ return false;
282
+ } }, 15_000);
283
+ if (!sockOk) {
284
+ log('grok leader: socket never appeared — reaping');
285
+ try {
286
+ (0, killProcessTree_1.killProcessTree)(leaderPid);
287
+ }
288
+ catch { /* ignore */ }
289
+ return null;
290
+ }
291
+ // 2) Spawn the KEEPALIVE client, detached — it holds the session warm. Seed it
292
+ // with the persisted id so a cold start does its ONE session/load here.
293
+ const sidFile = leaderSidPath(root);
294
+ try {
295
+ fs.unlinkSync(sidFile);
296
+ }
297
+ catch { /* ignore */ }
298
+ const seed = (0, sessionState_1.getSessionId)(root, providerId) || undefined;
299
+ const kaArgs = [keepaliveEntryPath(), '--socket', socket, '--cwd', root, '--sid-file', sidFile, '--bin', bin];
300
+ if (seed) {
301
+ kaArgs.push('--seed', seed);
302
+ }
303
+ let kaChild;
304
+ try {
305
+ kaChild = child_process.spawn(process.execPath, kaArgs, {
306
+ cwd: root, detached: true, stdio: 'ignore',
307
+ // ELECTRON_RUN_AS_NODE so an Electron-bundled CLI runs the keepalive as pure
308
+ // node (no window) — the inverse of the app's ELECTRON_RUN_AS_NODE trap.
309
+ env: { ...process.env, ELECTRON_RUN_AS_NODE: '1', TERM: process.env['TERM'] || 'xterm-256color' },
310
+ });
311
+ }
312
+ catch (err) {
313
+ log(`grok leader: keepalive spawn failed (${err?.message ?? err})`);
314
+ try {
315
+ (0, killProcessTree_1.killProcessTree)(leaderPid);
316
+ }
317
+ catch { /* ignore */ }
318
+ return null;
319
+ }
320
+ kaChild.unref();
321
+ const keepalivePid = kaChild.pid;
322
+ const gotSid = await waitUntil(() => readSidFile(sidFile) !== null, 25_000);
323
+ const sessionId = readSidFile(sidFile) || undefined;
324
+ if (!keepalivePid || !gotSid || !sessionId) {
325
+ log('grok leader: keepalive failed to establish a session — reaping');
326
+ if (keepalivePid) {
327
+ try {
328
+ (0, killProcessTree_1.killProcessTree)(keepalivePid);
329
+ }
330
+ catch { /* ignore */ }
331
+ }
332
+ try {
333
+ (0, killProcessTree_1.killProcessTree)(leaderPid);
334
+ }
335
+ catch { /* ignore */ }
336
+ return null;
337
+ }
338
+ const info = { pid: leaderPid, socket, startedAt: new Date().toISOString(), keepalivePid, sessionId, root };
339
+ writeLeaderInfo(root, info);
340
+ try {
341
+ (0, sessionState_1.saveSessionId)(root, providerId, sessionId);
342
+ }
343
+ catch { /* ignore */ }
344
+ log(`grok leader: backend up (leader=${leaderPid} keepalive=${keepalivePid} sid=${sessionId.slice(0, 8)}…)`);
345
+ return info;
346
+ }
347
+ /**
348
+ * `autodev leader-stop <root>` — kill the leader + keepalive pid-trees named in
349
+ * `.autodev/leader.json` and clean up the socket / sid / json. This is what the
350
+ * app calls on office stop / remove / quit. Best-effort; returns whether anything
351
+ * was reaped.
352
+ */
353
+ function stopLeader(root, log) {
354
+ const info = readLeaderInfo(root);
355
+ let killed = false;
356
+ if (info) {
357
+ if (info.keepalivePid && isPidAlive(info.keepalivePid)) {
358
+ try {
359
+ (0, killProcessTree_1.killProcessTree)(info.keepalivePid);
360
+ killed = true;
361
+ }
362
+ catch { /* ignore */ }
363
+ }
364
+ if (info.pid && isPidAlive(info.pid)) {
365
+ try {
366
+ (0, killProcessTree_1.killProcessTree)(info.pid);
367
+ killed = true;
368
+ }
369
+ catch { /* ignore */ }
370
+ }
371
+ try {
372
+ if (info.socket) {
373
+ fs.unlinkSync(info.socket);
374
+ }
375
+ }
376
+ catch { /* ignore */ }
377
+ }
378
+ try {
379
+ fs.unlinkSync(leaderSocketPath(root));
380
+ }
381
+ catch { /* ignore */ }
382
+ try {
383
+ fs.unlinkSync(leaderSidPath(root));
384
+ }
385
+ catch { /* ignore */ }
386
+ removeLeaderInfo(root);
387
+ log(killed ? `leader-stop: reaped grok leader backend for ${root}` : `leader-stop: no live grok leader for ${root}`);
388
+ return killed;
389
+ }
390
+ //# sourceMappingURL=leader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leader.js","sourceRoot":"","sources":["../../../src/providers/grok/leader.ts"],"names":[],"mappings":"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"}
@@ -111,6 +111,8 @@ const grokEvents_1 = require("../../core/grokEvents");
111
111
  const sessions_1 = require("./sessions");
112
112
  const redactSecrets_1 = require("../../core/redactSecrets");
113
113
  const GrokAcpSession_1 = require("./GrokAcpSession");
114
+ const AcpSession_1 = require("../acp/AcpSession");
115
+ const leader_1 = require("./leader");
114
116
  // ---------------------------------------------------------------------------
115
117
  // Constants
116
118
  // ---------------------------------------------------------------------------
@@ -2378,20 +2380,10 @@ function sendGrokCliPrompt(root, promptFilePath, resolvedSessionId, stdoutFile,
2378
2380
  // ---------------------------------------------------------------------------
2379
2381
  // PERSISTENT-PROCESS (ACP) path — one long-lived `grok agent stdio` per root.
2380
2382
  // ---------------------------------------------------------------------------
2381
- /** Get (or lazily create) the persistent ACP session for a root. */
2382
- function getOrCreateAcpSession(root, providerId, model, log, resolvedSessionId) {
2383
- let sess = _acpSessions.get(root);
2384
- if (sess) {
2385
- return sess;
2386
- }
2387
- // Resume-on-(re)start: seed the id to session/load from the persisted id, else
2388
- // the newest grok session on disk for this cwd (works whether that session was
2389
- // created by a prior ACP run or the legacy headless `--continue`).
2390
- // Layer A.2: PREFER the persisted id (session-state.json) over the disk-latest
2391
- // fallback — disk-latest can resume the WRONG session (a stray/newer dir). Use
2392
- // disk-latest ONLY when there is no persisted id.
2393
- const seedId = (0, sessionState_1.getSessionId)(root, providerId) || resolvedSessionId || (0, sessions_1.getGrokLatestSessionId)(root) || undefined;
2394
- const hooks = {
2383
+ /** Redaction-aware AcpHostHooks that append to this root's hook feed. Shared by
2384
+ * the non-leader and leader ACP paths so hook emission stays in one place. */
2385
+ function makeAcpHooks(root, log) {
2386
+ return {
2395
2387
  emitHook: (name, extra) => {
2396
2388
  if (name === 'Notification' && typeof extra['message'] === 'string') {
2397
2389
  const msg = (0, redactSecrets_1.redactSecrets)(String(extra['message'])).slice(0, 1200);
@@ -2406,10 +2398,88 @@ function getOrCreateAcpSession(root, providerId, model, log, resolvedSessionId)
2406
2398
  },
2407
2399
  log,
2408
2400
  };
2409
- sess = new GrokAcpSession_1.GrokAcpSession({ root, model, bin: GROK_BIN, hooks, initialSessionId: seedId });
2401
+ }
2402
+ /** Get (or lazily create) the NON-LEADER persistent ACP session for a root. */
2403
+ function getOrCreateAcpSession(root, providerId, model, log, resolvedSessionId) {
2404
+ let sess = _acpSessions.get(root);
2405
+ if (sess) {
2406
+ return sess;
2407
+ }
2408
+ // Resume-on-(re)start: seed the id to session/load from the persisted id, else
2409
+ // the newest grok session on disk for this cwd (works whether that session was
2410
+ // created by a prior ACP run or the legacy headless `--continue`).
2411
+ // Layer A.2: PREFER the persisted id (session-state.json) over the disk-latest
2412
+ // fallback — disk-latest can resume the WRONG session (a stray/newer dir). Use
2413
+ // disk-latest ONLY when there is no persisted id.
2414
+ const seedId = (0, sessionState_1.getSessionId)(root, providerId) || resolvedSessionId || (0, sessions_1.getGrokLatestSessionId)(root) || undefined;
2415
+ sess = new GrokAcpSession_1.GrokAcpSession({ root, model, bin: GROK_BIN, hooks: makeAcpHooks(root, log), initialSessionId: seedId });
2410
2416
  _acpSessions.set(root, sess);
2411
2417
  return sess;
2412
2418
  }
2419
+ /**
2420
+ * LEADER-AWARE ensure (Layer B, 1.4.193). When AUTODEV_GROK_LEADER is ON, ensure a
2421
+ * per-agent leader + keepalive backend (orphan-sweeping/adopting/spawning as
2422
+ * needed) and return a LEADER-CLIENT ACP session that ATTACHES to the warm session
2423
+ * WITHOUT session/load. When the flag is OFF — or the leader backend can't be
2424
+ * established — this returns the existing non-leader session UNCHANGED. Async
2425
+ * because ensuring the leader spawns detached processes on a cold start.
2426
+ */
2427
+ async function ensureAcpSession(root, providerId, model, log, resolvedSessionId) {
2428
+ if ((0, leader_1.leaderModeEnabled)()) {
2429
+ const cached = _acpSessions.get(root);
2430
+ if (cached && cached.isAlive()) {
2431
+ return cached;
2432
+ }
2433
+ if (cached) {
2434
+ try {
2435
+ cached.close();
2436
+ }
2437
+ catch { /* ignore */ }
2438
+ _acpSessions.delete(root);
2439
+ }
2440
+ const info = await (0, leader_1.ensureLeader)({ root, bin: GROK_BIN, providerId, model, log });
2441
+ if (info) {
2442
+ const sess = new GrokAcpSession_1.GrokAcpSession({
2443
+ root, model, bin: GROK_BIN, hooks: makeAcpHooks(root, log),
2444
+ initialSessionId: info.sessionId, leaderSocket: info.socket, attachWithoutLoad: true,
2445
+ });
2446
+ _acpSessions.set(root, sess);
2447
+ return sess;
2448
+ }
2449
+ log('grok leader: backend unavailable — falling back to the non-leader ACP path');
2450
+ }
2451
+ return getOrCreateAcpSession(root, providerId, model, log, resolvedSessionId);
2452
+ }
2453
+ /**
2454
+ * Leader POISON-ESCAPE: the attached session id was rejected by the leader
2455
+ * ("unknown session id" — the keepalive died / a wrong id). Reap the leader
2456
+ * backend, clear the persisted id, drop the thin client — the caller then
2457
+ * re-ensures a FRESH leader with session/new.
2458
+ */
2459
+ function recoverLeaderPoison(root, providerId, log) {
2460
+ try {
2461
+ (0, leader_1.stopLeader)(root, log);
2462
+ }
2463
+ catch { /* ignore */ }
2464
+ try {
2465
+ (0, sessionState_1.clearSessionId)(root, providerId);
2466
+ }
2467
+ catch { /* ignore */ }
2468
+ const s = _acpSessions.get(root);
2469
+ if (s) {
2470
+ if (s.pid) {
2471
+ try {
2472
+ (0, killProcessTree_1.killProcessTree)(s.pid);
2473
+ }
2474
+ catch { /* ignore */ }
2475
+ }
2476
+ try {
2477
+ s.close();
2478
+ }
2479
+ catch { /* ignore */ }
2480
+ _acpSessions.delete(root);
2481
+ }
2482
+ }
2413
2483
  /**
2414
2484
  * Fire-and-forget: run ONE turn through the persistent ACP session. Streams
2415
2485
  * assistant text to stdoutFile and writes the exit code to exitFile exactly once
@@ -2443,7 +2513,6 @@ function sendGrokAcpPrompt(root, promptFilePath, resolvedSessionId, stdoutFile,
2443
2513
  catch { /* ignore */ }
2444
2514
  return;
2445
2515
  }
2446
- const sess = getOrCreateAcpSession(root, providerId, model, log, resolvedSessionId);
2447
2516
  _busyRoots.add(root);
2448
2517
  if (showOutput) {
2449
2518
  try {
@@ -2456,23 +2525,29 @@ function sendGrokAcpPrompt(root, promptFilePath, resolvedSessionId, stdoutFile,
2456
2525
  }
2457
2526
  catch { /* ignore */ } };
2458
2527
  void (async () => {
2528
+ let sess;
2459
2529
  try {
2530
+ sess = await ensureAcpSession(root, providerId, model, log, resolvedSessionId);
2460
2531
  await sess.ensureReady();
2461
2532
  }
2462
2533
  catch (err) {
2463
- const startupErr = `${err?.message ?? err} ${sess.stderrSnapshot}`;
2464
- log(`Grok ACP: startup failed (${err?.message ?? err}); stderr="${sess.stderrSnapshot.slice(0, 200)}" — falling back to headless one-shot`);
2465
- try {
2466
- sess.close();
2534
+ const cur = _acpSessions.get(root);
2535
+ const stderr = cur?.stderrSnapshot ?? '';
2536
+ const startupErr = `${err?.message ?? err} ${stderr}`;
2537
+ log(`Grok ACP: startup failed (${err?.message ?? err}); stderr="${stderr.slice(0, 200)}" — falling back to headless one-shot`);
2538
+ if (cur) {
2539
+ try {
2540
+ cur.close();
2541
+ }
2542
+ catch { /* ignore */ }
2467
2543
  }
2468
- catch { /* ignore */ }
2469
2544
  _acpSessions.delete(root);
2470
2545
  _busyRoots.delete(root);
2471
2546
  // AUTH failure at ACP startup (grok not signed in): the headless fallback
2472
2547
  // would just re-hit the same logged-out gate, so short-circuit to
2473
2548
  // reauth_required (non-retryable) instead of falling through.
2474
2549
  if ((0, authClassify_1.isGrokAuthFailure)(startupErr)) {
2475
- _emitGrokReauthHook(root, stdoutFile, log, 'ACP startup (not signed in)', providerId, sess.stderrSnapshot.slice(0, 160));
2550
+ _emitGrokReauthHook(root, stdoutFile, log, 'ACP startup (not signed in)', providerId, stderr.slice(0, 160));
2476
2551
  try {
2477
2552
  fs.writeFileSync(exitFile, '1\n', 'utf8');
2478
2553
  }
@@ -2491,12 +2566,34 @@ function sendGrokAcpPrompt(root, promptFilePath, resolvedSessionId, stdoutFile,
2491
2566
  }
2492
2567
  catch { /* ignore */ }
2493
2568
  }
2494
- let res;
2495
- try {
2496
- res = await sess.runTurn(promptText, sink);
2497
- }
2498
- catch (err) {
2499
- res = { clean: false, stopReason: 'exception', error: err?.message ?? String(err) };
2569
+ const runTurnSafe = async (s) => {
2570
+ try {
2571
+ return await s.runTurn(promptText, sink);
2572
+ }
2573
+ catch (err) {
2574
+ return { clean: false, stopReason: 'exception', error: err?.message ?? String(err) };
2575
+ }
2576
+ };
2577
+ let res = await runTurnSafe(sess);
2578
+ // Leader POISON-ESCAPE: the warm session was rejected (keepalive died / stale
2579
+ // id). Reap the leader + session/new on a FRESH leader, then retry ONCE.
2580
+ if ((0, leader_1.leaderModeEnabled)() && sess.attachMode && !res.clean && AcpSession_1.AcpSession.isUnknownSessionError(res.error)) {
2581
+ log('Grok leader: attach rejected (poison) — reaping leader, starting a fresh session, retrying once');
2582
+ recoverLeaderPoison(root, providerId, log);
2583
+ try {
2584
+ sess = await ensureAcpSession(root, providerId, model, log, undefined);
2585
+ await sess.ensureReady();
2586
+ if (sess.sessionId) {
2587
+ try {
2588
+ (0, sessionState_1.saveSessionId)(root, providerId, sess.sessionId);
2589
+ }
2590
+ catch { /* ignore */ }
2591
+ }
2592
+ res = await runTurnSafe(sess);
2593
+ }
2594
+ catch (err) {
2595
+ res = { clean: false, stopReason: 'poison-recover-failed', error: err?.message ?? String(err) };
2596
+ }
2500
2597
  }
2501
2598
  if (!res.clean && res.error) {
2502
2599
  try {
@@ -2572,7 +2669,7 @@ async function ensureGrokAcpSteer(root, providerId, model, text, log) {
2572
2669
  if (!grokAcpEnabled()) {
2573
2670
  return null;
2574
2671
  }
2575
- const sess = getOrCreateAcpSession(root, providerId, model, log, undefined);
2672
+ const sess = await ensureAcpSession(root, providerId, model, log, undefined);
2576
2673
  try {
2577
2674
  await sess.ensureReady();
2578
2675
  }
@@ -2835,6 +2932,10 @@ function closeGrokTuiSession(root, _log) {
2835
2932
  if (acp) {
2836
2933
  // Reap grok's whole worker subtree (grok forks a per-turn worker child), then
2837
2934
  // close — SIGKILL of just the parent would orphan the worker to init.
2935
+ // LEADER-AWARE (Layer B): acp.pid here is the THIN CLIENT. The leader + keepalive
2936
+ // are DETACHED (their own process groups, not in the client's tree), so this
2937
+ // reaps ONLY the client — the leader survives the loop restart, exactly as
2938
+ // intended. The app reaps the leader itself via `autodev leader-stop <root>`.
2838
2939
  if (acp.pid) {
2839
2940
  try {
2840
2941
  (0, killProcessTree_1.killProcessTree)(acp.pid);