@bridge4dev/runner 0.60.0 → 0.62.0

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.
@@ -230,6 +230,8 @@ export interface SupervisorOptions {
230
230
  * `BACKGROUND_TASKS_HEARTBEAT_MS`. Real one is five minutes.
231
231
  */
232
232
  backgroundTasksHeartbeatMs?: number;
233
+ /** #348: the compaction net, as a test seam over `COMPACTION_WATCHDOG_MS`. */
234
+ compactionWatchdogMs?: number;
233
235
  }
234
236
  /**
235
237
  * How often a resting session says «I still hold N background tasks» — the
@@ -274,6 +276,8 @@ export declare class Supervisor {
274
276
  private static readonly EMPTY_TURN_SETTLE_MS;
275
277
  /** The window actually used — the constant, or a test's own shorter one. */
276
278
  private readonly emptyTurnSettleMs;
279
+ /** The compaction net actually used — the constant, or a test's own. */
280
+ private readonly compactionWatchdogMs;
277
281
  private readonly rateLimitsResendMs;
278
282
  /** The stop-settle window actually used — the constant, or a test's own. */
279
283
  private readonly stopSettleMs;
@@ -328,6 +332,22 @@ export declare class Supervisor {
328
332
  private restarting;
329
333
  /** A restore-point collection is running; a second reconnect must not start another (#388). */
330
334
  private checkpointGcInFlight;
335
+ /**
336
+ * Sessions the current `hello_ack` names, while `reconcile` is walking them
337
+ * (#392).
338
+ *
339
+ * The unacked replay at the top of `reconcile` sends every journal's
340
+ * leftovers before a single session is registered, and the acks come back
341
+ * while the loop is still awaiting an earlier session's worktree. The ack
342
+ * handler deletes the journal of a session it does not know once nothing is
343
+ * unacked — which used to be exactly the journal the loop was about to
344
+ * restore from: its open cards, its queued messages, its anchor, gone a
345
+ * moment before they were read. Membership here is what «this session is
346
+ * known» means until the loop reaches it.
347
+ */
348
+ private readonly reconciling;
349
+ /** How long a dashboard-requested compaction may go unreported before the session is handed back. */
350
+ private static readonly COMPACTION_WATCHDOG_MS;
331
351
  /** Session 14: one project-recipe run per machine, and its verdict queue. */
332
352
  private readonly verify;
333
353
  private readonly verifyReports;
@@ -913,6 +933,26 @@ export declare class Supervisor {
913
933
  * chance, and the alternative there is a card that stays clickable forever.
914
934
  */
915
935
  private withdrawOpenQuestions;
936
+ /**
937
+ * Close out the asks a PREVIOUS process of this runner left open (#392).
938
+ *
939
+ * `withdrawOpenQuestions` above reads `running.openQuestions`, and that set
940
+ * is memory: a process that died without reaching `shutdown()` — killed,
941
+ * crashed, machine rebooted — took it along, and the restore used to create
942
+ * a fresh empty set and write only «Runner reconnected». The card in the
943
+ * browser is closed by one thing, a `question_resolved` with its askId in
944
+ * the feed, so it stayed answerable for ever, and everything typed into it
945
+ * was lost (the 07.09.2026 session in the ticket: six restarts, zero
946
+ * tombstones).
947
+ *
948
+ * The journal is the set that survived: `SessionJournal.append` notes every
949
+ * card this runner published and every resolution it published, whoever
950
+ * wrote it. That is also the «already closed» check the plan asks to name:
951
+ * a graceful `shutdown()` sends its `question_resolved` through the same
952
+ * `append`, so by the time a restore runs, a card closed that way is no
953
+ * longer in the set — one tombstone per card, never two.
954
+ */
955
+ private withdrawJournaledQuestions;
916
956
  /**
917
957
  * Deliver messages that were held because every slot was taken.
918
958
  *
@@ -1019,6 +1059,23 @@ export declare class Supervisor {
1019
1059
  * phantom can never fire after the turn it belonged to has closed properly.
1020
1060
  */
1021
1061
  private clearEmptyTurn;
1062
+ /**
1063
+ * A compaction is over for a reason other than the agent saying so (#348):
1064
+ * the turn it rode on ended, the process died, a new process was launched.
1065
+ * The feed gets its closing line so the pair is complete, and the runner's
1066
+ * own state is dropped so the NEXT compaction opens a new pair — left set,
1067
+ * every later start would be read as a repeat and written nowhere (QA S4).
1068
+ */
1069
+ private endCompaction;
1070
+ /** A compaction has begun: say so in the feed, once, and remember who started it (#348). */
1071
+ private openCompaction;
1072
+ /**
1073
+ * The net under a dashboard-requested compaction (#348) — see
1074
+ * `RunningSession.compactionWatchdog`. Fires only into a session that has
1075
+ * said nothing since: every agent event and every ending clears it.
1076
+ */
1077
+ private armCompactionWatchdog;
1078
+ private clearCompactionWatchdog;
1022
1079
  /**
1023
1080
  * Record how many subagents are alive, and say so when it matters (#236).
1024
1081
  *
@@ -1060,8 +1117,9 @@ export declare class Supervisor {
1060
1117
  * It happens for real: a background subagent's report wakes a new turn inside
1061
1118
  * the agent process (SDK 0.3.226 emits a second `system:init` and a `result`
1062
1119
  * carrying `origin: {kind:'task-notification'}`), and nothing here reports a
1063
- * turn the runner did not start. Compaction does the same, and so does a
1064
- * question this runner withdraws by itself.
1120
+ * turn the runner did not start. So does a question this runner withdraws by
1121
+ * itself. (A compaction used to be the third case; `compact_context` reports
1122
+ * RUNNING itself now, and #348 gave the compaction lines of its own.)
1065
1123
  *
1066
1124
  * An open question is the one thing that must survive: there, WAITING_INPUT
1067
1125
  * means a tool call is parked on a person, and the agent narrating around its
@@ -1412,7 +1470,34 @@ export declare class Supervisor {
1412
1470
  * anything down. The name says what it does.
1413
1471
  */
1414
1472
  private finishSession;
1473
+ /**
1474
+ * The terminal status a reconnect should replay for this session instead
1475
+ * of restoring it, or null (QA-96 F1).
1476
+ *
1477
+ * Only a terminal status from THIS life of the session. A resumed session
1478
+ * carries a higher epoch, and replaying the FAILED it was resumed from would
1479
+ * kill it again the moment the runner reconnects. A journal written by a
1480
+ * runner from before session 13 could hold a `DONE` — it is no longer a
1481
+ * status this runner may report, and replaying one would close the session
1482
+ * for the user. Drop it and let the session be picked back up like any
1483
+ * other. Pure: reads the journal and decides, so the pre-registration pass
1484
+ * and the loop answer the question the same way.
1485
+ */
1486
+ private terminalStatusToReplay;
1487
+ /**
1488
+ * Build and register the entry for a session a runner restart interrupted,
1489
+ * and close the cards its dead process left open (#392).
1490
+ *
1491
+ * Synchronous on purpose, and called for EVERY such session before the
1492
+ * reconnect awaits anything: registered, the session is known (a message
1493
+ * for it waits in its queue behind the `starting` gate of #401 instead of
1494
+ * coming back as `session_unknown`), and its cards go dark first thing —
1495
+ * above the line about the restart, so a person reading down sees the card
1496
+ * close before they read why.
1497
+ */
1498
+ private registerRestoredSession;
1415
1499
  private reconcile;
1500
+ private reconcileKnown;
1416
1501
  /**
1417
1502
  * Housekeeping for the journal directory. Safe to call any time: live
1418
1503
  * sessions are skipped and a journal still holding unacked events survives