From 274c49acbcc51872da094e6a92782e93ddf8dbe9 Mon Sep 17 00:00:00 2001 From: William Valentin Date: Mon, 16 Feb 2026 19:36:01 -0800 Subject: [PATCH] feat(companion): expose platform connected state passthrough --- README.md | 2 +- docs/plans/state.json | 13 +++++++++++++ src/companion/platformClients.test.ts | 13 +++++++++++++ src/companion/platformClients.ts | 12 ++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 40ff2db..a954444 100644 --- a/README.md +++ b/README.md @@ -1199,7 +1199,7 @@ Companion runtime helper: - shared `publishHeartbeat()` helper for periodic `node.status.set` updates with safe defaults - `createHeartbeatLoop()` convenience helper that returns a bound `CompanionHeartbeatLoop` - optional `defaultSessionId` for canvas helper calls so `sessionId` can be omitted per call - - `dispose()` lifecycle helper for unified runtime teardown + - lifecycle passthroughs for connection state/teardown (`connected`, `dispose()`) - stream passthrough helpers (`subscribeEvents`, `subscribeEvent`, `clearEventSubscriptions`, `listKnownEventNames`, `eventSubscriptionCount`, `subscribeAgentStream/Typing/ContextWarning`, `waitForEvent`, `waitForAnyEvent`, `waitForAgentStream/Typing/ContextWarning`) - `src/companion/heartbeatLoop.ts` provides `CompanionHeartbeatLoop` for periodic heartbeat scheduling (`publishHeartbeat`) with start/stop safety, optional interval jitter (`jitterRatio`) to spread load, `tickNow()` for manual sends, success/error hooks, failure observability (`failureCount`, `lastFailure`, `getState()`), and optional auto-stop after repeated failures. diff --git a/docs/plans/state.json b/docs/plans/state.json index 70a9247..93f9866 100644 --- a/docs/plans/state.json +++ b/docs/plans/state.json @@ -699,6 +699,19 @@ ], "test_status": "pnpm test:run src/companion/platformClients.test.ts src/companion/runtimeClient.test.ts src/companion/heartbeatLoop.test.ts src/companion/platformClients.integration.test.ts + pnpm typecheck passing" }, + "companion-platform-connected-state-passthrough": { + "status": "completed", + "date": "2026-02-17", + "updated": "2026-02-17", + "summary": "Added `connected` passthrough getters on platform companion clients so wrappers expose runtime WebSocket connectivity state for lightweight health checks and flow guards.", + "files_modified": [ + "src/companion/platformClients.ts", + "src/companion/platformClients.test.ts", + "README.md", + "docs/plans/state.json" + ], + "test_status": "pnpm test:run src/companion/platformClients.test.ts src/companion/runtimeClient.test.ts src/companion/heartbeatLoop.test.ts src/companion/platformClients.integration.test.ts + pnpm typecheck passing" + }, "browser-tools-activation-clarity": { "status": "completed", "date": "2026-02-17", diff --git a/src/companion/platformClients.test.ts b/src/companion/platformClients.test.ts index f04e033..3b488a9 100644 --- a/src/companion/platformClients.test.ts +++ b/src/companion/platformClients.test.ts @@ -38,6 +38,7 @@ function createRuntimeMock(): { waitForContextWarning: ReturnType; waitForAnyEvent: ReturnType; eventSubscriptionCount: number; + connected: boolean; } { const connect = vi.fn(async () => undefined); const disconnect = vi.fn(() => undefined); @@ -76,6 +77,7 @@ function createRuntimeMock(): { const waitForContextWarning = vi.fn(async () => ({ thresholdPct: 75, estimatedPct: 90 })); const waitForAnyEvent = vi.fn(async () => ({ event: 'agent.stream', data: { token: 'any' } })); const eventSubscriptionCount = 3; + const connected = true; const runtime = { connect, @@ -110,6 +112,9 @@ function createRuntimeMock(): { get eventSubscriptionCount() { return eventSubscriptionCount; }, + get connected() { + return connected; + }, } as unknown as CompanionRuntimeClient; return { @@ -144,6 +149,7 @@ function createRuntimeMock(): { waitForContextWarning, waitForAnyEvent, eventSubscriptionCount, + connected, }; } @@ -204,6 +210,13 @@ describe('platform companion clients', () => { expect(mock.dispose).toHaveBeenCalledOnce(); }); + it('platform connected getter forwards to runtime connected state', async () => { + const mock = createRuntimeMock(); + const client = new MacOSCompanionClient({ runtime: mock.runtime, nodeId: 'mac-node' }); + + expect(client.connected).toBe(mock.connected); + }); + it('platform stream helper methods forward to runtime client', async () => { const mock = createRuntimeMock(); const client = new AndroidCompanionClient({ runtime: mock.runtime, nodeId: 'android-node' }); diff --git a/src/companion/platformClients.ts b/src/companion/platformClients.ts index 8409996..b6008a7 100644 --- a/src/companion/platformClients.ts +++ b/src/companion/platformClients.ts @@ -101,6 +101,10 @@ export class MacOSCompanionClient { return this.runtime.connect(); } + get connected(): boolean { + return this.runtime.connected; + } + disconnect(): void { this.runtime.disconnect(); } @@ -339,6 +343,10 @@ export class IOSCompanionClient { return this.runtime.connect(); } + get connected(): boolean { + return this.runtime.connected; + } + disconnect(): void { this.runtime.disconnect(); } @@ -577,6 +585,10 @@ export class AndroidCompanionClient { return this.runtime.connect(); } + get connected(): boolean { + return this.runtime.connected; + } + disconnect(): void { this.runtime.disconnect(); }