@pouchy_ai/companion-sdk 0.30.0 → 0.32.1

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +93 -1
  2. package/README.md +51 -2
  3. package/dist/adapter-core.d.ts +58 -0
  4. package/dist/adapter-core.d.ts.map +1 -0
  5. package/dist/adapter-core.js +158 -0
  6. package/dist/adapter-core.js.map +1 -0
  7. package/dist/call.d.ts +1 -0
  8. package/dist/call.d.ts.map +1 -0
  9. package/dist/call.js +1 -0
  10. package/dist/call.js.map +1 -0
  11. package/dist/client.d.ts +48 -1
  12. package/dist/client.d.ts.map +1 -0
  13. package/dist/client.js +56 -3
  14. package/dist/client.js.map +1 -0
  15. package/dist/errors.d.ts +1 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +1 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/index.d.ts +4 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +6 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/protocol.d.ts +1 -0
  24. package/dist/protocol.d.ts.map +1 -0
  25. package/dist/protocol.js +1 -0
  26. package/dist/protocol.js.map +1 -0
  27. package/dist/scopes.d.ts +1 -0
  28. package/dist/scopes.d.ts.map +1 -0
  29. package/dist/scopes.js +1 -0
  30. package/dist/scopes.js.map +1 -0
  31. package/dist/sse.d.ts +1 -0
  32. package/dist/sse.d.ts.map +1 -0
  33. package/dist/sse.js +1 -0
  34. package/dist/sse.js.map +1 -0
  35. package/dist/svelte.d.ts +16 -0
  36. package/dist/svelte.d.ts.map +1 -0
  37. package/dist/svelte.js +25 -0
  38. package/dist/svelte.js.map +1 -0
  39. package/dist/vue.d.ts +15 -0
  40. package/dist/vue.d.ts.map +1 -0
  41. package/dist/vue.js +27 -0
  42. package/dist/vue.js.map +1 -0
  43. package/dist/ws-transport.d.ts +1 -0
  44. package/dist/ws-transport.d.ts.map +1 -0
  45. package/dist/ws-transport.js +1 -0
  46. package/dist/ws-transport.js.map +1 -0
  47. package/package.json +24 -6
  48. package/src/adapter-core.ts +227 -0
  49. package/src/call.ts +699 -0
  50. package/src/client.ts +1960 -0
  51. package/src/errors.ts +77 -0
  52. package/src/index.ts +105 -0
  53. package/src/protocol.ts +316 -0
  54. package/src/scopes.ts +94 -0
  55. package/src/sse.ts +41 -0
  56. package/src/svelte.ts +47 -0
  57. package/src/vue.ts +46 -0
  58. package/src/ws-transport.ts +70 -0
package/src/vue.ts ADDED
@@ -0,0 +1,46 @@
1
+ // Vue 3 adapter (0.31.0) — `@pouchy_ai/companion-sdk/vue`.
2
+ // Thin binding of adapter-core onto a shallowRef; behavior lives (and is
3
+ // tested) in adapter-core.ts.
4
+ //
5
+ // const { snapshot, sendText } = useCompanion(client);
6
+ // snapshot.value.transcript / snapshot.value.streamState
7
+ //
8
+ // Inside a component/effect scope the subscription is torn down automatically
9
+ // (onScopeDispose); outside any scope, call dispose() yourself.
10
+
11
+ import { getCurrentScope, onScopeDispose, shallowRef, type ShallowRef } from 'vue';
12
+ import type { CompanionClient } from './client';
13
+ import {
14
+ createCompanionView,
15
+ type CompanionView,
16
+ type CompanionViewOptions,
17
+ type CompanionViewSnapshot
18
+ } from './adapter-core';
19
+
20
+ export interface UseCompanionReturn {
21
+ /** Reactive, immutable view snapshot (replaced wholesale per change). */
22
+ snapshot: ShallowRef<CompanionViewSnapshot>;
23
+ sendText: CompanionView['sendText'];
24
+ confirmAction: CompanionView['confirmAction'];
25
+ /** Manual teardown for use outside an effect scope. */
26
+ dispose(): void;
27
+ client: CompanionClient;
28
+ }
29
+
30
+ /** Reactive companion view state for a host-owned client. */
31
+ export function useCompanion(
32
+ client: CompanionClient,
33
+ opts?: CompanionViewOptions
34
+ ): UseCompanionReturn {
35
+ const view = createCompanionView(client, opts);
36
+ const snapshot = shallowRef(view.getSnapshot());
37
+ const unsub = view.subscribe(() => {
38
+ snapshot.value = view.getSnapshot();
39
+ });
40
+ const dispose = () => {
41
+ unsub();
42
+ view.dispose();
43
+ };
44
+ if (getCurrentScope()) onScopeDispose(dispose);
45
+ return { snapshot, sendText: view.sendText, confirmAction: view.confirmAction, dispose, client };
46
+ }
@@ -0,0 +1,70 @@
1
+ // Companion SDK — WebSocket transport helpers (pure core).
2
+ //
3
+ // The SDK streams replies over Server-Sent Events today; the client comment
4
+ // always anticipated "when the WebSocket plane lands it slots in behind this
5
+ // same interface". This module is that slot: pure, dependency-free helpers for
6
+ // deriving the ws(s) URL and framing/parsing messages, so the live WS loop in
7
+ // client.ts stays a thin wrapper and the framing logic is unit-testable.
8
+ //
9
+ // IMPORTANT — server support: a persistent WebSocket endpoint needs a
10
+ // WS-capable host (adapter-node, or a standalone WS gateway). The default
11
+ // serverless deploy (adapter-auto → Vercel/Netlify functions) can't hold an
12
+ // open socket, so the client keeps SSE as the default and FALLS BACK to it if
13
+ // the socket can't open. Self-hosters who run a WS gateway can opt in with
14
+ // `stream: 'websocket'`. This file ships the deployment-agnostic client side.
15
+
16
+ /** Build the WebSocket stream URL from the HTTP base. http→ws, https→wss. The
17
+ * token rides the query string because browsers can't set an Authorization
18
+ * header on a WebSocket handshake; the server authenticates it the same way it
19
+ * authenticates the SSE `?cursor=` stream. `cursor` resumes after a reconnect,
20
+ * mirroring the SSE plane. */
21
+ export function deriveWsStreamUrl(
22
+ baseUrl: string,
23
+ sessionId: string,
24
+ token: string,
25
+ cursor = 0
26
+ ): string {
27
+ const origin = baseUrl.replace(/\/+$/, '').replace(/^http(s?):\/\//i, (_m, s) => `ws${s}://`);
28
+ const id = encodeURIComponent(sessionId);
29
+ const q = new URLSearchParams({ cursor: String(cursor), token });
30
+ return `${origin}/api/companion/session/${id}/ws?${q.toString()}`;
31
+ }
32
+
33
+ /** Normalize an incoming WS message into the `{ event, data }` shape the SSE
34
+ * frame handler already consumes, so both transports converge on one code
35
+ * path. Accepts two wire forms:
36
+ * 1. a framed message: { "event": "...", "data": "<json string>" }
37
+ * 2. a bare envelope: { "type": "...", ... } → event defaults to "message"
38
+ * Returns null for anything unparseable (the caller drops it). */
39
+ export function decodeWsMessage(raw: string): { event: string; data: string } | null {
40
+ let parsed: unknown;
41
+ try {
42
+ parsed = JSON.parse(raw);
43
+ } catch {
44
+ return null;
45
+ }
46
+ if (!parsed || typeof parsed !== 'object') return null;
47
+ const obj = parsed as Record<string, unknown>;
48
+
49
+ // Form 1 — explicit frame.
50
+ if (typeof obj.event === 'string') {
51
+ const data = typeof obj.data === 'string' ? obj.data : JSON.stringify(obj.data ?? {});
52
+ return { event: obj.event, data };
53
+ }
54
+
55
+ // Form 2 — bare envelope. Re-serialize so handleFrame can JSON.parse it back
56
+ // into a CompanionEnvelope exactly as the SSE path does.
57
+ if (typeof obj.type === 'string') {
58
+ return { event: 'message', data: raw };
59
+ }
60
+
61
+ return null;
62
+ }
63
+
64
+ /** Frame an outbound user-input message for the WS plane (for hosts that also
65
+ * accept input over the socket). Mirrors the REST `/input` body. */
66
+ export function encodeWsInput(text: string, opts?: { image?: string }): string {
67
+ const payload: Record<string, unknown> = { type: 'input', text };
68
+ if (opts?.image) payload.image = opts.image;
69
+ return JSON.stringify(payload);
70
+ }