@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.
- package/README.md +55 -0
- package/dist/esm/index.js +2431 -0
- package/dist/sable.iife.js +1486 -0
- package/dist/types/browser-bridge/actions.d.ts +27 -0
- package/dist/types/browser-bridge/dom-state.d.ts +37 -0
- package/dist/types/browser-bridge/index.d.ts +19 -0
- package/dist/types/connection/index.d.ts +26 -0
- package/dist/types/events/index.d.ts +15 -0
- package/dist/types/global.d.ts +26 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/rpc.d.ts +22 -0
- package/dist/types/runtime/clipboard.d.ts +14 -0
- package/dist/types/runtime/index.d.ts +36 -0
- package/dist/types/runtime/video-overlay.d.ts +14 -0
- package/dist/types/session/debug-panel.d.ts +29 -0
- package/dist/types/session/index.d.ts +41 -0
- package/dist/types/types/index.d.ts +131 -0
- package/dist/types/version.d.ts +7 -0
- package/dist/types/vision/frame-source.d.ts +34 -0
- package/dist/types/vision/index.d.ts +29 -0
- package/dist/types/vision/publisher.d.ts +44 -0
- package/dist/types/vision/wireframe.d.ts +22 -0
- package/package.json +61 -0
- package/src/assets/visible-dom.js.txt +764 -0
- package/src/assets/wireframe.js.txt +678 -0
- package/src/assets.d.ts +24 -0
- package/src/browser-bridge/actions.ts +161 -0
- package/src/browser-bridge/dom-state.ts +103 -0
- package/src/browser-bridge/index.ts +99 -0
- package/src/connection/index.ts +49 -0
- package/src/events/index.ts +50 -0
- package/src/global.ts +35 -0
- package/src/index.test.ts +6 -0
- package/src/index.ts +43 -0
- package/src/rpc.ts +31 -0
- package/src/runtime/clipboard.ts +47 -0
- package/src/runtime/index.ts +138 -0
- package/src/runtime/video-overlay.ts +94 -0
- package/src/session/debug-panel.ts +254 -0
- package/src/session/index.ts +375 -0
- package/src/types/index.ts +176 -0
- package/src/version.ts +8 -0
- package/src/vision/frame-source.ts +111 -0
- package/src/vision/index.ts +70 -0
- package/src/vision/publisher.ts +106 -0
- 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
|
+
}
|