diff --git a/README.md b/README.md index 0f4b43c..00c0031 100644 --- a/README.md +++ b/README.md @@ -1200,7 +1200,7 @@ Companion runtime helper: - `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 - - stream passthrough helpers (`subscribeEvents`, `subscribeAgentStream/Typing/ContextWarning`, `waitForAnyEvent`, `waitForAgentStream/Typing/ContextWarning`) + - stream passthrough helpers (`subscribeEvents`, `clearEventSubscriptions`, `subscribeAgentStream/Typing/ContextWarning`, `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. ## Canvas / A2UI Foundation diff --git a/docs/plans/state.json b/docs/plans/state.json index b605f61..4eb9d36 100644 --- a/docs/plans/state.json +++ b/docs/plans/state.json @@ -620,6 +620,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-event-subscription-teardown": { + "status": "completed", + "date": "2026-02-17", + "updated": "2026-02-17", + "summary": "Added `clearEventSubscriptions()` passthrough to platform clients so event handler teardown can be managed directly at the platform wrapper layer.", + "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 6f61148..fd06718 100644 --- a/src/companion/platformClients.test.ts +++ b/src/companion/platformClients.test.ts @@ -29,6 +29,7 @@ function createRuntimeMock(): { subscribeAgentTyping: ReturnType; subscribeContextWarning: ReturnType; subscribeEvents: ReturnType; + clearEventSubscriptions: ReturnType; waitForAgentStream: ReturnType; waitForAgentTyping: ReturnType; waitForContextWarning: ReturnType; @@ -62,6 +63,7 @@ function createRuntimeMock(): { const subscribeAgentTyping = vi.fn(() => () => undefined); const subscribeContextWarning = vi.fn(() => () => undefined); const subscribeEvents = vi.fn(() => () => undefined); + const clearEventSubscriptions = vi.fn(() => undefined); const waitForAgentStream = vi.fn(async () => ({ token: 'streamed' })); const waitForAgentTyping = vi.fn(async () => ({ active: true })); const waitForContextWarning = vi.fn(async () => ({ thresholdPct: 75, estimatedPct: 90 })); @@ -89,6 +91,7 @@ function createRuntimeMock(): { subscribeAgentTyping, subscribeContextWarning, subscribeEvents, + clearEventSubscriptions, waitForAgentStream, waitForAgentTyping, waitForContextWarning, @@ -118,6 +121,7 @@ function createRuntimeMock(): { subscribeAgentTyping, subscribeContextWarning, subscribeEvents, + clearEventSubscriptions, waitForAgentStream, waitForAgentTyping, waitForContextWarning, @@ -217,6 +221,15 @@ describe('platform companion clients', () => { unsubscribeEvents(); }); + it('platform clearEventSubscriptions forwards to runtime client', async () => { + const mock = createRuntimeMock(); + const client = new IOSCompanionClient({ runtime: mock.runtime, nodeId: 'ios-node' }); + + client.clearEventSubscriptions(); + + expect(mock.clearEventSubscriptions).toHaveBeenCalledOnce(); + }); + it('macOS client forwards canvas methods to runtime client', async () => { const mock = createRuntimeMock(); const client = new MacOSCompanionClient({ runtime: mock.runtime, nodeId: 'mac-node' }); diff --git a/src/companion/platformClients.ts b/src/companion/platformClients.ts index 808e9bb..1d83c22 100644 --- a/src/companion/platformClients.ts +++ b/src/companion/platformClients.ts @@ -251,6 +251,10 @@ export class MacOSCompanionClient { return this.runtime.subscribeEvents(handler); } + clearEventSubscriptions(): void { + this.runtime.clearEventSubscriptions(); + } + waitForAnyEvent( eventNames: readonly string[], options?: { @@ -459,6 +463,10 @@ export class IOSCompanionClient { return this.runtime.subscribeEvents(handler); } + clearEventSubscriptions(): void { + this.runtime.clearEventSubscriptions(); + } + waitForAnyEvent( eventNames: readonly string[], options?: { @@ -665,6 +673,10 @@ export class AndroidCompanionClient { return this.runtime.subscribeEvents(handler); } + clearEventSubscriptions(): void { + this.runtime.clearEventSubscriptions(); + } + waitForAnyEvent( eventNames: readonly string[], options?: {