@threadbase-sh/streamer 1.40.0 → 1.41.1

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/dist/index.d.cts CHANGED
@@ -182,6 +182,16 @@ interface ManagedSession {
182
182
  statusUpdatedAt?: Date;
183
183
  filePath?: string;
184
184
  resumedFromConversationId?: string;
185
+ /**
186
+ * This session was seeded from the durable registry at boot, not spawned by
187
+ * this run — there is no PTY behind it and never was one in this process.
188
+ *
189
+ * INTERNAL. It never reaches the wire: `managedToResponse` translates it into
190
+ * the already-defined `ownership: "historical"` + `lifecycle: "resumable"`
191
+ * pair, so no client needs to learn a new field to render a recovered session.
192
+ * Cleared implicitly when a resume overwrites the stub with a real session.
193
+ */
194
+ rehydrated?: boolean;
185
195
  /**
186
196
  * Set once a live session's underlying persisted conversation file is
187
197
  * discovered after the fact (currently: fresh Codex sessions, whose
@@ -501,6 +511,7 @@ interface ServerConfig {
501
511
  skipStartupWarmup?: boolean;
502
512
  ptyGracePeriodMs?: number;
503
513
  cacheDir?: string;
514
+ runtimeDbPath?: string;
504
515
  tailSize?: number;
505
516
  directoryScanDebounceMs?: number;
506
517
  defaultSystemPrompt?: string;
@@ -1251,6 +1262,41 @@ declare class SessionsRepository {
1251
1262
  listManagedSessions(): ManagedSession[];
1252
1263
  }
1253
1264
 
1265
+ /**
1266
+ * `~/.threadbase/runtime.db` — the authoritative, non-derived half of the
1267
+ * streamer's SQLite state.
1268
+ *
1269
+ * Deliberately a separate file from `cache/cache.db`, and deliberately not
1270
+ * under `cache/`. The conversation cache is rebuildable from the provider
1271
+ * JSONLs at any time, which is why "delete the cache and restart" is reasonable
1272
+ * support advice and why the integrity monitor offers a reset-and-rescan
1273
+ * action. The managed-session registry is not rebuildable from anything on
1274
+ * disk, so it must not sit one plausible instruction away from deletion — nor
1275
+ * share a handle whose failure (a better-sqlite3 ABI mismatch, most commonly)
1276
+ * would silently disable session persistence along with the cache.
1277
+ *
1278
+ * Runs its own migrations from `src/db/runtime-migrations/`, tracked in this
1279
+ * file's own `schema_migrations` table.
1280
+ */
1281
+ declare class RuntimeStore {
1282
+ private readonly db;
1283
+ private constructor();
1284
+ static open(dbPath: string, migrationsDir?: string): RuntimeStore;
1285
+ getDatabase(): Database.Database;
1286
+ /**
1287
+ * One-time move of `managed_sessions` rows out of a pre-split `cache.db`.
1288
+ *
1289
+ * Non-destructive by design: the source table is left in place so an older
1290
+ * streamer rolled back onto the same machine still finds its registry. Runs
1291
+ * only when this file's table is empty, so a second boot is a no-op rather
1292
+ * than a re-copy that would resurrect rows deleted since.
1293
+ *
1294
+ * Returns the number of rows copied.
1295
+ */
1296
+ importLegacyManagedSessions(source: Database.Database): number;
1297
+ close(): void;
1298
+ }
1299
+
1254
1300
  declare class LiveSessionManager {
1255
1301
  private runners;
1256
1302
  constructor(options?: PTYManagerOptions);
@@ -1413,6 +1459,8 @@ type ApiDeps = {
1413
1459
  conversationsRepo: () => ConversationsRepository | null;
1414
1460
  sessionsRepo: () => SessionsRepository | null;
1415
1461
  cacheMetadataRepo: () => CacheMetadataRepository | null;
1462
+ /** Authoritative session registry (~/.threadbase/runtime.db). Null when it failed to open. */
1463
+ runtimeStore: () => RuntimeStore | null;
1416
1464
  ptyAttachedIds: () => Set<string>;
1417
1465
  handleListSessions: (url: URL, res: ServerResponse) => Promise<void>;
1418
1466
  handleSessionsCount: (res: ServerResponse) => void;
@@ -1533,6 +1581,7 @@ declare class PTYManager implements SessionRunner {
1533
1581
  private chunkIndex;
1534
1582
  private lastChunkAt;
1535
1583
  private quietCheckers;
1584
+ private readyFallbackTimers;
1536
1585
  private startPromises;
1537
1586
  constructor(options?: PTYManagerOptions);
1538
1587
  start(sessionId: string, options: StartSessionOptions): Promise<ManagedSession>;
@@ -1558,6 +1607,8 @@ declare class PTYManager implements SessionRunner {
1558
1607
  private detectLivePrompts;
1559
1608
  private handleQuiet;
1560
1609
  private recheckReadyFromScreen;
1610
+ private armReadyFallback;
1611
+ private clearReadyFallback;
1561
1612
  private markReady;
1562
1613
  private handleExit;
1563
1614
  }
@@ -1632,6 +1683,7 @@ declare class StreamerServer {
1632
1683
  private conversationsRepo;
1633
1684
  private sessionsRepo;
1634
1685
  private managedSessionsRepo;
1686
+ private runtimeStore;
1635
1687
  private readonly streamerInstanceId;
1636
1688
  private cacheMetadataRepo;
1637
1689
  private pushRepo;
@@ -1641,6 +1693,7 @@ declare class StreamerServer {
1641
1693
  private liveActivityRenewal;
1642
1694
  private discoveryCache;
1643
1695
  private cacheDir;
1696
+ private runtimeDbPath;
1644
1697
  private tailSize;
1645
1698
  private directoryDebounceMs;
1646
1699
  private markScannerStaleDebounced;
@@ -1702,6 +1755,22 @@ declare class StreamerServer {
1702
1755
  * signals anything. `orphaned` is a report, not a cleanup trigger.
1703
1756
  */
1704
1757
  private reconcilePreviousSessions;
1758
+ /**
1759
+ * Seed the session list with what previous runs left behind (persistence plan
1760
+ * Phase 1, gaps G1/G2/G8).
1761
+ *
1762
+ * Reconciliation classifies rows and stops there; a verdict is overlaid onto a
1763
+ * SessionResponse that already exists, and after a clean restart none does —
1764
+ * `SessionStore` starts empty. So the user's session did not become
1765
+ * `resumable`, it became *absent*. This is the half that puts it back.
1766
+ *
1767
+ * The seeded stubs hold no PTY and are never handed to `LiveSessionManager`,
1768
+ * so `reapIdleSessions` and `startGraceTimer` — both of which iterate
1769
+ * `ptyManager.listSessions()` — cannot observe them. A later resume calls
1770
+ * `sessionStore.addManaged` with the real session, which overwrites the stub
1771
+ * by id rather than duplicating it.
1772
+ */
1773
+ private rehydratePreviousSessions;
1705
1774
  /**
1706
1775
  * Pick a token guaranteed to appear in the spawned process's argv, for the
1707
1776
  * reconciler's pid-reuse guard.
package/dist/index.d.ts CHANGED
@@ -182,6 +182,16 @@ interface ManagedSession {
182
182
  statusUpdatedAt?: Date;
183
183
  filePath?: string;
184
184
  resumedFromConversationId?: string;
185
+ /**
186
+ * This session was seeded from the durable registry at boot, not spawned by
187
+ * this run — there is no PTY behind it and never was one in this process.
188
+ *
189
+ * INTERNAL. It never reaches the wire: `managedToResponse` translates it into
190
+ * the already-defined `ownership: "historical"` + `lifecycle: "resumable"`
191
+ * pair, so no client needs to learn a new field to render a recovered session.
192
+ * Cleared implicitly when a resume overwrites the stub with a real session.
193
+ */
194
+ rehydrated?: boolean;
185
195
  /**
186
196
  * Set once a live session's underlying persisted conversation file is
187
197
  * discovered after the fact (currently: fresh Codex sessions, whose
@@ -501,6 +511,7 @@ interface ServerConfig {
501
511
  skipStartupWarmup?: boolean;
502
512
  ptyGracePeriodMs?: number;
503
513
  cacheDir?: string;
514
+ runtimeDbPath?: string;
504
515
  tailSize?: number;
505
516
  directoryScanDebounceMs?: number;
506
517
  defaultSystemPrompt?: string;
@@ -1251,6 +1262,41 @@ declare class SessionsRepository {
1251
1262
  listManagedSessions(): ManagedSession[];
1252
1263
  }
1253
1264
 
1265
+ /**
1266
+ * `~/.threadbase/runtime.db` — the authoritative, non-derived half of the
1267
+ * streamer's SQLite state.
1268
+ *
1269
+ * Deliberately a separate file from `cache/cache.db`, and deliberately not
1270
+ * under `cache/`. The conversation cache is rebuildable from the provider
1271
+ * JSONLs at any time, which is why "delete the cache and restart" is reasonable
1272
+ * support advice and why the integrity monitor offers a reset-and-rescan
1273
+ * action. The managed-session registry is not rebuildable from anything on
1274
+ * disk, so it must not sit one plausible instruction away from deletion — nor
1275
+ * share a handle whose failure (a better-sqlite3 ABI mismatch, most commonly)
1276
+ * would silently disable session persistence along with the cache.
1277
+ *
1278
+ * Runs its own migrations from `src/db/runtime-migrations/`, tracked in this
1279
+ * file's own `schema_migrations` table.
1280
+ */
1281
+ declare class RuntimeStore {
1282
+ private readonly db;
1283
+ private constructor();
1284
+ static open(dbPath: string, migrationsDir?: string): RuntimeStore;
1285
+ getDatabase(): Database.Database;
1286
+ /**
1287
+ * One-time move of `managed_sessions` rows out of a pre-split `cache.db`.
1288
+ *
1289
+ * Non-destructive by design: the source table is left in place so an older
1290
+ * streamer rolled back onto the same machine still finds its registry. Runs
1291
+ * only when this file's table is empty, so a second boot is a no-op rather
1292
+ * than a re-copy that would resurrect rows deleted since.
1293
+ *
1294
+ * Returns the number of rows copied.
1295
+ */
1296
+ importLegacyManagedSessions(source: Database.Database): number;
1297
+ close(): void;
1298
+ }
1299
+
1254
1300
  declare class LiveSessionManager {
1255
1301
  private runners;
1256
1302
  constructor(options?: PTYManagerOptions);
@@ -1413,6 +1459,8 @@ type ApiDeps = {
1413
1459
  conversationsRepo: () => ConversationsRepository | null;
1414
1460
  sessionsRepo: () => SessionsRepository | null;
1415
1461
  cacheMetadataRepo: () => CacheMetadataRepository | null;
1462
+ /** Authoritative session registry (~/.threadbase/runtime.db). Null when it failed to open. */
1463
+ runtimeStore: () => RuntimeStore | null;
1416
1464
  ptyAttachedIds: () => Set<string>;
1417
1465
  handleListSessions: (url: URL, res: ServerResponse) => Promise<void>;
1418
1466
  handleSessionsCount: (res: ServerResponse) => void;
@@ -1533,6 +1581,7 @@ declare class PTYManager implements SessionRunner {
1533
1581
  private chunkIndex;
1534
1582
  private lastChunkAt;
1535
1583
  private quietCheckers;
1584
+ private readyFallbackTimers;
1536
1585
  private startPromises;
1537
1586
  constructor(options?: PTYManagerOptions);
1538
1587
  start(sessionId: string, options: StartSessionOptions): Promise<ManagedSession>;
@@ -1558,6 +1607,8 @@ declare class PTYManager implements SessionRunner {
1558
1607
  private detectLivePrompts;
1559
1608
  private handleQuiet;
1560
1609
  private recheckReadyFromScreen;
1610
+ private armReadyFallback;
1611
+ private clearReadyFallback;
1561
1612
  private markReady;
1562
1613
  private handleExit;
1563
1614
  }
@@ -1632,6 +1683,7 @@ declare class StreamerServer {
1632
1683
  private conversationsRepo;
1633
1684
  private sessionsRepo;
1634
1685
  private managedSessionsRepo;
1686
+ private runtimeStore;
1635
1687
  private readonly streamerInstanceId;
1636
1688
  private cacheMetadataRepo;
1637
1689
  private pushRepo;
@@ -1641,6 +1693,7 @@ declare class StreamerServer {
1641
1693
  private liveActivityRenewal;
1642
1694
  private discoveryCache;
1643
1695
  private cacheDir;
1696
+ private runtimeDbPath;
1644
1697
  private tailSize;
1645
1698
  private directoryDebounceMs;
1646
1699
  private markScannerStaleDebounced;
@@ -1702,6 +1755,22 @@ declare class StreamerServer {
1702
1755
  * signals anything. `orphaned` is a report, not a cleanup trigger.
1703
1756
  */
1704
1757
  private reconcilePreviousSessions;
1758
+ /**
1759
+ * Seed the session list with what previous runs left behind (persistence plan
1760
+ * Phase 1, gaps G1/G2/G8).
1761
+ *
1762
+ * Reconciliation classifies rows and stops there; a verdict is overlaid onto a
1763
+ * SessionResponse that already exists, and after a clean restart none does —
1764
+ * `SessionStore` starts empty. So the user's session did not become
1765
+ * `resumable`, it became *absent*. This is the half that puts it back.
1766
+ *
1767
+ * The seeded stubs hold no PTY and are never handed to `LiveSessionManager`,
1768
+ * so `reapIdleSessions` and `startGraceTimer` — both of which iterate
1769
+ * `ptyManager.listSessions()` — cannot observe them. A later resume calls
1770
+ * `sessionStore.addManaged` with the real session, which overwrites the stub
1771
+ * by id rather than duplicating it.
1772
+ */
1773
+ private rehydratePreviousSessions;
1705
1774
  /**
1706
1775
  * Pick a token guaranteed to appear in the spawned process's argv, for the
1707
1776
  * reconciler's pid-reuse guard.