@sable-ai/sdk-core 0.1.3 → 0.1.5
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 +321 -27
- package/dist/esm/index.js +2 -2
- package/dist/sable-core.mjs +1486 -0
- package/dist/sable.iife.js +1 -1486
- package/dist/types/loader.d.ts +23 -0
- package/dist/types/session/index.d.ts +1 -1
- package/dist/types/types/index.d.ts +2 -2
- package/dist/types/version.d.ts +1 -1
- package/package.json +5 -3
- package/src/index.test.ts +2 -2
- package/src/loader.ts +143 -0
- package/src/types/index.ts +2 -2
- package/src/version.ts +1 -1
- package/src/vision/index.ts +1 -1
package/README.md
CHANGED
|
@@ -1,55 +1,349 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Adding a Sable agent to your website
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
> **Beta.** The platform UI, the SDK API, and the error codes described below
|
|
4
|
+
> may still change before general availability. If you're integrating now,
|
|
5
|
+
> pin the SDK to an exact version.
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
Sable is a voice + vision agent that lives inside your users' browsers. Drop
|
|
8
|
+
a single `<script>` tag on the pages where you want it, call `Sable.start()`,
|
|
9
|
+
and an agent you configured on the Sable platform can talk to your user and
|
|
10
|
+
see what they see — no iframes, no overlays you don't control, no changes to
|
|
11
|
+
your backend.
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
|
|
13
|
+
This guide walks you through the end-to-end integration as a web engineer at
|
|
14
|
+
a company that already has a Sable account.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Create an agent on the Sable platform
|
|
19
|
+
|
|
20
|
+
1. Sign in to <https://platform.withsable.com>.
|
|
21
|
+
2. **Agents → New agent**. Give it a name and pick a template (voice-only,
|
|
22
|
+
voice + vision, etc).
|
|
23
|
+
3. Write the agent's system prompt and configure its tools. This is the same
|
|
24
|
+
agent config you'd use in any Sable channel — the SDK just exposes it in
|
|
25
|
+
your user's browser instead of a Sable-hosted call page.
|
|
26
|
+
4. Publish the agent. You'll land on the agent detail page.
|
|
27
|
+
|
|
28
|
+
## 2. Add your domain to the allowlist
|
|
29
|
+
|
|
30
|
+
Still on the agent detail page:
|
|
31
|
+
|
|
32
|
+
1. Open the **Web SDK** tab.
|
|
33
|
+
2. Under **Allowed domains**, add every origin where you intend to load the
|
|
34
|
+
SDK. Exact hostnames only — `example.com`, `www.example.com`,
|
|
35
|
+
`app.example.com`. Wildcards are supported as a leading `*.`
|
|
36
|
+
(e.g. `*.example.com` covers every subdomain).
|
|
37
|
+
3. Save.
|
|
38
|
+
|
|
39
|
+
Why this matters: the SDK is loaded inside the user's browser and talks
|
|
40
|
+
directly to the Sable API from the page. Sable rejects requests from any
|
|
41
|
+
origin that isn't on this list, so adding your domain here is the gate
|
|
42
|
+
that lets the script tag on your site actually connect. **If you forget
|
|
43
|
+
this step, you'll see a CORS error in the devtools console and nothing
|
|
44
|
+
else — the call never starts.**
|
|
45
|
+
|
|
46
|
+
Allowed domains are scoped to the agent. An agent for your marketing site
|
|
47
|
+
and an agent for your product dashboard can have different allowlists.
|
|
48
|
+
|
|
49
|
+
## 3. Copy your public key
|
|
50
|
+
|
|
51
|
+
On the same **Web SDK** tab, under **Public key**, you'll see a value like:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
pk_live_8f3a9c2d4e5b6a7c
|
|
10
55
|
```
|
|
11
56
|
|
|
12
|
-
|
|
57
|
+
This is the key you'll paste into your page. It's a **publishable** key —
|
|
58
|
+
safe to ship in client-side code, visible in devtools, not a secret. It
|
|
59
|
+
identifies the agent and is validated by Sable against the allowed-domains
|
|
60
|
+
list above, so even if someone copies it onto their own site it won't work
|
|
61
|
+
unless their origin is on your allowlist.
|
|
62
|
+
|
|
63
|
+
If you ever need to rotate it (e.g. the key shows up somewhere you don't
|
|
64
|
+
want it), click **Rotate key** and update the snippet on your site. The old
|
|
65
|
+
key is immediately invalidated.
|
|
66
|
+
|
|
67
|
+
## 4. Load the SDK
|
|
68
|
+
|
|
69
|
+
You have two options. Both expose the same API; pick whichever fits your
|
|
70
|
+
stack.
|
|
71
|
+
|
|
72
|
+
### Option A: Script tag (works everywhere)
|
|
73
|
+
|
|
74
|
+
Add this to every page you want the agent to be available on:
|
|
13
75
|
|
|
14
76
|
```html
|
|
15
|
-
<script src="
|
|
77
|
+
<script src="https://sdk.withsable.com/v1/sable.js" async></script>
|
|
16
78
|
```
|
|
17
79
|
|
|
18
|
-
|
|
80
|
+
That's a **~530 B gzipped loader stub** — no dependencies, no
|
|
81
|
+
stylesheet, no livekit, no vision runtime. The loader installs a single
|
|
82
|
+
global (`window.Sable`) and does nothing else until you call `start()`:
|
|
83
|
+
no network requests, no microphone prompt, no DOM mutations. Idle cost
|
|
84
|
+
to your site is effectively zero.
|
|
85
|
+
|
|
86
|
+
On the first `Sable.start()` call, the loader dynamic-imports the full
|
|
87
|
+
SDK (`sable-core.mjs`, ~150 KB gzipped with livekit-client inlined) from
|
|
88
|
+
the **same CDN path** it was served from — so loading
|
|
89
|
+
`https://sdk.withsable.com/v0.1.5/sable.js` always pulls
|
|
90
|
+
`https://sdk.withsable.com/v0.1.5/sable-core.mjs`, and version cohesion
|
|
91
|
+
is automatic. Pages that never start a session pay only the loader cost;
|
|
92
|
+
pages that do start a session pay the core download exactly once, cached
|
|
93
|
+
aggressively for the lifetime of the version pin.
|
|
94
|
+
|
|
95
|
+
The script is served from Sable's CDN (`sdk.withsable.com`, Cloudflare
|
|
96
|
+
Pages), cached at the edge, and versioned. Three path conventions:
|
|
97
|
+
|
|
98
|
+
| URL | Cache | Use case |
|
|
99
|
+
| --- | --- | --- |
|
|
100
|
+
| `https://sdk.withsable.com/v0.1.5/sable.js` | 1 year, immutable | **Recommended for production** — exact version pin |
|
|
101
|
+
| `https://sdk.withsable.com/v1/sable.js` | 1 hour | Latest `0.x` — accepts patch releases automatically |
|
|
102
|
+
| `https://sdk.withsable.com/latest/sable.js` | 5 minutes | Demos and smoke tests only |
|
|
103
|
+
|
|
104
|
+
`v1` is a stable major line — the API won't change without a major
|
|
105
|
+
bump. Pin to an exact version (`v0.1.5`) if you want bit-for-bit
|
|
106
|
+
reproducibility.
|
|
107
|
+
|
|
108
|
+
### Option B: npm package (React, Vue, Svelte, Next, etc.)
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npm install @sable-ai/sdk-core
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
import Sable from "@sable-ai/sdk-core";
|
|
116
|
+
|
|
117
|
+
await Sable.start({ publicKey: "pk_live_..." });
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The npm package is a standalone ESM bundle (livekit-client declared as a
|
|
121
|
+
regular dependency so your bundler can dedupe it) — it does **not**
|
|
122
|
+
fetch from the CDN. Importing the module also installs `window.Sable`
|
|
123
|
+
with first-write-wins semantics, so mixing the script tag and the npm
|
|
124
|
+
package on the same page is safe: whichever loads first installs the
|
|
125
|
+
global and the other becomes a no-op. You get TypeScript types and
|
|
126
|
+
bundler-integrated imports. Use this path if you want typed autocomplete
|
|
127
|
+
and a bundler-controlled dependency graph.
|
|
128
|
+
|
|
129
|
+
## 5. Start a session from your app code
|
|
130
|
+
|
|
131
|
+
When you want the agent to actually connect — on a button click, on page
|
|
132
|
+
load, when the user opens a help menu, whatever — call:
|
|
19
133
|
|
|
20
134
|
```js
|
|
21
135
|
await window.Sable.start({
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
136
|
+
publicKey: "pk_live_8f3a9c2d4e5b6a7c",
|
|
137
|
+
|
|
138
|
+
// Optional — what the agent can see.
|
|
139
|
+
vision: {
|
|
140
|
+
enabled: true,
|
|
141
|
+
// How frames are produced. Defaults to the built-in wireframe renderer.
|
|
142
|
+
// Discriminated on `type`:
|
|
143
|
+
// { type: "wireframe", features: { includeImages?: boolean } }
|
|
144
|
+
// { type: "fn", captureFn: () => HTMLCanvasElement | ImageBitmap }
|
|
145
|
+
frameSource: {
|
|
146
|
+
type: "wireframe",
|
|
147
|
+
rate: 2, // frames per second; default 2
|
|
148
|
+
features: {
|
|
149
|
+
includeImages: true, // include rendered images, not just layout boxes
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// Optional — implementations for methods the agent can RPC into your page.
|
|
155
|
+
// The SDK defines a small set of "UI stub" methods (e.g. showMessage,
|
|
156
|
+
// highlightElement). You can override any of them here, and add your own
|
|
157
|
+
// for agent tools specific to your app. Anything you pass becomes callable
|
|
158
|
+
// by the agent.
|
|
159
|
+
runtime: {
|
|
160
|
+
showMessage: (text) => myToast.show(text),
|
|
161
|
+
highlightElement: (selector) => { /* ... */ },
|
|
162
|
+
openDocument: (docId) => router.push(`/docs/${docId}`),
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
// Optional — arbitrary context forwarded to the agent at session start.
|
|
166
|
+
// Surfaces verbatim in the agent's initial prompt.
|
|
167
|
+
context: {
|
|
168
|
+
userId: currentUser.id,
|
|
169
|
+
userName: currentUser.name,
|
|
170
|
+
currentPage: "dashboard",
|
|
171
|
+
},
|
|
25
172
|
});
|
|
173
|
+
```
|
|
26
174
|
|
|
27
|
-
|
|
175
|
+
`Sable.start()` returns a promise that resolves when the mic is live and
|
|
176
|
+
the agent has greeted the user. It rejects if the public key is invalid,
|
|
177
|
+
the origin isn't on the allowlist, or the user denies microphone access.
|
|
28
178
|
|
|
179
|
+
To end the session:
|
|
180
|
+
|
|
181
|
+
```js
|
|
29
182
|
await window.Sable.stop();
|
|
30
183
|
```
|
|
31
184
|
|
|
32
|
-
|
|
185
|
+
You can call `start()` and `stop()` as many times as you like during a page
|
|
186
|
+
lifetime. Only one session can be active at a time.
|
|
187
|
+
|
|
188
|
+
## 6. React to session events (optional)
|
|
189
|
+
|
|
190
|
+
If you want to, say, show your own UI when the agent is talking, subscribe
|
|
191
|
+
to events:
|
|
192
|
+
|
|
193
|
+
```js
|
|
194
|
+
window.Sable.on("session:started", () => { ... });
|
|
195
|
+
window.Sable.on("session:ended", (reason) => { ... });
|
|
196
|
+
window.Sable.on("agent:speaking", (speaking) => { ... });
|
|
197
|
+
window.Sable.on("user:speaking", (speaking) => { ... });
|
|
198
|
+
window.Sable.on("error", (err) => { ... });
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
All events are fire-and-forget; the SDK does not care whether you subscribe.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Reference: `Sable.start()` options
|
|
206
|
+
|
|
207
|
+
| Option | Type | Default | Description |
|
|
208
|
+
| --- | --- | --- | --- |
|
|
209
|
+
| `publicKey` | `string` | **required** | The `pk_live_*` key from the platform. |
|
|
210
|
+
| `vision.enabled` | `boolean` | `false` | Whether to publish a video track of the page to the agent. |
|
|
211
|
+
| `vision.frameSource` | `FrameSource` | `{ type: "wireframe" }` | Where frames come from. Either the built-in wireframe renderer or a custom function (see below). |
|
|
212
|
+
| `vision.frameSource.rate` | `number` | `2` | Capture rate in frames per second. Applies to both `wireframe` and `fn`. Higher = more responsive agent vision, more bandwidth. |
|
|
213
|
+
| `vision.frameSource.features.includeImages` | `boolean` | `true` | (Wireframe only.) Include rendered images, not just layout boxes. Set to `false` to ship only layout boxes (lower bandwidth). |
|
|
214
|
+
| `vision.frameSource.captureFn` | `() => HTMLCanvasElement \| ImageBitmap` | — | (Required when `type: "fn"`.) Called at `rate` Hz. Useful for feeding the agent a custom canvas (e.g. a 3D scene, a video element, a WebGL surface). |
|
|
215
|
+
| `runtime` | `Record<string, Function>` | `{}` | Implementations for UI-stub methods the agent can call, plus any additional methods you want to expose as agent tools. |
|
|
216
|
+
| `context` | `Record<string, unknown>` | `{}` | Forwarded verbatim to the agent at session start. Appears in the agent's initial prompt. |
|
|
217
|
+
|
|
218
|
+
## Reference: errors
|
|
219
|
+
|
|
220
|
+
| Code | Cause | Fix |
|
|
221
|
+
| --- | --- | --- |
|
|
222
|
+
| `SABLE_INVALID_KEY` | Public key doesn't exist or was rotated. | Copy the current key from the platform. |
|
|
223
|
+
| `SABLE_ORIGIN_NOT_ALLOWED` | Page's origin isn't on the agent's allowlist. | Add the exact origin in the platform's Web SDK → Allowed domains. |
|
|
224
|
+
| `SABLE_MIC_DENIED` | User denied microphone permission. | Prompt them again, or show a message explaining why voice is needed. |
|
|
225
|
+
| `SABLE_RATE_LIMITED` | Too many sessions started from this origin in a short window. | Back off and retry. |
|
|
226
|
+
| `SABLE_NETWORK` | Couldn't reach the Sable API. | Usually transient — retry with backoff. |
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## FAQ
|
|
231
|
+
|
|
232
|
+
**Do I need a backend integration?**
|
|
233
|
+
No. The SDK talks directly to the Sable API from the page. There's no
|
|
234
|
+
webhook to install, no server-to-server auth, no token exchange you have to
|
|
235
|
+
implement. The public key + allowed-domains check is the entire trust model.
|
|
236
|
+
|
|
237
|
+
**Is the public key a secret?**
|
|
238
|
+
No — it's designed to be shipped in client-side code. The security boundary
|
|
239
|
+
is the allowed-domains list, not the key itself. Someone who copies your
|
|
240
|
+
key to their own site can't use it unless their origin is also on your
|
|
241
|
+
allowlist.
|
|
242
|
+
|
|
243
|
+
**Script tag or npm package — which should I use?**
|
|
244
|
+
Either. Use the script tag for static HTML or if you want to avoid
|
|
245
|
+
bundler configuration — you get the Stripe.js-style split bundle
|
|
246
|
+
(~530 B on page load, core lazy-loaded from the CDN on first
|
|
247
|
+
`Sable.start()`). Use the npm package if you want TypeScript types and a
|
|
248
|
+
bundler-integrated, self-contained dependency graph — the package is a
|
|
249
|
+
standalone ESM build that does not fetch from the CDN. Mixing on the
|
|
250
|
+
same page is safe: both install `window.Sable` with first-write-wins
|
|
251
|
+
semantics, so whichever runs first wins and the other becomes a no-op.
|
|
252
|
+
|
|
253
|
+
**Why a global (`window.Sable`) instead of an import-only API?**
|
|
254
|
+
To keep the drop-in story honest. A single `<script>` tag works in every
|
|
255
|
+
web framework — React, Vue, Svelte, Next, Rails, plain HTML — without
|
|
256
|
+
bundler setup. The npm package also installs the same global on import,
|
|
257
|
+
so mixed usage stays coherent.
|
|
258
|
+
|
|
259
|
+
**Does the SDK render anything by default?**
|
|
260
|
+
The SDK itself is headless — it handles voice, vision, and agent
|
|
261
|
+
communication but renders nothing on its own. A default UI (mic button,
|
|
262
|
+
avatar, agent-driven overlays) can be enabled via a separate package, or
|
|
263
|
+
you can build your own using the event API and your own component library.
|
|
264
|
+
|
|
265
|
+
**Does the agent see my users' data?**
|
|
266
|
+
Only what's on the page at the moment the session is active, and only if
|
|
267
|
+
`vision.enabled` is `true`. The wireframe is generated client-side and
|
|
268
|
+
streamed as a video track; Sable never scrapes, indexes, or stores page
|
|
269
|
+
content server-side. Microphone audio is end-to-end within the voice
|
|
270
|
+
session.
|
|
271
|
+
|
|
272
|
+
**What happens if the user navigates away?**
|
|
273
|
+
The session ends. The SDK tears down the connection, stops the microphone,
|
|
274
|
+
and unmounts anything it mounted. A fresh `Sable.start()` on the next page
|
|
275
|
+
creates a new session.
|
|
276
|
+
|
|
277
|
+
**How do I test this locally?**
|
|
278
|
+
Add `http://localhost:3000` (or whatever port you use) to the allowed
|
|
279
|
+
domains list alongside your production origin. The SDK treats localhost
|
|
280
|
+
the same as any other origin — there's no special dev bypass.
|
|
281
|
+
|
|
282
|
+
**Can I self-host the script?**
|
|
283
|
+
Not recommended. The CDN copy is versioned, cached globally, and updated
|
|
284
|
+
automatically. If you really need to — e.g. an air-gapped customer —
|
|
285
|
+
you need to host **both** files side-by-side under the same path:
|
|
286
|
+
`sable.js` (the loader) and `sable-core.mjs` (the full SDK). The loader
|
|
287
|
+
resolves the core via `new URL("./sable-core.mjs", document.currentScript.src)`,
|
|
288
|
+
so they must live in the same directory. Contact support for details.
|
|
289
|
+
|
|
290
|
+
**Does the SDK work with strict CSP (`script-src` nonces, etc.)?**
|
|
291
|
+
Yes. Apply your nonce to the `<script>` tag as usual. The loader
|
|
292
|
+
dynamic-imports `sable-core.mjs` from the **same origin** it was served
|
|
293
|
+
from (`sdk.withsable.com` by default), so your CSP needs to allow that
|
|
294
|
+
origin in `script-src` — typically `script-src 'self' https://sdk.withsable.com`
|
|
295
|
+
if you load from the public CDN. No third-party origins are contacted
|
|
296
|
+
at runtime beyond `sdk.withsable.com` (for the core bundle) and
|
|
297
|
+
`sable-api-gateway-9dfmhij9.wl.gateway.dev` (for the session — plus
|
|
298
|
+
the LiveKit WebSocket origin returned by the session endpoint). If
|
|
299
|
+
you self-host `sable.js` + `sable-core.mjs` under your own origin,
|
|
300
|
+
`'self'` covers the script side; the API + LiveKit origins still
|
|
301
|
+
need to be in `connect-src`.
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Development
|
|
306
|
+
|
|
307
|
+
> This section is for contributors to `@sable-ai/sdk-core` itself. If
|
|
308
|
+
> you're integrating Sable into your site, stop here.
|
|
309
|
+
|
|
310
|
+
The package source lives at
|
|
311
|
+
[`sable-inc/js-sdk`](https://github.com/sable-inc/js-sdk) under
|
|
312
|
+
`packages/sdk-core`. Build:
|
|
33
313
|
|
|
34
314
|
```bash
|
|
35
315
|
bun install
|
|
36
316
|
bun run --filter @sable-ai/sdk-core build
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
This produces three artifacts in `dist/`:
|
|
320
|
+
|
|
321
|
+
| Artifact | Entry | Consumer |
|
|
322
|
+
| --- | --- | --- |
|
|
323
|
+
| `dist/sable.iife.js` | `src/loader.ts` (minified IIFE) | CDN script tag |
|
|
324
|
+
| `dist/sable-core.mjs` | `src/index.ts` (minified ESM, livekit inlined) | CDN lazy-import from the loader |
|
|
325
|
+
| `dist/esm/index.js` + `dist/types/` | `src/index.ts` (ESM, livekit external) | `npm install @sable-ai/sdk-core` |
|
|
326
|
+
|
|
327
|
+
### Local smoke test
|
|
328
|
+
|
|
329
|
+
```bash
|
|
37
330
|
python3 -m http.server 5173 --directory packages/sdk-core
|
|
38
331
|
```
|
|
39
332
|
|
|
40
|
-
Open `http://localhost:5173/examples/test.html`, paste an agent public
|
|
41
|
-
Grant mic permission when prompted.
|
|
42
|
-
|
|
333
|
+
Open `http://localhost:5173/examples/test.html`, paste an agent public
|
|
334
|
+
ID, click Start. Grant mic permission when prompted.
|
|
335
|
+
|
|
336
|
+
**Only `http://localhost:*` origins work against the hosted API** —
|
|
337
|
+
`sable-api`'s CORS policy does not yet allow arbitrary origins.
|
|
43
338
|
|
|
44
|
-
|
|
45
|
-
does not yet allow arbitrary origins. See the v0 spec at
|
|
46
|
-
`docs/superpowers/specs/2026-04-07-sdk-voice-integration-design.md` for the
|
|
47
|
-
deferred per-org allowed-origins work.
|
|
339
|
+
### Releasing
|
|
48
340
|
|
|
49
|
-
|
|
341
|
+
Tagging `sdk-core-v<x.y.z>` triggers
|
|
342
|
+
`.github/workflows/release-sdk-core.yml`, which:
|
|
50
343
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
344
|
+
1. Runs typecheck + tests + `bun run build`.
|
|
345
|
+
2. Publishes to npm with OIDC trusted-publisher provenance.
|
|
346
|
+
3. Stages `dist/sable.iife.js` + `dist/sable-core.mjs` under `/v<version>/`,
|
|
347
|
+
`/v1/`, and `/latest/` via `infra/cdn/stage.sh`.
|
|
348
|
+
4. Deploys the staged directory to Cloudflare Pages
|
|
349
|
+
(`sable-sdk-cdn` project, custom domain `sdk.withsable.com`).
|
package/dist/esm/index.js
CHANGED
|
@@ -2001,7 +2001,7 @@ async function publishCanvasAsVideoTrack(room, lib, canvas, fps) {
|
|
|
2001
2001
|
var DEFAULT_FRAME_SOURCE = {
|
|
2002
2002
|
type: "wireframe",
|
|
2003
2003
|
rate: 2,
|
|
2004
|
-
features: { includeImages:
|
|
2004
|
+
features: { includeImages: true }
|
|
2005
2005
|
};
|
|
2006
2006
|
async function startVision(args) {
|
|
2007
2007
|
const source = args.options.frameSource ?? DEFAULT_FRAME_SOURCE;
|
|
@@ -2027,7 +2027,7 @@ async function startVision(args) {
|
|
|
2027
2027
|
}
|
|
2028
2028
|
|
|
2029
2029
|
// src/version.ts
|
|
2030
|
-
var VERSION = "0.1.
|
|
2030
|
+
var VERSION = "0.1.4";
|
|
2031
2031
|
|
|
2032
2032
|
// src/session/debug-panel.ts
|
|
2033
2033
|
var DEBUG_PANEL_STATE_KEY = "sable:debug:panel";
|