@rool-dev/sdk 0.9.0-dev.9cb655b → 0.9.0-dev.bab1e95
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 +258 -128
- package/dist/channel.d.ts +87 -158
- package/dist/channel.d.ts.map +1 -1
- package/dist/channel.js +268 -320
- package/dist/channel.js.map +1 -1
- package/dist/client.d.ts +21 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +31 -3
- package/dist/client.js.map +1 -1
- package/dist/graphql.d.ts +41 -19
- package/dist/graphql.d.ts.map +1 -1
- package/dist/graphql.js +120 -117
- package/dist/graphql.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- 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/space.d.ts +1 -1
- package/dist/space.d.ts.map +1 -1
- package/dist/space.js +6 -6
- package/dist/space.js.map +1 -1
- package/dist/subscription.js +4 -2
- package/dist/subscription.js.map +1 -1
- package/dist/types.d.ts +58 -32
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ The SDK manages authentication, real-time synchronization, and per-space file st
|
|
|
9
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
14
|
- **File storage** — Every space has WebDAV file storage
|
|
15
15
|
|
|
@@ -43,24 +43,20 @@ await channel.createCollection('body', [
|
|
|
43
43
|
{ name: 'orbits', type: { kind: 'maybe', inner: { kind: 'ref' } } },
|
|
44
44
|
]);
|
|
45
45
|
|
|
46
|
-
// Create objects with AI-generated content using {{placeholders}}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
mass: '{{mass in Earth masses}}',
|
|
61
|
-
radius: '{{radius in km}}',
|
|
62
|
-
orbits: sun.id // Reference to the sun object
|
|
63
|
-
}
|
|
46
|
+
// Create objects with AI-generated content using {{placeholders}}.
|
|
47
|
+
// First arg is the collection. Second is the body — no `id` or `type`,
|
|
48
|
+
// identity lives in the location.
|
|
49
|
+
const { object: sun } = await channel.createObject('body', {
|
|
50
|
+
name: 'Sun',
|
|
51
|
+
mass: '{{mass in solar masses}}',
|
|
52
|
+
radius: '{{radius in km}}',
|
|
53
|
+
}, { basename: 'sun' });
|
|
54
|
+
|
|
55
|
+
const { object: earth } = await channel.createObject('body', {
|
|
56
|
+
name: 'Earth',
|
|
57
|
+
mass: '{{mass in Earth masses}}',
|
|
58
|
+
radius: '{{radius in km}}',
|
|
59
|
+
orbits: sun.location, // Reference to the sun via its location
|
|
64
60
|
});
|
|
65
61
|
|
|
66
62
|
// Use the AI agent to work with your data
|
|
@@ -68,7 +64,7 @@ const { message, objects } = await channel.prompt(
|
|
|
68
64
|
'Add the other planets in our solar system, each referencing the Sun'
|
|
69
65
|
);
|
|
70
66
|
console.log(message); // AI explains what it did
|
|
71
|
-
console.log(`
|
|
67
|
+
console.log(`Modified ${objects.length} objects`);
|
|
72
68
|
|
|
73
69
|
// Query with natural language
|
|
74
70
|
const { objects: innerPlanets } = await channel.findObjects({
|
|
@@ -187,52 +183,91 @@ thread.getInteractions(); // Returns the blue branch (root → leaf)
|
|
|
187
183
|
- `prompt()` with no `parentInteractionId` auto-continues from `activeLeafId`
|
|
188
184
|
- `prompt()` with `parentInteractionId: null` starts a new root-level branch
|
|
189
185
|
|
|
190
|
-
### Objects
|
|
186
|
+
### Objects, Locations, and References
|
|
191
187
|
|
|
192
|
-
|
|
188
|
+
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.
|
|
193
189
|
|
|
194
190
|
```typescript
|
|
195
|
-
{
|
|
191
|
+
{
|
|
192
|
+
location: '/space/article/welcome.json',
|
|
193
|
+
collection: 'article',
|
|
194
|
+
basename: 'welcome',
|
|
195
|
+
body: { title: 'Hello World', status: 'draft' },
|
|
196
|
+
}
|
|
196
197
|
```
|
|
197
198
|
|
|
198
|
-
**
|
|
199
|
+
**Body** is the user-defined data. It never contains `id` or `type` — identity lives entirely in the location.
|
|
200
|
+
|
|
201
|
+
**References** between objects are body fields whose values are location strings:
|
|
199
202
|
|
|
200
203
|
```typescript
|
|
201
|
-
// A planet references a star
|
|
202
|
-
{
|
|
204
|
+
// A planet references a star
|
|
205
|
+
{
|
|
206
|
+
location: '/space/body/earth.json',
|
|
207
|
+
collection: 'body',
|
|
208
|
+
basename: 'earth',
|
|
209
|
+
body: { name: 'Earth', orbits: '/space/body/sun.json' },
|
|
210
|
+
}
|
|
203
211
|
|
|
204
212
|
// An array of references
|
|
205
|
-
{
|
|
213
|
+
{
|
|
214
|
+
location: '/space/team/alpha.json',
|
|
215
|
+
collection: 'team',
|
|
216
|
+
basename: 'alpha',
|
|
217
|
+
body: {
|
|
218
|
+
name: 'Alpha',
|
|
219
|
+
members: [
|
|
220
|
+
'/space/user/alice.json',
|
|
221
|
+
'/space/user/bob.json',
|
|
222
|
+
'/space/user/carol.json',
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
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.
|
|
229
|
+
|
|
230
|
+
#### Location helpers
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import { loc, parseLocation, normalizeLocation, generateBasename } from '@rool-dev/sdk';
|
|
234
|
+
|
|
235
|
+
loc('article', 'welcome'); // '/space/article/welcome.json'
|
|
236
|
+
parseLocation('/space/article/welcome.json'); // { collection: 'article', basename: 'welcome' }
|
|
237
|
+
|
|
238
|
+
// normalizeLocation accepts canonical or short form and returns canonical
|
|
239
|
+
normalizeLocation('article/welcome'); // '/space/article/welcome.json'
|
|
240
|
+
normalizeLocation('/space/article/welcome.json'); // unchanged
|
|
241
|
+
|
|
242
|
+
// 6-char random basename — same generator the SDK uses by default
|
|
243
|
+
generateBasename(); // e.g., 'X7kQ9p'
|
|
206
244
|
```
|
|
207
245
|
|
|
208
|
-
|
|
246
|
+
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.
|
|
209
247
|
|
|
210
248
|
### AI Placeholder Pattern
|
|
211
249
|
|
|
212
|
-
Use `{{description}}` in field values to have AI generate content:
|
|
250
|
+
Use `{{description}}` in body field values to have AI generate content:
|
|
213
251
|
|
|
214
252
|
```typescript
|
|
215
253
|
// Create with AI-generated content
|
|
216
|
-
await channel.createObject({
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
headline: '{{catchy headline about coffee}}',
|
|
220
|
-
body: '{{informative paragraph}}'
|
|
221
|
-
}
|
|
254
|
+
await channel.createObject('article', {
|
|
255
|
+
headline: '{{catchy headline about coffee}}',
|
|
256
|
+
body: '{{informative paragraph}}',
|
|
222
257
|
});
|
|
223
258
|
|
|
224
259
|
// Update existing content with AI
|
|
225
|
-
await channel.updateObject('
|
|
260
|
+
await channel.updateObject('/space/article/welcome.json', {
|
|
226
261
|
prompt: 'Make the body shorter and more casual'
|
|
227
262
|
});
|
|
228
263
|
|
|
229
264
|
// Add new AI-generated field to existing object
|
|
230
|
-
await channel.updateObject('
|
|
265
|
+
await channel.updateObject('/space/article/welcome.json', {
|
|
231
266
|
data: { summary: '{{one-sentence summary}}' }
|
|
232
267
|
});
|
|
233
268
|
```
|
|
234
269
|
|
|
235
|
-
When resolving placeholders, the agent has access to the full
|
|
270
|
+
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.
|
|
236
271
|
|
|
237
272
|
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.
|
|
238
273
|
|
|
@@ -243,7 +278,7 @@ Undo/redo works on **checkpoints**, not individual operations. Call `checkpoint(
|
|
|
243
278
|
```typescript
|
|
244
279
|
// Create a checkpoint before user action
|
|
245
280
|
await channel.checkpoint('Delete object');
|
|
246
|
-
await channel.deleteObjects([
|
|
281
|
+
await channel.deleteObjects([location]);
|
|
247
282
|
|
|
248
283
|
// User can now undo back to the checkpoint
|
|
249
284
|
if (await channel.canUndo()) {
|
|
@@ -260,16 +295,13 @@ Checkpoints are **space-wide**: one shared stack across all channels and users.
|
|
|
260
295
|
|
|
261
296
|
### Hidden Fields
|
|
262
297
|
|
|
263
|
-
|
|
298
|
+
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:
|
|
264
299
|
|
|
265
300
|
```typescript
|
|
266
|
-
await channel.createObject({
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
author: "John Doe",
|
|
271
|
-
_ui: { x: 100, y: 200, collapsed: false }
|
|
272
|
-
}
|
|
301
|
+
await channel.createObject('article', {
|
|
302
|
+
title: 'My Article',
|
|
303
|
+
author: 'John Doe',
|
|
304
|
+
_ui: { x: 100, y: 200, collapsed: false }
|
|
273
305
|
});
|
|
274
306
|
```
|
|
275
307
|
|
|
@@ -284,42 +316,50 @@ Events fire for both local and remote changes. The `source` field indicates orig
|
|
|
284
316
|
|
|
285
317
|
```typescript
|
|
286
318
|
// All UI updates happen in one place, regardless of change source
|
|
287
|
-
channel.on('objectUpdated', ({
|
|
288
|
-
renderObject(
|
|
319
|
+
channel.on('objectUpdated', ({ location, object, source }) => {
|
|
320
|
+
renderObject(location, object);
|
|
289
321
|
if (source === 'remote_agent') {
|
|
290
322
|
doLayout(); // AI might have added content
|
|
291
323
|
}
|
|
292
324
|
});
|
|
293
325
|
|
|
294
326
|
// Caller just makes the change - event handler does the UI work
|
|
295
|
-
channel.updateObject(
|
|
327
|
+
channel.updateObject(location, { prompt: 'expand this' });
|
|
296
328
|
```
|
|
297
329
|
|
|
298
|
-
###
|
|
330
|
+
### Locations & Basenames
|
|
299
331
|
|
|
300
|
-
By default, `createObject`
|
|
332
|
+
By default, `createObject` mints a 6-character alphanumeric basename. Provide your own via `options.basename` for meaningful identifiers:
|
|
301
333
|
|
|
302
334
|
```typescript
|
|
303
|
-
await channel.createObject(
|
|
335
|
+
await channel.createObject('article',
|
|
336
|
+
{ title: 'The Meaning of Life' },
|
|
337
|
+
{ basename: 'meaning-of-life' },
|
|
338
|
+
);
|
|
339
|
+
// → location: /space/article/meaning-of-life.json
|
|
304
340
|
```
|
|
305
341
|
|
|
306
|
-
**Why
|
|
307
|
-
- **Fire-and-forget creation** — Know the
|
|
308
|
-
- **Meaningful
|
|
342
|
+
**Why pin a basename?**
|
|
343
|
+
- **Fire-and-forget creation** — Know the location immediately without awaiting the response.
|
|
344
|
+
- **Meaningful identifiers** — Use domain-specific names like `welcome` or `2026-budget` for easier debugging and external references.
|
|
309
345
|
|
|
310
346
|
```typescript
|
|
311
347
|
// Fire-and-forget: create and reference without waiting
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
348
|
+
const basename = RoolClient.generateBasename();
|
|
349
|
+
const location = loc('note', basename);
|
|
350
|
+
|
|
351
|
+
channel.createObject('note', { text: '{{expand this idea}}' }, { basename });
|
|
352
|
+
channel.updateObject(parentLocation, {
|
|
353
|
+
data: { notes: [...existingNotes, location] },
|
|
354
|
+
}); // Add reference immediately
|
|
315
355
|
```
|
|
316
356
|
|
|
317
|
-
**
|
|
318
|
-
- Must
|
|
319
|
-
-
|
|
320
|
-
-
|
|
357
|
+
**Basename constraints:**
|
|
358
|
+
- Must start with an alphanumeric character.
|
|
359
|
+
- Other characters may be alphanumeric, hyphens (`-`), or underscores (`_`).
|
|
360
|
+
- Must be unique within its collection (throws if the location already exists).
|
|
321
361
|
|
|
322
|
-
Use `
|
|
362
|
+
Use `moveObject` to rename an object or move it to a different collection — see [Moving and Renaming](#moving-and-renaming).
|
|
323
363
|
|
|
324
364
|
## Authentication
|
|
325
365
|
|
|
@@ -366,7 +406,7 @@ if (!authenticated) {
|
|
|
366
406
|
|
|
367
407
|
## AI Agent
|
|
368
408
|
|
|
369
|
-
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.
|
|
409
|
+
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.
|
|
370
410
|
|
|
371
411
|
```typescript
|
|
372
412
|
const { message, objects } = await channel.prompt(
|
|
@@ -390,11 +430,11 @@ Returns a message (the AI's response) and the list of objects that were created
|
|
|
390
430
|
|
|
391
431
|
| Option | Description |
|
|
392
432
|
|--------|-------------|
|
|
393
|
-
| `
|
|
433
|
+
| `locations` | Focus the AI on specific objects, identified by location (given primary attention in context). |
|
|
394
434
|
| `responseSchema` | Request structured JSON instead of text summary |
|
|
395
435
|
| `effort` | Effort level: `'QUICK'`, `'STANDARD'` (default), `'REASONING'`, or `'RESEARCH'` |
|
|
396
436
|
| `ephemeral` | If true, don't record in interaction history (useful for tab completion) |
|
|
397
|
-
| `readOnly` | If true, disable mutation tools (create, update, delete). Use for questions. |
|
|
437
|
+
| `readOnly` | If true, disable mutation tools (create, update, move, delete). Use for questions. |
|
|
398
438
|
| `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). |
|
|
399
439
|
| `attachments` | Files to attach (`File`, `Blob`, or `{ data, contentType }`). Stored as authenticated space files; resulting `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. |
|
|
400
440
|
| `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. |
|
|
@@ -419,7 +459,7 @@ const { objects } = await channel.prompt(
|
|
|
419
459
|
// Work with specific objects
|
|
420
460
|
const result = await channel.prompt(
|
|
421
461
|
"Summarize these articles",
|
|
422
|
-
{
|
|
462
|
+
{ locations: ['/space/article/intro.json', '/space/article/conclusion.json'] }
|
|
423
463
|
);
|
|
424
464
|
|
|
425
465
|
// Quick question without mutations (fast model + read-only)
|
|
@@ -456,7 +496,11 @@ Use `responseSchema` to get structured JSON instead of a text message:
|
|
|
456
496
|
|
|
457
497
|
```typescript
|
|
458
498
|
const { message } = await channel.prompt("Categorize these items", {
|
|
459
|
-
|
|
499
|
+
locations: [
|
|
500
|
+
'/space/item/widget.json',
|
|
501
|
+
'/space/item/gadget.json',
|
|
502
|
+
'/space/item/gizmo.json',
|
|
503
|
+
],
|
|
460
504
|
responseSchema: {
|
|
461
505
|
type: 'object',
|
|
462
506
|
properties: {
|
|
@@ -478,7 +522,7 @@ console.log(result.categories, result.summary);
|
|
|
478
522
|
AI operations automatically receive context:
|
|
479
523
|
- **Interaction history** — Previous interactions and their results from this channel
|
|
480
524
|
- **Recently modified objects** — Objects created or changed recently
|
|
481
|
-
- **Selected objects** — Objects passed via `
|
|
525
|
+
- **Selected objects** — Objects passed via `locations` are given primary focus
|
|
482
526
|
|
|
483
527
|
This context flows automatically — no configuration needed. The AI sees enough history to maintain coherent interactions while respecting the `_`-prefixed field hiding rules.
|
|
484
528
|
|
|
@@ -506,7 +550,7 @@ await space.addUser(user.id, 'editor');
|
|
|
506
550
|
|------|--------------|
|
|
507
551
|
| `owner` | Full control, can delete space and manage all users |
|
|
508
552
|
| `admin` | All editor capabilities, plus can manage users (except other admins/owners) |
|
|
509
|
-
| `editor` | Can create, modify, and delete objects |
|
|
553
|
+
| `editor` | Can create, modify, move, and delete objects |
|
|
510
554
|
| `viewer` | Read-only access (can query with `prompt` and `findObjects`) |
|
|
511
555
|
|
|
512
556
|
### Space Collaboration Methods
|
|
@@ -549,6 +593,7 @@ When a user accesses a space via URL, they're granted the corresponding role (`v
|
|
|
549
593
|
| `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. |
|
|
550
594
|
| `getCurrentUser(): Promise<CurrentUser>` | Fetch fresh user profile from server (id, email, name, photoUrl, slug, plan, creditsBalance, totalCreditsUsed, createdAt, lastActivity, processedAt, storage) |
|
|
551
595
|
| `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. |
|
|
596
|
+
| `deleteCurrentUser(): Promise<void>` | Mark the current user's account for deletion (10-minute grace period before irreversible). Logs out the client. |
|
|
552
597
|
| `searchUser(email): Promise<UserResult \| null>` | Find user by exact email address (no partial matching) |
|
|
553
598
|
|
|
554
599
|
### Real-time Collaboration
|
|
@@ -556,7 +601,7 @@ When a user accesses a space via URL, they're granted the corresponding role (`v
|
|
|
556
601
|
When multiple users have a space open, changes sync in real-time. The `source` field in events tells you who made the change:
|
|
557
602
|
|
|
558
603
|
```typescript
|
|
559
|
-
channel.on('objectUpdated', ({
|
|
604
|
+
channel.on('objectUpdated', ({ location, object, source }) => {
|
|
560
605
|
if (source === 'remote_user') {
|
|
561
606
|
// Another user made this change
|
|
562
607
|
showCollaboratorActivity(object);
|
|
@@ -592,6 +637,7 @@ const client = new RoolClient({
|
|
|
592
637
|
| `listSpaces(): Promise<RoolSpaceInfo[]>` | List available spaces |
|
|
593
638
|
| `openSpace(spaceId): Promise<RoolSpace>` | Open a space with live SSE subscription. Caches and reuses open spaces. Call `space.openChannel(channelId)` to get a channel. |
|
|
594
639
|
| `createSpace(name): Promise<RoolSpace>` | Create a new space, returns live handle with SSE subscription |
|
|
640
|
+
| `duplicateSpace(sourceSpaceId, name): Promise<RoolSpace>` | Duplicate an existing space. Returns a handle to the new space. |
|
|
595
641
|
| `deleteSpace(id): Promise<void>` | Permanently delete a space (cannot be undone) |
|
|
596
642
|
| `importArchive(name, archive): Promise<RoolSpace>` | Import from a zip archive, creating a new space |
|
|
597
643
|
| `webdav(spaceId): RoolWebDAV` | Open a WebDAV client for a space's file storage |
|
|
@@ -679,7 +725,8 @@ Discover and install extensions published by other users.
|
|
|
679
725
|
|
|
680
726
|
| Method | Description |
|
|
681
727
|
|--------|-------------|
|
|
682
|
-
| `RoolClient.
|
|
728
|
+
| `RoolClient.generateBasename(): string` | Generate a 6-char alphanumeric basename for new object identities. |
|
|
729
|
+
| `RoolClient.generateId(): string` | Same as `generateBasename()`; retained for callers minting non-object IDs (interactions, conversations, channels). |
|
|
683
730
|
| `destroy(): void` | Clean up resources |
|
|
684
731
|
|
|
685
732
|
### Client Events
|
|
@@ -787,49 +834,126 @@ A channel is a named context within a space. All object operations, AI prompts,
|
|
|
787
834
|
|
|
788
835
|
### Object Operations
|
|
789
836
|
|
|
790
|
-
Objects are
|
|
837
|
+
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 user-defined fields only; identity lives in the location.
|
|
838
|
+
|
|
839
|
+
All methods that accept a location accept either the canonical form or the short form (`collection/basename`).
|
|
791
840
|
|
|
792
841
|
| Method | Description |
|
|
793
842
|
|--------|-------------|
|
|
794
|
-
| `getObject(
|
|
795
|
-
| `stat(
|
|
796
|
-
| `findObjects(options): Promise<{ objects, message }>` | Find objects using structured filters and natural language. Results sorted by modifiedAt (desc by default). |
|
|
797
|
-
| `
|
|
798
|
-
| `createObject(options): Promise<{ object, message }>` | Create a new object
|
|
799
|
-
| `updateObject(
|
|
800
|
-
| `
|
|
843
|
+
| `getObject(location): Promise<RoolObject \| undefined>` | Get an object, or undefined if not found. |
|
|
844
|
+
| `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. |
|
|
845
|
+
| `findObjects(options): Promise<{ objects, message }>` | Find objects using structured filters and/or natural language. Results sorted by modifiedAt (desc by default). |
|
|
846
|
+
| `getObjectLocations(options?): string[]` | Get all object locations. Sorted by modifiedAt (desc by default). Options: `{ limit?, order? }`. |
|
|
847
|
+
| `createObject(collection, body, options?): Promise<{ object, message }>` | Create a new object in `collection`. The SDK mints a random basename unless you pass `options.basename`. |
|
|
848
|
+
| `updateObject(location, options): Promise<{ object, message }>` | Update an existing object's body. |
|
|
849
|
+
| `moveObject(from, to, options?): Promise<{ object, message }>` | Rename or relocate an object. See [Moving and Renaming](#moving-and-renaming). |
|
|
850
|
+
| `deleteObjects(locations): Promise<void>` | Delete objects by location. Other objects' refs become stale. |
|
|
851
|
+
|
|
852
|
+
#### createObject
|
|
801
853
|
|
|
802
|
-
|
|
854
|
+
```typescript
|
|
855
|
+
// Auto-generated basename
|
|
856
|
+
const { object } = await channel.createObject('article', {
|
|
857
|
+
title: 'Hello',
|
|
858
|
+
body: 'World',
|
|
859
|
+
});
|
|
860
|
+
// → object.location: '/space/article/X7kQ9p.json'
|
|
861
|
+
|
|
862
|
+
// Pinned basename
|
|
863
|
+
await channel.createObject('article',
|
|
864
|
+
{ title: 'Welcome' },
|
|
865
|
+
{ basename: 'welcome' },
|
|
866
|
+
);
|
|
867
|
+
// → location: '/space/article/welcome.json'
|
|
868
|
+
|
|
869
|
+
// AI placeholders
|
|
870
|
+
await channel.createObject('article', {
|
|
871
|
+
headline: '{{catchy headline}}',
|
|
872
|
+
body: '{{long-form intro}}',
|
|
873
|
+
});
|
|
874
|
+
```
|
|
803
875
|
|
|
804
876
|
| Option | Description |
|
|
805
877
|
|--------|-------------|
|
|
806
|
-
| `
|
|
807
|
-
| `ephemeral` | If true, the operation won't be recorded in interaction history.
|
|
878
|
+
| `basename` | Specific basename to use. If omitted, the SDK generates a random 6-char one. |
|
|
879
|
+
| `ephemeral` | If true, the operation won't be recorded in interaction history. |
|
|
880
|
+
| `parentInteractionId` | Conversation tree parent. Omit to auto-continue; pass `null` for a new root. |
|
|
881
|
+
|
|
882
|
+
The body must not contain `id` or `type` — those names are reserved for identity. The SDK throws if either is present.
|
|
883
|
+
|
|
884
|
+
#### updateObject
|
|
885
|
+
|
|
886
|
+
```typescript
|
|
887
|
+
// Add/update fields
|
|
888
|
+
await channel.updateObject('/space/article/welcome.json', {
|
|
889
|
+
data: { status: 'published' },
|
|
890
|
+
});
|
|
808
891
|
|
|
809
|
-
|
|
892
|
+
// Delete a field (pass null)
|
|
893
|
+
await channel.updateObject('/space/article/welcome.json', {
|
|
894
|
+
data: { draft: null },
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
// AI-driven rewrite
|
|
898
|
+
await channel.updateObject('/space/article/welcome.json', {
|
|
899
|
+
prompt: 'Tighten the intro by 30%.',
|
|
900
|
+
});
|
|
901
|
+
```
|
|
810
902
|
|
|
811
903
|
| Option | Description |
|
|
812
904
|
|--------|-------------|
|
|
813
|
-
| `data` |
|
|
905
|
+
| `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. |
|
|
814
906
|
| `prompt` | Natural language instruction for AI to modify content. |
|
|
815
|
-
| `ephemeral` | If true, the operation won't be recorded in interaction history.
|
|
907
|
+
| `ephemeral` | If true, the operation won't be recorded in interaction history. |
|
|
908
|
+
| `parentInteractionId` | Conversation tree parent. Omit to auto-continue; pass `null` for a new root. |
|
|
816
909
|
|
|
817
|
-
|
|
910
|
+
`data` must not contain `id` or `type` — use `moveObject` to change identity.
|
|
911
|
+
|
|
912
|
+
#### Moving and Renaming
|
|
913
|
+
|
|
914
|
+
`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.
|
|
915
|
+
|
|
916
|
+
```typescript
|
|
917
|
+
// Rename within the same collection
|
|
918
|
+
await channel.moveObject(
|
|
919
|
+
'/space/article/welcome.json',
|
|
920
|
+
'/space/article/hello-world.json',
|
|
921
|
+
);
|
|
922
|
+
|
|
923
|
+
// Move into a different collection
|
|
924
|
+
await channel.moveObject(
|
|
925
|
+
'/space/draft/post-42.json',
|
|
926
|
+
'/space/article/post-42.json',
|
|
927
|
+
);
|
|
928
|
+
|
|
929
|
+
// Move and replace body in one go
|
|
930
|
+
await channel.moveObject(from, to, {
|
|
931
|
+
body: { title: 'Hello, world', status: 'published' },
|
|
932
|
+
});
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
| Option | Description |
|
|
936
|
+
|--------|-------------|
|
|
937
|
+
| `body` | Replace the body atomically as part of the move. If omitted, the body is preserved. Must not contain `id` or `type`. |
|
|
938
|
+
| `ephemeral` | If true, the operation won't be recorded in interaction history. |
|
|
939
|
+
| `parentInteractionId` | Conversation tree parent. Omit to auto-continue; pass `null` for a new root. |
|
|
940
|
+
|
|
941
|
+
#### findObjects
|
|
818
942
|
|
|
819
943
|
Find objects using structured filters and/or natural language.
|
|
820
944
|
|
|
821
945
|
- **`where` only** — exact-match filtering, no AI, no credits.
|
|
822
|
-
- **`collection` only** — filter by collection name
|
|
946
|
+
- **`collection` only** — filter by collection name, no AI, no credits.
|
|
823
947
|
- **`prompt` only** — AI-powered semantic query over all objects.
|
|
824
|
-
- **`where` + `prompt`** — `where` (and `
|
|
948
|
+
- **`where` + `prompt`** — `where` (and `locations`) narrow the data set first, then the AI queries within the constrained set.
|
|
825
949
|
|
|
826
950
|
| Option | Description |
|
|
827
951
|
|--------|-------------|
|
|
828
|
-
| `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. |
|
|
829
|
-
| `collection` | Filter by collection name.
|
|
952
|
+
| `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. |
|
|
953
|
+
| `collection` | Filter by collection name. |
|
|
830
954
|
| `prompt` | Natural language query. Triggers AI evaluation (uses credits). |
|
|
831
955
|
| `limit` | Maximum number of results. |
|
|
832
|
-
| `
|
|
956
|
+
| `locations` | Scope to specific object locations. Constrains the candidate set in both structured and AI queries. |
|
|
833
957
|
| `order` | Sort order by modifiedAt: `'asc'` or `'desc'` (default: `'desc'`). |
|
|
834
958
|
| `ephemeral` | If true, the query won't be recorded in interaction history. Useful for responsive search. |
|
|
835
959
|
|
|
@@ -865,7 +989,7 @@ const { objects } = await channel.findObjects({
|
|
|
865
989
|
});
|
|
866
990
|
```
|
|
867
991
|
|
|
868
|
-
When `where` or `
|
|
992
|
+
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.
|
|
869
993
|
|
|
870
994
|
### Undo/Redo
|
|
871
995
|
|
|
@@ -882,7 +1006,7 @@ See [Checkpoints & Undo/Redo](#checkpoints--undoredo) for semantics.
|
|
|
882
1006
|
|
|
883
1007
|
### Space Metadata
|
|
884
1008
|
|
|
885
|
-
Store arbitrary data alongside the
|
|
1009
|
+
Store arbitrary data alongside the space without it being part of an object's body (e.g., viewport state, user preferences).
|
|
886
1010
|
|
|
887
1011
|
| Method | Description |
|
|
888
1012
|
|--------|-------------|
|
|
@@ -943,6 +1067,8 @@ Paths are space-relative (`docs/readme.md`, not `/docs/readme.md`). `rool-drive:
|
|
|
943
1067
|
| `webdav.lock(pathOrRef, options)` / `webdav.refreshLock(pathOrRef, token)` / `webdav.unlock(token)` | WebDAV Class 2 write locks |
|
|
944
1068
|
| `webdav.request(method, path, init?)` | Raw authenticated WebDAV request escape hatch |
|
|
945
1069
|
|
|
1070
|
+
> **Note**: file storage `rool-drive:/...` and object locations `/space/...` are two different reference forms. `rool-drive:/...` always points at user-visible files in the space's WebDAV storage. Object locations identify records inside the space (and live at `/space/<collection>/<basename>.json`). They're not interchangeable — pass file refs to WebDAV methods and object locations to channel methods.
|
|
1071
|
+
|
|
946
1072
|
#### File references from AI responses
|
|
947
1073
|
|
|
948
1074
|
When an agent refers to a user-visible file, the SDK contract is `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-drive:/docs/read%20me.md`).
|
|
@@ -978,7 +1104,7 @@ const response = await channel.fetch('https://api.example.com/submit', {
|
|
|
978
1104
|
|
|
979
1105
|
### Collection Schema
|
|
980
1106
|
|
|
981
|
-
Collections are the types you use to group objects in a space. Every object
|
|
1107
|
+
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.
|
|
982
1108
|
|
|
983
1109
|
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.
|
|
984
1110
|
|
|
@@ -1023,7 +1149,7 @@ await channel.dropCollection('article');
|
|
|
1023
1149
|
| `string` | Text value | `{ kind: 'string' }` |
|
|
1024
1150
|
| `number` | Numeric value | `{ kind: 'number' }` |
|
|
1025
1151
|
| `boolean` | True/false | `{ kind: 'boolean' }` |
|
|
1026
|
-
| `ref` | Reference to another object | `{ kind: 'ref' }` |
|
|
1152
|
+
| `ref` | Reference to another object (location string) | `{ kind: 'ref' }` |
|
|
1027
1153
|
| `enum` | One of a set of values | `{ kind: 'enum', values: ['a', 'b'] }` |
|
|
1028
1154
|
| `literal` | Exact value | `{ kind: 'literal', value: 'fixed' }` |
|
|
1029
1155
|
| `array` | List of values | `{ kind: 'array', inner: { kind: 'string' } }` |
|
|
@@ -1065,10 +1191,11 @@ Semantic events describe what changed. Events fire for both local changes and re
|
|
|
1065
1191
|
// - 'remote_agent': AI agent made the change
|
|
1066
1192
|
// - 'system': Resync after error
|
|
1067
1193
|
|
|
1068
|
-
// Object events
|
|
1069
|
-
channel.on('objectCreated', ({
|
|
1070
|
-
channel.on('objectUpdated', ({
|
|
1071
|
-
channel.on('objectDeleted', ({
|
|
1194
|
+
// Object events — payload includes the full RoolObject
|
|
1195
|
+
channel.on('objectCreated', ({ location, object, source }) => void)
|
|
1196
|
+
channel.on('objectUpdated', ({ location, object, source }) => void)
|
|
1197
|
+
channel.on('objectDeleted', ({ location, source }) => void)
|
|
1198
|
+
channel.on('objectMoved', ({ from, to, object, source }) => void)
|
|
1072
1199
|
|
|
1073
1200
|
// Space metadata
|
|
1074
1201
|
channel.on('metadataUpdated', ({ metadata, source }) => void)
|
|
@@ -1095,7 +1222,7 @@ AI operations may fail due to rate limiting or other transient errors. Check `er
|
|
|
1095
1222
|
|
|
1096
1223
|
```typescript
|
|
1097
1224
|
try {
|
|
1098
|
-
await channel.updateObject(
|
|
1225
|
+
await channel.updateObject(location, { prompt: 'expand this' });
|
|
1099
1226
|
} catch (error) {
|
|
1100
1227
|
if (error.message.includes('temporarily unavailable')) {
|
|
1101
1228
|
showToast('Service busy, please try again in a moment');
|
|
@@ -1129,7 +1256,7 @@ Channel management (listing, renaming, deleting channels) is done via the client
|
|
|
1129
1256
|
|
|
1130
1257
|
The `ai` field in interactions distinguishes AI-generated responses from synthetic confirmations:
|
|
1131
1258
|
- `ai: true` — AI processed this operation (prompt, or createObject/updateObject with placeholders)
|
|
1132
|
-
- `ai: false` — System confirmation only (e.g., "Created object
|
|
1259
|
+
- `ai: false` — System confirmation only (e.g., "Created object /space/note/welcome.json")
|
|
1133
1260
|
|
|
1134
1261
|
### Tool Calls
|
|
1135
1262
|
|
|
@@ -1167,17 +1294,19 @@ type SpaceSchema = Record<string, CollectionDef>;
|
|
|
1167
1294
|
### Object Data
|
|
1168
1295
|
|
|
1169
1296
|
```typescript
|
|
1170
|
-
//
|
|
1171
|
-
//
|
|
1172
|
-
//
|
|
1173
|
-
// References between objects are fields whose values are object IDs
|
|
1297
|
+
// An object — identity in the envelope, data in body. Body never contains
|
|
1298
|
+
// `id` or `type`; references between objects are body fields whose values
|
|
1299
|
+
// are location strings.
|
|
1174
1300
|
interface RoolObject {
|
|
1175
|
-
|
|
1176
|
-
|
|
1301
|
+
location: string; // "/space/<collection>/<basename>.json"
|
|
1302
|
+
collection: string;
|
|
1303
|
+
basename: string;
|
|
1304
|
+
body: Record<string, unknown>;
|
|
1177
1305
|
}
|
|
1178
1306
|
|
|
1179
|
-
// Object stat
|
|
1307
|
+
// Object stat — audit information returned by channel.stat()
|
|
1180
1308
|
interface RoolObjectStat {
|
|
1309
|
+
location: string;
|
|
1181
1310
|
modifiedAt: number;
|
|
1182
1311
|
modifiedBy: string;
|
|
1183
1312
|
modifiedByName: string | null;
|
|
@@ -1249,19 +1378,19 @@ interface ToolCall {
|
|
|
1249
1378
|
type InteractionStatus = 'pending' | 'streaming' | 'done' | 'error';
|
|
1250
1379
|
|
|
1251
1380
|
interface Interaction {
|
|
1252
|
-
id: string;
|
|
1253
|
-
parentId: string | null;
|
|
1381
|
+
id: string; // Unique ID for this interaction
|
|
1382
|
+
parentId: string | null; // Parent in conversation tree (null = root)
|
|
1254
1383
|
timestamp: number;
|
|
1255
|
-
userId: string;
|
|
1256
|
-
userName?: string | null;
|
|
1257
|
-
operation: 'prompt' | 'createObject' | 'updateObject' | 'deleteObjects';
|
|
1258
|
-
input: string;
|
|
1259
|
-
output: string | null;
|
|
1260
|
-
status: InteractionStatus;
|
|
1261
|
-
ai: boolean;
|
|
1262
|
-
|
|
1263
|
-
toolCalls: ToolCall[];
|
|
1264
|
-
attachments?: string[];
|
|
1384
|
+
userId: string; // Who performed this interaction
|
|
1385
|
+
userName?: string | null; // Display name at time of interaction
|
|
1386
|
+
operation: 'prompt' | 'createObject' | 'updateObject' | 'moveObject' | 'deleteObjects';
|
|
1387
|
+
input: string; // What the user did: prompt text or action description
|
|
1388
|
+
output: string | null; // AI response or confirmation message (may be partial when streaming)
|
|
1389
|
+
status: InteractionStatus; // Lifecycle status (pending → streaming → done/error)
|
|
1390
|
+
ai: boolean; // Whether AI was invoked (vs synthetic confirmation)
|
|
1391
|
+
modifiedObjectLocations: string[]; // Locations of objects affected by this interaction
|
|
1392
|
+
toolCalls: ToolCall[]; // Tools called during this interaction (for AI prompts)
|
|
1393
|
+
attachments?: string[]; // rool-drive:/... file references attached by the user
|
|
1265
1394
|
}
|
|
1266
1395
|
```
|
|
1267
1396
|
|
|
@@ -1284,13 +1413,14 @@ type ChangeSource = 'local_user' | 'remote_user' | 'remote_agent' | 'system';
|
|
|
1284
1413
|
type PromptEffort = 'QUICK' | 'STANDARD' | 'REASONING' | 'RESEARCH';
|
|
1285
1414
|
|
|
1286
1415
|
interface PromptOptions {
|
|
1287
|
-
|
|
1416
|
+
locations?: string[]; // Scope to specific objects
|
|
1288
1417
|
responseSchema?: Record<string, unknown>;
|
|
1289
|
-
effort?: PromptEffort;
|
|
1290
|
-
ephemeral?: boolean;
|
|
1291
|
-
readOnly?: boolean;
|
|
1292
|
-
parentInteractionId?: string | null;
|
|
1293
|
-
attachments?: Array<File | Blob | { data: string; contentType: string }>;
|
|
1418
|
+
effort?: PromptEffort; // Effort level (default: 'STANDARD')
|
|
1419
|
+
ephemeral?: boolean; // Don't record in interaction history
|
|
1420
|
+
readOnly?: boolean; // Disable mutation tools (default: false)
|
|
1421
|
+
parentInteractionId?: string | null; // Branch from a specific interaction (omit to auto-continue)
|
|
1422
|
+
attachments?: Array<File | Blob | { data: string; contentType: string }>; // Files to attach
|
|
1423
|
+
signal?: AbortSignal; // Cancel an in-flight prompt
|
|
1294
1424
|
}
|
|
1295
1425
|
```
|
|
1296
1426
|
|