diff --git a/docs/refactors/session-decomposition-plan.md b/docs/refactors/session-decomposition-plan.md new file mode 100644 index 000000000..531730ec8 --- /dev/null +++ b/docs/refactors/session-decomposition-plan.md @@ -0,0 +1,149 @@ +# Session God-File Decomposition Plan + +`packages/server/src/server/session.ts` — 9116 lines, one `Session` class (declared line 724), 128 handlers, ~60 instance fields, 7 entangled domains. Goal: a **strictly behavior-preserving, incremental** decomposition into per-domain controllers, mirroring the existing `TerminalSessionController`. + +## Chosen strategy: controller-context (per-domain option-bag controllers) + +Each domain becomes a controller class in its own file with the **exact** contract the repo already proved twice (`TerminalSessionController` at `packages/server/src/terminal/terminal-session-controller.ts`, and `CreateAgentLifecycleDispatch`): + +- An **options-bag constructor** injecting only what that domain reads. +- An **owned-type `ReadonlySet`** of message types. +- A **NON-async `dispatch(msg): Promise | undefined`** that checks the owned-type set FIRST and returns `undefined` synchronously on a miss (verified at terminal-session-controller.ts:140-143). +- `start()` wired from `subscribeToOptionalManagers`, `dispose()` called by the shell's ordered `cleanup()`. + +Session shrinks to a connection/dispatch shell: it keeps `handleMessage`, the `??` chain (1739-1751), `emit`/`emitBinary`, `sessionLogger`, connection identity, inflight metrics, lifecycle intents, and the **ordered** `cleanup()`. Each `dispatchXMessage` collapses to `return this.xController.dispatch(msg)`. + +### Why this is safe at the dispatch seam (verified) + +`dispatchInboundMessage` builds `a() ?? b() ?? ... ?? dispatchMiscMessage()` and short-circuits on the first non-`undefined` **Promise object** (not its resolved value). Message-type spaces are **disjoint** (no duplicate `case` labels across switches), so at most one dispatcher matches any message — collapsing to delegation cannot change which handler runs. `dispatchTerminalMessage` (2150-2153) already proves this. Two quirks preserved verbatim: schedule/\* is reached via the chat dispatcher's OWN `default` arm (2183), not the top-level `??`; and `start_workspace_script_request` (a workspace type) is special-cased before terminal delegation (2150). + +Rejected alternatives: **feature-module** (free functions + wide context bag) cannot own the live state machines (workspaceUpdatesSubscription, agentUpdatesSubscription, ~25 voice fields) and adds a competing idiom; **mixin-composition** preserves the shared-`this` god object verbatim and requires widening ~325 private fields to protected. + +## Slice ordering (least-coupled first) + +The task recommended **git/checkout as the first slice — OVERRIDDEN.** Verification: `emitCheckoutStatusUpdate` is called from exactly ONE site (session.ts:4915), inside the workspace-owned `syncWorkspaceGitObserver` callback that ALSO fires workspace effects over shared watch-target maps. Extracting checkout first forces splitting the hardest workspace/git seam before workspace is touched. The strictly safer first cuts are **chat-schedule-loop** (only knot: `handleChatPostRequest`; touches no shared observer/git/voice state) and **provider-catalog** (one shared collaborator + injected predicates). + +| # | Slice | Effort | Risk | +| --- | ----------------------------------------------------------------------------- | ------ | ------ | +| 0 | Test net + disjointness tripwire (no extraction) | M | low | +| 1 | ChatScheduleLoopController — **STOP FOR REVIEW after green** | M | low | +| 2 | ProviderCatalogController | M | medium | +| 3 | Split shared workspace-git observer + agent-subscribe fan-out (no controller) | M | high | +| 4 | GitCheckoutController | L | medium | +| 5 | WorkspaceController | XL | high | +| 6 | Voice prereqs: emit() purity + abortController ownership | M | high | +| 7 | VoiceSessionController | XL | high | +| 8a | Agent-lifecycle config setters | M | medium | +| 8b | AgentLifecycleController | XL | high | + +--- + +## Slice 0 — Test net + disjointness tripwire (prerequisite) + +No production code moves. Add `session.dispatch-seam.test.ts`. This is the gate the whole plan rests on, because chat/schedule/loop have **zero** handleMessage coverage today (verified). + +Write RED-then-GREEN against the **current in-place** Session: + +- `chat/post` happy path (asserts `chat/post` response emitted) + fanout-limit error path (asserts the `chat/post` error envelope, NOT a bubbled `rpc_error`). +- one `schedule/*` and one `loop/*` round-trip. +- a handler that throws **synchronously** emits `rpc_error{code:"handler_error"}` + an `activity_log` error frame. +- a handler that **rejects async** emits the SAME pair. +- a table-driven assertion that the union of all controllers' owned-type `ReadonlySet`s is pairwise disjoint and covers the dispatched `SessionInboundMessage` union (grows as controllers land). + +**Tests:** `session.dispatch-seam.test.ts`, `session.test.ts`. + +--- + +## Slice 1 — ChatScheduleLoopController ← STOP FOR HUMAN REVIEW after this ships green + +**Move:** all 21 handlers (`handleChat*` ×7, `handleSchedule*` ×9, `handleLoop*` ×5), the three rpc-error emitters (`emitChatRpcError`/`emitScheduleRpcError`/`emitLoopRpcError` — **kept separate, not merged**), `toScheduleSummary` → `packages/server/src/server/chat/chat-schedule-loop-controller.ts`. Collapse `dispatchChatScheduleLoopMessage` + `dispatchScheduleMessage` to `return this.chatScheduleLoopController.dispatch(msg)`. + +**SessionContext surface:** `emit`, `sessionLogger`, `clientId` (authorAgentId fallback), `chatService`, `scheduleService`, `loopService`, and a narrow agent-control port `{ listAgents, resolveAgentIdentifier, agentStorage.list }` for `handleChatPostRequest` mention fanout. + +**Owned-type set MUST include all 7 `chat/*` + 5 `loop/*` + 9 `schedule/*` types** — schedule/\* is currently routed via the chat dispatcher's own `default` arm, so it must stay inside this one controller, or schedule requests silently no-op. + +**Behavior note:** least-coupled domain. Move the three rpc-error emitters verbatim (they differ in default code + the `ChatServiceError` branch). **Tests:** `session.dispatch-seam.test.ts`, `loop-service.test.ts`, `session.test.ts`. + +--- + +## Slice 2 — ProviderCatalogController + +**Move:** 7 provider handlers + `emitProviderDisabledResponse` + `getProviderSnapshotEntryForRead` → `packages/server/src/server/provider/provider-catalog-controller.ts`. Move the `providers_snapshot_update` PUSH wiring (1235-1254) into the controller's `start()`/`dispose()`. Collapse `dispatchProviderMessage`. + +**SessionContext surface:** `emit`, `sessionLogger`, `providerSnapshotManager` (**shared by reference** — stays a daemon singleton read by checkout/lifecycle/workspace), `isProviderVisibleToClient` (predicate closing over `this`, reads `appVersion` live), `downgradeModeIconsForClient`, `downgradeEntryModesForClient`, agent-control reads `{ listProviderAvailability, listDraftFeatures }`. + +**Behavior note:** COMPAT correctness — PUSH and PULL paths MUST call the SAME injected visibility/downgrade closures, reading `appVersion` LIVE (mutated post-construction via `updateAppVersion`). Keep `COMPAT(providersSnapshot)` and `COMPAT(customModeIcons)` comments verbatim. Do NOT pull `resolveStructuredGenerationProviders`/`getFocusedAgentSelectionForCwd` in. **Tests:** `session.dispatch-seam.test.ts`, `daemon-e2e/models.e2e.test.ts`, `session.test.ts`. + +--- + +## Slice 3 — Split the shared observer seams (prerequisite, no controller) + +In-place refactor on the shell, two named fan-outs: + +1. **workspace-git observer** (4910-4917): make `emitCheckoutStatusUpdate` and `onBranchChanged` injectable callbacks; keep `workspaceGitWatchTargets`/`workspaceGitSubscriptions` shared by reference. +2. **agentManager.subscribe callback** (~1298): refactor into `{ onAgentUpdate, shouldAutoAllowVoicePermission(event), onStreamEvent }`. + +**Behavior note:** the single hardest seam, split exactly once before the two domains that co-own it. The observer fires BOTH workspace (`handleWorkspaceGitBranchSnapshot`, `emitWorkspaceUpdateForCwd`) and checkout (`emitCheckoutStatusUpdate`) effects; the agent-subscribe callback is invoked by agent EVENTS (not the `??` chain) and does lifecycle + voice work. Add a test asserting BOTH a `workspace_update` and a `checkout_status_update` fire from one simulated git snapshot change, and a voice-permission test for the auto-allow path. **Tests:** `session.workspace-git-watch.test.ts`, `session.workspaces.test.ts`, `voice-permission-policy.test.ts`, `session.test.ts`. + +--- + +## Slice 4 — GitCheckoutController + +**Move:** ~22 `checkout_*`/`stash_*`/PR/github handlers + `handleSubscribeCheckoutDiffRequest`/`handleUnsubscribeCheckoutDiffRequest` + `emitCheckoutStatusUpdate` + `checkoutDiffSubscriptions` → `packages/server/src/server/checkout/git-checkout-controller.ts`. Collapse `dispatchCheckoutMessage`. + +**SessionContext surface:** `emit`, `sessionLogger`, `checkoutDiffManager` (move in + dispose teardown), `github` (shared), `workspaceGitService` (**shared spine**), `workspaceGitWatchTargets`/`workspaceGitSubscriptions` (**shared**), `providerSnapshotManager.listRegisteredProviderIds`. `emitCheckoutStatusUpdate` is now owned here and injected back into the workspace observer seam from Slice 3. + +**Behavior note:** safe now that Slice 3 split the observer. `checkoutDiffSubscriptions` teardown moves to `dispose()`, called by `cleanup()` at its current ordinal (8530). **Tests:** `session.dispatch-seam.test.ts`, `checkout-diff-manager.test.ts`, `daemon-e2e/checkout-diff-subscription.e2e.test.ts`, `session.test.ts`. + +--- + +## Slice 5 — WorkspaceController (XL) + +**Move:** all workspace handlers (incl. re-homed `handleProjectRenameRequest` and `start_workspace_script_request`) + ~25 private workspace helpers + the whole `workspaceUpdatesSubscription` state machine → `packages/server/src/server/workspace/workspace-controller.ts`. + +**SessionContext surface:** `emit`, `sessionLogger`, `projectRegistry`/`workspaceRegistry`/`downloadTokenStore`/script stores/editor cache (**owned**), `workspaceGitService` + watch maps (**shared with checkout**), injected `emitCheckoutStatusUpdate`/`onBranchChanged`, `terminalManager`/`killTerminalsUnderPath`, an `agentUpdatesSubscription` write via a narrow `bufferAgentUpdate` command, `providerSnapshotManager.listRegisteredProviderIds`. + +**Behavior note:** the workspaceUpdatesSubscription machine moves WHOLE. The eight already-public workspace methods stay a public surface re-exposed via the shell. Re-homes are atomic remove-from-old-dispatcher + add-to-new-owned-set. **Tests:** `session.workspaces.test.ts`, `session.workspace-git-watch.test.ts`, `session.workspace-resolution-invariants.test.ts`, `session.test.ts`. + +--- + +## Slice 6 — Voice prerequisites (emit purity + abort ownership) + +In-place, separately reviewable. Split the `audio_output` TTS-debug branch out of `emit()` (8421-8468, bypasses to `onMessage` at 8454) so `emit` is a pure trace+onMessage sink. Move `convertPCMToWavBuffer` (674-701) to `speech/audio.ts`. Decide abortController ownership. + +**Behavior note:** TTS-debug split and abortController ownership are the SAME decision (`ttsDebugStreams.clear()` is tied to `createAbortController` reassignment at 8359). Keep `emit` (with the universal trace) on the shell and inject it everywhere — no trace-less emit. Do NOT inject the AbortController by value. Add: a TTS-debug persistence test (with the debug env flag) before the move, and a barge-in→cleanup regression test asserting the NEW run's signal is aborted. **Tests:** `voice-roundtrip.e2e.test.ts`, `voice-permission-policy.test.ts`, `session.test.ts`. + +--- + +## Slice 7 — VoiceSessionController (XL) + +**Move:** voice handlers + ~25 voice fields + the TTS-debug hook (Slice 6) + `voiceModeAgentId`/`isVoiceMode` + the `shouldAutoAllowVoicePermission` predicate (Slice 3) → `packages/server/src/server/voice/voice-session-controller.ts`. Carve voice types out of `dispatchVoiceAndControlMessage`, leaving infra (restart/shutdown/heartbeat/ping/abort) on the shell. + +**SessionContext surface:** pure `emit`, `emitBinary`, `hasBinaryChannel`, `sessionLogger`/`sessionId`/`paseoHome`, `getSpeechReadiness`, agent-control port `{ loadAgent, reloadWithSystemPrompt, interruptIfRunning, isRunning, sendSpokenText, buildAgentPrompt }`, `getSignal`/`abortCurrent` (Slice 6). + +**Behavior note:** depends on Slices 3 + 6. `cleanup()` stays the ordered orchestrator and calls `voiceController.dispose()` at the position the inlined voice teardown occupies today (8505-8525). **Tests:** `voice-roundtrip.e2e.test.ts`, `voice-local-agent.e2e.test.ts`, `session.voice-mcp-config.test.ts`, `session.test.ts`. + +--- + +## Slice 8a — Agent-lifecycle config setters + +Parameterize the 4 setter envelopes `handleSetAgentMode/Model/Feature/Thinking` (4209-4390) into one helper; re-home `handleListCommandsRequest` (misfiled in `dispatchMiscMessage`). Add a handleMessage-driven **failure** test per setter (force the command to reject, assert both the `*_response{accepted:false}` AND the `activity_log` error frame in order) BEFORE collapsing. **Tests:** `session.test.ts`, `session.lifecycle-boundary.test.ts`. + +## Slice 8b — AgentLifecycleController (XL, LAST) + +**Move:** remaining lifecycle handlers + the `agentUpdatesSubscription` fan-out (`bufferOrEmitAgentUpdate`, `flushBootstrappedAgentUpdates`, `matchesAgentFilter`, `forwardAgentUpdate`) → `packages/server/src/server/agent/agent-lifecycle-controller.ts`. Collapse the three lifecycle dispatchers. + +**SessionContext surface:** `emit`, `sessionLogger`, `agentManager`/`agentStorage` (**owned**), injected `forwardAgentUpdate` → `buildProjectPlacementForCwd` (backed by WorkspaceController), `agentUpdatesSubscription` accessor (owned; workspace writes via `bufferAgentUpdate`), `isProviderVisibleToClient`, `resolveCreateAgentWorkspace`, `supports`, `mcpBaseUrl`, `terminalController.killTerminalForClose`. + +**Behavior note:** done LAST — the shared-projection hub. `handleCloseItemsRequest` splits its terminal-kill half from its agent-archive half. **Tests:** `session.test.ts`, `session.wait-for-finish.test.ts`, `session.create-agent-title.test.ts`, `session.lifecycle-boundary.test.ts`, `daemon-client.e2e.test.ts`. + +--- + +## Cross-cutting invariants (every slice) + +- **Always** run `npm run typecheck` and `npm run lint` after each slice; run `npm run build:server` before diagnosing cross-package type errors. +- Controller `dispatch` is **NON-async**, guarded by an owned-type `ReadonlySet` check returning `undefined` synchronously on miss. Never `async dispatch`. +- Controllers add **no** try/catch inside `dispatch` — error handling stays in `handleMessage`. +- `cleanup()` stays the single ordered teardown orchestrator on the shell. +- Move domain error emitters **verbatim**; treat any cross-domain emitter merge as a separate, test-guarded change. +- Per-slice typecheck/lint/format via `npm run` scripts; never re-run the full suite locally (run only the listed files with `--bail=1`). diff --git a/package-lock.json b/package-lock.json index d1a5698e0..79522d598 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "paseo", - "version": "0.1.86", + "version": "0.1.87", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "paseo", - "version": "0.1.86", + "version": "0.1.87", "hasInstallScript": true, "license": "AGPL-3.0-or-later", "workspaces": [ @@ -36885,7 +36885,7 @@ }, "packages/app": { "name": "@getpaseo/app", - "version": "0.1.86", + "version": "0.1.87", "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", @@ -37110,12 +37110,12 @@ }, "packages/cli": { "name": "@getpaseo/cli", - "version": "0.1.86", + "version": "0.1.87", "dependencies": { "@clack/prompts": "^1.0.0", - "@getpaseo/client": "0.1.86", - "@getpaseo/protocol": "0.1.86", - "@getpaseo/server": "0.1.86", + "@getpaseo/client": "0.1.87", + "@getpaseo/protocol": "0.1.87", + "@getpaseo/server": "0.1.87", "chalk": "^5.3.0", "commander": "^12.0.0", "mime-types": "^2.1.35", @@ -37361,10 +37361,10 @@ }, "packages/client": { "name": "@getpaseo/client", - "version": "0.1.86", + "version": "0.1.87", "dependencies": { - "@getpaseo/protocol": "0.1.86", - "@getpaseo/relay": "0.1.86", + "@getpaseo/protocol": "0.1.87", + "@getpaseo/relay": "0.1.87", "zod": "^3.23.8" }, "devDependencies": { @@ -37384,7 +37384,7 @@ }, "packages/desktop": { "name": "@getpaseo/desktop", - "version": "0.1.86", + "version": "0.1.87", "license": "AGPL-3.0-or-later", "dependencies": { "@getpaseo/cli": "*", @@ -37636,7 +37636,7 @@ }, "packages/expo-two-way-audio": { "name": "@getpaseo/expo-two-way-audio", - "version": "0.1.86", + "version": "0.1.87", "license": "MIT", "devDependencies": { "@types/react": "^18.0.25", @@ -37672,7 +37672,7 @@ }, "packages/highlight": { "name": "@getpaseo/highlight", - "version": "0.1.86", + "version": "0.1.87", "dependencies": { "@lezer/common": "^1.5.0", "@lezer/cpp": "^1.1.5", @@ -37901,7 +37901,7 @@ }, "packages/protocol": { "name": "@getpaseo/protocol", - "version": "0.1.86", + "version": "0.1.87", "dependencies": { "zod": "^3.23.8" }, @@ -37922,7 +37922,7 @@ }, "packages/relay": { "name": "@getpaseo/relay", - "version": "0.1.86", + "version": "0.1.87", "dependencies": { "base64-js": "^1.5.1", "tweetnacl": "^1.0.3", @@ -38140,14 +38140,14 @@ }, "packages/server": { "name": "@getpaseo/server", - "version": "0.1.86", + "version": "0.1.87", "dependencies": { "@agentclientprotocol/sdk": "^0.17.1", "@anthropic-ai/claude-agent-sdk": "^0.2.133", - "@getpaseo/client": "0.1.86", - "@getpaseo/highlight": "0.1.86", - "@getpaseo/protocol": "0.1.86", - "@getpaseo/relay": "0.1.86", + "@getpaseo/client": "0.1.87", + "@getpaseo/highlight": "0.1.87", + "@getpaseo/protocol": "0.1.87", + "@getpaseo/relay": "0.1.87", "@isaacs/ttlcache": "^2.1.4", "@modelcontextprotocol/sdk": "^1.20.1", "@opencode-ai/sdk": "1.14.46", @@ -38919,7 +38919,7 @@ }, "packages/website": { "name": "@getpaseo/website", - "version": "0.1.86", + "version": "0.1.87", "dependencies": { "@cloudflare/vite-plugin": "^1.29.1", "@cloudflare/workers-types": "^4.20260317.1", diff --git a/package.json b/package.json index 536b13255..34755bf83 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "paseo", - "version": "0.1.86", + "version": "0.1.87", "private": true, "description": "Paseo: voice-controlled development environment with OpenAI Realtime API", "keywords": [ diff --git a/packages/app/package.json b/packages/app/package.json index 925efcab1..96b4f57f6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/app", - "version": "0.1.86", + "version": "0.1.87", "private": true, "main": "index.ts", "scripts": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 5bca7501e..a3285b227 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/cli", - "version": "0.1.86", + "version": "0.1.87", "description": "Paseo CLI - control your AI coding agents from the command line", "bin": { "paseo": "bin/paseo" @@ -25,9 +25,9 @@ }, "dependencies": { "@clack/prompts": "^1.0.0", - "@getpaseo/client": "0.1.86", - "@getpaseo/protocol": "0.1.86", - "@getpaseo/server": "0.1.86", + "@getpaseo/client": "0.1.87", + "@getpaseo/protocol": "0.1.87", + "@getpaseo/server": "0.1.87", "chalk": "^5.3.0", "commander": "^12.0.0", "mime-types": "^2.1.35", diff --git a/packages/client/package.json b/packages/client/package.json index dbfed65cf..c719e955b 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/client", - "version": "0.1.86", + "version": "0.1.87", "description": "Paseo client SDK package", "files": [ "dist", @@ -33,8 +33,8 @@ "test": "vitest run" }, "dependencies": { - "@getpaseo/protocol": "0.1.86", - "@getpaseo/relay": "0.1.86", + "@getpaseo/protocol": "0.1.87", + "@getpaseo/relay": "0.1.87", "zod": "^3.23.8" }, "devDependencies": { diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 552d79019..13c1a537d 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/desktop", - "version": "0.1.86", + "version": "0.1.87", "private": true, "description": "Paseo desktop app (Electron wrapper)", "homepage": "https://paseo.sh", diff --git a/packages/expo-two-way-audio/package.json b/packages/expo-two-way-audio/package.json index 7ec2e80c2..b7f6d3de6 100644 --- a/packages/expo-two-way-audio/package.json +++ b/packages/expo-two-way-audio/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/expo-two-way-audio", - "version": "0.1.86", + "version": "0.1.87", "description": "Native module for two way audio streaming", "keywords": [ "ExpoTwoWayAudio", diff --git a/packages/highlight/package.json b/packages/highlight/package.json index ea5c984ab..a913198c3 100644 --- a/packages/highlight/package.json +++ b/packages/highlight/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/highlight", - "version": "0.1.86", + "version": "0.1.87", "files": [ "dist", "!dist/**/*.map" diff --git a/packages/protocol/package.json b/packages/protocol/package.json index f1588fc74..632c9a82a 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/protocol", - "version": "0.1.86", + "version": "0.1.87", "description": "Paseo shared protocol schemas and wire types", "files": [ "dist", diff --git a/packages/relay/package.json b/packages/relay/package.json index c7dbeadd8..ba24c0a36 100644 --- a/packages/relay/package.json +++ b/packages/relay/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/relay", - "version": "0.1.86", + "version": "0.1.87", "description": "Paseo relay for bridging daemon and client connections", "files": [ "dist", diff --git a/packages/server/package.json b/packages/server/package.json index aafc5dc35..7bbe7a10b 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/server", - "version": "0.1.86", + "version": "0.1.87", "description": "Paseo backend server", "files": [ "dist/server", @@ -57,10 +57,10 @@ "dependencies": { "@agentclientprotocol/sdk": "^0.17.1", "@anthropic-ai/claude-agent-sdk": "^0.2.133", - "@getpaseo/client": "0.1.86", - "@getpaseo/highlight": "0.1.86", - "@getpaseo/protocol": "0.1.86", - "@getpaseo/relay": "0.1.86", + "@getpaseo/client": "0.1.87", + "@getpaseo/highlight": "0.1.87", + "@getpaseo/protocol": "0.1.87", + "@getpaseo/relay": "0.1.87", "@isaacs/ttlcache": "^2.1.4", "@modelcontextprotocol/sdk": "^1.20.1", "@opencode-ai/sdk": "1.14.46", diff --git a/packages/website/package.json b/packages/website/package.json index 500d9d0c3..5875f1900 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/website", - "version": "0.1.86", + "version": "0.1.87", "private": true, "type": "module", "scripts": {