@threadbase-sh/streamer 1.36.4 → 1.37.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.
@@ -0,0 +1,65 @@
1
+ -- Managed-session registry (durable session runtime, C1 Phase 2).
2
+ -- See docs/architecture/2026-07-24-durable-session-runtime.md.
3
+ --
4
+ -- Until now managed session state lived only in SessionStore's in-memory Maps,
5
+ -- so a streamer restart lost startedAt, promptCount, sessionName, projectId,
6
+ -- the Codex placeholder→rollout binding, and failureReason outright. Sessions
7
+ -- did not come back as "recoverable" — they ceased to exist, reappearing at
8
+ -- best as external discovered processes with no managed metadata.
9
+ --
10
+ -- This table stores identity and provenance so the boot reconciler can say what
11
+ -- happened to each session. It deliberately does NOT store the byte stream:
12
+ -- outputBuffer is 64KiB of raw ANSI rewritten on every PTY chunk, and its
13
+ -- authoritative copy is already the provider's JSONL. Persisting it would turn
14
+ -- every chunk into a DB write to duplicate data we can re-read. Post-restart
15
+ -- replay is therefore conversation-accurate, not byte-accurate.
16
+ CREATE TABLE IF NOT EXISTS managed_sessions (
17
+ -- Provider-native resume identifier: the JSONL UUID for Claude Code, the
18
+ -- rollout id for Codex. This is what --resume / `codex resume` consumes, so
19
+ -- the registry never invents an identifier of its own.
20
+ session_id TEXT PRIMARY KEY,
21
+ provider TEXT NOT NULL,
22
+
23
+ -- Liveness probing. pid alone is never treated as identity: PIDs get reused,
24
+ -- so the reconciler matches the recorded cmdline before claiming a live
25
+ -- process is ours, and reports `orphaned` on a mismatch rather than guessing.
26
+ pid INTEGER,
27
+ cmdline TEXT,
28
+
29
+ project_path TEXT NOT NULL,
30
+ project_name TEXT NOT NULL,
31
+ branch TEXT NOT NULL DEFAULT '',
32
+
33
+ -- Semantic status (running/waiting_input/idle) as last observed. The
34
+ -- reconciler never trusts this over a live PID probe — a SIGKILLed streamer
35
+ -- never ran its exit writes, so a stored 'running' can be arbitrarily stale.
36
+ -- status_source records how the value was obtained so that staleness is
37
+ -- visible instead of implied.
38
+ status TEXT NOT NULL,
39
+ status_source TEXT NOT NULL,
40
+ status_updated_at INTEGER NOT NULL,
41
+
42
+ started_at INTEGER NOT NULL,
43
+ completed_at INTEGER,
44
+ last_activity_at INTEGER,
45
+ prompt_count INTEGER NOT NULL DEFAULT 0,
46
+
47
+ -- User-visible identity that today vanishes silently on restart.
48
+ session_name TEXT,
49
+ project_id TEXT,
50
+ -- Codex two-id model: the placeholder id the session was created under vs the
51
+ -- rollout id its history is indexed by. Losing this on restart orphans the
52
+ -- conversation from the session (types.ts:36-44).
53
+ bound_conversation_id TEXT,
54
+ resumed_from_conversation_id TEXT,
55
+ failure_reason TEXT,
56
+
57
+ -- Which streamer run started this session. A row whose instance differs from
58
+ -- the current run is the orphan test: the process outlived its streamer.
59
+ streamer_instance_id TEXT NOT NULL
60
+ );
61
+
62
+ -- The reconciler's only read path on boot: every row not already in a terminal
63
+ -- state. Terminal rows are retained for history but never re-probed.
64
+ CREATE INDEX IF NOT EXISTS idx_managed_sessions_status
65
+ ON managed_sessions (status);
@@ -0,0 +1,39 @@
1
+ -- Paired-device registry (C5 scoped device capabilities).
2
+ -- See docs/architecture/2026-07-24-device-identity-and-capabilities.md.
3
+ --
4
+ -- Pairing exchanged a short-lived token for the streamer's API key — the SAME
5
+ -- string for every device that ever paired. Nothing recorded that a device
6
+ -- existed, so there was no attribution, no per-device revocation (rotating the
7
+ -- key de-authenticated everyone at once), and no way to scope authority.
8
+ CREATE TABLE IF NOT EXISTS devices (
9
+ -- Minted server-side. A client-supplied id would let one device claim
10
+ -- another's identity.
11
+ device_id TEXT PRIMARY KEY,
12
+
13
+ -- The client public key already supplied at pairing, previously used once as
14
+ -- a sealing target and then discarded.
15
+ public_key TEXT NOT NULL,
16
+
17
+ -- SHA-256 of the device token, never the token itself: a read of this table
18
+ -- must not let anyone impersonate a device. Same reasoning as password hashes.
19
+ token_hash TEXT NOT NULL UNIQUE,
20
+
21
+ -- Client-supplied label ("Ronen's iPhone"). Display only, never trusted for
22
+ -- authorization.
23
+ name TEXT,
24
+
25
+ -- JSON array of capability strings. Unknown entries are dropped on read, so a
26
+ -- downgrade cannot silently grant a capability this build does not understand.
27
+ capabilities TEXT NOT NULL,
28
+
29
+ created_at INTEGER NOT NULL,
30
+ last_seen_at INTEGER,
31
+
32
+ -- Set to revoke. Checked per request rather than cached — a stale cache is
33
+ -- exactly the window that makes revocation useless.
34
+ revoked_at INTEGER
35
+ );
36
+
37
+ -- Authentication looks a device up by token hash on every request, so this is
38
+ -- the hot path.
39
+ CREATE INDEX IF NOT EXISTS idx_devices_token_hash ON devices (token_hash);
@@ -0,0 +1,49 @@
1
+ -- Push registration and delivery state (C7 notification reliability).
2
+ --
3
+ -- POST /api/push/register was a no-op returning { ok: true }: mobile registered,
4
+ -- received success, and nothing was stored. No token existed, so no notification
5
+ -- could ever be delivered, no failure could be observed, and the client had no
6
+ -- way to discover that its "successful" registration meant nothing.
7
+ CREATE TABLE IF NOT EXISTS push_tokens (
8
+ -- The provider push token (Expo). Natural key: re-registering the same token
9
+ -- must update the existing row rather than accumulate duplicates, which is
10
+ -- how the same device ends up receiving one notification several times.
11
+ token TEXT PRIMARY KEY,
12
+ platform TEXT NOT NULL,
13
+
14
+ -- Optional device attribution. Nullable because push registration predates
15
+ -- device identity (C5) and must keep working without it.
16
+ device_id TEXT,
17
+
18
+ registered_at INTEGER NOT NULL,
19
+
20
+ -- Delivery health. Distinguishing "we have never tried" from "we tried and it
21
+ -- failed" is the difference between a client showing "not yet delivered" and
22
+ -- "your notifications are broken" — the report the user actually needs.
23
+ last_success_at INTEGER,
24
+ last_failure_at INTEGER,
25
+ last_failure_code TEXT,
26
+
27
+ -- Consecutive failures. Reset on success. A token the provider has rejected
28
+ -- repeatedly is dead (app uninstalled, token rotated) and should stop being
29
+ -- retried rather than failing forever.
30
+ failure_streak INTEGER NOT NULL DEFAULT 0,
31
+
32
+ -- Set when the provider tells us the token is permanently invalid, or the
33
+ -- user unregisters. Retained rather than deleted so the health report can
34
+ -- explain why delivery stopped.
35
+ revoked_at INTEGER
36
+ );
37
+
38
+ -- Delivery attempts are keyed by event id so a retry, a reconnect
39
+ -- reconciliation, or a duplicate trigger cannot notify the user twice for the
40
+ -- same underlying event.
41
+ CREATE TABLE IF NOT EXISTS push_events (
42
+ event_id TEXT PRIMARY KEY,
43
+ session_id TEXT,
44
+ created_at INTEGER NOT NULL,
45
+ delivered_at INTEGER
46
+ );
47
+
48
+ CREATE INDEX IF NOT EXISTS idx_push_tokens_revoked ON push_tokens (revoked_at);
49
+ CREATE INDEX IF NOT EXISTS idx_push_events_created ON push_events (created_at);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threadbase-sh/streamer",
3
- "version": "1.36.4",
3
+ "version": "1.37.0",
4
4
  "description": "PTY session management, WebSocket streaming, and REST API server for Claude Code conversations",
5
5
  "license": "MIT",
6
6
  "author": "Ronen Mars",