autodev-cli 1.4.192 → 1.4.193

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/taskLoop.d.ts CHANGED
@@ -234,6 +234,14 @@ export declare class TaskLoopRunner {
234
234
  private _drainPendingSteers;
235
235
  private readonly _acpSteerInflight;
236
236
  private readonly _acpSteerAcks;
237
+ /**
238
+ * Synthesize a stable local steer id for an INSTANT steer that arrived with no
239
+ * A2A delivery id (office/browser ⚡). Hash of text+ts so the durable queue can
240
+ * track / dedup it across a restart-drain; NEVER sent to the server (an id-less
241
+ * frame has no server delivery to ack). Distinct arrivals get distinct ids
242
+ * (at-least-once favours delivery over rare double-run).
243
+ */
244
+ private _synthSteerId;
237
245
  /** Which persistent-ACP steer path applies to this provider (null = none). */
238
246
  private _acpSteerKind;
239
247
  /** Effective model string for a (re)spawned ACP session on the restart-drain. */
package/out/taskLoop.js CHANGED
@@ -1201,12 +1201,20 @@ class TaskLoopRunner {
1201
1201
  // DURABLE-STEER path (persistent ACP: grok-cli/grok-tui + opencode via the
1202
1202
  // shared AcpSession). A steer here is PERSISTED to .autodev/pending-steers.jsonl
1203
1203
  // BEFORE it is acked, delivered to the live process as a loop-tracked turn, and
1204
- // acked (+ de-queued) only when that steered turn COMPLETES — so a restart mid-
1205
- // steer re-delivers it (at-least-once) instead of silently losing it. Requires a
1206
- // delivery id (the A2A id) for restart-safe dedup; without one we fall through to
1207
- // the buffer path below (unchanged). NOT TODO.md — grok rewrites TODO.md mid-turn.
1208
- if (root && steerId && this._acpSteerKind(provider)) {
1209
- this._deliverAcpSteerDurable(root, provider, steerId, clean, onDelivered);
1204
+ // acked (+ de-queued) only when that steered turn COMPLETES CLEANLY — so a
1205
+ // restart / watchdog teardown mid-steer re-delivers it (at-least-once) instead of
1206
+ // silently losing it. NOT TODO.md — grok rewrites TODO.md mid-turn.
1207
+ //
1208
+ // 1.4.193: route ALL grok/opencode ACP steers here REGARDLESS of steerId. Office/
1209
+ // browser INSTANT steers whose A2A frame carries no id previously fell through to
1210
+ // the LOSSY `steerGrokAcp`/`AcpSession.steer()` branch below (fire-and-forget:
1211
+ // onDelivered() immediately, so a teardown before the queued turn drained lost it
1212
+ // forever). When there is no A2A id we synthesize a stable local one (text+ts) so
1213
+ // the durable queue owns it — the id is only used for OUR restart-safe dedup /
1214
+ // tracking, never sent to the server.
1215
+ if (root && this._acpSteerKind(provider)) {
1216
+ const id = steerId || this._synthSteerId(clean);
1217
+ this._deliverAcpSteerDurable(root, provider, id, clean, onDelivered);
1210
1218
  return;
1211
1219
  }
1212
1220
  // True mid-turn injection — only claude-tui keeps a live steerable session,
@@ -1321,6 +1329,16 @@ class TaskLoopRunner {
1321
1329
  // id → onDelivered (the server outbox ack). The latest replay's callback wins;
1322
1330
  // fired ONLY when the steered turn completes.
1323
1331
  _acpSteerAcks = new Map();
1332
+ /**
1333
+ * Synthesize a stable local steer id for an INSTANT steer that arrived with no
1334
+ * A2A delivery id (office/browser ⚡). Hash of text+ts so the durable queue can
1335
+ * track / dedup it across a restart-drain; NEVER sent to the server (an id-less
1336
+ * frame has no server delivery to ack). Distinct arrivals get distinct ids
1337
+ * (at-least-once favours delivery over rare double-run).
1338
+ */
1339
+ _synthSteerId(text) {
1340
+ return 'local-' + crypto.createHash('sha1').update(`${text}${Date.now()}`).digest('hex').slice(0, 16);
1341
+ }
1324
1342
  /** Which persistent-ACP steer path applies to this provider (null = none). */
1325
1343
  _acpSteerKind(provider) {
1326
1344
  if ((provider === 'grok-cli' || provider === 'grok-tui') && (0, grokTuiProvider_1.grokAcpEnabled)()) {
@@ -1371,7 +1389,21 @@ class TaskLoopRunner {
1371
1389
  }
1372
1390
  this._acpSteerInflight.add(id);
1373
1391
  this._cb?.log(`⚡ Steered live (durable, ${kind}): "${text.slice(0, 80)}"`);
1374
- void p.then(() => this._settleAcpSteer(root, id), () => this._settleAcpSteer(root, id));
1392
+ // Settle (de-queue + markDeliverySeen) ONLY when the steered turn completes
1393
+ // CLEANLY. steerGrokAcpTracked/steerOpencodeAcpTracked RESOLVE (never reject)
1394
+ // with {clean:false} when the turn is torn down (watchdog cancel+close, grok
1395
+ // child death, restart) — settling on that would de-queue a steer that never
1396
+ // ran (the 1.4.192 loss). Instead we LEAVE it on the durable queue (do NOT
1397
+ // markDeliverySeen) so _drainDurableAcpSteers re-delivers it after respawn +
1398
+ // session/load — genuine at-least-once.
1399
+ void p.then((res) => {
1400
+ if (res && res.clean) {
1401
+ this._settleAcpSteer(root, id);
1402
+ return;
1403
+ }
1404
+ this._acpSteerInflight.delete(id); // kept queued for redelivery
1405
+ this._cb?.log(`⚡ Durable steer did not complete cleanly (id=${id}, ${res?.stopReason ?? 'unknown'}) — kept queued for redelivery`);
1406
+ }, () => { this._acpSteerInflight.delete(id); });
1375
1407
  }
1376
1408
  /**
1377
1409
  * Drain the durable steer queue into the live ACP session — called at each turn
@@ -1404,7 +1436,15 @@ class TaskLoopRunner {
1404
1436
  this._acpSteerInflight.delete(s.id);
1405
1437
  return;
1406
1438
  }
1407
- this._settleAcpSteer(root, s.id);
1439
+ // Settle ONLY on a clean completion — a redelivered steer torn down again
1440
+ // stays queued (do NOT markDeliverySeen) so the next boundary retries it.
1441
+ if (res.clean) {
1442
+ this._settleAcpSteer(root, s.id);
1443
+ }
1444
+ else {
1445
+ this._acpSteerInflight.delete(s.id);
1446
+ this._cb?.log(`⚡ Durable steer redelivery not clean (id=${s.id}, ${res.stopReason}) — kept queued`);
1447
+ }
1408
1448
  }
1409
1449
  catch {
1410
1450
  this._acpSteerInflight.delete(s.id);