@rool-dev/sdk 0.9.0-dev.10b2f38 → 0.9.0-dev.14c5d65
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 +357 -165
- package/dist/channel.d.ts +94 -184
- package/dist/channel.d.ts.map +1 -1
- package/dist/channel.js +356 -351
- package/dist/channel.js.map +1 -1
- package/dist/client.d.ts +32 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +57 -57
- package/dist/client.js.map +1 -1
- package/dist/graphql.d.ts +35 -19
- package/dist/graphql.d.ts.map +1 -1
- package/dist/graphql.js +112 -128
- package/dist/graphql.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/locations.d.ts +34 -0
- package/dist/locations.d.ts.map +1 -0
- package/dist/locations.js +90 -0
- package/dist/locations.js.map +1 -0
- package/dist/machine.d.ts +30 -0
- package/dist/machine.d.ts.map +1 -0
- package/dist/machine.js +70 -0
- package/dist/machine.js.map +1 -0
- package/dist/rest.d.ts +18 -0
- package/dist/rest.d.ts.map +1 -0
- package/dist/rest.js +46 -0
- package/dist/rest.js.map +1 -0
- package/dist/space.d.ts +20 -4
- package/dist/space.d.ts.map +1 -1
- package/dist/space.js +33 -45
- package/dist/space.js.map +1 -1
- package/dist/subscription.d.ts.map +1 -1
- package/dist/subscription.js +9 -12
- package/dist/subscription.js.map +1 -1
- package/dist/types.d.ts +72 -59
- package/dist/types.d.ts.map +1 -1
- package/dist/webdav.d.ts +153 -0
- package/dist/webdav.d.ts.map +1 -0
- package/dist/webdav.js +458 -0
- package/dist/webdav.js.map +1 -0
- package/package.json +1 -1
- package/dist/media.d.ts +0 -70
- package/dist/media.d.ts.map +0 -1
- package/dist/media.js +0 -228
- package/dist/media.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,13 +4,14 @@ The TypeScript SDK for Rool, a persistent and collaborative environment for orga
|
|
|
4
4
|
|
|
5
5
|
> **Building a new Rool extension?** Start with [`@rool-dev/extension`](/extension/) — it handles hosting, dev server, and gives you a reactive Svelte channel out of the box. This SDK is for advanced use cases: integrating Rool into an existing application, building Node.js scripts, or working outside the extension sandbox.
|
|
6
6
|
|
|
7
|
-
The SDK manages authentication, real-time synchronization, and
|
|
7
|
+
The SDK manages authentication, real-time synchronization, and per-space file storage. Core primitives:
|
|
8
8
|
|
|
9
|
-
- **Spaces** — Containers for objects, schema, metadata, and
|
|
9
|
+
- **Spaces** — Containers for objects, schema, metadata, channels, and files
|
|
10
10
|
- **Channels** — Named contexts within a space. All object and AI operations go through a channel.
|
|
11
11
|
- **Conversations** — Independent interaction histories within a channel.
|
|
12
|
-
- **Objects** —
|
|
12
|
+
- **Objects** — Records addressed by a **location** path (`/space/<collection>/<basename>.json`). The body holds user-defined fields. References between objects are body fields whose values are location strings.
|
|
13
13
|
- **AI operations** — Create, update, or query objects using natural language and `{{placeholders}}`
|
|
14
|
+
- **File storage** — Every space has WebDAV file storage
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -42,24 +43,19 @@ await channel.createCollection('body', [
|
|
|
42
43
|
{ name: 'orbits', type: { kind: 'maybe', inner: { kind: 'ref' } } },
|
|
43
44
|
]);
|
|
44
45
|
|
|
45
|
-
// Create objects with AI-generated content using {{placeholders}}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
name: 'Earth',
|
|
59
|
-
mass: '{{mass in Earth masses}}',
|
|
60
|
-
radius: '{{radius in km}}',
|
|
61
|
-
orbits: sun.id // Reference to the sun object
|
|
62
|
-
}
|
|
46
|
+
// Create objects with AI-generated content using {{placeholders}}.
|
|
47
|
+
// First arg is the collection, second is the body.
|
|
48
|
+
const { object: sun } = await channel.createObject('body', {
|
|
49
|
+
name: 'Sun',
|
|
50
|
+
mass: '{{mass in solar masses}}',
|
|
51
|
+
radius: '{{radius in km}}',
|
|
52
|
+
}, { basename: 'sun' });
|
|
53
|
+
|
|
54
|
+
const { object: earth } = await channel.createObject('body', {
|
|
55
|
+
name: 'Earth',
|
|
56
|
+
mass: '{{mass in Earth masses}}',
|
|
57
|
+
radius: '{{radius in km}}',
|
|
58
|
+
orbits: sun.location, // Reference to the sun via its location
|
|
63
59
|
});
|
|
64
60
|
|
|
65
61
|
// Use the AI agent to work with your data
|
|
@@ -67,7 +63,7 @@ const { message, objects } = await channel.prompt(
|
|
|
67
63
|
'Add the other planets in our solar system, each referencing the Sun'
|
|
68
64
|
);
|
|
69
65
|
console.log(message); // AI explains what it did
|
|
70
|
-
console.log(`
|
|
66
|
+
console.log(`Modified ${objects.length} objects`);
|
|
71
67
|
|
|
72
68
|
// Query with natural language
|
|
73
69
|
const { objects: innerPlanets } = await channel.findObjects({
|
|
@@ -82,11 +78,11 @@ channel.close();
|
|
|
82
78
|
|
|
83
79
|
### Spaces and Channels
|
|
84
80
|
|
|
85
|
-
A **space** is a container that holds objects, schema, metadata, and
|
|
81
|
+
A **space** is a container that holds objects, schema, metadata, channels, and files. A **channel** is a named context within a space — it's the handle you use for all object and AI operations. Each channel contains one or more **conversations**, each with independent interaction history.
|
|
86
82
|
|
|
87
83
|
There are two main handles:
|
|
88
|
-
- **`RoolSpace`** — Live handle with SSE subscription for user management, link access, channel management, export, and channel lifecycle events. Extends `EventEmitter`.
|
|
89
|
-
- **`RoolChannel`** — Full real-time handle for objects, AI prompts,
|
|
84
|
+
- **`RoolSpace`** — Live handle with SSE subscription for user management, link access, channel management, file storage, export, and channel lifecycle events. Extends `EventEmitter`.
|
|
85
|
+
- **`RoolChannel`** — Full real-time handle for objects, AI prompts, schema, and undo/redo.
|
|
90
86
|
|
|
91
87
|
```typescript
|
|
92
88
|
// Open a space — live handle with SSE subscription
|
|
@@ -186,52 +182,107 @@ thread.getInteractions(); // Returns the blue branch (root → leaf)
|
|
|
186
182
|
- `prompt()` with no `parentInteractionId` auto-continues from `activeLeafId`
|
|
187
183
|
- `prompt()` with `parentInteractionId: null` starts a new root-level branch
|
|
188
184
|
|
|
189
|
-
### Objects
|
|
185
|
+
### Objects, Locations, and References
|
|
190
186
|
|
|
191
|
-
|
|
187
|
+
Every object lives at a **location** — a path of the form `/space/<collection>/<basename>.json`. The collection is the parent directory, the basename is the filename without `.json`, and together they fully identify the object.
|
|
192
188
|
|
|
193
189
|
```typescript
|
|
194
|
-
{
|
|
190
|
+
{
|
|
191
|
+
location: '/space/article/welcome.json',
|
|
192
|
+
collection: 'article',
|
|
193
|
+
basename: 'welcome',
|
|
194
|
+
body: { title: 'Hello World', status: 'draft' },
|
|
195
|
+
}
|
|
195
196
|
```
|
|
196
197
|
|
|
197
|
-
**
|
|
198
|
+
The **body** holds the user-defined data.
|
|
199
|
+
|
|
200
|
+
**References** between objects are body fields whose values are location strings:
|
|
198
201
|
|
|
199
202
|
```typescript
|
|
200
|
-
// A planet references a star
|
|
201
|
-
{
|
|
203
|
+
// A planet references a star
|
|
204
|
+
{
|
|
205
|
+
location: '/space/body/earth.json',
|
|
206
|
+
collection: 'body',
|
|
207
|
+
basename: 'earth',
|
|
208
|
+
body: { name: 'Earth', orbits: '/space/body/sun.json' },
|
|
209
|
+
}
|
|
202
210
|
|
|
203
211
|
// An array of references
|
|
204
|
-
{
|
|
212
|
+
{
|
|
213
|
+
location: '/space/team/alpha.json',
|
|
214
|
+
collection: 'team',
|
|
215
|
+
basename: 'alpha',
|
|
216
|
+
body: {
|
|
217
|
+
name: 'Alpha',
|
|
218
|
+
members: [
|
|
219
|
+
'/space/user/alice.json',
|
|
220
|
+
'/space/user/bob.json',
|
|
221
|
+
'/space/user/carol.json',
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
References are just data — no special API is needed to create or remove them. Set a field to a location string to create a reference; clear it to remove it.
|
|
228
|
+
|
|
229
|
+
#### Location helpers
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { loc, parseLocation, normalizeLocation, generateBasename } from '@rool-dev/sdk';
|
|
233
|
+
|
|
234
|
+
loc('article', 'welcome'); // '/space/article/welcome.json'
|
|
235
|
+
parseLocation('/space/article/welcome.json'); // { collection: 'article', basename: 'welcome' }
|
|
236
|
+
|
|
237
|
+
// normalizeLocation accepts canonical or short form and returns canonical
|
|
238
|
+
normalizeLocation('article/welcome'); // '/space/article/welcome.json'
|
|
239
|
+
normalizeLocation('/space/article/welcome.json'); // unchanged
|
|
240
|
+
|
|
241
|
+
// 6-char random basename — same generator the SDK uses by default
|
|
242
|
+
generateBasename(); // e.g., 'X7kQ9p'
|
|
205
243
|
```
|
|
206
244
|
|
|
207
|
-
|
|
245
|
+
SDK methods that accept a location (`getObject`, `updateObject`, `deleteObjects`, `moveObject`, etc.) accept either form and normalize internally. SDK return values always use the canonical full form.
|
|
246
|
+
|
|
247
|
+
#### Machine resource links
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { machineRef, parseMachineRef, resolveMachineRef, resolveMachineHref } from '@rool-dev/sdk';
|
|
251
|
+
|
|
252
|
+
const objectRef = machineRef('/space/article/welcome.json');
|
|
253
|
+
parseMachineRef(objectRef); // '/space/article/welcome.json'
|
|
254
|
+
resolveMachineRef(objectRef).kind; // 'object'
|
|
255
|
+
|
|
256
|
+
const fileRef = machineRef('/rool-drive/docs/readme.md');
|
|
257
|
+
resolveMachineRef(fileRef).kind; // 'file'
|
|
258
|
+
resolveMachineHref(fileRef)?.kind; // 'file' — safe for browser/Markdown hrefs
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
`rool-machine:` is the canonical URI scheme for user-visible resources from the Rool machine filesystem. `resolveMachineRef()` classifies canonical refs as `object`, `file`, or `unsupported`; `resolveMachineHref()` does the same for browser/Markdown href attributes and returns `null` for non-machine links. Fetch file refs through `space.fetchMachineResource(ref)`.
|
|
208
262
|
|
|
209
263
|
### AI Placeholder Pattern
|
|
210
264
|
|
|
211
|
-
Use `{{description}}` in field values to have AI generate content:
|
|
265
|
+
Use `{{description}}` in body field values to have AI generate content:
|
|
212
266
|
|
|
213
267
|
```typescript
|
|
214
268
|
// Create with AI-generated content
|
|
215
|
-
await channel.createObject({
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
headline: '{{catchy headline about coffee}}',
|
|
219
|
-
body: '{{informative paragraph}}'
|
|
220
|
-
}
|
|
269
|
+
await channel.createObject('article', {
|
|
270
|
+
headline: '{{catchy headline about coffee}}',
|
|
271
|
+
body: '{{informative paragraph}}',
|
|
221
272
|
});
|
|
222
273
|
|
|
223
274
|
// Update existing content with AI
|
|
224
|
-
await channel.updateObject('
|
|
275
|
+
await channel.updateObject('/space/article/welcome.json', {
|
|
225
276
|
prompt: 'Make the body shorter and more casual'
|
|
226
277
|
});
|
|
227
278
|
|
|
228
279
|
// Add new AI-generated field to existing object
|
|
229
|
-
await channel.updateObject('
|
|
280
|
+
await channel.updateObject('/space/article/welcome.json', {
|
|
230
281
|
data: { summary: '{{one-sentence summary}}' }
|
|
231
282
|
});
|
|
232
283
|
```
|
|
233
284
|
|
|
234
|
-
When resolving placeholders, the agent has access to the full
|
|
285
|
+
When resolving placeholders, the agent has access to the full body and the surrounding space context (except for `_`-prefixed fields). Placeholders are instructions, not templates, and do not need to repeat information already present in other fields.
|
|
235
286
|
|
|
236
287
|
Placeholders are resolved by the AI during the mutation and replaced with concrete values. The `{{...}}` syntax is never stored — it only guides the agent while creating or updating the object.
|
|
237
288
|
|
|
@@ -242,7 +293,7 @@ Undo/redo works on **checkpoints**, not individual operations. Call `checkpoint(
|
|
|
242
293
|
```typescript
|
|
243
294
|
// Create a checkpoint before user action
|
|
244
295
|
await channel.checkpoint('Delete object');
|
|
245
|
-
await channel.deleteObjects([
|
|
296
|
+
await channel.deleteObjects([location]);
|
|
246
297
|
|
|
247
298
|
// User can now undo back to the checkpoint
|
|
248
299
|
if (await channel.canUndo()) {
|
|
@@ -259,16 +310,13 @@ Checkpoints are **space-wide**: one shared stack across all channels and users.
|
|
|
259
310
|
|
|
260
311
|
### Hidden Fields
|
|
261
312
|
|
|
262
|
-
|
|
313
|
+
Body fields starting with `_` (e.g., `_ui`, `_cache`) are hidden from AI and ignored by the schema — you can add them to any object regardless of its collection definition. Otherwise they behave like normal fields: they sync in real-time, persist to the server, support undo/redo, and are visible to all users of the space. Use them for UI state, positions, or other data the AI shouldn't see or modify:
|
|
263
314
|
|
|
264
315
|
```typescript
|
|
265
|
-
await channel.createObject({
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
author: "John Doe",
|
|
270
|
-
_ui: { x: 100, y: 200, collapsed: false }
|
|
271
|
-
}
|
|
316
|
+
await channel.createObject('article', {
|
|
317
|
+
title: 'My Article',
|
|
318
|
+
author: 'John Doe',
|
|
319
|
+
_ui: { x: 100, y: 200, collapsed: false }
|
|
272
320
|
});
|
|
273
321
|
```
|
|
274
322
|
|
|
@@ -283,42 +331,50 @@ Events fire for both local and remote changes. The `source` field indicates orig
|
|
|
283
331
|
|
|
284
332
|
```typescript
|
|
285
333
|
// All UI updates happen in one place, regardless of change source
|
|
286
|
-
channel.on('objectUpdated', ({
|
|
287
|
-
renderObject(
|
|
334
|
+
channel.on('objectUpdated', ({ location, object, source }) => {
|
|
335
|
+
renderObject(location, object);
|
|
288
336
|
if (source === 'remote_agent') {
|
|
289
337
|
doLayout(); // AI might have added content
|
|
290
338
|
}
|
|
291
339
|
});
|
|
292
340
|
|
|
293
341
|
// Caller just makes the change - event handler does the UI work
|
|
294
|
-
channel.updateObject(
|
|
342
|
+
channel.updateObject(location, { prompt: 'expand this' });
|
|
295
343
|
```
|
|
296
344
|
|
|
297
|
-
###
|
|
345
|
+
### Locations & Basenames
|
|
298
346
|
|
|
299
|
-
By default, `createObject`
|
|
347
|
+
By default, `createObject` mints a 6-character alphanumeric basename. Provide your own via `options.basename` for meaningful identifiers:
|
|
300
348
|
|
|
301
349
|
```typescript
|
|
302
|
-
await channel.createObject(
|
|
350
|
+
await channel.createObject('article',
|
|
351
|
+
{ title: 'The Meaning of Life' },
|
|
352
|
+
{ basename: 'meaning-of-life' },
|
|
353
|
+
);
|
|
354
|
+
// → location: /space/article/meaning-of-life.json
|
|
303
355
|
```
|
|
304
356
|
|
|
305
|
-
**Why
|
|
306
|
-
- **Fire-and-forget creation** — Know the
|
|
307
|
-
- **Meaningful
|
|
357
|
+
**Why pin a basename?**
|
|
358
|
+
- **Fire-and-forget creation** — Know the location immediately without awaiting the response.
|
|
359
|
+
- **Meaningful identifiers** — Use domain-specific names like `welcome` or `2026-budget` for easier debugging and external references.
|
|
308
360
|
|
|
309
361
|
```typescript
|
|
310
362
|
// Fire-and-forget: create and reference without waiting
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
363
|
+
const basename = RoolClient.generateBasename();
|
|
364
|
+
const location = loc('note', basename);
|
|
365
|
+
|
|
366
|
+
channel.createObject('note', { text: '{{expand this idea}}' }, { basename });
|
|
367
|
+
channel.updateObject(parentLocation, {
|
|
368
|
+
data: { notes: [...existingNotes, location] },
|
|
369
|
+
}); // Add reference immediately
|
|
314
370
|
```
|
|
315
371
|
|
|
316
|
-
**
|
|
317
|
-
- Must
|
|
318
|
-
-
|
|
319
|
-
-
|
|
372
|
+
**Basename constraints:**
|
|
373
|
+
- Must start with an alphanumeric character.
|
|
374
|
+
- Other characters may be alphanumeric, hyphens (`-`), or underscores (`_`).
|
|
375
|
+
- Must be unique within its collection (throws if the location already exists).
|
|
320
376
|
|
|
321
|
-
Use `
|
|
377
|
+
Use `moveObject` to rename an object or move it to a different collection — see [Moving and Renaming](#moving-and-renaming).
|
|
322
378
|
|
|
323
379
|
## Authentication
|
|
324
380
|
|
|
@@ -365,7 +421,7 @@ if (!authenticated) {
|
|
|
365
421
|
|
|
366
422
|
## AI Agent
|
|
367
423
|
|
|
368
|
-
The `prompt()` method is the primary way to invoke the AI agent. The agent has editor-level capabilities — it can create, modify, and delete objects — but cannot see or modify `_`-prefixed fields.
|
|
424
|
+
The `prompt()` method is the primary way to invoke the AI agent. The agent has editor-level capabilities — it can create, modify, move, and delete objects — but cannot see or modify `_`-prefixed fields.
|
|
369
425
|
|
|
370
426
|
```typescript
|
|
371
427
|
const { message, objects } = await channel.prompt(
|
|
@@ -389,13 +445,13 @@ Returns a message (the AI's response) and the list of objects that were created
|
|
|
389
445
|
|
|
390
446
|
| Option | Description |
|
|
391
447
|
|--------|-------------|
|
|
392
|
-
| `
|
|
448
|
+
| `locations` | Focus the AI on specific objects, identified by location (given primary attention in context). |
|
|
393
449
|
| `responseSchema` | Request structured JSON instead of text summary |
|
|
394
450
|
| `effort` | Effort level: `'QUICK'`, `'STANDARD'` (default), `'REASONING'`, or `'RESEARCH'` |
|
|
395
451
|
| `ephemeral` | If true, don't record in interaction history (useful for tab completion) |
|
|
396
|
-
| `readOnly` | If true, disable mutation tools (create, update, delete). Use for questions. |
|
|
452
|
+
| `readOnly` | If true, disable mutation tools (create, update, move, delete). Use for questions. |
|
|
397
453
|
| `parentInteractionId` | Parent interaction in the conversation tree. Omit to auto-continue from the active leaf. Pass `null` to start a new root-level branch. Pass a specific ID to branch from that point (edit/reroll). |
|
|
398
|
-
| `attachments` | Files to attach (`File`, `Blob`, or `{ data, contentType }`).
|
|
454
|
+
| `attachments` | Files to attach (`File`, `Blob`, or `{ data, contentType }`). Stored as authenticated space files; resulting `rool-machine:/rool-drive/...` references are stored on the interaction's `attachments` field for UI rendering. The AI can interpret images (JPEG, PNG, GIF, WebP, SVG), PDFs, text-based files (plain text, Markdown, CSV, HTML, XML, JSON), and DOCX documents. Other file types are uploaded and stored but the AI cannot natively consume their contents, only use shell tools on them. |
|
|
399
455
|
| `signal` | `AbortSignal` to stop the prompt mid-flight. When aborted, the agent loop halts and the streaming response closes. Note that any LLM turn already in flight on Vertex keeps generating server-side and is billed. |
|
|
400
456
|
|
|
401
457
|
### Effort Levels
|
|
@@ -418,7 +474,7 @@ const { objects } = await channel.prompt(
|
|
|
418
474
|
// Work with specific objects
|
|
419
475
|
const result = await channel.prompt(
|
|
420
476
|
"Summarize these articles",
|
|
421
|
-
{
|
|
477
|
+
{ locations: ['/space/article/intro.json', '/space/article/conclusion.json'] }
|
|
422
478
|
);
|
|
423
479
|
|
|
424
480
|
// Quick question without mutations (fast model + read-only)
|
|
@@ -455,7 +511,11 @@ Use `responseSchema` to get structured JSON instead of a text message:
|
|
|
455
511
|
|
|
456
512
|
```typescript
|
|
457
513
|
const { message } = await channel.prompt("Categorize these items", {
|
|
458
|
-
|
|
514
|
+
locations: [
|
|
515
|
+
'/space/item/widget.json',
|
|
516
|
+
'/space/item/gadget.json',
|
|
517
|
+
'/space/item/gizmo.json',
|
|
518
|
+
],
|
|
459
519
|
responseSchema: {
|
|
460
520
|
type: 'object',
|
|
461
521
|
properties: {
|
|
@@ -477,7 +537,7 @@ console.log(result.categories, result.summary);
|
|
|
477
537
|
AI operations automatically receive context:
|
|
478
538
|
- **Interaction history** — Previous interactions and their results from this channel
|
|
479
539
|
- **Recently modified objects** — Objects created or changed recently
|
|
480
|
-
- **Selected objects** — Objects passed via `
|
|
540
|
+
- **Selected objects** — Objects passed via `locations` are given primary focus
|
|
481
541
|
|
|
482
542
|
This context flows automatically — no configuration needed. The AI sees enough history to maintain coherent interactions while respecting the `_`-prefixed field hiding rules.
|
|
483
543
|
|
|
@@ -505,7 +565,7 @@ await space.addUser(user.id, 'editor');
|
|
|
505
565
|
|------|--------------|
|
|
506
566
|
| `owner` | Full control, can delete space and manage all users |
|
|
507
567
|
| `admin` | All editor capabilities, plus can manage users (except other admins/owners) |
|
|
508
|
-
| `editor` | Can create, modify, and delete objects |
|
|
568
|
+
| `editor` | Can create, modify, move, and delete objects |
|
|
509
569
|
| `viewer` | Read-only access (can query with `prompt` and `findObjects`) |
|
|
510
570
|
|
|
511
571
|
### Space Collaboration Methods
|
|
@@ -548,6 +608,7 @@ When a user accesses a space via URL, they're granted the corresponding role (`v
|
|
|
548
608
|
| `currentUser: CurrentUser \| null` | Cached user profile from `initialize()`. Use for sync access to user info (id, email, name, etc.). Returns `null` before `initialize()` is called. |
|
|
549
609
|
| `getCurrentUser(): Promise<CurrentUser>` | Fetch fresh user profile from server (id, email, name, photoUrl, slug, plan, creditsBalance, totalCreditsUsed, createdAt, lastActivity, processedAt, storage) |
|
|
550
610
|
| `updateCurrentUser(input): Promise<CurrentUser>` | Update the current user's profile (`name`, `slug`). Returns the updated user. Slug must be 3–32 chars, start with a letter, and contain only lowercase alphanumeric characters, hyphens, and underscores. |
|
|
611
|
+
| `deleteCurrentUser(): Promise<void>` | Mark the current user's account for deletion (10-minute grace period before irreversible). Logs out the client. |
|
|
551
612
|
| `searchUser(email): Promise<UserResult \| null>` | Find user by exact email address (no partial matching) |
|
|
552
613
|
|
|
553
614
|
### Real-time Collaboration
|
|
@@ -555,7 +616,7 @@ When a user accesses a space via URL, they're granted the corresponding role (`v
|
|
|
555
616
|
When multiple users have a space open, changes sync in real-time. The `source` field in events tells you who made the change:
|
|
556
617
|
|
|
557
618
|
```typescript
|
|
558
|
-
channel.on('objectUpdated', ({
|
|
619
|
+
channel.on('objectUpdated', ({ location, object, source }) => {
|
|
559
620
|
if (source === 'remote_user') {
|
|
560
621
|
// Another user made this change
|
|
561
622
|
showCollaboratorActivity(object);
|
|
@@ -591,8 +652,11 @@ const client = new RoolClient({
|
|
|
591
652
|
| `listSpaces(): Promise<RoolSpaceInfo[]>` | List available spaces |
|
|
592
653
|
| `openSpace(spaceId): Promise<RoolSpace>` | Open a space with live SSE subscription. Caches and reuses open spaces. Call `space.openChannel(channelId)` to get a channel. |
|
|
593
654
|
| `createSpace(name): Promise<RoolSpace>` | Create a new space, returns live handle with SSE subscription |
|
|
655
|
+
| `duplicateSpace(sourceSpaceId, name): Promise<RoolSpace>` | Duplicate an existing space. Returns a handle to the new space. |
|
|
594
656
|
| `deleteSpace(id): Promise<void>` | Permanently delete a space (cannot be undone) |
|
|
595
657
|
| `importArchive(name, archive): Promise<RoolSpace>` | Import from a zip archive, creating a new space |
|
|
658
|
+
| `webdav(spaceId): RoolWebDAV` | Open a WebDAV client for a space's file storage |
|
|
659
|
+
| `getSpaceStorageUsage(spaceId): Promise<SpaceFileStorageUsage>` | Get WebDAV quota usage for a space |
|
|
596
660
|
|
|
597
661
|
### Channel Management
|
|
598
662
|
|
|
@@ -676,7 +740,8 @@ Discover and install extensions published by other users.
|
|
|
676
740
|
|
|
677
741
|
| Method | Description |
|
|
678
742
|
|--------|-------------|
|
|
679
|
-
| `RoolClient.
|
|
743
|
+
| `RoolClient.generateBasename(): string` | Generate a 6-char alphanumeric basename for new object identities. |
|
|
744
|
+
| `RoolClient.generateId(): string` | Same as `generateBasename()`; retained for callers minting non-object IDs (interactions, conversations, channels). |
|
|
680
745
|
| `destroy(): void` | Clean up resources |
|
|
681
746
|
|
|
682
747
|
### Client Events
|
|
@@ -710,7 +775,7 @@ client.on('spaceRenamed', (id, name) => {
|
|
|
710
775
|
|
|
711
776
|
## RoolSpace API
|
|
712
777
|
|
|
713
|
-
A space handle with a live SSE subscription. Extends `EventEmitter`. Manages user access, link sharing, channels, and export. The `channels` property auto-updates via SSE, and channel lifecycle events fire in real-time.
|
|
778
|
+
A space handle with a live SSE subscription. Extends `EventEmitter`. Manages user access, link sharing, channels, file storage, and export. The `channels` property auto-updates via SSE, and channel lifecycle events fire in real-time.
|
|
714
779
|
|
|
715
780
|
`openSpace()` caches and reuses open spaces — calling it twice with the same ID returns the same instance. Call `close()` when done to stop the subscription and close all open channels.
|
|
716
781
|
|
|
@@ -724,6 +789,7 @@ A space handle with a live SSE subscription. Extends `EventEmitter`. Manages use
|
|
|
724
789
|
| `linkAccess: LinkAccess` | URL sharing level |
|
|
725
790
|
| `memberCount: number` | Number of users with access to the space |
|
|
726
791
|
| `channels: ChannelInfo[]` | Live channel list (auto-updates via SSE) |
|
|
792
|
+
| `webdav: RoolWebDAV` | WebDAV client for this space's file storage |
|
|
727
793
|
|
|
728
794
|
### Methods
|
|
729
795
|
|
|
@@ -742,6 +808,8 @@ A space handle with a live SSE subscription. Extends `EventEmitter`. Manages use
|
|
|
742
808
|
| `deleteChannel(channelId): Promise<void>` | Delete a channel |
|
|
743
809
|
| `installExtension(extensionId, channelId): Promise<string>` | Install an extension into a channel of this space. If you own it, wires it directly. If it's a marketplace extension, copies and builds a new extension in your library. Returns the channel ID. |
|
|
744
810
|
| `exportArchive(): Promise<Blob>` | Export space as zip archive |
|
|
811
|
+
| `getStorageUsage(): Promise<SpaceFileStorageUsage>` | Get WebDAV quota usage for this space |
|
|
812
|
+
| `fetchMachineResource(ref): Promise<Response>` | Fetch a resolved `rool-machine:/rool-drive/...` file through this space |
|
|
745
813
|
| `refresh(): Promise<void>` | Refresh space data from server |
|
|
746
814
|
|
|
747
815
|
### Space Events
|
|
@@ -782,49 +850,124 @@ A channel is a named context within a space. All object operations, AI prompts,
|
|
|
782
850
|
|
|
783
851
|
### Object Operations
|
|
784
852
|
|
|
785
|
-
Objects are
|
|
853
|
+
Objects are records addressed by location (`/space/<collection>/<basename>.json`). Every object must belong to a collection — create the collection first (see [Collection Schema](#collection-schema)). The body holds the user-defined fields.
|
|
854
|
+
|
|
855
|
+
All methods that accept a location accept either the canonical form or the short form (`collection/basename`).
|
|
786
856
|
|
|
787
857
|
| Method | Description |
|
|
788
858
|
|--------|-------------|
|
|
789
|
-
| `getObject(
|
|
790
|
-
| `stat(
|
|
791
|
-
| `findObjects(options): Promise<{ objects, message }>` | Find objects using structured filters and natural language. Results sorted by modifiedAt (desc by default). |
|
|
792
|
-
| `
|
|
793
|
-
| `createObject(options): Promise<{ object, message }>` | Create a new object
|
|
794
|
-
| `updateObject(
|
|
795
|
-
| `
|
|
859
|
+
| `getObject(location): Promise<RoolObject \| undefined>` | Get an object, or undefined if not found. |
|
|
860
|
+
| `stat(location): RoolObjectStat \| undefined` | Get audit info for an object: when it was last modified, by whom, and where (channel/conversation/interaction). Sync read from local cache. |
|
|
861
|
+
| `findObjects(options): Promise<{ objects, message }>` | Find objects using structured filters and/or natural language. Results sorted by modifiedAt (desc by default). |
|
|
862
|
+
| `getObjectLocations(options?): string[]` | Get all object locations. Sorted by modifiedAt (desc by default). Options: `{ limit?, order? }`. |
|
|
863
|
+
| `createObject(collection, body, options?): Promise<{ object, message }>` | Create a new object in `collection`. The SDK mints a random basename unless you pass `options.basename`. |
|
|
864
|
+
| `updateObject(location, options): Promise<{ object, message }>` | Update an existing object's body. |
|
|
865
|
+
| `moveObject(from, to, options?): Promise<{ object, message }>` | Rename or relocate an object. See [Moving and Renaming](#moving-and-renaming). |
|
|
866
|
+
| `deleteObjects(locations): Promise<void>` | Delete objects by location. Other objects' refs become stale. |
|
|
796
867
|
|
|
797
|
-
#### createObject
|
|
868
|
+
#### createObject
|
|
869
|
+
|
|
870
|
+
```typescript
|
|
871
|
+
// Auto-generated basename
|
|
872
|
+
const { object } = await channel.createObject('article', {
|
|
873
|
+
title: 'Hello',
|
|
874
|
+
body: 'World',
|
|
875
|
+
});
|
|
876
|
+
// → object.location: '/space/article/X7kQ9p.json'
|
|
877
|
+
|
|
878
|
+
// Pinned basename
|
|
879
|
+
await channel.createObject('article',
|
|
880
|
+
{ title: 'Welcome' },
|
|
881
|
+
{ basename: 'welcome' },
|
|
882
|
+
);
|
|
883
|
+
// → location: '/space/article/welcome.json'
|
|
884
|
+
|
|
885
|
+
// AI placeholders
|
|
886
|
+
await channel.createObject('article', {
|
|
887
|
+
headline: '{{catchy headline}}',
|
|
888
|
+
body: '{{long-form intro}}',
|
|
889
|
+
});
|
|
890
|
+
```
|
|
798
891
|
|
|
799
892
|
| Option | Description |
|
|
800
893
|
|--------|-------------|
|
|
801
|
-
| `
|
|
802
|
-
| `ephemeral` | If true, the operation won't be recorded in interaction history.
|
|
894
|
+
| `basename` | Specific basename to use. If omitted, the SDK generates a random 6-char one. |
|
|
895
|
+
| `ephemeral` | If true, the operation won't be recorded in interaction history. |
|
|
896
|
+
| `parentInteractionId` | Conversation tree parent. Omit to auto-continue; pass `null` for a new root. |
|
|
897
|
+
|
|
898
|
+
#### updateObject
|
|
899
|
+
|
|
900
|
+
```typescript
|
|
901
|
+
// Add/update fields
|
|
902
|
+
await channel.updateObject('/space/article/welcome.json', {
|
|
903
|
+
data: { status: 'published' },
|
|
904
|
+
});
|
|
803
905
|
|
|
804
|
-
|
|
906
|
+
// Delete a field (pass null)
|
|
907
|
+
await channel.updateObject('/space/article/welcome.json', {
|
|
908
|
+
data: { draft: null },
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
// AI-driven rewrite
|
|
912
|
+
await channel.updateObject('/space/article/welcome.json', {
|
|
913
|
+
prompt: 'Tighten the intro by 30%.',
|
|
914
|
+
});
|
|
915
|
+
```
|
|
805
916
|
|
|
806
917
|
| Option | Description |
|
|
807
918
|
|--------|-------------|
|
|
808
|
-
| `data` |
|
|
919
|
+
| `data` | Body fields to add, update, or delete. `null` removes the field. Use `{{placeholder}}` for AI-generated content. Fields prefixed with `_` are hidden from AI. |
|
|
809
920
|
| `prompt` | Natural language instruction for AI to modify content. |
|
|
810
|
-
| `ephemeral` | If true, the operation won't be recorded in interaction history.
|
|
921
|
+
| `ephemeral` | If true, the operation won't be recorded in interaction history. |
|
|
922
|
+
| `parentInteractionId` | Conversation tree parent. Omit to auto-continue; pass `null` for a new root. |
|
|
923
|
+
|
|
924
|
+
Use `moveObject` to change an object's location (collection or basename).
|
|
925
|
+
|
|
926
|
+
#### Moving and Renaming
|
|
927
|
+
|
|
928
|
+
`moveObject` is how you rename an object (new basename in the same collection) or move it across collections. Pass `options.body` to atomically rewrite the body as part of the move.
|
|
929
|
+
|
|
930
|
+
```typescript
|
|
931
|
+
// Rename within the same collection
|
|
932
|
+
await channel.moveObject(
|
|
933
|
+
'/space/article/welcome.json',
|
|
934
|
+
'/space/article/hello-world.json',
|
|
935
|
+
);
|
|
936
|
+
|
|
937
|
+
// Move into a different collection
|
|
938
|
+
await channel.moveObject(
|
|
939
|
+
'/space/draft/post-42.json',
|
|
940
|
+
'/space/article/post-42.json',
|
|
941
|
+
);
|
|
942
|
+
|
|
943
|
+
// Move and replace body in one go
|
|
944
|
+
await channel.moveObject(from, to, {
|
|
945
|
+
body: { title: 'Hello, world', status: 'published' },
|
|
946
|
+
});
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
| Option | Description |
|
|
950
|
+
|--------|-------------|
|
|
951
|
+
| `body` | Replace the body atomically as part of the move. If omitted, the body is preserved. |
|
|
952
|
+
| `ephemeral` | If true, the operation won't be recorded in interaction history. |
|
|
953
|
+
| `parentInteractionId` | Conversation tree parent. Omit to auto-continue; pass `null` for a new root. |
|
|
811
954
|
|
|
812
|
-
#### findObjects
|
|
955
|
+
#### findObjects
|
|
813
956
|
|
|
814
957
|
Find objects using structured filters and/or natural language.
|
|
815
958
|
|
|
816
959
|
- **`where` only** — exact-match filtering, no AI, no credits.
|
|
817
|
-
- **`collection` only** — filter by collection name
|
|
960
|
+
- **`collection` only** — filter by collection name, no AI, no credits.
|
|
818
961
|
- **`prompt` only** — AI-powered semantic query over all objects.
|
|
819
|
-
- **`where` + `prompt`** — `where` (and `
|
|
962
|
+
- **`where` + `prompt`** — `where` (and `locations`) narrow the data set first, then the AI queries within the constrained set.
|
|
820
963
|
|
|
821
964
|
| Option | Description |
|
|
822
965
|
|--------|-------------|
|
|
823
|
-
| `where` | Exact-match field filter (e.g. `{ status: 'published' }`). Values must match literally — no operators or `{{placeholders}}`. When combined with `prompt`, constrains which objects the AI can see. |
|
|
824
|
-
| `collection` | Filter by collection name.
|
|
966
|
+
| `where` | Exact-match body-field filter (e.g. `{ status: 'published' }`). Values must match literally — no operators or `{{placeholders}}`. When combined with `prompt`, constrains which objects the AI can see. |
|
|
967
|
+
| `collection` | Filter by collection name. |
|
|
825
968
|
| `prompt` | Natural language query. Triggers AI evaluation (uses credits). |
|
|
826
969
|
| `limit` | Maximum number of results. |
|
|
827
|
-
| `
|
|
970
|
+
| `locations` | Scope to specific object locations. Constrains the candidate set in both structured and AI queries. |
|
|
828
971
|
| `order` | Sort order by modifiedAt: `'asc'` or `'desc'` (default: `'desc'`). |
|
|
829
972
|
| `ephemeral` | If true, the query won't be recorded in interaction history. Useful for responsive search. |
|
|
830
973
|
|
|
@@ -860,7 +1003,7 @@ const { objects } = await channel.findObjects({
|
|
|
860
1003
|
});
|
|
861
1004
|
```
|
|
862
1005
|
|
|
863
|
-
When `where` or `
|
|
1006
|
+
When `where` or `locations` are provided with a `prompt`, the AI only sees the filtered subset — not the full space. The returned `message` explains the query result.
|
|
864
1007
|
|
|
865
1008
|
### Undo/Redo
|
|
866
1009
|
|
|
@@ -877,7 +1020,7 @@ See [Checkpoints & Undo/Redo](#checkpoints--undoredo) for semantics.
|
|
|
877
1020
|
|
|
878
1021
|
### Space Metadata
|
|
879
1022
|
|
|
880
|
-
Store arbitrary data alongside the
|
|
1023
|
+
Store arbitrary data alongside the space without it being part of an object's body (e.g., viewport state, user preferences).
|
|
881
1024
|
|
|
882
1025
|
| Method | Description |
|
|
883
1026
|
|--------|-------------|
|
|
@@ -885,35 +1028,74 @@ Store arbitrary data alongside the Space without it being part of the object dat
|
|
|
885
1028
|
| `getMetadata(key): unknown` | Get metadata value, or undefined if key not set |
|
|
886
1029
|
| `getAllMetadata(): Record<string, unknown>` | Get all metadata |
|
|
887
1030
|
|
|
888
|
-
###
|
|
1031
|
+
### Space File Storage
|
|
889
1032
|
|
|
890
|
-
|
|
1033
|
+
Every space has authenticated file storage. WebDAV is the SDK surface for that storage: paths are relative to the space root and collection operations use WebDAV collection semantics. Human/AI file links use `rool-machine:/rool-drive/...`; fetch those links with `space.fetchMachineResource(ref)`.
|
|
891
1034
|
|
|
892
|
-
|
|
893
|
-
|--------|-------------|
|
|
894
|
-
| `uploadMedia(file): Promise<string>` | Upload file, returns URL |
|
|
895
|
-
| `fetchMedia(url, options?): Promise<MediaResponse>` | Fetch any URL, returns headers and blob() method (adds auth for backend URLs, works for external URLs too). Pass `{ forceProxy: true }` to skip the direct fetch and route through the server proxy immediately. |
|
|
896
|
-
| `deleteMedia(url): Promise<void>` | Delete media file by URL |
|
|
897
|
-
| `listMedia(): Promise<MediaInfo[]>` | List all media with metadata |
|
|
1035
|
+
Use `client.webdav(spaceId)` when you only have an ID, or `space.webdav` when you already have an open space.
|
|
898
1036
|
|
|
899
1037
|
```typescript
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1038
|
+
import { machineRef } from '@rool-dev/sdk';
|
|
1039
|
+
|
|
1040
|
+
const webdav = client.webdav('space-id');
|
|
903
1041
|
|
|
904
|
-
|
|
905
|
-
await
|
|
906
|
-
|
|
1042
|
+
await webdav.mkcol('docs');
|
|
1043
|
+
await webdav.put('docs/readme.md', '# Hello', {
|
|
1044
|
+
contentType: 'text/markdown',
|
|
1045
|
+
ifNoneMatch: '*',
|
|
907
1046
|
});
|
|
908
1047
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1048
|
+
const listing = await webdav.propfind('docs/', {
|
|
1049
|
+
depth: '1',
|
|
1050
|
+
props: ['displayname', 'getcontentlength', 'getcontenttype', 'getetag'],
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
const file = await webdav.get('docs/readme.md');
|
|
1054
|
+
console.log(await file.text());
|
|
1055
|
+
|
|
1056
|
+
const ref = machineRef('/rool-drive/docs/read me.md'); // "rool-machine:/rool-drive/docs/read%20me.md"
|
|
1057
|
+
const sameFile = await space.fetchMachineResource(ref);
|
|
1058
|
+
|
|
1059
|
+
const usage = await space.getStorageUsage();
|
|
1060
|
+
console.log(usage.usedBytes);
|
|
1061
|
+
console.log(usage.availableBytes); // null means unlimited
|
|
1062
|
+
console.log(usage.limitBytes); // null means unlimited
|
|
915
1063
|
```
|
|
916
1064
|
|
|
1065
|
+
Paths are space-relative (`docs/readme.md`, not `/docs/readme.md`). WebDAV methods accept WebDAV paths only. Build human/AI file links with `machineRef('/rool-drive/...')` and fetch them with `space.fetchMachineResource(ref)`. `PUT` writes an exact path and does not create parent collections; create parents with `mkcol()` first. Helpers preserve WebDAV status semantics: non-success responses throw `WebDAVError` with `status`, `statusText`, and `body`.
|
|
1066
|
+
|
|
1067
|
+
| Method | Description |
|
|
1068
|
+
|--------|-------------|
|
|
1069
|
+
| `client.webdav(spaceId)` | Create a WebDAV client for a space |
|
|
1070
|
+
| `client.getSpaceStorageUsage(spaceId)` | Get WebDAV quota usage for a space |
|
|
1071
|
+
| `space.webdav` | WebDAV client for an open space |
|
|
1072
|
+
| `space.getStorageUsage()` | Get WebDAV quota usage for an open space |
|
|
1073
|
+
| `webdav.getStorageUsage()` | Get WebDAV quota usage through the WebDAV client |
|
|
1074
|
+
| `webdav.path(path)` | Normalize a WebDAV path |
|
|
1075
|
+
| `webdav.propfind(path, options)` | Read properties/list collections; explicit `depth` required |
|
|
1076
|
+
| `webdav.get(path, options?)` / `webdav.head(path)` | Read a file, including optional byte ranges for `get` |
|
|
1077
|
+
| `webdav.put(path, body, options?)` | Write an exact file path; parents must already exist |
|
|
1078
|
+
| `webdav.mkcol(path)` | Create one collection |
|
|
1079
|
+
| `webdav.copy(source, destination, options?)` | Copy a file or collection within the same space |
|
|
1080
|
+
| `webdav.move(source, destination, options?)` | Move a file or collection within the same space |
|
|
1081
|
+
| `webdav.delete(path, options?)` | Delete a file or collection |
|
|
1082
|
+
| `webdav.lock(path, options)` / `webdav.refreshLock(path, token)` / `webdav.unlock(token)` | WebDAV Class 2 write locks |
|
|
1083
|
+
| `webdav.request(method, path, init?)` | Raw authenticated WebDAV request escape hatch |
|
|
1084
|
+
|
|
1085
|
+
> **Note**: machine file refs `rool-machine:/rool-drive/...` and object machine refs `rool-machine:/space/...` are two supported machine reference forms. `rool-machine:/rool-drive/...` points at user-visible files in the space's WebDAV storage and is fetched with `space.fetchMachineResource(ref)`. Object locations identify records inside the space (and live at `/space/<collection>/<basename>.json`). They're not interchangeable.
|
|
1086
|
+
|
|
1087
|
+
#### File references from AI responses
|
|
1088
|
+
|
|
1089
|
+
When an agent refers to a user-visible file, the SDK contract is `rool-machine:/rool-drive/path/to/file.ext`. That prefix makes a file reference unambiguous without exposing the authenticated WebDAV URL. In free text, ambiguous characters such as spaces are percent-encoded (`rool-machine:/rool-drive/docs/read%20me.md`).
|
|
1090
|
+
|
|
1091
|
+
```typescript
|
|
1092
|
+
const response = await space.fetchMachineResource('rool-machine:/rool-drive/docs/readme.md');
|
|
1093
|
+
const blob = await response.blob();
|
|
1094
|
+
img.src = URL.createObjectURL(blob);
|
|
1095
|
+
```
|
|
1096
|
+
|
|
1097
|
+
Plain relative strings like `docs/readme.md` are valid WebDAV paths when you already know you are working with file storage. In user text or agent output, use `rool-machine:/rool-drive/docs/readme.md` so clients do not have to guess whether a string is a file. Prefer `machineRef('/rool-drive/docs/readme.md')` rather than building refs by hand.
|
|
1098
|
+
|
|
917
1099
|
### Proxied Fetch
|
|
918
1100
|
|
|
919
1101
|
Fetch external URLs via the server, bypassing CORS restrictions. Requires editor role or above. Private/internal IP ranges are blocked (SSRF protection).
|
|
@@ -937,7 +1119,7 @@ const response = await channel.fetch('https://api.example.com/submit', {
|
|
|
937
1119
|
|
|
938
1120
|
### Collection Schema
|
|
939
1121
|
|
|
940
|
-
Collections are the types you use to group objects in a space. Every object
|
|
1122
|
+
Collections are the types you use to group objects in a space. Every object belongs to exactly one collection: the collection is the parent directory of its location, and the server validates the object's body against that collection's definition. Renaming a collection changes the location of every object bound to it; dropping a collection is blocked while any object still lives there.
|
|
941
1123
|
|
|
942
1124
|
Collections make up the schema and are stored in the space data, syncing in real time. The schema is visible to the AI agent so it knows which collections exist and what fields they contain, producing more consistent objects.
|
|
943
1125
|
|
|
@@ -982,7 +1164,7 @@ await channel.dropCollection('article');
|
|
|
982
1164
|
| `string` | Text value | `{ kind: 'string' }` |
|
|
983
1165
|
| `number` | Numeric value | `{ kind: 'number' }` |
|
|
984
1166
|
| `boolean` | True/false | `{ kind: 'boolean' }` |
|
|
985
|
-
| `ref` | Reference to another object | `{ kind: 'ref' }` |
|
|
1167
|
+
| `ref` | Reference to another object (location string) | `{ kind: 'ref' }` |
|
|
986
1168
|
| `enum` | One of a set of values | `{ kind: 'enum', values: ['a', 'b'] }` |
|
|
987
1169
|
| `literal` | Exact value | `{ kind: 'literal', value: 'fixed' }` |
|
|
988
1170
|
| `array` | List of values | `{ kind: 'array', inner: { kind: 'string' } }` |
|
|
@@ -994,7 +1176,7 @@ Export and import space data as zip archives for backup, portability, or migrati
|
|
|
994
1176
|
|
|
995
1177
|
| Method | Description |
|
|
996
1178
|
|--------|-------------|
|
|
997
|
-
| `space.exportArchive(): Promise<Blob>` | Export objects, metadata, channels, and
|
|
1179
|
+
| `space.exportArchive(): Promise<Blob>` | Export objects, metadata, channels, and files as a zip archive |
|
|
998
1180
|
| `client.importArchive(name, archive): Promise<RoolSpace>` | Import from a zip archive, creating a new space |
|
|
999
1181
|
|
|
1000
1182
|
**Export:**
|
|
@@ -1011,7 +1193,7 @@ const space = await client.importArchive('Imported Data', archiveBlob);
|
|
|
1011
1193
|
const channel = await space.openChannel('main');
|
|
1012
1194
|
```
|
|
1013
1195
|
|
|
1014
|
-
The archive
|
|
1196
|
+
The archive bundles `data.json` (objects, metadata, and channels) together with the space file storage. File references are rewritten to relative paths within the archive and restored on import.
|
|
1015
1197
|
|
|
1016
1198
|
### Channel Events
|
|
1017
1199
|
|
|
@@ -1024,10 +1206,11 @@ Semantic events describe what changed. Events fire for both local changes and re
|
|
|
1024
1206
|
// - 'remote_agent': AI agent made the change
|
|
1025
1207
|
// - 'system': Resync after error
|
|
1026
1208
|
|
|
1027
|
-
// Object events
|
|
1028
|
-
channel.on('objectCreated', ({
|
|
1029
|
-
channel.on('objectUpdated', ({
|
|
1030
|
-
channel.on('objectDeleted', ({
|
|
1209
|
+
// Object events — payload includes the full RoolObject
|
|
1210
|
+
channel.on('objectCreated', ({ location, object, source }) => void)
|
|
1211
|
+
channel.on('objectUpdated', ({ location, object, source }) => void)
|
|
1212
|
+
channel.on('objectDeleted', ({ location, source }) => void)
|
|
1213
|
+
channel.on('objectMoved', ({ from, to, object, source }) => void)
|
|
1031
1214
|
|
|
1032
1215
|
// Space metadata
|
|
1033
1216
|
channel.on('metadataUpdated', ({ metadata, source }) => void)
|
|
@@ -1054,7 +1237,7 @@ AI operations may fail due to rate limiting or other transient errors. Check `er
|
|
|
1054
1237
|
|
|
1055
1238
|
```typescript
|
|
1056
1239
|
try {
|
|
1057
|
-
await channel.updateObject(
|
|
1240
|
+
await channel.updateObject(location, { prompt: 'expand this' });
|
|
1058
1241
|
} catch (error) {
|
|
1059
1242
|
if (error.message.includes('temporarily unavailable')) {
|
|
1060
1243
|
showToast('Service busy, please try again in a moment');
|
|
@@ -1088,11 +1271,11 @@ Channel management (listing, renaming, deleting channels) is done via the client
|
|
|
1088
1271
|
|
|
1089
1272
|
The `ai` field in interactions distinguishes AI-generated responses from synthetic confirmations:
|
|
1090
1273
|
- `ai: true` — AI processed this operation (prompt, or createObject/updateObject with placeholders)
|
|
1091
|
-
- `ai: false` — System confirmation only (e.g., "Created object
|
|
1274
|
+
- `ai: false` — System confirmation only (e.g., "Created object /space/note/welcome.json")
|
|
1092
1275
|
|
|
1093
1276
|
### Tool Calls
|
|
1094
1277
|
|
|
1095
|
-
The `toolCalls` array captures what the AI agent did during execution. The `conversationUpdated` event fires when each tool starts and completes. A tool call
|
|
1278
|
+
The `toolCalls` array captures what the AI agent did during execution. The `conversationUpdated` event fires when each tool starts and completes. A tool call with `status: 'running'` has no result; once `status: 'done'`, `result` contains the truncated result string.
|
|
1096
1279
|
|
|
1097
1280
|
## Data Types
|
|
1098
1281
|
|
|
@@ -1126,17 +1309,18 @@ type SpaceSchema = Record<string, CollectionDef>;
|
|
|
1126
1309
|
### Object Data
|
|
1127
1310
|
|
|
1128
1311
|
```typescript
|
|
1129
|
-
//
|
|
1130
|
-
//
|
|
1131
|
-
// Fields prefixed with _ are hidden from AI
|
|
1132
|
-
// References between objects are fields whose values are object IDs
|
|
1312
|
+
// An object addressed by location. References between objects are body
|
|
1313
|
+
// fields whose values are location strings.
|
|
1133
1314
|
interface RoolObject {
|
|
1134
|
-
|
|
1135
|
-
|
|
1315
|
+
location: string; // "/space/<collection>/<basename>.json"
|
|
1316
|
+
collection: string;
|
|
1317
|
+
basename: string;
|
|
1318
|
+
body: Record<string, unknown>;
|
|
1136
1319
|
}
|
|
1137
1320
|
|
|
1138
|
-
// Object stat
|
|
1321
|
+
// Object stat — audit information returned by channel.stat()
|
|
1139
1322
|
interface RoolObjectStat {
|
|
1323
|
+
location: string;
|
|
1140
1324
|
modifiedAt: number;
|
|
1141
1325
|
modifiedBy: string;
|
|
1142
1326
|
modifiedByName: string | null;
|
|
@@ -1199,28 +1383,37 @@ Note: `Channel` and `ChannelInfo` are data types describing the stored channel m
|
|
|
1199
1383
|
### Interaction Types
|
|
1200
1384
|
|
|
1201
1385
|
```typescript
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1386
|
+
type ToolCall =
|
|
1387
|
+
| {
|
|
1388
|
+
id: string;
|
|
1389
|
+
name: string; // Tool name (e.g., "create_object", "update_object", "search_web")
|
|
1390
|
+
input: unknown; // Arguments passed to the tool
|
|
1391
|
+
status: 'running';
|
|
1392
|
+
}
|
|
1393
|
+
| {
|
|
1394
|
+
id: string;
|
|
1395
|
+
name: string;
|
|
1396
|
+
input: unknown;
|
|
1397
|
+
status: 'done';
|
|
1398
|
+
result: string; // Truncated result
|
|
1399
|
+
};
|
|
1207
1400
|
|
|
1208
1401
|
type InteractionStatus = 'pending' | 'streaming' | 'done' | 'error';
|
|
1209
1402
|
|
|
1210
1403
|
interface Interaction {
|
|
1211
|
-
id: string;
|
|
1212
|
-
parentId: string | null;
|
|
1404
|
+
id: string; // Unique ID for this interaction
|
|
1405
|
+
parentId: string | null; // Parent in conversation tree (null = root)
|
|
1213
1406
|
timestamp: number;
|
|
1214
|
-
userId: string;
|
|
1215
|
-
userName?: string | null;
|
|
1216
|
-
operation: 'prompt' | 'createObject' | 'updateObject' | 'deleteObjects';
|
|
1217
|
-
input: string;
|
|
1218
|
-
output: string | null;
|
|
1219
|
-
status: InteractionStatus;
|
|
1220
|
-
ai: boolean;
|
|
1221
|
-
|
|
1222
|
-
toolCalls: ToolCall[];
|
|
1223
|
-
attachments?: string[];
|
|
1407
|
+
userId: string; // Who performed this interaction
|
|
1408
|
+
userName?: string | null; // Display name at time of interaction
|
|
1409
|
+
operation: 'prompt' | 'createObject' | 'updateObject' | 'moveObject' | 'deleteObjects';
|
|
1410
|
+
input: string; // What the user did: prompt text or action description
|
|
1411
|
+
output: string | null; // AI response or confirmation message (may be partial when streaming)
|
|
1412
|
+
status: InteractionStatus; // Lifecycle status (pending → streaming → done/error)
|
|
1413
|
+
ai: boolean; // Whether AI was invoked (vs synthetic confirmation)
|
|
1414
|
+
modifiedObjectLocations: string[]; // Locations of objects affected by this interaction
|
|
1415
|
+
toolCalls: ToolCall[]; // Tools called during this interaction (for AI prompts)
|
|
1416
|
+
attachments?: string[]; // rool-machine:/rool-drive/... file references attached by the user
|
|
1224
1417
|
}
|
|
1225
1418
|
```
|
|
1226
1419
|
|
|
@@ -1234,8 +1427,6 @@ interface RoolSpaceInfo { id: string; name: string; role: RoolUserRole; ownerId:
|
|
|
1234
1427
|
interface SpaceMember { id: string; email: string; role: RoolUserRole; photoUrl: string | null; }
|
|
1235
1428
|
interface UserResult { id: string; email: string; name: string | null; photoUrl: string | null; }
|
|
1236
1429
|
interface CurrentUser { id: string; email: string; name: string | null; photoUrl: string | null; slug: string; plan: string; creditsBalance: number; totalCreditsUsed: number; createdAt: string; lastActivity: string; processedAt: string; storage: Record<string, unknown>; }
|
|
1237
|
-
interface MediaInfo { url: string; contentType: string; size: number; createdAt: string; }
|
|
1238
|
-
interface MediaResponse { contentType: string; size: number | null; blob(): Promise<Blob>; }
|
|
1239
1430
|
type ChangeSource = 'local_user' | 'remote_user' | 'remote_agent' | 'system';
|
|
1240
1431
|
```
|
|
1241
1432
|
|
|
@@ -1245,13 +1436,14 @@ type ChangeSource = 'local_user' | 'remote_user' | 'remote_agent' | 'system';
|
|
|
1245
1436
|
type PromptEffort = 'QUICK' | 'STANDARD' | 'REASONING' | 'RESEARCH';
|
|
1246
1437
|
|
|
1247
1438
|
interface PromptOptions {
|
|
1248
|
-
|
|
1439
|
+
locations?: string[]; // Scope to specific objects
|
|
1249
1440
|
responseSchema?: Record<string, unknown>;
|
|
1250
|
-
effort?: PromptEffort;
|
|
1251
|
-
ephemeral?: boolean;
|
|
1252
|
-
readOnly?: boolean;
|
|
1253
|
-
parentInteractionId?: string | null;
|
|
1254
|
-
attachments?: Array<File | Blob | { data: string; contentType: string }>;
|
|
1441
|
+
effort?: PromptEffort; // Effort level (default: 'STANDARD')
|
|
1442
|
+
ephemeral?: boolean; // Don't record in interaction history
|
|
1443
|
+
readOnly?: boolean; // Disable mutation tools (default: false)
|
|
1444
|
+
parentInteractionId?: string | null; // Branch from a specific interaction (omit to auto-continue)
|
|
1445
|
+
attachments?: Array<File | Blob | { data: string; contentType: string }>; // Files to attach
|
|
1446
|
+
signal?: AbortSignal; // Cancel an in-flight prompt
|
|
1255
1447
|
}
|
|
1256
1448
|
```
|
|
1257
1449
|
|