@rool-dev/sdk 0.2.0-dev.64c2b97 → 0.2.0-dev.6769bdc
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 +190 -141
- package/dist/channel.d.ts +316 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/channel.js +793 -0
- package/dist/channel.js.map +1 -0
- package/dist/client.d.ts +39 -32
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +108 -70
- package/dist/client.js.map +1 -1
- package/dist/graphql.d.ts +17 -8
- package/dist/graphql.d.ts.map +1 -1
- package/dist/graphql.js +43 -28
- package/dist/graphql.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/space.d.ts +28 -295
- package/dist/space.d.ts.map +1 -1
- package/dist/space.js +48 -879
- package/dist/space.js.map +1 -1
- package/dist/subscription.d.ts.map +1 -1
- package/dist/subscription.js +18 -7
- package/dist/subscription.js.map +1 -1
- package/dist/types.d.ts +18 -36
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Rool SDK
|
|
2
2
|
|
|
3
|
-
The TypeScript SDK for Rool
|
|
3
|
+
The TypeScript SDK for Rool, a persistent and collaborative environment for organizing objects.
|
|
4
4
|
|
|
5
|
-
Rool
|
|
5
|
+
Rool enables you to build applications where AI operates on a structured world model rather than a text conversation. The context for all AI operations is the full object graph, allowing the system to reason about, update, and expand the state of your application directly.
|
|
6
6
|
|
|
7
7
|
Use Rool to programmatically instruct agents to generate content, research topics, or reorganize data. The client manages authentication, real-time synchronization, and media storage, supporting both single-user and multi-user workflows.
|
|
8
8
|
|
|
9
9
|
**Core primitives:**
|
|
10
|
+
- **Spaces** — Containers for objects, schema, metadata, and channels
|
|
11
|
+
- **Channels** — A space + conversationId pair. All object and AI operations go through a channel.
|
|
10
12
|
- **Objects** — Key-value records with any fields you define. References between objects are data fields whose values are object IDs.
|
|
11
13
|
- **AI operations** — Create, update, or query objects using natural language and `{{placeholders}}`
|
|
12
14
|
|
|
@@ -30,48 +32,79 @@ if (!authenticated) {
|
|
|
30
32
|
client.login('My App'); // Redirects to auth page, shows "Sign in to My App"
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
// Create a new space
|
|
35
|
+
// Create a new space, then open a channel on it
|
|
34
36
|
const space = await client.createSpace('Solar System');
|
|
37
|
+
const channel = await space.openChannel('main');
|
|
38
|
+
|
|
39
|
+
// Define the schema — what types of objects exist and their fields
|
|
40
|
+
await channel.createCollection('body', [
|
|
41
|
+
{ name: 'name', type: { kind: 'string' } },
|
|
42
|
+
{ name: 'mass', type: { kind: 'string' } },
|
|
43
|
+
{ name: 'radius', type: { kind: 'string' } },
|
|
44
|
+
{ name: 'orbits', type: { kind: 'maybe', inner: { kind: 'ref' } } },
|
|
45
|
+
]);
|
|
35
46
|
|
|
36
47
|
// Create objects with AI-generated content using {{placeholders}}
|
|
37
|
-
const { object: sun } = await
|
|
48
|
+
const { object: sun } = await channel.createObject({
|
|
38
49
|
data: {
|
|
39
|
-
type: 'star',
|
|
40
50
|
name: 'Sun',
|
|
41
51
|
mass: '{{mass in solar masses}}',
|
|
42
|
-
|
|
52
|
+
radius: '{{radius in km}}'
|
|
43
53
|
}
|
|
44
54
|
});
|
|
45
55
|
|
|
46
|
-
const { object: earth } = await
|
|
56
|
+
const { object: earth } = await channel.createObject({
|
|
47
57
|
data: {
|
|
48
|
-
type: 'planet',
|
|
49
58
|
name: 'Earth',
|
|
50
59
|
mass: '{{mass in Earth masses}}',
|
|
51
60
|
radius: '{{radius in km}}',
|
|
52
|
-
orbitalPeriod: '{{orbital period in days}}',
|
|
53
61
|
orbits: sun.id // Reference to the sun object
|
|
54
62
|
}
|
|
55
63
|
});
|
|
56
64
|
|
|
57
65
|
// Use the AI agent to work with your data
|
|
58
|
-
const { message, objects } = await
|
|
66
|
+
const { message, objects } = await channel.prompt(
|
|
59
67
|
'Add the other planets in our solar system, each referencing the Sun'
|
|
60
68
|
);
|
|
61
69
|
console.log(message); // AI explains what it did
|
|
62
70
|
console.log(`Created ${objects.length} objects`);
|
|
63
71
|
|
|
64
72
|
// Query with natural language
|
|
65
|
-
const { objects: innerPlanets } = await
|
|
73
|
+
const { objects: innerPlanets } = await channel.findObjects({
|
|
66
74
|
prompt: 'planets closer to the sun than Earth'
|
|
67
75
|
});
|
|
68
76
|
|
|
69
77
|
// Clean up
|
|
70
|
-
|
|
78
|
+
channel.close();
|
|
71
79
|
```
|
|
72
80
|
|
|
73
81
|
## Core Concepts
|
|
74
82
|
|
|
83
|
+
### Spaces and Channels
|
|
84
|
+
|
|
85
|
+
A **space** is a container that holds objects, schema, metadata, and channels. A **channel** is a space + conversationId pair — it's the handle you use for all object and AI operations.
|
|
86
|
+
|
|
87
|
+
There are two main handles:
|
|
88
|
+
- **`RoolSpace`** — Lightweight admin handle for user management, link access, channel management, and export. No real-time subscription.
|
|
89
|
+
- **`RoolChannel`** — Full real-time handle for objects, AI prompts, media, schema, and undo/redo.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// Open a space for admin operations
|
|
93
|
+
const space = await client.openSpace('space-id');
|
|
94
|
+
await space.addUser(userId, 'editor');
|
|
95
|
+
await space.setLinkAccess('viewer');
|
|
96
|
+
|
|
97
|
+
// Open a channel for object and AI operations
|
|
98
|
+
const channel = await client.openChannel('space-id', 'my-conversation');
|
|
99
|
+
await channel.prompt('Create some planets');
|
|
100
|
+
|
|
101
|
+
// Or open a channel via the space handle
|
|
102
|
+
const channel2 = await space.openChannel('research');
|
|
103
|
+
await channel2.prompt('Analyze the data'); // Independent conversation
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The `conversationId` is fixed when you open a channel and cannot be changed. To use a different conversation, open a new channel. Both channels share the same objects and schema — only the conversation history differs.
|
|
107
|
+
|
|
75
108
|
### Objects & References
|
|
76
109
|
|
|
77
110
|
**Objects** are plain key-value records. The `id` field is reserved; everything else is application-defined.
|
|
@@ -98,7 +131,7 @@ Use `{{description}}` in field values to have AI generate content:
|
|
|
98
131
|
|
|
99
132
|
```typescript
|
|
100
133
|
// Create with AI-generated content
|
|
101
|
-
await
|
|
134
|
+
await channel.createObject({
|
|
102
135
|
data: {
|
|
103
136
|
type: 'article',
|
|
104
137
|
headline: '{{catchy headline about coffee}}',
|
|
@@ -107,12 +140,12 @@ await space.createObject({
|
|
|
107
140
|
});
|
|
108
141
|
|
|
109
142
|
// Update existing content with AI
|
|
110
|
-
await
|
|
143
|
+
await channel.updateObject('abc123', {
|
|
111
144
|
prompt: 'Make the body shorter and more casual'
|
|
112
145
|
});
|
|
113
146
|
|
|
114
147
|
// Add new AI-generated field to existing object
|
|
115
|
-
await
|
|
148
|
+
await channel.updateObject('abc123', {
|
|
116
149
|
data: { summary: '{{one-sentence summary}}' }
|
|
117
150
|
});
|
|
118
151
|
```
|
|
@@ -127,17 +160,17 @@ Undo/redo works on **checkpoints**, not individual operations. Call `checkpoint(
|
|
|
127
160
|
|
|
128
161
|
```typescript
|
|
129
162
|
// Create a checkpoint before user action
|
|
130
|
-
await
|
|
131
|
-
await
|
|
163
|
+
await channel.checkpoint('Delete object');
|
|
164
|
+
await channel.deleteObjects([objectId]);
|
|
132
165
|
|
|
133
166
|
// User can now undo back to the checkpoint
|
|
134
|
-
if (await
|
|
135
|
-
await
|
|
167
|
+
if (await channel.canUndo()) {
|
|
168
|
+
await channel.undo(); // Restores the deleted object
|
|
136
169
|
}
|
|
137
170
|
|
|
138
171
|
// Redo reapplies the undone action
|
|
139
|
-
if (await
|
|
140
|
-
await
|
|
172
|
+
if (await channel.canRedo()) {
|
|
173
|
+
await channel.redo(); // Deletes the object again
|
|
141
174
|
}
|
|
142
175
|
```
|
|
143
176
|
|
|
@@ -150,7 +183,7 @@ In collaborative scenarios, conflicting changes (modified by others since your c
|
|
|
150
183
|
Fields starting with `_` (e.g., `_ui`, `_cache`) are hidden from AI but otherwise 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:
|
|
151
184
|
|
|
152
185
|
```typescript
|
|
153
|
-
await
|
|
186
|
+
await channel.createObject({
|
|
154
187
|
data: {
|
|
155
188
|
title: 'My Article',
|
|
156
189
|
author: "John Doe",
|
|
@@ -170,7 +203,7 @@ Events fire for both local and remote changes. The `source` field indicates orig
|
|
|
170
203
|
|
|
171
204
|
```typescript
|
|
172
205
|
// All UI updates happen in one place, regardless of change source
|
|
173
|
-
|
|
206
|
+
channel.on('objectUpdated', ({ objectId, object, source }) => {
|
|
174
207
|
renderObject(objectId, object);
|
|
175
208
|
if (source === 'remote_agent') {
|
|
176
209
|
doLayout(); // AI might have added content
|
|
@@ -178,7 +211,7 @@ space.on('objectUpdated', ({ objectId, object, source }) => {
|
|
|
178
211
|
});
|
|
179
212
|
|
|
180
213
|
// Caller just makes the change - event handler does the UI work
|
|
181
|
-
|
|
214
|
+
channel.updateObject(objectId, { prompt: 'expand this' });
|
|
182
215
|
```
|
|
183
216
|
|
|
184
217
|
### Custom Object IDs
|
|
@@ -186,7 +219,7 @@ space.updateObject(objectId, { prompt: 'expand this' });
|
|
|
186
219
|
By default, `createObject` generates a 6-character alphanumeric ID. Provide your own via `data.id`:
|
|
187
220
|
|
|
188
221
|
```typescript
|
|
189
|
-
await
|
|
222
|
+
await channel.createObject({ data: { id: 'article-42', title: 'The Meaning of Life' } });
|
|
190
223
|
```
|
|
191
224
|
|
|
192
225
|
**Why use custom IDs?**
|
|
@@ -196,8 +229,8 @@ await space.createObject({ data: { id: 'article-42', title: 'The Meaning of Life
|
|
|
196
229
|
```typescript
|
|
197
230
|
// Fire-and-forget: create and reference without waiting
|
|
198
231
|
const id = RoolClient.generateId();
|
|
199
|
-
|
|
200
|
-
|
|
232
|
+
channel.createObject({ data: { id, type: 'note', text: '{{expand this idea}}' } });
|
|
233
|
+
channel.updateObject(parentId, { data: { notes: [...existingNotes, id] } }); // Add reference immediately
|
|
201
234
|
```
|
|
202
235
|
|
|
203
236
|
**Constraints:**
|
|
@@ -252,7 +285,7 @@ if (!authenticated) {
|
|
|
252
285
|
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.
|
|
253
286
|
|
|
254
287
|
```typescript
|
|
255
|
-
const { message, objects } = await
|
|
288
|
+
const { message, objects } = await channel.prompt(
|
|
256
289
|
"Create a topic node for the solar system, then child nodes for each planet."
|
|
257
290
|
);
|
|
258
291
|
console.log(`AI: ${message}`);
|
|
@@ -293,31 +326,31 @@ Returns a message (the AI's response) and the list of objects that were created
|
|
|
293
326
|
|
|
294
327
|
```typescript
|
|
295
328
|
// Reorganize existing objects
|
|
296
|
-
const { objects } = await
|
|
329
|
+
const { objects } = await channel.prompt(
|
|
297
330
|
"Group these notes by topic and create a parent node for each group."
|
|
298
331
|
);
|
|
299
332
|
|
|
300
333
|
// Work with specific objects
|
|
301
|
-
const result = await
|
|
334
|
+
const result = await channel.prompt(
|
|
302
335
|
"Summarize these articles",
|
|
303
336
|
{ objectIds: ['article-1', 'article-2'] }
|
|
304
337
|
);
|
|
305
338
|
|
|
306
339
|
// Quick question without mutations (fast model + read-only)
|
|
307
|
-
const { message } = await
|
|
340
|
+
const { message } = await channel.prompt(
|
|
308
341
|
"What topics are covered?",
|
|
309
342
|
{ effort: 'QUICK', readOnly: true }
|
|
310
343
|
);
|
|
311
344
|
|
|
312
345
|
// Complex analysis with extended reasoning
|
|
313
|
-
await
|
|
346
|
+
await channel.prompt(
|
|
314
347
|
"Analyze relationships and reorganize",
|
|
315
348
|
{ effort: 'REASONING' }
|
|
316
349
|
);
|
|
317
350
|
|
|
318
351
|
// Attach files for the AI to see (File from <input>, Blob, or base64)
|
|
319
352
|
const file = fileInput.files[0]; // from <input type="file">
|
|
320
|
-
await
|
|
353
|
+
await channel.prompt(
|
|
321
354
|
"Describe what's in this photo and create an object for it",
|
|
322
355
|
{ attachments: [file] }
|
|
323
356
|
);
|
|
@@ -328,7 +361,7 @@ await space.prompt(
|
|
|
328
361
|
Use `responseSchema` to get structured JSON instead of a text message:
|
|
329
362
|
|
|
330
363
|
```typescript
|
|
331
|
-
const { message } = await
|
|
364
|
+
const { message } = await channel.prompt("Categorize these items", {
|
|
332
365
|
objectIds: ['item-1', 'item-2', 'item-3'],
|
|
333
366
|
responseSchema: {
|
|
334
367
|
type: 'object',
|
|
@@ -349,7 +382,7 @@ console.log(result.categories, result.summary);
|
|
|
349
382
|
### Context Flow
|
|
350
383
|
|
|
351
384
|
AI operations automatically receive context:
|
|
352
|
-
- **Interaction history** — Previous interactions and their results from this conversation
|
|
385
|
+
- **Interaction history** — Previous interactions and their results from this channel's conversation
|
|
353
386
|
- **Recently modified objects** — Objects created or changed recently
|
|
354
387
|
- **Selected objects** — Objects passed via `objectIds` are given primary focus
|
|
355
388
|
|
|
@@ -369,6 +402,7 @@ if (!user) {
|
|
|
369
402
|
}
|
|
370
403
|
|
|
371
404
|
// Add them to the space
|
|
405
|
+
const space = await client.openSpace('space-id');
|
|
372
406
|
await space.addUser(user.id, 'editor');
|
|
373
407
|
```
|
|
374
408
|
|
|
@@ -383,6 +417,8 @@ await space.addUser(user.id, 'editor');
|
|
|
383
417
|
|
|
384
418
|
### Space Collaboration Methods
|
|
385
419
|
|
|
420
|
+
These methods are available on `RoolSpace`:
|
|
421
|
+
|
|
386
422
|
| Method | Description |
|
|
387
423
|
|--------|-------------|
|
|
388
424
|
| `listUsers(): Promise<SpaceMember[]>` | List users with access |
|
|
@@ -395,6 +431,8 @@ await space.addUser(user.id, 'editor');
|
|
|
395
431
|
Enable public URL access to allow anyone with the space URL to access it:
|
|
396
432
|
|
|
397
433
|
```typescript
|
|
434
|
+
const space = await client.openSpace('space-id');
|
|
435
|
+
|
|
398
436
|
// Allow anyone with the URL to view
|
|
399
437
|
await space.setLinkAccess('viewer');
|
|
400
438
|
|
|
@@ -423,7 +461,7 @@ When a user accesses a space via URL, they're granted the corresponding role (`v
|
|
|
423
461
|
When multiple users have a space open, changes sync in real-time. The `source` field in events tells you who made the change:
|
|
424
462
|
|
|
425
463
|
```typescript
|
|
426
|
-
|
|
464
|
+
channel.on('objectUpdated', ({ objectId, object, source }) => {
|
|
427
465
|
if (source === 'remote_user') {
|
|
428
466
|
// Another user made this change
|
|
429
467
|
showCollaboratorActivity(object);
|
|
@@ -452,14 +490,28 @@ const client = new RoolClient({
|
|
|
452
490
|
});
|
|
453
491
|
```
|
|
454
492
|
|
|
455
|
-
### Space Lifecycle
|
|
493
|
+
### Space & Channel Lifecycle
|
|
456
494
|
|
|
457
495
|
| Method | Description |
|
|
458
496
|
|--------|-------------|
|
|
459
497
|
| `listSpaces(): Promise<RoolSpaceInfo[]>` | List available spaces |
|
|
460
|
-
| `openSpace(
|
|
461
|
-
| `
|
|
498
|
+
| `openSpace(spaceId): Promise<RoolSpace>` | Open a space for admin operations (no real-time subscription) |
|
|
499
|
+
| `openChannel(spaceId, conversationId): Promise<RoolChannel>` | Open a channel on a space with a specific conversation |
|
|
500
|
+
| `createSpace(name): Promise<RoolSpace>` | Create a new space, returns admin handle |
|
|
462
501
|
| `deleteSpace(id): Promise<void>` | Permanently delete a space (cannot be undone) |
|
|
502
|
+
| `importArchive(name, archive): Promise<RoolSpace>` | Import from a zip archive, creating a new space |
|
|
503
|
+
|
|
504
|
+
### Channel Management
|
|
505
|
+
|
|
506
|
+
Manage channels (conversations) within a space. Available on both the client and space handles:
|
|
507
|
+
|
|
508
|
+
| Method | Description |
|
|
509
|
+
|--------|-------------|
|
|
510
|
+
| `client.renameChannel(spaceId, channelId, name): Promise<void>` | Rename a channel |
|
|
511
|
+
| `client.deleteChannel(spaceId, channelId): Promise<void>` | Delete a channel and its history |
|
|
512
|
+
| `space.getChannels(): ConversationInfo[]` | List channels (from cached snapshot) |
|
|
513
|
+
| `space.deleteChannel(channelId): Promise<void>` | Delete a channel |
|
|
514
|
+
| `channel.rename(name): Promise<void>` | Rename the current channel |
|
|
463
515
|
|
|
464
516
|
### User Storage
|
|
465
517
|
|
|
@@ -516,6 +568,9 @@ client.on('authStateChanged', (authenticated: boolean) => void)
|
|
|
516
568
|
client.on('spaceAdded', (space: RoolSpaceInfo) => void) // Space created or access granted
|
|
517
569
|
client.on('spaceRemoved', (spaceId: string) => void) // Space deleted or access revoked
|
|
518
570
|
client.on('spaceRenamed', (spaceId: string, newName: string) => void)
|
|
571
|
+
client.on('channelCreated', (spaceId: string, channel: ConversationInfo) => void)
|
|
572
|
+
client.on('channelRenamed', (spaceId: string, channelId: string, newName: string) => void)
|
|
573
|
+
client.on('channelDeleted', (spaceId: string, channelId: string) => void)
|
|
519
574
|
client.on('userStorageChanged', ({ key, value, source }: UserStorageChangedEvent) => void)
|
|
520
575
|
client.on('connectionStateChanged', (state: 'connected' | 'disconnected' | 'reconnecting') => void)
|
|
521
576
|
client.on('error', (error: Error, context?: string) => void)
|
|
@@ -535,7 +590,36 @@ client.on('spaceRenamed', (id, name) => {
|
|
|
535
590
|
|
|
536
591
|
## RoolSpace API
|
|
537
592
|
|
|
538
|
-
|
|
593
|
+
A space is a lightweight admin handle for space-level operations. It does not have a real-time subscription — use channels for live data and object operations.
|
|
594
|
+
|
|
595
|
+
### Properties
|
|
596
|
+
|
|
597
|
+
| Property | Description |
|
|
598
|
+
|----------|-------------|
|
|
599
|
+
| `id: string` | Space ID |
|
|
600
|
+
| `name: string` | Space name |
|
|
601
|
+
| `role: RoolUserRole` | User's role |
|
|
602
|
+
| `linkAccess: LinkAccess` | URL sharing level |
|
|
603
|
+
|
|
604
|
+
### Methods
|
|
605
|
+
|
|
606
|
+
| Method | Description |
|
|
607
|
+
|--------|-------------|
|
|
608
|
+
| `openChannel(conversationId): Promise<RoolChannel>` | Open a channel on this space |
|
|
609
|
+
| `rename(newName): Promise<void>` | Rename this space |
|
|
610
|
+
| `delete(): Promise<void>` | Permanently delete this space |
|
|
611
|
+
| `listUsers(): Promise<SpaceMember[]>` | List users with access |
|
|
612
|
+
| `addUser(userId, role): Promise<void>` | Add user to space |
|
|
613
|
+
| `removeUser(userId): Promise<void>` | Remove user from space |
|
|
614
|
+
| `setLinkAccess(linkAccess): Promise<void>` | Set URL sharing level |
|
|
615
|
+
| `getChannels(): ConversationInfo[]` | List channels (from cached snapshot) |
|
|
616
|
+
| `deleteChannel(channelId): Promise<void>` | Delete a channel |
|
|
617
|
+
| `exportArchive(): Promise<Blob>` | Export space as zip archive |
|
|
618
|
+
| `refresh(): Promise<void>` | Refresh space data from server |
|
|
619
|
+
|
|
620
|
+
## RoolChannel API
|
|
621
|
+
|
|
622
|
+
A channel is a space + conversationId pair. All object operations, AI prompts, and real-time sync go through a channel. The `conversationId` is fixed at open time — to use a different conversation, open a new channel.
|
|
539
623
|
|
|
540
624
|
### Properties
|
|
541
625
|
|
|
@@ -546,15 +630,15 @@ Spaces are first-class objects with built-in undo/redo, event emission, and real
|
|
|
546
630
|
| `role: RoolUserRole` | User's role (`'owner' \| 'admin' \| 'editor' \| 'viewer'`) |
|
|
547
631
|
| `linkAccess: LinkAccess` | URL sharing level (`'none' \| 'viewer' \| 'editor'`) |
|
|
548
632
|
| `userId: string` | Current user's ID |
|
|
549
|
-
| `conversationId: string` | ID
|
|
550
|
-
| `isReadOnly
|
|
633
|
+
| `conversationId: string` | Conversation ID (read-only, fixed at open time) |
|
|
634
|
+
| `isReadOnly: boolean` | True if viewer role |
|
|
551
635
|
|
|
552
636
|
### Lifecycle
|
|
553
637
|
|
|
554
638
|
| Method | Description |
|
|
555
639
|
|--------|-------------|
|
|
556
640
|
| `close(): void` | Clean up resources and stop receiving updates |
|
|
557
|
-
| `rename(
|
|
641
|
+
| `rename(name): Promise<void>` | Rename this channel (conversation) |
|
|
558
642
|
|
|
559
643
|
### Object Operations
|
|
560
644
|
|
|
@@ -563,7 +647,7 @@ Objects are plain key/value records. `id` is the only reserved field; everything
|
|
|
563
647
|
| Method | Description |
|
|
564
648
|
|--------|-------------|
|
|
565
649
|
| `getObject(objectId): Promise<RoolObject \| undefined>` | Get object data, or undefined if not found. |
|
|
566
|
-
| `stat(objectId):
|
|
650
|
+
| `stat(objectId): RoolObjectStat \| undefined` | Get object stat (audit info: modifiedAt, modifiedBy, modifiedByName), or undefined if not found. Sync read from local cache. |
|
|
567
651
|
| `findObjects(options): Promise<{ objects, message }>` | Find objects using structured filters and natural language. Results sorted by modifiedAt (desc by default). |
|
|
568
652
|
| `getObjectIds(options?): string[]` | Get all object IDs. Sorted by modifiedAt (desc by default). Options: `{ limit?, order? }`. |
|
|
569
653
|
| `createObject(options): Promise<{ object, message }>` | Create a new object. Returns the object (with AI-filled content) and message. |
|
|
@@ -606,17 +690,17 @@ Find objects using structured filters and/or natural language.
|
|
|
606
690
|
|
|
607
691
|
```typescript
|
|
608
692
|
// Exact field matching (no AI, no credits)
|
|
609
|
-
const { objects } = await
|
|
693
|
+
const { objects } = await channel.findObjects({
|
|
610
694
|
where: { type: 'article', status: 'published' }
|
|
611
695
|
});
|
|
612
696
|
|
|
613
697
|
// Pure natural language query (AI interprets)
|
|
614
|
-
const { objects, message } = await
|
|
698
|
+
const { objects, message } = await channel.findObjects({
|
|
615
699
|
prompt: 'articles about space exploration published this year'
|
|
616
700
|
});
|
|
617
701
|
|
|
618
702
|
// Combined: where narrows the data, prompt queries within it
|
|
619
|
-
const { objects } = await
|
|
703
|
+
const { objects } = await channel.findObjects({
|
|
620
704
|
where: { type: 'article' },
|
|
621
705
|
prompt: 'that discuss climate solutions positively',
|
|
622
706
|
limit: 10
|
|
@@ -661,16 +745,16 @@ Media URLs in object fields are visible to AI. Both uploaded and AI-generated me
|
|
|
661
745
|
|
|
662
746
|
```typescript
|
|
663
747
|
// Upload an image
|
|
664
|
-
const url = await
|
|
665
|
-
await
|
|
748
|
+
const url = await channel.uploadMedia(file);
|
|
749
|
+
await channel.createObject({ data: { title: 'Photo', image: url } });
|
|
666
750
|
|
|
667
751
|
// Or let AI generate one using a placeholder
|
|
668
|
-
await
|
|
752
|
+
await channel.createObject({
|
|
669
753
|
data: { title: 'Mascot', image: '{{generate an image of a flying tortoise}}' }
|
|
670
754
|
});
|
|
671
755
|
|
|
672
756
|
// Display media (handles auth automatically)
|
|
673
|
-
const response = await
|
|
757
|
+
const response = await channel.fetchMedia(object.image);
|
|
674
758
|
if (response.contentType.startsWith('image/')) {
|
|
675
759
|
const blob = await response.blob();
|
|
676
760
|
img.src = URL.createObjectURL(blob);
|
|
@@ -684,7 +768,7 @@ Collections are types you can use to group objects in a space. Every object must
|
|
|
684
768
|
|
|
685
769
|
```typescript
|
|
686
770
|
// Define a collection with typed fields
|
|
687
|
-
await
|
|
771
|
+
await channel.createCollection('article', [
|
|
688
772
|
{ name: 'title', type: { kind: 'string' } },
|
|
689
773
|
{ name: 'status', type: { kind: 'enum', values: ['draft', 'published', 'archived'] } },
|
|
690
774
|
{ name: 'tags', type: { kind: 'array', inner: { kind: 'string' } } },
|
|
@@ -692,11 +776,11 @@ await space.createCollection('article', [
|
|
|
692
776
|
]);
|
|
693
777
|
|
|
694
778
|
// Read the current schema
|
|
695
|
-
const schema =
|
|
779
|
+
const schema = channel.getSchema();
|
|
696
780
|
console.log(schema.article.fields); // FieldDef[]
|
|
697
781
|
|
|
698
782
|
// Modify an existing collection's fields
|
|
699
|
-
await
|
|
783
|
+
await channel.alterCollection('article', [
|
|
700
784
|
{ name: 'title', type: { kind: 'string' } },
|
|
701
785
|
{ name: 'status', type: { kind: 'enum', values: ['draft', 'review', 'published', 'archived'] } },
|
|
702
786
|
{ name: 'tags', type: { kind: 'array', inner: { kind: 'string' } } },
|
|
@@ -705,7 +789,7 @@ await space.alterCollection('article', [
|
|
|
705
789
|
]);
|
|
706
790
|
|
|
707
791
|
// Remove a collection
|
|
708
|
-
await
|
|
792
|
+
await channel.dropCollection('article');
|
|
709
793
|
```
|
|
710
794
|
|
|
711
795
|
| Method | Description |
|
|
@@ -739,6 +823,7 @@ Export and import space data as zip archives for backup, portability, or migrati
|
|
|
739
823
|
|
|
740
824
|
**Export:**
|
|
741
825
|
```typescript
|
|
826
|
+
const space = await client.openSpace('space-id');
|
|
742
827
|
const archive = await space.exportArchive();
|
|
743
828
|
// Save as .zip file
|
|
744
829
|
const url = URL.createObjectURL(archive);
|
|
@@ -746,12 +831,13 @@ const url = URL.createObjectURL(archive);
|
|
|
746
831
|
|
|
747
832
|
**Import:**
|
|
748
833
|
```typescript
|
|
749
|
-
const
|
|
834
|
+
const space = await client.importArchive('Imported Data', archiveBlob);
|
|
835
|
+
const channel = await space.openChannel('main');
|
|
750
836
|
```
|
|
751
837
|
|
|
752
838
|
The archive format bundles `data.json` (with objects, metadata, and conversations) and a `media/` folder containing all media files. Media URLs are rewritten to relative paths within the archive and restored on import.
|
|
753
839
|
|
|
754
|
-
###
|
|
840
|
+
### Channel Events
|
|
755
841
|
|
|
756
842
|
Semantic events describe what changed. Events fire for both local changes and remote changes.
|
|
757
843
|
|
|
@@ -763,27 +849,21 @@ Semantic events describe what changed. Events fire for both local changes and re
|
|
|
763
849
|
// - 'system': Resync after error
|
|
764
850
|
|
|
765
851
|
// Object events
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
852
|
+
channel.on('objectCreated', ({ objectId, object, source }) => void)
|
|
853
|
+
channel.on('objectUpdated', ({ objectId, object, source }) => void)
|
|
854
|
+
channel.on('objectDeleted', ({ objectId, source }) => void)
|
|
769
855
|
|
|
770
856
|
// Space metadata
|
|
771
|
-
|
|
857
|
+
channel.on('metadataUpdated', ({ metadata, source }) => void)
|
|
772
858
|
|
|
773
859
|
// Conversation updated (fetch with getInteractions())
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
// Conversation list changed (created, deleted, renamed)
|
|
777
|
-
space.on('conversationsChanged', ({ action, conversationId, name, source }) => void)
|
|
860
|
+
channel.on('conversationUpdated', ({ conversationId, source }) => void)
|
|
778
861
|
|
|
779
862
|
// Full state replacement (undo/redo, resync after error)
|
|
780
|
-
|
|
863
|
+
channel.on('reset', ({ source }) => void)
|
|
781
864
|
|
|
782
|
-
//
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
// Sync error occurred, space resynced from server
|
|
786
|
-
space.on('syncError', (error: Error) => void)
|
|
865
|
+
// Sync error occurred, channel resynced from server
|
|
866
|
+
channel.on('syncError', (error: Error) => void)
|
|
787
867
|
```
|
|
788
868
|
|
|
789
869
|
### Error Handling
|
|
@@ -792,7 +872,7 @@ AI operations may fail due to rate limiting or other transient errors. Check `er
|
|
|
792
872
|
|
|
793
873
|
```typescript
|
|
794
874
|
try {
|
|
795
|
-
await
|
|
875
|
+
await channel.updateObject(objectId, { prompt: 'expand this' });
|
|
796
876
|
} catch (error) {
|
|
797
877
|
if (error.message.includes('temporarily unavailable')) {
|
|
798
878
|
showToast('Service busy, please try again in a moment');
|
|
@@ -804,13 +884,13 @@ try {
|
|
|
804
884
|
|
|
805
885
|
## Interaction History
|
|
806
886
|
|
|
807
|
-
Each
|
|
887
|
+
Each channel has a `conversationId` that identifies its conversation. The history records all meaningful interactions (prompts, object mutations) as self-contained entries, each capturing the request and its result. History is stored in the space data itself and syncs in real-time to all clients.
|
|
808
888
|
|
|
809
889
|
### What the AI Receives
|
|
810
890
|
|
|
811
891
|
AI operations (`prompt`, `createObject`, `updateObject`, `findObjects`) automatically receive:
|
|
812
892
|
|
|
813
|
-
- **Interaction history** — Previous interactions and their results from this conversation
|
|
893
|
+
- **Interaction history** — Previous interactions and their results from this channel's conversation
|
|
814
894
|
- **Recently modified objects** — Objects in the space recently created or changed
|
|
815
895
|
- **Selected objects** — Objects passed via `objectIds` are given primary focus
|
|
816
896
|
|
|
@@ -819,31 +899,20 @@ This context flows automatically — no configuration needed. The AI sees enough
|
|
|
819
899
|
### Accessing History
|
|
820
900
|
|
|
821
901
|
```typescript
|
|
822
|
-
// Get interactions for
|
|
823
|
-
const interactions =
|
|
824
|
-
// Returns: Interaction[]
|
|
825
|
-
|
|
826
|
-
// Get interactions for a specific conversation ID
|
|
827
|
-
const interactions = space.getInteractionsById('other-conversation-id');
|
|
902
|
+
// Get interactions for this channel's conversation
|
|
903
|
+
const interactions = channel.getInteractions();
|
|
828
904
|
// Returns: Interaction[]
|
|
829
|
-
|
|
830
|
-
// List all conversation IDs that have interactions
|
|
831
|
-
const conversationIds = space.getConversationIds();
|
|
832
|
-
// Returns: string[]
|
|
833
905
|
```
|
|
834
906
|
|
|
835
|
-
### Conversation
|
|
907
|
+
### Conversation Methods
|
|
836
908
|
|
|
837
909
|
| Method | Description |
|
|
838
910
|
|--------|-------------|
|
|
839
|
-
| `getInteractions(): Interaction[]` | Get interactions for
|
|
840
|
-
| `
|
|
841
|
-
| `
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
| `listConversations(): ConversationInfo[]` | List all conversations with summary info. |
|
|
845
|
-
| `getSystemInstruction(): string \| undefined` | Get system instruction for current conversation. |
|
|
846
|
-
| `setSystemInstruction(instruction): Promise<void>` | Set system instruction for current conversation. Pass `null` to clear. |
|
|
911
|
+
| `getInteractions(): Interaction[]` | Get interactions for this channel's conversation |
|
|
912
|
+
| `getSystemInstruction(): string \| undefined` | Get system instruction for this conversation |
|
|
913
|
+
| `setSystemInstruction(instruction): Promise<void>` | Set system instruction for this conversation. Pass `null` to clear. |
|
|
914
|
+
|
|
915
|
+
Channel management (listing, renaming, deleting channels) is done via the client — see [Channel Management](#channel-management).
|
|
847
916
|
|
|
848
917
|
### System Instructions
|
|
849
918
|
|
|
@@ -851,18 +920,18 @@ System instructions customize how the AI behaves within a conversation. The inst
|
|
|
851
920
|
|
|
852
921
|
```typescript
|
|
853
922
|
// Make the AI behave like an SQL interpreter
|
|
854
|
-
await
|
|
923
|
+
await channel.setSystemInstruction(
|
|
855
924
|
'Behave like an intelligent SQL interpreter. Respond with simple markdown tables. ' +
|
|
856
925
|
'Translate the objects in the space to the implied structure in your responses.'
|
|
857
926
|
);
|
|
858
927
|
|
|
859
928
|
// Now prompts are interpreted as SQL-like queries
|
|
860
|
-
const { message } = await
|
|
929
|
+
const { message } = await channel.prompt('SELECT task, due_date FROM tasks ORDER BY due_date');
|
|
861
930
|
// Returns a markdown table of tasks, even if no "tasks" objects exist -
|
|
862
931
|
// the AI infers actual tasks from the space content
|
|
863
932
|
|
|
864
933
|
// Clear the instruction to return to default behavior
|
|
865
|
-
await
|
|
934
|
+
await channel.setSystemInstruction(null);
|
|
866
935
|
```
|
|
867
936
|
|
|
868
937
|
System instructions are useful for:
|
|
@@ -874,56 +943,37 @@ System instructions are useful for:
|
|
|
874
943
|
### Listening for Updates
|
|
875
944
|
|
|
876
945
|
```typescript
|
|
877
|
-
|
|
946
|
+
channel.on('conversationUpdated', ({ conversationId, source }) => {
|
|
878
947
|
// Conversation changed - refresh if needed
|
|
879
|
-
const interactions =
|
|
948
|
+
const interactions = channel.getInteractions();
|
|
880
949
|
renderInteractions(interactions);
|
|
881
950
|
});
|
|
882
951
|
```
|
|
883
952
|
|
|
884
|
-
###
|
|
885
|
-
|
|
886
|
-
By default, each call to `openSpace()` or `createSpace()` generates a new `conversationId`. This means:
|
|
887
|
-
- Opening a space twice gives you two independent AI conversation histories
|
|
888
|
-
- Closing and reopening a space starts fresh
|
|
953
|
+
### Multiple Conversations
|
|
889
954
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
Set `conversationId` to switch conversations without reopening the space:
|
|
955
|
+
Each channel is bound to a single conversation. To work with multiple conversations on the same space, open multiple channels:
|
|
893
956
|
|
|
894
957
|
```typescript
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
space.conversationId = 'research-thread';
|
|
899
|
-
await space.prompt("Analyze this data");
|
|
900
|
-
|
|
901
|
-
// User clicks "Main" thread
|
|
902
|
-
space.conversationId = 'main-thread';
|
|
903
|
-
await space.prompt("Summarize findings");
|
|
958
|
+
// Open two channels on the same space with different conversations
|
|
959
|
+
const research = await client.openChannel('space-id', 'research');
|
|
960
|
+
const main = await client.openChannel('space-id', 'main');
|
|
904
961
|
|
|
905
|
-
//
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
renderChat(space.getInteractions());
|
|
909
|
-
});
|
|
910
|
-
```
|
|
911
|
-
|
|
912
|
-
### Resuming Conversations
|
|
962
|
+
// Each has independent history
|
|
963
|
+
await research.prompt("Analyze this data");
|
|
964
|
+
await main.prompt("Summarize findings");
|
|
913
965
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
// Resume a known conversation when opening
|
|
918
|
-
const space = await client.openSpace('abc123', { conversationId: 'research-thread' });
|
|
966
|
+
// Close when done
|
|
967
|
+
research.close();
|
|
968
|
+
main.close();
|
|
919
969
|
```
|
|
920
970
|
|
|
921
971
|
**Use cases:**
|
|
922
|
-
- **
|
|
923
|
-
- **
|
|
924
|
-
- **Collaborative conversations** — Share a
|
|
972
|
+
- **Chat app with sidebar** — Each sidebar entry is a channel with a different conversationId
|
|
973
|
+
- **Page refresh** — Store the conversationId in localStorage to resume the same conversation
|
|
974
|
+
- **Collaborative conversations** — Share a conversationId between users to enable shared AI conversation history
|
|
925
975
|
|
|
926
|
-
**Tip:** Use the user's id as
|
|
976
|
+
**Tip:** Use the user's id as conversationId to share context across tabs/devices, or a fixed string like `'shared'` to share context across all users.
|
|
927
977
|
|
|
928
978
|
Note: Interaction history is truncated to the most recent 50 entries to manage space size.
|
|
929
979
|
|
|
@@ -978,7 +1028,7 @@ interface RoolObject {
|
|
|
978
1028
|
[key: string]: unknown;
|
|
979
1029
|
}
|
|
980
1030
|
|
|
981
|
-
// Object stat - audit information returned by
|
|
1031
|
+
// Object stat - audit information returned by channel.stat()
|
|
982
1032
|
interface RoolObjectStat {
|
|
983
1033
|
modifiedAt: number;
|
|
984
1034
|
modifiedBy: string;
|
|
@@ -999,7 +1049,7 @@ interface Conversation {
|
|
|
999
1049
|
interactions: Interaction[]; // Interaction history
|
|
1000
1050
|
}
|
|
1001
1051
|
|
|
1002
|
-
// Conversation summary info (returned by
|
|
1052
|
+
// Conversation summary info (returned by client.getChannels)
|
|
1003
1053
|
interface ConversationInfo {
|
|
1004
1054
|
id: string;
|
|
1005
1055
|
name: string | null;
|
|
@@ -1008,7 +1058,6 @@ interface ConversationInfo {
|
|
|
1008
1058
|
createdByName: string | null;
|
|
1009
1059
|
interactionCount: number;
|
|
1010
1060
|
}
|
|
1011
|
-
|
|
1012
1061
|
```
|
|
1013
1062
|
|
|
1014
1063
|
### Interaction Types
|
|
@@ -1070,7 +1119,7 @@ interface PromptOptions {
|
|
|
1070
1119
|
A Rool Space is a persistent, shared world model. Applications project different interaction patterns onto the same core primitives:
|
|
1071
1120
|
|
|
1072
1121
|
- **Objects** store durable state, with references to other objects via data fields
|
|
1073
|
-
- **
|
|
1122
|
+
- **Channels** provide independent AI conversation contexts over shared objects
|
|
1074
1123
|
- **Events** describe what changed in real-time
|
|
1075
1124
|
|
|
1076
1125
|
Below are a few representative patterns.
|
|
@@ -1078,12 +1127,12 @@ Below are a few representative patterns.
|
|
|
1078
1127
|
### Chat With Generated Artifacts
|
|
1079
1128
|
|
|
1080
1129
|
- **Space**: documents, notes, images, tasks as objects
|
|
1081
|
-
- **
|
|
1130
|
+
- **Channels**: each chat thread is a separate channel on the same space
|
|
1082
1131
|
- **UI**: renders interactions from `getInteractions()` as chat; derives artifact lists from object events
|
|
1083
1132
|
|
|
1084
1133
|
**Pattern**
|
|
1085
1134
|
- Interaction history syncs in real-time; UI renders entries as chat bubbles
|
|
1086
|
-
- Artifacts are persistent objects
|
|
1135
|
+
- Artifacts are persistent objects shared across all channels
|
|
1087
1136
|
- Listen to `conversationUpdated` to update chat UI
|
|
1088
1137
|
- Selecting objects defines the AI working set via `objectIds`
|
|
1089
1138
|
|
|
@@ -1091,7 +1140,7 @@ Below are a few representative patterns.
|
|
|
1091
1140
|
|
|
1092
1141
|
- **Space**: rooms, items, NPCs, players as objects
|
|
1093
1142
|
- **References**: navigation, containment, location via data fields
|
|
1094
|
-
- **
|
|
1143
|
+
- **Channel**: player commands and narrative continuity
|
|
1095
1144
|
|
|
1096
1145
|
**Pattern**
|
|
1097
1146
|
- The space is the shared world state
|
|
@@ -1102,7 +1151,7 @@ Below are a few representative patterns.
|
|
|
1102
1151
|
|
|
1103
1152
|
- **Space**: concepts, sources, hypotheses as objects
|
|
1104
1153
|
- **References**: semantic connections between objects via data fields
|
|
1105
|
-
- **
|
|
1154
|
+
- **Channel**: exploratory analysis and questioning
|
|
1106
1155
|
|
|
1107
1156
|
**Pattern**
|
|
1108
1157
|
- Graph structure lives in object data fields containing other object IDs
|
|
@@ -1113,7 +1162,7 @@ Below are a few representative patterns.
|
|
|
1113
1162
|
|
|
1114
1163
|
- Durable content lives in space objects
|
|
1115
1164
|
- References between objects are data fields whose values are object IDs
|
|
1116
|
-
- Interaction history lives in
|
|
1165
|
+
- Interaction history lives in channels (persistent, synced, truncated to 50 entries)
|
|
1117
1166
|
- UI state lives in the client, space metadata, or `_`-prefixed fields
|
|
1118
1167
|
- AI focus is controlled by object selection, not by replaying history
|
|
1119
1168
|
|