@threadbase-sh/streamer 1.40.0 → 1.41.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.
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;
@@ -1632,6 +1680,7 @@ declare class StreamerServer {
1632
1680
  private conversationsRepo;
1633
1681
  private sessionsRepo;
1634
1682
  private managedSessionsRepo;
1683
+ private runtimeStore;
1635
1684
  private readonly streamerInstanceId;
1636
1685
  private cacheMetadataRepo;
1637
1686
  private pushRepo;
@@ -1641,6 +1690,7 @@ declare class StreamerServer {
1641
1690
  private liveActivityRenewal;
1642
1691
  private discoveryCache;
1643
1692
  private cacheDir;
1693
+ private runtimeDbPath;
1644
1694
  private tailSize;
1645
1695
  private directoryDebounceMs;
1646
1696
  private markScannerStaleDebounced;
@@ -1702,6 +1752,22 @@ declare class StreamerServer {
1702
1752
  * signals anything. `orphaned` is a report, not a cleanup trigger.
1703
1753
  */
1704
1754
  private reconcilePreviousSessions;
1755
+ /**
1756
+ * Seed the session list with what previous runs left behind (persistence plan
1757
+ * Phase 1, gaps G1/G2/G8).
1758
+ *
1759
+ * Reconciliation classifies rows and stops there; a verdict is overlaid onto a
1760
+ * SessionResponse that already exists, and after a clean restart none does —
1761
+ * `SessionStore` starts empty. So the user's session did not become
1762
+ * `resumable`, it became *absent*. This is the half that puts it back.
1763
+ *
1764
+ * The seeded stubs hold no PTY and are never handed to `LiveSessionManager`,
1765
+ * so `reapIdleSessions` and `startGraceTimer` — both of which iterate
1766
+ * `ptyManager.listSessions()` — cannot observe them. A later resume calls
1767
+ * `sessionStore.addManaged` with the real session, which overwrites the stub
1768
+ * by id rather than duplicating it.
1769
+ */
1770
+ private rehydratePreviousSessions;
1705
1771
  /**
1706
1772
  * Pick a token guaranteed to appear in the spawned process's argv, for the
1707
1773
  * 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;
@@ -1632,6 +1680,7 @@ declare class StreamerServer {
1632
1680
  private conversationsRepo;
1633
1681
  private sessionsRepo;
1634
1682
  private managedSessionsRepo;
1683
+ private runtimeStore;
1635
1684
  private readonly streamerInstanceId;
1636
1685
  private cacheMetadataRepo;
1637
1686
  private pushRepo;
@@ -1641,6 +1690,7 @@ declare class StreamerServer {
1641
1690
  private liveActivityRenewal;
1642
1691
  private discoveryCache;
1643
1692
  private cacheDir;
1693
+ private runtimeDbPath;
1644
1694
  private tailSize;
1645
1695
  private directoryDebounceMs;
1646
1696
  private markScannerStaleDebounced;
@@ -1702,6 +1752,22 @@ declare class StreamerServer {
1702
1752
  * signals anything. `orphaned` is a report, not a cleanup trigger.
1703
1753
  */
1704
1754
  private reconcilePreviousSessions;
1755
+ /**
1756
+ * Seed the session list with what previous runs left behind (persistence plan
1757
+ * Phase 1, gaps G1/G2/G8).
1758
+ *
1759
+ * Reconciliation classifies rows and stops there; a verdict is overlaid onto a
1760
+ * SessionResponse that already exists, and after a clean restart none does —
1761
+ * `SessionStore` starts empty. So the user's session did not become
1762
+ * `resumable`, it became *absent*. This is the half that puts it back.
1763
+ *
1764
+ * The seeded stubs hold no PTY and are never handed to `LiveSessionManager`,
1765
+ * so `reapIdleSessions` and `startGraceTimer` — both of which iterate
1766
+ * `ptyManager.listSessions()` — cannot observe them. A later resume calls
1767
+ * `sessionStore.addManaged` with the real session, which overwrites the stub
1768
+ * by id rather than duplicating it.
1769
+ */
1770
+ private rehydratePreviousSessions;
1705
1771
  /**
1706
1772
  * Pick a token guaranteed to appear in the spawned process's argv, for the
1707
1773
  * reconciler's pid-reuse guard.