@sable-ai/sdk-core 0.1.0

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 (46) hide show
  1. package/README.md +55 -0
  2. package/dist/esm/index.js +2431 -0
  3. package/dist/sable.iife.js +1486 -0
  4. package/dist/types/browser-bridge/actions.d.ts +27 -0
  5. package/dist/types/browser-bridge/dom-state.d.ts +37 -0
  6. package/dist/types/browser-bridge/index.d.ts +19 -0
  7. package/dist/types/connection/index.d.ts +26 -0
  8. package/dist/types/events/index.d.ts +15 -0
  9. package/dist/types/global.d.ts +26 -0
  10. package/dist/types/index.d.ts +23 -0
  11. package/dist/types/rpc.d.ts +22 -0
  12. package/dist/types/runtime/clipboard.d.ts +14 -0
  13. package/dist/types/runtime/index.d.ts +36 -0
  14. package/dist/types/runtime/video-overlay.d.ts +14 -0
  15. package/dist/types/session/debug-panel.d.ts +29 -0
  16. package/dist/types/session/index.d.ts +41 -0
  17. package/dist/types/types/index.d.ts +131 -0
  18. package/dist/types/version.d.ts +7 -0
  19. package/dist/types/vision/frame-source.d.ts +34 -0
  20. package/dist/types/vision/index.d.ts +29 -0
  21. package/dist/types/vision/publisher.d.ts +44 -0
  22. package/dist/types/vision/wireframe.d.ts +22 -0
  23. package/package.json +61 -0
  24. package/src/assets/visible-dom.js.txt +764 -0
  25. package/src/assets/wireframe.js.txt +678 -0
  26. package/src/assets.d.ts +24 -0
  27. package/src/browser-bridge/actions.ts +161 -0
  28. package/src/browser-bridge/dom-state.ts +103 -0
  29. package/src/browser-bridge/index.ts +99 -0
  30. package/src/connection/index.ts +49 -0
  31. package/src/events/index.ts +50 -0
  32. package/src/global.ts +35 -0
  33. package/src/index.test.ts +6 -0
  34. package/src/index.ts +43 -0
  35. package/src/rpc.ts +31 -0
  36. package/src/runtime/clipboard.ts +47 -0
  37. package/src/runtime/index.ts +138 -0
  38. package/src/runtime/video-overlay.ts +94 -0
  39. package/src/session/debug-panel.ts +254 -0
  40. package/src/session/index.ts +375 -0
  41. package/src/types/index.ts +176 -0
  42. package/src/version.ts +8 -0
  43. package/src/vision/frame-source.ts +111 -0
  44. package/src/vision/index.ts +70 -0
  45. package/src/vision/publisher.ts +106 -0
  46. package/src/vision/wireframe.ts +43 -0
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Vision entry point.
3
+ *
4
+ * `startVision` owns the full lifecycle of "what the agent sees":
5
+ *
6
+ * 1. create a capture canvas sized to the viewport
7
+ * 2. start the configured frame source, drawing into that canvas
8
+ * 3. publish the canvas as a LiveKit screenshare video track
9
+ *
10
+ * Returns both the canvas (so the session can hand it to the debug panel,
11
+ * which just renders the exact pixels we publish) and a combined async stop
12
+ * function. Everything is off by default — callers pass `vision: { enabled:
13
+ * true }` in `Sable.start({ ... })` to opt in.
14
+ */
15
+
16
+ import type { FrameSource, VisionOptions } from "../types";
17
+ import { startFrameSource } from "./frame-source";
18
+ import {
19
+ publishCanvasAsVideoTrack,
20
+ type LiveKitPublishLib,
21
+ type PublishCapableRoom,
22
+ } from "./publisher";
23
+
24
+ export type { LiveKitPublishLib, PublishCapableRoom } from "./publisher";
25
+
26
+ const DEFAULT_FRAME_SOURCE: FrameSource = {
27
+ type: "wireframe",
28
+ rate: 2,
29
+ features: { includeImages: false },
30
+ };
31
+
32
+ export interface StartVisionArgs {
33
+ room: PublishCapableRoom;
34
+ lib: LiveKitPublishLib;
35
+ options: VisionOptions;
36
+ }
37
+
38
+ export interface VisionHandle {
39
+ /** The canvas being published. Useful for the debug panel. */
40
+ canvas: HTMLCanvasElement;
41
+ /** Stop the frame loop and unpublish the track. */
42
+ stop: () => Promise<void>;
43
+ }
44
+
45
+ export async function startVision(args: StartVisionArgs): Promise<VisionHandle> {
46
+ const source: FrameSource = args.options.frameSource ?? DEFAULT_FRAME_SOURCE;
47
+ const fps = typeof source.rate === "number" && source.rate > 0 ? source.rate : 2;
48
+
49
+ const canvas = document.createElement("canvas");
50
+ canvas.width = Math.max(1, window.innerWidth);
51
+ canvas.height = Math.max(1, window.innerHeight);
52
+
53
+ const stopFrameSource = startFrameSource(source, canvas);
54
+
55
+ let stopPublish: () => Promise<void>;
56
+ try {
57
+ stopPublish = await publishCanvasAsVideoTrack(args.room, args.lib, canvas, fps);
58
+ } catch (err) {
59
+ stopFrameSource();
60
+ throw err;
61
+ }
62
+
63
+ return {
64
+ canvas,
65
+ stop: async () => {
66
+ stopFrameSource();
67
+ await stopPublish();
68
+ },
69
+ };
70
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Publish a canvas as a LiveKit screenshare video track.
3
+ *
4
+ * Vision is delivered as a regular LiveKit video track rather than raw
5
+ * bytes: we draw each frame into a persistent canvas (see `frame-source.ts`)
6
+ * and hand that canvas to `canvas.captureStream(fps)`. The resulting
7
+ * MediaStreamTrack gets wrapped in a LocalVideoTrack and published as
8
+ * `Track.Source.ScreenShare`, so the agent subscribes to it the same way
9
+ * it would subscribe to any screenshare — no custom byte-stream handler,
10
+ * no per-frame PNG decode, and the codec delta-compresses mostly-static
11
+ * pages so bandwidth stays low.
12
+ *
13
+ * The fps passed to `captureStream` should match the frame source's render
14
+ * rate; a mismatch either drops frames (encoder faster than source) or
15
+ * wastes bandwidth (encoder slower than source). `vision/index.ts` owns
16
+ * the pairing.
17
+ */
18
+
19
+ // ── livekit-client structural types ───────────────────────────────────────
20
+ //
21
+ // sdk-core does NOT statically import `livekit-client` — the heavy runtime
22
+ // lives behind a dynamic import so the IIFE entry bundle stays small. We
23
+ // describe only the minimum surface the publisher actually touches here.
24
+
25
+ interface LocalTrackPublication {
26
+ trackSid?: string;
27
+ }
28
+
29
+ export interface PublishCapableRoom {
30
+ localParticipant: {
31
+ publishTrack(
32
+ track: unknown,
33
+ options?: { source?: unknown; name?: string },
34
+ ): Promise<LocalTrackPublication>;
35
+ unpublishTrack(track: unknown, stopOnUnpublish?: boolean): Promise<unknown>;
36
+ };
37
+ }
38
+
39
+ export interface LiveKitPublishLib {
40
+ LocalVideoTrack: new (
41
+ mediaStreamTrack: MediaStreamTrack,
42
+ constraints?: unknown,
43
+ userProvidedTrack?: boolean,
44
+ ) => unknown;
45
+ Track: {
46
+ Source: {
47
+ ScreenShare: unknown;
48
+ };
49
+ };
50
+ }
51
+
52
+ const BROWSER_TRACK_NAME = "browser";
53
+
54
+ /**
55
+ * Publish `canvas` as a screenshare track at `fps` frames per second.
56
+ * Returns an async teardown that unpublishes the track and stops the
57
+ * underlying MediaStreamTrack.
58
+ */
59
+ export async function publishCanvasAsVideoTrack(
60
+ room: PublishCapableRoom,
61
+ lib: LiveKitPublishLib,
62
+ canvas: HTMLCanvasElement,
63
+ fps: number,
64
+ ): Promise<() => Promise<void>> {
65
+ const mediaStream = (canvas as unknown as {
66
+ captureStream: (fps?: number) => MediaStream;
67
+ }).captureStream(fps);
68
+
69
+ const videoTracks = mediaStream.getVideoTracks();
70
+ if (videoTracks.length === 0) {
71
+ throw new Error("canvas.captureStream produced no video tracks");
72
+ }
73
+ const mediaStreamTrack = videoTracks[0];
74
+
75
+ // userProvidedTrack=true → livekit-client won't try to restart the
76
+ // track (which would fail for a canvas-backed MediaStreamTrack).
77
+ const localTrack = new lib.LocalVideoTrack(
78
+ mediaStreamTrack,
79
+ undefined,
80
+ true,
81
+ );
82
+
83
+ const publication = await room.localParticipant.publishTrack(localTrack, {
84
+ source: lib.Track.Source.ScreenShare,
85
+ name: BROWSER_TRACK_NAME,
86
+ });
87
+
88
+ console.log("[Sable] vision track published", {
89
+ trackSid: publication.trackSid,
90
+ fps,
91
+ });
92
+
93
+ return async () => {
94
+ try {
95
+ await room.localParticipant.unpublishTrack(localTrack, true);
96
+ } catch (e) {
97
+ console.warn("[Sable] vision unpublishTrack failed", e);
98
+ }
99
+ try {
100
+ mediaStreamTrack.stop();
101
+ } catch {
102
+ /* ignore */
103
+ }
104
+ console.log("[Sable] vision track stopped");
105
+ };
106
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Lazy bootstrap for the Wireframe class.
3
+ *
4
+ * `wireframe.js` is shipped as a text asset (see `assets/wireframe.js.txt`)
5
+ * and eval'd once at first use. Evaluating inside an IIFE with a shadowed
6
+ * `console` keeps the library's per-capture "[wireframe] drew N elements"
7
+ * log out of the host page's devtools — at 1 fps it would flood the console.
8
+ * `.warn`/`.error` still go through so real problems surface.
9
+ *
10
+ * Used by:
11
+ * - `vision/frame-source.ts` — to render the wireframe canvas at `rate` Hz
12
+ * - `browser-bridge/dom-state.ts` — to produce the `screenshot_jpeg_b64`
13
+ * field returned by `browser.get_dom_state`
14
+ */
15
+
16
+ import wireframeJs from "../assets/wireframe.js.txt";
17
+
18
+ export type WireframeInstance = {
19
+ toDataURL(): Promise<string>;
20
+ capture: () => Promise<{ canvas: HTMLCanvasElement }>;
21
+ };
22
+
23
+ export type WireframeCtor = new (
24
+ root?: Element,
25
+ opts?: Record<string, unknown>,
26
+ ) => WireframeInstance;
27
+
28
+ let wireframeCtor: WireframeCtor | null = null;
29
+
30
+ export function getWireframeCtor(): WireframeCtor {
31
+ if (!wireframeCtor) {
32
+ wireframeCtor = (0, eval)(
33
+ `(function(){
34
+ var console = Object.assign({}, globalThis.console, {
35
+ log: function(){}, info: function(){}, debug: function(){}
36
+ });
37
+ ${wireframeJs};
38
+ return Wireframe;
39
+ })()`,
40
+ ) as WireframeCtor;
41
+ }
42
+ return wireframeCtor;
43
+ }