instar 1.3.388 → 1.3.390

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,104 @@
1
+ /**
2
+ * PeerStreamProxy — phase 1 of Pool Dashboard Streaming
3
+ * (POOL-DASHBOARD-STREAM-SPEC §2.2, the scalability core).
4
+ *
5
+ * ONE multiplexed upstream connection per PEER machine, shared by every local
6
+ * dashboard client and every remote session on that peer. The local
7
+ * WebSocketManager (phase 2) owns local clients and the browser WS; this class
8
+ * owns ONLY the single upstream link and the bookkeeping that makes it correct:
9
+ *
10
+ * - reference-counted subscriptions (per (session) → set of local clientIds);
11
+ * - a 60s idle grace after the last unsubscribe, cancelled if a subscribe
12
+ * arrives during it (no thrash opening/closing on a flapping client);
13
+ * - ONE bounded reconnect on an upstream drop, resubscribing every current
14
+ * subscription; a reconnect that does not open within reconnectTimeoutMs, or
15
+ * a second drop, surfaces `machine-unreachable` (P19 — never a reconnect
16
+ * storm);
17
+ * - subscriptions that arrive mid-(re)connect are QUEUED and merged into the
18
+ * resubscribe batch the moment the link opens — never lost;
19
+ * - the peer URL is re-resolved on every subscribe; a changed URL tears the
20
+ * old link down and opens a new one (no stale-URL duplicate links).
21
+ *
22
+ * Everything external is injected (transport, timers, clock) so the whole state
23
+ * machine is deterministically unit-testable without real sockets or wall time.
24
+ * This module performs NO tmux capture and NO local fan-out polling — capture
25
+ * happens only on the owning machine; fan-out to local clients is the injected
26
+ * `onFrameToClients`. (TAP POINT, ops#1: remote subs never enter the local
27
+ * polling loop.)
28
+ */
29
+ export type ProxyState = 'idle' | 'connecting' | 'active' | 'idle-scheduled' | 'closing' | 'closed';
30
+ /** A live upstream connection handle (one peer's /ws/pool-stream). */
31
+ export interface UpstreamTransport {
32
+ /** Send a frame to the peer (subscribe/unsubscribe/input/key/history). */
33
+ send(frame: Record<string, unknown>): void;
34
+ /** Tear the connection down. Idempotent. */
35
+ close(): void;
36
+ }
37
+ /** Handlers the proxy gives the transport at connect time. */
38
+ export interface UpstreamHandlers {
39
+ /** The link opened and is ready to carry frames. */
40
+ onOpen(): void;
41
+ /** A frame arrived from the peer (output/history/session_ended/error/…). */
42
+ onFrame(frame: Record<string, unknown>): void;
43
+ /** The link closed (clean or error). */
44
+ onClose(): void;
45
+ }
46
+ export type TimerHandle = {
47
+ __brand: 'PeerStreamProxyTimer';
48
+ };
49
+ export interface PeerStreamProxyDeps {
50
+ peerMachineId: string;
51
+ /** Resolve the peer's CURRENT url (re-read each subscribe for URL-change detection). */
52
+ resolveUrl: () => string | null;
53
+ /** Open an upstream link to `url`; the returned transport drives `handlers`. */
54
+ connect: (url: string, handlers: UpstreamHandlers) => UpstreamTransport;
55
+ /** Fan a peer frame out to every LOCAL client subscribed to (session). */
56
+ onFrameToClients: (session: string, frame: Record<string, unknown>) => void;
57
+ /** Surface an error to every local client subscribed to (session). */
58
+ onError: (session: string, code: 'peer-stream-lost' | 'machine-unreachable', detail?: string) => void;
59
+ now: () => number;
60
+ setTimer: (ms: number, fn: () => void) => TimerHandle;
61
+ clearTimer: (t: TimerHandle) => void;
62
+ /** Idle grace before closing an unsubscribed link (default 60_000). */
63
+ idleGraceMs?: number;
64
+ /** Reconnect open deadline (default 10_000). */
65
+ reconnectTimeoutMs?: number;
66
+ logger?: (line: string) => void;
67
+ }
68
+ export declare class PeerStreamProxy {
69
+ private readonly d;
70
+ private state;
71
+ private transport;
72
+ private connectedUrl;
73
+ /** session → set of local clientIds subscribed to it. THE source of truth:
74
+ * on every (re)open we resubscribe from this map, so a subscribe that
75
+ * arrives mid-connect is never lost and a mid-connect unsubscribe is never
76
+ * replayed. */
77
+ private readonly subs;
78
+ private idleTimer;
79
+ private reconnectTimer;
80
+ private reconnectUsed;
81
+ constructor(d: PeerStreamProxyDeps);
82
+ get currentState(): ProxyState;
83
+ /** Total live subscriptions across all sessions (refcount). */
84
+ get refCount(): number;
85
+ /** Snapshot of session → clientIds (tests / introspection). */
86
+ sessionsView(): Record<string, string[]>;
87
+ private log;
88
+ /** A local client subscribes to a remote session on this peer. */
89
+ subscribe(session: string, clientId: string): void;
90
+ /** A local client unsubscribes (or disconnects). */
91
+ unsubscribe(session: string, clientId: string): void;
92
+ /** Forward an input/key frame for a session (caller already gated allowRemoteInput). */
93
+ relayInput(frame: Record<string, unknown>): void;
94
+ private openFresh;
95
+ private onUpstreamOpen;
96
+ private onUpstreamFrame;
97
+ private onUpstreamClose;
98
+ private scheduleIdleClose;
99
+ private failAll;
100
+ private teardownTransport;
101
+ /** Force-close (peer removed, server shutdown). Idempotent. */
102
+ close(): void;
103
+ }
104
+ //# sourceMappingURL=PeerStreamProxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PeerStreamProxy.d.ts","sourceRoot":"","sources":["../../src/server/PeerStreamProxy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,270 @@
1
+ /**
2
+ * PeerStreamProxy — phase 1 of Pool Dashboard Streaming
3
+ * (POOL-DASHBOARD-STREAM-SPEC §2.2, the scalability core).
4
+ *
5
+ * ONE multiplexed upstream connection per PEER machine, shared by every local
6
+ * dashboard client and every remote session on that peer. The local
7
+ * WebSocketManager (phase 2) owns local clients and the browser WS; this class
8
+ * owns ONLY the single upstream link and the bookkeeping that makes it correct:
9
+ *
10
+ * - reference-counted subscriptions (per (session) → set of local clientIds);
11
+ * - a 60s idle grace after the last unsubscribe, cancelled if a subscribe
12
+ * arrives during it (no thrash opening/closing on a flapping client);
13
+ * - ONE bounded reconnect on an upstream drop, resubscribing every current
14
+ * subscription; a reconnect that does not open within reconnectTimeoutMs, or
15
+ * a second drop, surfaces `machine-unreachable` (P19 — never a reconnect
16
+ * storm);
17
+ * - subscriptions that arrive mid-(re)connect are QUEUED and merged into the
18
+ * resubscribe batch the moment the link opens — never lost;
19
+ * - the peer URL is re-resolved on every subscribe; a changed URL tears the
20
+ * old link down and opens a new one (no stale-URL duplicate links).
21
+ *
22
+ * Everything external is injected (transport, timers, clock) so the whole state
23
+ * machine is deterministically unit-testable without real sockets or wall time.
24
+ * This module performs NO tmux capture and NO local fan-out polling — capture
25
+ * happens only on the owning machine; fan-out to local clients is the injected
26
+ * `onFrameToClients`. (TAP POINT, ops#1: remote subs never enter the local
27
+ * polling loop.)
28
+ */
29
+ const DEFAULT_IDLE_GRACE_MS = 60_000;
30
+ const DEFAULT_RECONNECT_TIMEOUT_MS = 10_000;
31
+ export class PeerStreamProxy {
32
+ d;
33
+ state = 'idle';
34
+ transport = null;
35
+ connectedUrl = null;
36
+ /** session → set of local clientIds subscribed to it. THE source of truth:
37
+ * on every (re)open we resubscribe from this map, so a subscribe that
38
+ * arrives mid-connect is never lost and a mid-connect unsubscribe is never
39
+ * replayed. */
40
+ subs = new Map();
41
+ idleTimer = null;
42
+ reconnectTimer = null;
43
+ reconnectUsed = false;
44
+ constructor(d) {
45
+ this.d = d;
46
+ }
47
+ get currentState() {
48
+ return this.state;
49
+ }
50
+ /** Total live subscriptions across all sessions (refcount). */
51
+ get refCount() {
52
+ let n = 0;
53
+ for (const set of this.subs.values())
54
+ n += set.size;
55
+ return n;
56
+ }
57
+ /** Snapshot of session → clientIds (tests / introspection). */
58
+ sessionsView() {
59
+ const out = {};
60
+ for (const [s, set] of this.subs)
61
+ out[s] = [...set];
62
+ return out;
63
+ }
64
+ log(line) {
65
+ this.d.logger?.(`[peer-stream-proxy ${this.d.peerMachineId}] ${line}`);
66
+ }
67
+ // ── subscribe / unsubscribe (refcounted) ───────────────────────────────
68
+ /** A local client subscribes to a remote session on this peer. */
69
+ subscribe(session, clientId) {
70
+ if (this.state === 'closed') {
71
+ this.log('subscribe after close — ignored');
72
+ return;
73
+ }
74
+ // Cancel a pending idle close — we have demand again.
75
+ if (this.idleTimer) {
76
+ this.d.clearTimer(this.idleTimer);
77
+ this.idleTimer = null;
78
+ if (this.state === 'idle-scheduled')
79
+ this.state = 'active';
80
+ }
81
+ let set = this.subs.get(session);
82
+ if (!set) {
83
+ set = new Set();
84
+ this.subs.set(session, set);
85
+ }
86
+ const firstForSession = set.size === 0;
87
+ set.add(clientId);
88
+ // URL re-resolution (stale-URL guard): if the peer moved, reconnect fresh.
89
+ const url = this.d.resolveUrl();
90
+ if (this.state === 'active' && this.connectedUrl && url && url !== this.connectedUrl) {
91
+ this.log(`peer url changed (${this.connectedUrl} → ${url}); reconnecting`);
92
+ this.teardownTransport();
93
+ this.openFresh(url);
94
+ return; // openFresh replays ALL current subs (incl. this one) on open
95
+ }
96
+ if (this.state === 'idle' || this.state === 'closing') {
97
+ // No link yet → open one; openFresh flushes all subs on open.
98
+ if (!url) {
99
+ // No reachable URL — honest failure, drop the just-added sub for it.
100
+ set.delete(clientId);
101
+ if (set.size === 0)
102
+ this.subs.delete(session);
103
+ this.d.onError(session, 'machine-unreachable', 'no url for peer');
104
+ return;
105
+ }
106
+ this.openFresh(url);
107
+ return;
108
+ }
109
+ if (this.state === 'active') {
110
+ // Link is live → forward only when this is the first client for the
111
+ // session (the upstream is already streaming it for any later client).
112
+ if (firstForSession)
113
+ this.transport?.send({ type: 'subscribe', session });
114
+ return;
115
+ }
116
+ // connecting → the sub is now in `subs`; onUpstreamOpen resubscribes from
117
+ // `subs`, so it is automatically included in the batch (never lost).
118
+ }
119
+ /** A local client unsubscribes (or disconnects). */
120
+ unsubscribe(session, clientId) {
121
+ const set = this.subs.get(session);
122
+ if (!set)
123
+ return;
124
+ set.delete(clientId);
125
+ if (set.size === 0) {
126
+ this.subs.delete(session);
127
+ if (this.state === 'active')
128
+ this.transport?.send({ type: 'unsubscribe', session });
129
+ }
130
+ if (this.refCount === 0)
131
+ this.scheduleIdleClose();
132
+ }
133
+ /** Forward an input/key frame for a session (caller already gated allowRemoteInput). */
134
+ relayInput(frame) {
135
+ if (this.state === 'active')
136
+ this.transport?.send(frame);
137
+ // Not active → input is dropped (caller's terminal shows the live state);
138
+ // we never queue keystrokes across a reconnect (stale input is worse than none).
139
+ }
140
+ // ── connection lifecycle ───────────────────────────────────────────────
141
+ openFresh(url, reconnect = false) {
142
+ // A user-initiated open resets the one-shot reconnect budget; a reconnect
143
+ // leaves `reconnectUsed` true so a further drop → machine-unreachable.
144
+ if (!reconnect)
145
+ this.reconnectUsed = false;
146
+ this.state = 'connecting';
147
+ this.connectedUrl = url;
148
+ const handlers = {
149
+ onOpen: () => this.onUpstreamOpen(),
150
+ onFrame: (f) => this.onUpstreamFrame(f),
151
+ onClose: () => this.onUpstreamClose(),
152
+ };
153
+ try {
154
+ this.transport = this.d.connect(url, handlers);
155
+ }
156
+ catch (e) {
157
+ this.log(`connect threw: ${e?.message ?? e}`);
158
+ this.onUpstreamClose();
159
+ }
160
+ }
161
+ onUpstreamOpen() {
162
+ if (this.state === 'closed') {
163
+ this.transport?.close();
164
+ return;
165
+ }
166
+ this.state = 'active';
167
+ if (this.reconnectTimer) {
168
+ this.d.clearTimer(this.reconnectTimer);
169
+ this.reconnectTimer = null;
170
+ }
171
+ // Resubscribe from `subs` (the source of truth) — this naturally includes
172
+ // any subscribe that arrived mid-connect and excludes any mid-connect
173
+ // unsubscribe. No stale snapshot to lose.
174
+ for (const session of this.subs.keys())
175
+ this.transport?.send({ type: 'subscribe', session });
176
+ // If nothing is subscribed anymore (all unsubscribed during connect), idle-close.
177
+ if (this.refCount === 0)
178
+ this.scheduleIdleClose();
179
+ }
180
+ onUpstreamFrame(frame) {
181
+ const session = typeof frame.session === 'string' ? frame.session : null;
182
+ if (!session)
183
+ return; // peer frames without a session are not fan-out-able
184
+ // Only fan out to sessions we still have local subscribers for.
185
+ if (!this.subs.has(session))
186
+ return;
187
+ this.d.onFrameToClients(session, frame);
188
+ }
189
+ onUpstreamClose() {
190
+ if (this.state === 'closed' || this.state === 'closing') {
191
+ this.state = 'closed';
192
+ return;
193
+ }
194
+ this.transport = null;
195
+ // Nothing subscribed → just go idle, no reconnect.
196
+ if (this.refCount === 0) {
197
+ this.state = 'idle';
198
+ return;
199
+ }
200
+ if (this.reconnectUsed) {
201
+ // Second failure → declare the peer unreachable for every affected session.
202
+ this.log('second upstream drop → machine-unreachable');
203
+ this.failAll('machine-unreachable', 'peer unreachable after reconnect');
204
+ return;
205
+ }
206
+ // First drop → tell clients, attempt ONE bounded reconnect.
207
+ for (const session of this.subs.keys())
208
+ this.d.onError(session, 'peer-stream-lost', 'upstream dropped');
209
+ this.reconnectUsed = true;
210
+ const url = this.d.resolveUrl();
211
+ if (!url) {
212
+ this.failAll('machine-unreachable', 'no url on reconnect');
213
+ return;
214
+ }
215
+ this.openFresh(url, true);
216
+ // Bound the reconnect: if it doesn't open in time, declare unreachable.
217
+ this.reconnectTimer = this.d.setTimer(this.d.reconnectTimeoutMs ?? DEFAULT_RECONNECT_TIMEOUT_MS, () => {
218
+ this.reconnectTimer = null;
219
+ if (this.state !== 'active') {
220
+ this.log('reconnect timed out → machine-unreachable');
221
+ this.teardownTransport();
222
+ this.failAll('machine-unreachable', 'reconnect timed out');
223
+ }
224
+ });
225
+ }
226
+ scheduleIdleClose() {
227
+ if (this.idleTimer || this.state === 'closed')
228
+ return;
229
+ if (this.state === 'active')
230
+ this.state = 'idle-scheduled';
231
+ this.idleTimer = this.d.setTimer(this.d.idleGraceMs ?? DEFAULT_IDLE_GRACE_MS, () => {
232
+ this.idleTimer = null;
233
+ if (this.refCount === 0) {
234
+ this.log('idle grace elapsed → closing');
235
+ this.teardownTransport();
236
+ this.state = 'closed';
237
+ }
238
+ });
239
+ }
240
+ failAll(code, detail) {
241
+ for (const session of this.subs.keys())
242
+ this.d.onError(session, code, detail);
243
+ this.subs.clear();
244
+ this.teardownTransport();
245
+ this.state = 'closed';
246
+ }
247
+ teardownTransport() {
248
+ if (this.reconnectTimer) {
249
+ this.d.clearTimer(this.reconnectTimer);
250
+ this.reconnectTimer = null;
251
+ }
252
+ try {
253
+ this.transport?.close();
254
+ }
255
+ catch { /* @silent-fallback-ok: closing an already-dead upstream is best-effort */ }
256
+ this.transport = null;
257
+ this.connectedUrl = null;
258
+ }
259
+ /** Force-close (peer removed, server shutdown). Idempotent. */
260
+ close() {
261
+ if (this.idleTimer) {
262
+ this.d.clearTimer(this.idleTimer);
263
+ this.idleTimer = null;
264
+ }
265
+ this.subs.clear();
266
+ this.teardownTransport();
267
+ this.state = 'closed';
268
+ }
269
+ }
270
+ //# sourceMappingURL=PeerStreamProxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PeerStreamProxy.js","sourceRoot":"","sources":["../../src/server/PeerStreamProxy.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.388",
3
+ "version": "1.3.390",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-06-07T00:43:22.312Z",
5
- "instarVersion": "1.3.388",
4
+ "generatedAt": "2026-06-07T01:55:10.660Z",
5
+ "instarVersion": "1.3.390",
6
6
  "entryCount": 199,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -11,7 +11,7 @@
11
11
  "domain": "identity",
12
12
  "sourcePath": "src/core/PostUpdateMigrator.ts",
13
13
  "installedPath": ".instar/hooks/instar/session-start.sh",
14
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
14
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
15
15
  "since": "2025-01-01"
16
16
  },
17
17
  "hook:dangerous-command-guard": {
@@ -20,7 +20,7 @@
20
20
  "domain": "safety",
21
21
  "sourcePath": "src/core/PostUpdateMigrator.ts",
22
22
  "installedPath": ".instar/hooks/instar/dangerous-command-guard.sh",
23
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
23
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
24
24
  "since": "2025-01-01"
25
25
  },
26
26
  "hook:grounding-before-messaging": {
@@ -29,7 +29,7 @@
29
29
  "domain": "safety",
30
30
  "sourcePath": "src/core/PostUpdateMigrator.ts",
31
31
  "installedPath": ".instar/hooks/instar/grounding-before-messaging.sh",
32
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
32
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
33
33
  "since": "2025-01-01"
34
34
  },
35
35
  "hook:compaction-recovery": {
@@ -38,7 +38,7 @@
38
38
  "domain": "identity",
39
39
  "sourcePath": "src/core/PostUpdateMigrator.ts",
40
40
  "installedPath": ".instar/hooks/instar/compaction-recovery.sh",
41
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
41
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
42
42
  "since": "2025-01-01"
43
43
  },
44
44
  "hook:external-operation-gate": {
@@ -47,7 +47,7 @@
47
47
  "domain": "safety",
48
48
  "sourcePath": "src/core/PostUpdateMigrator.ts",
49
49
  "installedPath": ".instar/hooks/instar/external-operation-gate.js",
50
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
50
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
51
51
  "since": "2025-01-01"
52
52
  },
53
53
  "hook:deferral-detector": {
@@ -56,7 +56,7 @@
56
56
  "domain": "safety",
57
57
  "sourcePath": "src/core/PostUpdateMigrator.ts",
58
58
  "installedPath": ".instar/hooks/instar/deferral-detector.js",
59
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
59
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
60
60
  "since": "2025-01-01"
61
61
  },
62
62
  "hook:self-stop-guard": {
@@ -65,7 +65,7 @@
65
65
  "domain": "coherence",
66
66
  "sourcePath": "src/core/PostUpdateMigrator.ts",
67
67
  "installedPath": ".instar/hooks/instar/self-stop-guard.js",
68
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
68
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
69
69
  "since": "2025-01-01"
70
70
  },
71
71
  "hook:post-action-reflection": {
@@ -74,7 +74,7 @@
74
74
  "domain": "evolution",
75
75
  "sourcePath": "src/core/PostUpdateMigrator.ts",
76
76
  "installedPath": ".instar/hooks/instar/post-action-reflection.js",
77
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
77
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
78
78
  "since": "2025-01-01"
79
79
  },
80
80
  "hook:external-communication-guard": {
@@ -83,7 +83,7 @@
83
83
  "domain": "safety",
84
84
  "sourcePath": "src/core/PostUpdateMigrator.ts",
85
85
  "installedPath": ".instar/hooks/instar/external-communication-guard.js",
86
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
86
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
87
87
  "since": "2025-01-01"
88
88
  },
89
89
  "hook:scope-coherence-collector": {
@@ -92,7 +92,7 @@
92
92
  "domain": "coherence",
93
93
  "sourcePath": "src/core/PostUpdateMigrator.ts",
94
94
  "installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
95
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
95
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
96
96
  "since": "2025-01-01"
97
97
  },
98
98
  "hook:scope-coherence-checkpoint": {
@@ -101,7 +101,7 @@
101
101
  "domain": "coherence",
102
102
  "sourcePath": "src/core/PostUpdateMigrator.ts",
103
103
  "installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
104
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
104
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
105
105
  "since": "2025-01-01"
106
106
  },
107
107
  "hook:free-text-guard": {
@@ -110,7 +110,7 @@
110
110
  "domain": "safety",
111
111
  "sourcePath": "src/core/PostUpdateMigrator.ts",
112
112
  "installedPath": ".instar/hooks/instar/free-text-guard.sh",
113
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
113
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
114
114
  "since": "2025-01-01"
115
115
  },
116
116
  "hook:claim-intercept": {
@@ -119,7 +119,7 @@
119
119
  "domain": "coherence",
120
120
  "sourcePath": "src/core/PostUpdateMigrator.ts",
121
121
  "installedPath": ".instar/hooks/instar/claim-intercept.js",
122
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
122
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
123
123
  "since": "2025-01-01"
124
124
  },
125
125
  "hook:claim-intercept-response": {
@@ -128,7 +128,7 @@
128
128
  "domain": "coherence",
129
129
  "sourcePath": "src/core/PostUpdateMigrator.ts",
130
130
  "installedPath": ".instar/hooks/instar/claim-intercept-response.js",
131
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
131
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
132
132
  "since": "2025-01-01"
133
133
  },
134
134
  "hook:stop-gate-router": {
@@ -137,7 +137,7 @@
137
137
  "domain": "safety",
138
138
  "sourcePath": "src/core/PostUpdateMigrator.ts",
139
139
  "installedPath": ".instar/hooks/instar/stop-gate-router.js",
140
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
140
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
141
141
  "since": "2025-01-01"
142
142
  },
143
143
  "hook:auto-approve-permissions": {
@@ -146,7 +146,7 @@
146
146
  "domain": "safety",
147
147
  "sourcePath": "src/core/PostUpdateMigrator.ts",
148
148
  "installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
149
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
149
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
150
150
  "since": "2025-01-01"
151
151
  },
152
152
  "job:health-check": {
@@ -1538,7 +1538,7 @@
1538
1538
  "type": "subsystem",
1539
1539
  "domain": "updates",
1540
1540
  "sourcePath": "src/core/PostUpdateMigrator.ts",
1541
- "contentHash": "8d2952d0097dacb8077f2cf4dc3e48ee95de7aacf3517ca1d36aad156a3a4a7b",
1541
+ "contentHash": "3e0828b523c2cece3ea4c3c73e77867c2d6884b7493c93e523318e63f5c09e30",
1542
1542
  "since": "2025-01-01"
1543
1543
  },
1544
1544
  "subsystem:scheduler": {
@@ -0,0 +1,45 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ First building block of the single-dashboard cross-machine streaming feature
9
+ (POOL-DASHBOARD-STREAM-SPEC): the `PeerStreamProxy` — one multiplexed upstream
10
+ connection per peer machine that the dashboard's WebSocket layer will use
11
+ (phase 2) to relay a remote session's terminal stream. This phase ships the
12
+ state machine ONLY; it is not yet wired to the live WebSocketManager, so it
13
+ changes no user-visible behavior on its own.
14
+
15
+ The proxy is the scalability + correctness core the reviewers stressed:
16
+ reference-counted subscriptions, a 60s idle grace (cancelled if demand
17
+ returns), ONE bounded reconnect on a drop with full resubscribe (a second drop
18
+ or a reconnect timeout → machine-unreachable, never a reconnect storm),
19
+ mid-reconnect subscribes merged from the source-of-truth subscription map (never
20
+ lost), and peer-URL-change detection (stale-URL links are torn down). Capture
21
+ stays on the owning machine; this only relays — so fleet cost is
22
+ O(subscriptions), not O(sessions × clients × peers).
23
+
24
+ ## What to Tell Your User
25
+
26
+ Nothing yet — this is internal plumbing. The visible result (click any
27
+ machine's session and it streams) lands when phases 2–3 wire it to the
28
+ dashboard.
29
+
30
+ - audience: agent-only
31
+ - maturity: preview
32
+
33
+ ## Summary of New Capabilities
34
+
35
+ - `src/server/PeerStreamProxy.ts` — the per-peer upstream relay state machine,
36
+ fully transport/timer/clock-injected (no live wiring yet).
37
+
38
+ ## Evidence
39
+
40
+ - `tests/unit/PeerStreamProxy.test.ts` (13 tests, deterministic): open+multiplex
41
+ (one upstream, no double-subscribe for a second client), idle-close +
42
+ mid-grace reactivate, bounded reconnect + resubscribe, second-drop →
43
+ unreachable, reconnect-timeout → unreachable, mid-reconnect subscribe merged,
44
+ url-change reopen, no-url → machine-unreachable, input-relay-only-when-active,
45
+ idempotent close.
@@ -0,0 +1,50 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ macOS `mediaanalysisd` (~72-78% of a core) and Spotlight `mds_stores` (~28-48%)
9
+ were top constant CPU consumers on a busy fleet box — pinning it at load average
10
+ 30 — because the agent's OWN runtime data dir (`<agentHome>/.instar/`) was never
11
+ excluded from indexing. It holds `telegram-images/` (every user photo, vision-
12
+ analyzed by mediaanalysisd), `server-data/` (SQLite DBs rewritten continuously),
13
+ `logs/`, and `state/`. Instar already excluded `.worktrees/` (#588),
14
+ `node_modules/` (#606), and Claude transcripts (#903); this closes the last gap.
15
+
16
+ New `ensureAgentDataSpotlightExclusion()` helper drops the standard
17
+ `.metadata_never_index` marker in each high-churn subdir + a `PostUpdateMigrator`
18
+ backfill so existing agents get the relief on their next update.
19
+ `.instar/telegram-images/` and `.instar/server-data/` are also gitignored.
20
+
21
+ ## What to Tell Your User
22
+
23
+ Nothing required — silent OS-level resource hygiene. If their Mac was running
24
+ hot, part of the cause was macOS endlessly indexing and photo-analyzing the
25
+ agent's own chat images and databases; this stops that going forward.
26
+
27
+ ## Summary of New Capabilities
28
+
29
+ - `ensureAgentDataSpotlightExclusion(stateDir)` — drops `.metadata_never_index`
30
+ in the agent's `.instar/{telegram-images,server-data,logs,state}` so macOS
31
+ Spotlight + mediaanalysisd stop indexing the agent's own churning runtime data.
32
+ - `PostUpdateMigrator` backfill — existing agents get the exclusion on their next
33
+ update. Internal OS hygiene; no agent-facing API or config surface.
34
+
35
+ ## Evidence
36
+
37
+ Unit + migration tests (both sides of every boundary: marker dropped in each
38
+ existing subdir, only-existing subdirs marked, `[]` for a new agent, idempotent;
39
+ migration backfills/skips/idempotent). 22/22 green in the spotlight-exclusion
40
+ suite. `tsc --noEmit` clean. Mirrors the existing node_modules/worktree/transcript
41
+ exclusion pattern exactly.
42
+
43
+ ## Scope (honest)
44
+
45
+ This prevents FUTURE indexing/analysis. It does not instantly drop
46
+ `mediaanalysisd`/`mds_stores` — macOS releases already-indexed content on its own
47
+ schedule; forcing immediate eviction needs a one-time `sudo mdutil -E` the
48
+ operator runs. One of several resource-efficiency fixes (2026-06-06). Does not
49
+ address the ~300 accumulated worktrees (~100GB) — that is the AgentWorktreeReaper,
50
+ gated separately on operator approval.