@rool-dev/client 0.6.5 → 0.6.6-dev.3e5f7f7
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 +476 -336
- package/dist/jsonld.d.ts +10 -0
- package/dist/jsonld.d.ts.map +1 -1
- package/dist/jsonld.js +50 -0
- package/dist/jsonld.js.map +1 -1
- package/dist/space.d.ts +12 -6
- package/dist/space.d.ts.map +1 -1
- package/dist/space.js +132 -12
- package/dist/space.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,12 +6,48 @@ Rool Spaces enables you to build applications where AI operates on a structured
|
|
|
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
|
+
**Core primitives:**
|
|
10
|
+
- **Objects** — Key-value records with any fields you define
|
|
11
|
+
- **Relations** — Directional links between objects (e.g., `earth` → `orbits` → `sun`)
|
|
12
|
+
- **AI operations** — Create, update, or query objects using natural language and `{{placeholders}}`
|
|
13
|
+
|
|
14
|
+
See [Patterns & Examples](#patterns--examples) for what you can build.
|
|
15
|
+
|
|
9
16
|
## Installation
|
|
10
17
|
|
|
11
18
|
```bash
|
|
12
19
|
npm install @rool-dev/client
|
|
13
20
|
```
|
|
14
21
|
|
|
22
|
+
## Configuration
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { RoolClient } from '@rool-dev/client';
|
|
26
|
+
|
|
27
|
+
const client = new RoolClient({
|
|
28
|
+
baseUrl: 'https://api.dev.rool.dev',
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### RoolClientConfig
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
interface RoolClientConfig {
|
|
36
|
+
baseUrl: string; // Base URL (e.g., 'https://api.dev.rool.dev')
|
|
37
|
+
graphqlUrl?: string; // Override GraphQL endpoint (default: {baseUrl}/graphql)
|
|
38
|
+
mediaUrl?: string; // Override media endpoint (default: {baseUrl}/media)
|
|
39
|
+
authUrl?: string; // Override auth endpoint (default: {baseUrl}/auth)
|
|
40
|
+
authProvider?: AuthProvider; // Optional, defaults to browser auth
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Base URLs
|
|
45
|
+
|
|
46
|
+
| Environment | URL |
|
|
47
|
+
|-------------|-----|
|
|
48
|
+
| Development | `https://api.dev.rool.dev` |
|
|
49
|
+
| Production | `https://api.rool.dev` |
|
|
50
|
+
|
|
15
51
|
## Quick Start
|
|
16
52
|
|
|
17
53
|
```typescript
|
|
@@ -23,7 +59,7 @@ const client = new RoolClient({
|
|
|
23
59
|
});
|
|
24
60
|
|
|
25
61
|
// Process auth callbacks if we are returning from the login page
|
|
26
|
-
client.initialize();
|
|
62
|
+
client.initialize();
|
|
27
63
|
|
|
28
64
|
if (!client.isAuthenticated()) {
|
|
29
65
|
client.login(); // Redirects to auth page
|
|
@@ -44,7 +80,7 @@ space.on('linked', ({ sourceId, relation, targetId, source }) => {
|
|
|
44
80
|
console.log('New link:', sourceId, '->', targetId, `[${relation}]`, 'from', source);
|
|
45
81
|
});
|
|
46
82
|
|
|
47
|
-
// Create objects with AI-generated content.
|
|
83
|
+
// Create objects with AI-generated content.
|
|
48
84
|
// Field names are yours to define — only 'id' is reserved.
|
|
49
85
|
const { object: sun } = await space.createObject({
|
|
50
86
|
data: {
|
|
@@ -79,31 +115,158 @@ await space.redo(); // The link is back ...
|
|
|
79
115
|
space.close();
|
|
80
116
|
```
|
|
81
117
|
|
|
82
|
-
##
|
|
118
|
+
## Core Concepts
|
|
119
|
+
|
|
120
|
+
### Objects & Relations
|
|
121
|
+
|
|
122
|
+
**Objects** are plain key-value records. The `id` field is reserved; everything else is application-defined.
|
|
83
123
|
|
|
84
124
|
```typescript
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
125
|
+
{ id: 'abc123', type: 'article', title: 'Hello World', status: 'draft' }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Relations** connect objects directionally through named links. Each relation name represents a multi-valued reference set on the source object.
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
await space.link(earth.id, 'orbits', sun.id);
|
|
132
|
+
// Reads as: "earth.orbits includes sun"
|
|
133
|
+
|
|
134
|
+
const orbited = space.getChildren(earth.id, 'orbits'); // [sun] - what earth links TO
|
|
135
|
+
const orbiters = space.getParents(sun.id, 'orbits'); // [earth] - what links TO sun
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Relations are indexed and idempotent — creating the same link twice has no effect.
|
|
139
|
+
|
|
140
|
+
**Notes:**
|
|
141
|
+
- Relation names are application-defined strings
|
|
142
|
+
- Relations are not stored in object data fields
|
|
143
|
+
- Only relations created via `link()` participate in traversal (`getParents`, `getChildren`) and indexing
|
|
144
|
+
- Storing object IDs inside regular object fields is possible, but those references are opaque to the system
|
|
145
|
+
|
|
146
|
+
### AI Placeholder Pattern
|
|
147
|
+
|
|
148
|
+
Use `{{description}}` in field values to have AI generate content:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// Create with AI-generated content
|
|
152
|
+
await space.createObject({
|
|
153
|
+
data: {
|
|
154
|
+
type: 'article',
|
|
155
|
+
headline: '{{catchy headline about coffee}}',
|
|
156
|
+
body: '{{informative paragraph}}'
|
|
157
|
+
},
|
|
158
|
+
prompt: 'Write about specialty coffee brewing'
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Update existing content with AI
|
|
162
|
+
await space.updateObject('abc123', {
|
|
163
|
+
prompt: 'Make the body shorter and more casual'
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Add new AI-generated field to existing object
|
|
167
|
+
await space.updateObject('abc123', {
|
|
168
|
+
data: { summary: '{{one-sentence summary}}' }
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
When resolving placeholders, the agent has access to the full object data 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.
|
|
173
|
+
|
|
174
|
+
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.
|
|
175
|
+
|
|
176
|
+
### Checkpoints & Undo/Redo
|
|
177
|
+
|
|
178
|
+
Undo/redo works on **checkpoints**, not individual operations. Call `checkpoint()` before making changes to create a restore point.
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// Create a checkpoint before user action
|
|
182
|
+
await space.checkpoint('Delete object');
|
|
183
|
+
await space.deleteObjects([objectId]);
|
|
184
|
+
|
|
185
|
+
// User can now undo back to the checkpoint
|
|
186
|
+
if (await space.canUndo()) {
|
|
187
|
+
await space.undo(); // Restores the deleted object
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Redo reapplies the undone action
|
|
191
|
+
if (await space.canRedo()) {
|
|
192
|
+
await space.redo(); // Deletes the object again
|
|
91
193
|
}
|
|
92
194
|
```
|
|
93
195
|
|
|
94
|
-
|
|
196
|
+
Without a checkpoint, `undo()` has nothing to restore to. Undo always restores the space to the last checkpoint, regardless of how many changes were made since.
|
|
95
197
|
|
|
96
|
-
|
|
97
|
-
|-------------|-----|
|
|
98
|
-
| Development | `https://api.dev.rool.dev` |
|
|
99
|
-
| Production | `https://api.rool.dev` |
|
|
198
|
+
In collaborative scenarios, conflicting changes (modified by others since your checkpoint) are silently skipped.
|
|
100
199
|
|
|
101
|
-
###
|
|
200
|
+
### Hidden Fields
|
|
102
201
|
|
|
103
|
-
|
|
202
|
+
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:
|
|
104
203
|
|
|
105
204
|
```typescript
|
|
106
|
-
|
|
205
|
+
await space.createObject({
|
|
206
|
+
data: {
|
|
207
|
+
title: 'My Article',
|
|
208
|
+
author: "John Doe",
|
|
209
|
+
_ui: { x: 100, y: 200, collapsed: false }
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Real-time Sync
|
|
215
|
+
|
|
216
|
+
Events fire for both local and remote changes. The `source` field indicates origin:
|
|
217
|
+
|
|
218
|
+
- `local_user` — This client made the change
|
|
219
|
+
- `remote_user` — Another user/client made the change
|
|
220
|
+
- `remote_agent` — AI agent made the change
|
|
221
|
+
- `system` — Resync after error
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
// All UI updates happen in one place, regardless of change source
|
|
225
|
+
space.on('objectUpdated', ({ objectId, object, source }) => {
|
|
226
|
+
renderObject(objectId, object);
|
|
227
|
+
if (source === 'remote_agent') {
|
|
228
|
+
doLayout(); // AI might have added content
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Caller just makes the change - event handler does the UI work
|
|
233
|
+
space.updateObject(objectId, { prompt: 'expand this' });
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Custom Object IDs
|
|
237
|
+
|
|
238
|
+
By default, `createObject` generates a 6-character alphanumeric ID. Provide your own via `data.id`:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
await space.createObject({ data: { id: 'article-42', title: 'The Meaning of Life' } });
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Why use custom IDs?**
|
|
245
|
+
- **Fire-and-forget creation** — Know the ID immediately without awaiting the response. You can create an object and link to it in parallel; the sync happens in the background.
|
|
246
|
+
- **Meaningful IDs** — Use domain-specific IDs like `user-123` or `doc-abc` for easier debugging and external references.
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
// Fire-and-forget: create and link without waiting
|
|
250
|
+
const id = RoolClient.generateId();
|
|
251
|
+
space.createObject({ data: { id, type: 'note', text: '{{expand this idea}}' } });
|
|
252
|
+
space.link(parentId, 'hasNote', id); // Can link immediately
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Constraints:**
|
|
256
|
+
- Must contain only alphanumeric characters, hyphens (`-`), and underscores (`_`)
|
|
257
|
+
- Must be unique within the space (throws if ID exists)
|
|
258
|
+
- Cannot be changed after creation (immutable)
|
|
259
|
+
|
|
260
|
+
Use `RoolClient.generateId()` when you need an ID before calling `createObject` but don't need it to be meaningful — it gives you a valid random ID without writing your own generator.
|
|
261
|
+
|
|
262
|
+
## Authentication
|
|
263
|
+
|
|
264
|
+
### Browser (Default)
|
|
265
|
+
|
|
266
|
+
No configuration needed. Uses localStorage for tokens, redirects to login page.
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
const client = new RoolClient({ baseUrl: 'https://api.rool.dev' });
|
|
107
270
|
client.initialize(); // Process auth callbacks if this is a callback from the auth page
|
|
108
271
|
|
|
109
272
|
if (!client.isAuthenticated()) {
|
|
@@ -111,13 +274,15 @@ if (!client.isAuthenticated()) {
|
|
|
111
274
|
}
|
|
112
275
|
```
|
|
113
276
|
|
|
114
|
-
|
|
277
|
+
### Node.js
|
|
278
|
+
|
|
279
|
+
For CLI tools and scripts. Stores credentials in `~/.config/rool/`, opens browser for login.
|
|
115
280
|
|
|
116
281
|
```typescript
|
|
117
282
|
import { NodeAuthProvider } from '@rool-dev/client/node';
|
|
118
283
|
|
|
119
284
|
const client = new RoolClient({
|
|
120
|
-
baseUrl: 'https://api.
|
|
285
|
+
baseUrl: 'https://api.rool.dev',
|
|
121
286
|
authProvider: new NodeAuthProvider()
|
|
122
287
|
});
|
|
123
288
|
|
|
@@ -126,128 +291,172 @@ if (!client.isAuthenticated()) {
|
|
|
126
291
|
}
|
|
127
292
|
```
|
|
128
293
|
|
|
129
|
-
|
|
294
|
+
### Auth Methods
|
|
295
|
+
|
|
296
|
+
| Method | Description |
|
|
297
|
+
|--------|-------------|
|
|
298
|
+
| `initialize(): boolean` | **Call on app startup if running in browser.** Processes auth callback from URL, sets up token refresh. |
|
|
299
|
+
| `login(): void` | Redirect to login page |
|
|
300
|
+
| `logout(): void` | Clear tokens and state |
|
|
301
|
+
| `isAuthenticated(): boolean` | Check auth status |
|
|
302
|
+
| `getToken(): Promise<string \| undefined>` | Get current access token |
|
|
303
|
+
| `getAuthUser(): AuthUser` | Get auth identity from JWT (`{ email, name }`) |
|
|
304
|
+
|
|
305
|
+
## AI Agent
|
|
306
|
+
|
|
307
|
+
The `prompt()` method is the primary way to invoke the AI agent. The agent has editor-level capabilities — it can create, modify, delete, link, and research — but cannot see or modify `_`-prefixed fields.
|
|
130
308
|
|
|
131
309
|
```typescript
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
getUser: () => ({ email: 'user@example.com', name: 'User' }),
|
|
138
|
-
isAuthenticated: () => myStore.hasValidToken(),
|
|
139
|
-
login: async () => { /* your login flow */ },
|
|
140
|
-
logout: () => myStore.clear(),
|
|
141
|
-
}
|
|
142
|
-
});
|
|
310
|
+
const { message, objects } = await space.prompt(
|
|
311
|
+
"Create a topic node for the solar system, then child nodes for each planet."
|
|
312
|
+
);
|
|
313
|
+
console.log(`AI: ${message}`);
|
|
314
|
+
console.log(`Modified ${objects.length} objects:`, objects);
|
|
143
315
|
```
|
|
144
316
|
|
|
145
|
-
|
|
317
|
+
Use `checkpoint()` before prompting to make operations undoable.
|
|
146
318
|
|
|
147
|
-
|
|
319
|
+
### Method Signature
|
|
148
320
|
|
|
149
|
-
|
|
321
|
+
```typescript
|
|
322
|
+
prompt(text: string, options?: PromptOptions): Promise<{ message: string; objects: RoolObject[] }>
|
|
323
|
+
```
|
|
150
324
|
|
|
151
|
-
|
|
325
|
+
Returns a message (the AI's response) and the list of objects that were created or modified.
|
|
152
326
|
|
|
153
|
-
|
|
154
|
-
- **Recently modified objects** — Objects created or changed during this conversation
|
|
155
|
-
- **Selected objects** — Objects passed via `objectIds` are given primary focus
|
|
327
|
+
### Options
|
|
156
328
|
|
|
157
|
-
|
|
329
|
+
| Option | Description |
|
|
330
|
+
|--------|-------------|
|
|
331
|
+
| `objectIds` | Limit context to specific objects |
|
|
332
|
+
| `responseSchema` | Request structured JSON instead of text summary |
|
|
333
|
+
| `effort` | Effort level: `'QUICK'`, `'STANDARD'` (default), `'REASONING'`, or `'RESEARCH'` |
|
|
334
|
+
| `ephemeral` | If true, don't record in conversation history (useful for tab completion) |
|
|
158
335
|
|
|
159
|
-
###
|
|
336
|
+
### Effort Levels
|
|
160
337
|
|
|
161
|
-
|
|
338
|
+
| Level | Description |
|
|
339
|
+
|-------|-------------|
|
|
340
|
+
| `QUICK` | Fast responses, read-only. Cannot create/update objects or links. Use for questions. |
|
|
341
|
+
| `STANDARD` | Default behavior with full capabilities. |
|
|
342
|
+
| `REASONING` | Extended reasoning for complex tasks. |
|
|
343
|
+
| `RESEARCH` | Pre-analysis and context gathering (reserved for future use). |
|
|
344
|
+
|
|
345
|
+
### Examples
|
|
162
346
|
|
|
163
347
|
```typescript
|
|
164
|
-
//
|
|
165
|
-
const
|
|
166
|
-
|
|
348
|
+
// Reorganize and link existing objects
|
|
349
|
+
const { objects } = await space.prompt(
|
|
350
|
+
"Group these notes by topic and create a parent node for each group."
|
|
351
|
+
);
|
|
167
352
|
|
|
168
|
-
//
|
|
169
|
-
const
|
|
170
|
-
|
|
353
|
+
// Work with specific objects
|
|
354
|
+
const result = await space.prompt(
|
|
355
|
+
"Summarize these articles",
|
|
356
|
+
{ objectIds: ['article-1', 'article-2'] }
|
|
357
|
+
);
|
|
171
358
|
|
|
172
|
-
//
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
359
|
+
// Quick question without mutations
|
|
360
|
+
const { message } = await space.prompt(
|
|
361
|
+
"What topics are covered?",
|
|
362
|
+
{ effort: 'QUICK' }
|
|
363
|
+
);
|
|
176
364
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
userName?: string | null; // Display name at time of interaction
|
|
183
|
-
operation: 'prompt' | 'createObject' | 'updateObject' | 'link' | 'unlink' | 'deleteObjects';
|
|
184
|
-
input: string; // What the user did: prompt text or action description
|
|
185
|
-
output: string; // Result: AI response or confirmation message
|
|
186
|
-
ai: boolean; // Whether AI was invoked (vs synthetic confirmation)
|
|
187
|
-
modifiedObjectIds: string[]; // Objects affected by this interaction
|
|
188
|
-
}
|
|
365
|
+
// Complex analysis with extended reasoning
|
|
366
|
+
await space.prompt(
|
|
367
|
+
"Analyze relationships and reorganize",
|
|
368
|
+
{ effort: 'REASONING' }
|
|
369
|
+
);
|
|
189
370
|
```
|
|
190
371
|
|
|
191
|
-
|
|
192
|
-
- `ai: true` — AI processed this operation (prompt, or createObject/updateObject with placeholders)
|
|
193
|
-
- `ai: false` — System confirmation only (e.g., "Linked X to Y", "Created object abc123")
|
|
194
|
-
|
|
195
|
-
### Interaction History Events
|
|
372
|
+
### Structured Responses
|
|
196
373
|
|
|
197
|
-
|
|
374
|
+
Use `responseSchema` to get structured JSON instead of a text message:
|
|
198
375
|
|
|
199
376
|
```typescript
|
|
200
|
-
space.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
377
|
+
const { message } = await space.prompt("Categorize these items", {
|
|
378
|
+
objectIds: ['item-1', 'item-2', 'item-3'],
|
|
379
|
+
responseSchema: {
|
|
380
|
+
type: 'object',
|
|
381
|
+
properties: {
|
|
382
|
+
categories: {
|
|
383
|
+
type: 'array',
|
|
384
|
+
items: { type: 'string' }
|
|
385
|
+
},
|
|
386
|
+
summary: { type: 'string' }
|
|
387
|
+
}
|
|
388
|
+
}
|
|
204
389
|
});
|
|
390
|
+
|
|
391
|
+
const result = JSON.parse(message);
|
|
392
|
+
console.log(result.categories, result.summary);
|
|
205
393
|
```
|
|
206
394
|
|
|
207
|
-
###
|
|
395
|
+
### Context Flow
|
|
208
396
|
|
|
209
|
-
|
|
210
|
-
-
|
|
211
|
-
-
|
|
397
|
+
AI operations automatically receive context:
|
|
398
|
+
- **Interaction history** — Previous interactions and their results from this conversation
|
|
399
|
+
- **Recently modified objects** — Objects created or changed recently
|
|
400
|
+
- **Selected objects** — Objects passed via `objectIds` are given primary focus
|
|
212
401
|
|
|
213
|
-
|
|
402
|
+
This context flows automatically — no configuration needed. The AI sees enough history to maintain coherent interactions while respecting the `_`-prefixed field hiding rules.
|
|
214
403
|
|
|
215
|
-
|
|
404
|
+
## Collaboration
|
|
405
|
+
|
|
406
|
+
### Adding Users to a Space
|
|
407
|
+
|
|
408
|
+
To add a user to a space, you need their user ID. Use `searchUser()` to find them by email:
|
|
216
409
|
|
|
217
410
|
```typescript
|
|
218
|
-
//
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
411
|
+
// Find the user by email
|
|
412
|
+
const user = await client.searchUser('colleague@example.com');
|
|
413
|
+
if (!user) {
|
|
414
|
+
throw new Error('User not found');
|
|
415
|
+
}
|
|
222
416
|
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
// AI now has access to conversation history from before
|
|
417
|
+
// Add them to the space
|
|
418
|
+
await space.addUser(user.id, 'editor');
|
|
226
419
|
```
|
|
227
420
|
|
|
228
|
-
|
|
229
|
-
- **Page refresh** — Store `conversationId` in localStorage to maintain context across reloads
|
|
230
|
-
- **Multiple conversations** — Maintain and switch between different conversation contexts with the same space
|
|
231
|
-
- **Collaborative conversations** — Share a `conversationId` between users to enable shared AI conversation history
|
|
232
|
-
|
|
233
|
-
**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.
|
|
421
|
+
### Roles
|
|
234
422
|
|
|
235
|
-
|
|
423
|
+
| Role | Capabilities |
|
|
424
|
+
|------|--------------|
|
|
425
|
+
| `owner` | Full control, can delete space and manage users |
|
|
426
|
+
| `editor` | Can create, modify, delete objects and links |
|
|
427
|
+
| `viewer` | Read-only access |
|
|
236
428
|
|
|
237
|
-
|
|
429
|
+
### Space Collaboration Methods
|
|
238
430
|
|
|
239
|
-
|
|
431
|
+
| Method | Description |
|
|
432
|
+
|--------|-------------|
|
|
433
|
+
| `listUsers(): Promise<SpaceMember[]>` | List users with access |
|
|
434
|
+
| `addUser(userId, role): Promise<void>` | Add user to space |
|
|
435
|
+
| `removeUser(userId): Promise<void>` | Remove user from space |
|
|
240
436
|
|
|
241
|
-
###
|
|
437
|
+
### Client User Methods
|
|
242
438
|
|
|
243
439
|
| Method | Description |
|
|
244
440
|
|--------|-------------|
|
|
245
|
-
| `
|
|
246
|
-
| `
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
441
|
+
| `getCurrentUser(): Promise<CurrentUser>` | Get current Rool user (id, email, name, plan, credits, storage) |
|
|
442
|
+
| `searchUser(email): Promise<UserResult \| null>` | Find user by exact email address (no partial matching) |
|
|
443
|
+
|
|
444
|
+
### Real-time Collaboration
|
|
445
|
+
|
|
446
|
+
When multiple users have a space open, changes sync in real-time. The `source` field in events tells you who made the change:
|
|
447
|
+
|
|
448
|
+
```typescript
|
|
449
|
+
space.on('objectUpdated', ({ objectId, object, source }) => {
|
|
450
|
+
if (source === 'remote_user') {
|
|
451
|
+
// Another user made this change
|
|
452
|
+
showCollaboratorActivity(object);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
See [Real-time Sync](#real-time-sync) for more on event sources.
|
|
458
|
+
|
|
459
|
+
## RoolClient API
|
|
251
460
|
|
|
252
461
|
### Space Lifecycle
|
|
253
462
|
|
|
@@ -256,14 +465,7 @@ Note: Interaction history is truncated to the most recent 50 entries to manage s
|
|
|
256
465
|
| `listSpaces(): Promise<RoolSpaceInfo[]>` | List available spaces |
|
|
257
466
|
| `openSpace(id, options?): Promise<RoolSpace>` | Open a space for editing. Options: `{ conversationId?: string }` |
|
|
258
467
|
| `createSpace(name?, options?): Promise<RoolSpace>` | Create a new space. Options: `{ conversationId?: string }` |
|
|
259
|
-
| `deleteSpace(id): Promise<void>` |
|
|
260
|
-
|
|
261
|
-
### Current User
|
|
262
|
-
|
|
263
|
-
| Method | Description |
|
|
264
|
-
|--------|-------------|
|
|
265
|
-
| `getCurrentUser(): Promise<CurrentUser>` | Get current Rool user (id, email, name, plan, credits, storage) |
|
|
266
|
-
| `searchUser(email): Promise<UserResult \| null>` | Search user by email (returns id, email, name) |
|
|
468
|
+
| `deleteSpace(id): Promise<void>` | Permanently delete a space (cannot be undone) |
|
|
267
469
|
|
|
268
470
|
### User Storage
|
|
269
471
|
|
|
@@ -293,10 +495,11 @@ client.setUserStorage('sidebar', { collapsed: true, width: 280 });
|
|
|
293
495
|
// Delete a key
|
|
294
496
|
client.setUserStorage('theme', null);
|
|
295
497
|
|
|
296
|
-
//
|
|
498
|
+
// The cache may be stale from a previous session — listen for updates
|
|
499
|
+
// to apply fresh values once sync completes (or when other tabs/devices change values)
|
|
297
500
|
client.on('userStorageChanged', ({ key, value, source }) => {
|
|
501
|
+
// source: 'local' (this client) or 'remote' (server/other client)
|
|
298
502
|
if (key === 'theme') applyTheme(value as string);
|
|
299
|
-
console.log(`Storage updated from ${source}`); // 'local' or 'remote'
|
|
300
503
|
});
|
|
301
504
|
```
|
|
302
505
|
|
|
@@ -320,8 +523,6 @@ client.on('connectionStateChanged', (state: 'connected' | 'disconnected' | 'reco
|
|
|
320
523
|
client.on('error', (error: Error, context?: string) => void)
|
|
321
524
|
```
|
|
322
525
|
|
|
323
|
-
---
|
|
324
|
-
|
|
325
526
|
## RoolSpace API
|
|
326
527
|
|
|
327
528
|
Spaces are first-class objects with built-in undo/redo, event emission, and real-time sync.
|
|
@@ -334,57 +535,15 @@ Spaces are first-class objects with built-in undo/redo, event emission, and real
|
|
|
334
535
|
| `name: string` | Space name |
|
|
335
536
|
| `role: RoolUserRole` | User's role (`'owner' \| 'editor' \| 'viewer'`) |
|
|
336
537
|
| `userId: string` | Current user's ID |
|
|
337
|
-
| `conversationId: string` | ID for interaction history (tracks AI context) |
|
|
338
|
-
| `isReadOnly(): boolean` | True if viewer role |
|
|
339
|
-
|
|
340
|
-
### Conversation Access
|
|
341
|
-
|
|
342
|
-
| Method | Description |
|
|
343
|
-
|--------|-------------|
|
|
344
|
-
| `getInteractions(): Interaction[]` | Get interactions for the current conversationId |
|
|
345
|
-
| `getInteractionsById(id): Interaction[]` | Get interactions for a specific conversation ID |
|
|
346
|
-
| `getConversationIds(): string[]` | List all conversation IDs that have interactions |
|
|
347
|
-
| `clearInteractions(conversationId?): Promise<void>` | Clear interaction history. Defaults to current conversation. |
|
|
348
|
-
|
|
349
|
-
### Lifecycle
|
|
350
|
-
|
|
351
|
-
| Method | Description |
|
|
352
|
-
|--------|-------------|
|
|
353
|
-
| `close(): void` | Clean up resources and stop receiving updates |
|
|
354
|
-
| `rename(newName): Promise<void>` | Rename the space |
|
|
355
|
-
|
|
356
|
-
### Undo/Redo
|
|
357
|
-
|
|
358
|
-
| Method | Description |
|
|
359
|
-
|--------|-------------|
|
|
360
|
-
| `checkpoint(label?): Promise<string>` | Call before mutations. Saves current state for undo. |
|
|
361
|
-
| `canUndo(): Promise<boolean>` | Check if undo available |
|
|
362
|
-
| `canRedo(): Promise<boolean>` | Check if redo available |
|
|
363
|
-
| `undo(): Promise<boolean>` | Undo to previous checkpoint |
|
|
364
|
-
| `redo(): Promise<boolean>` | Redo undone action |
|
|
365
|
-
| `clearHistory(): Promise<void>` | Clear undo/redo stack |
|
|
366
|
-
|
|
367
|
-
Call `checkpoint()` before making changes to create a restore point. Without a checkpoint, `undo()` has nothing to restore to.
|
|
368
|
-
|
|
369
|
-
Undo always restores the space to the last checkpoint, regardless of how many changes were made since.
|
|
370
|
-
|
|
371
|
-
In collaborative scenarios, conflicting changes (modified by others since checkpoint) are silently skipped.
|
|
372
|
-
|
|
373
|
-
```typescript
|
|
374
|
-
// Create a checkpoint before user action
|
|
375
|
-
await space.checkpoint('Delete object');
|
|
376
|
-
await space.deleteObjects([objectId]);
|
|
377
|
-
|
|
378
|
-
// User can now undo back to the checkpoint
|
|
379
|
-
if (await space.canUndo()) {
|
|
380
|
-
await space.undo(); // Restores the deleted object
|
|
381
|
-
}
|
|
538
|
+
| `conversationId: string` | ID for interaction history (tracks AI context) |
|
|
539
|
+
| `isReadOnly(): boolean` | True if viewer role |
|
|
382
540
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
541
|
+
### Lifecycle
|
|
542
|
+
|
|
543
|
+
| Method | Description |
|
|
544
|
+
|--------|-------------|
|
|
545
|
+
| `close(): void` | Clean up resources and stop receiving updates |
|
|
546
|
+
| `rename(newName): Promise<void>` | Rename the space |
|
|
388
547
|
|
|
389
548
|
### Object Operations
|
|
390
549
|
|
|
@@ -392,8 +551,7 @@ Objects are plain key/value records. `id` is the only reserved field; everything
|
|
|
392
551
|
|
|
393
552
|
| Method | Description |
|
|
394
553
|
|--------|-------------|
|
|
395
|
-
| `getObject(objectId): RoolObject` | Get object data
|
|
396
|
-
| `getObjectOrUndefined(objectId): RoolObject \| undefined` | Get object data, or undefined if not found. |
|
|
554
|
+
| `getObject(objectId): RoolObject \| undefined` | Get object data, or undefined if not found. |
|
|
397
555
|
| `findObjects(options): Promise<{ objects, message }>` | Find objects using structured filters and natural language. Server-side execution with AI. |
|
|
398
556
|
| `getObjectIds(): string[]` | Get all object IDs in the space. |
|
|
399
557
|
| `createObject(options): Promise<{ object, message }>` | Create a new object. Returns the object (with AI-filled content) and message. |
|
|
@@ -424,28 +582,28 @@ Find objects using structured filters, semantic matching, and natural language.
|
|
|
424
582
|
|
|
425
583
|
```typescript
|
|
426
584
|
// Exact field matching (no AI needed)
|
|
427
|
-
const { objects } = await space.findObjects({
|
|
428
|
-
where: { type: 'article', status: 'published' }
|
|
585
|
+
const { objects } = await space.findObjects({
|
|
586
|
+
where: { type: 'article', status: 'published' }
|
|
429
587
|
});
|
|
430
588
|
|
|
431
589
|
// Pure natural language query (AI interprets)
|
|
432
|
-
const { objects, message } = await space.findObjects({
|
|
433
|
-
prompt: 'articles about space exploration published this year'
|
|
590
|
+
const { objects, message } = await space.findObjects({
|
|
591
|
+
prompt: 'articles about space exploration published this year'
|
|
434
592
|
});
|
|
435
593
|
|
|
436
594
|
// Semantic field matching with {{...}} placeholders
|
|
437
|
-
const { objects } = await space.findObjects({
|
|
438
|
-
where: {
|
|
595
|
+
const { objects } = await space.findObjects({
|
|
596
|
+
where: {
|
|
439
597
|
type: 'product',
|
|
440
598
|
category: '{{something edible}}' // AI interprets this
|
|
441
599
|
}
|
|
442
600
|
});
|
|
443
601
|
|
|
444
602
|
// Combined: structured + semantic + natural language
|
|
445
|
-
const { objects } = await space.findObjects({
|
|
446
|
-
where: {
|
|
603
|
+
const { objects } = await space.findObjects({
|
|
604
|
+
where: {
|
|
447
605
|
type: 'article',
|
|
448
|
-
topic: '{{related to climate}}'
|
|
606
|
+
topic: '{{related to climate}}'
|
|
449
607
|
},
|
|
450
608
|
prompt: 'that discuss solutions positively',
|
|
451
609
|
limit: 10
|
|
@@ -454,71 +612,8 @@ const { objects } = await space.findObjects({
|
|
|
454
612
|
|
|
455
613
|
The AI has access to the full object graph context (except `_`-prefixed fields) when evaluating queries. The returned `message` explains why objects matched the criteria.
|
|
456
614
|
|
|
457
|
-
#### Underscore-Prefixed Fields
|
|
458
|
-
|
|
459
|
-
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:
|
|
460
|
-
|
|
461
|
-
```typescript
|
|
462
|
-
await space.createObject({
|
|
463
|
-
data: {
|
|
464
|
-
title: 'My Article',
|
|
465
|
-
author: "John Doe",
|
|
466
|
-
_ui: { x: 100, y: 200, collapsed: false }
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
#### Custom Object IDs
|
|
472
|
-
|
|
473
|
-
By default, `createObject` generates a 6-character alphanumeric ID. Provide your own via `data.id`:
|
|
474
|
-
|
|
475
|
-
```typescript
|
|
476
|
-
await space.createObject({ data: { id: 'article-42', title: 'The Meaning of Life' } });
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
**Constraints:**
|
|
480
|
-
- Must contain only alphanumeric characters, hyphens (`-`), and underscores (`_`)
|
|
481
|
-
- Must be unique within the space (throws if ID exists)
|
|
482
|
-
- Cannot be changed after creation (immutable)
|
|
483
|
-
|
|
484
|
-
If generating your own IDs, use `RoolClient.generateId()` for efficient, guaranteed-valid IDs.
|
|
485
|
-
|
|
486
|
-
#### AI Placeholder Pattern
|
|
487
|
-
|
|
488
|
-
Use `{{description}}` in field values to have AI generate content:
|
|
489
|
-
|
|
490
|
-
```typescript
|
|
491
|
-
// Create with AI-generated content
|
|
492
|
-
await space.createObject({
|
|
493
|
-
data: {
|
|
494
|
-
type: 'article',
|
|
495
|
-
headline: '{{catchy headline about coffee}}',
|
|
496
|
-
body: '{{informative paragraph}}'
|
|
497
|
-
},
|
|
498
|
-
prompt: 'Write about specialty coffee brewing'
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
// Update existing content with AI
|
|
502
|
-
await space.updateObject('abc123', {
|
|
503
|
-
prompt: 'Make the body shorter and more casual'
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
// Add new AI-generated field to existing object
|
|
507
|
-
await space.updateObject('abc123', {
|
|
508
|
-
data: { summary: '{{one-sentence summary}}' }
|
|
509
|
-
});
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
When resolving placeholders, the agent has access to the full object data 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.
|
|
513
|
-
|
|
514
|
-
AI-generated placeholders are resolved during the mutation and replaced with concrete values. The {{...}} syntax is not stored — it is only used to guide the agent while creating or updating the object.
|
|
515
|
-
|
|
516
615
|
### Relations
|
|
517
616
|
|
|
518
|
-
Relations connect objects directionally through named relations on the source object. Each relation name represents a multi-valued reference set on the source.
|
|
519
|
-
|
|
520
|
-
Internally, relations are indexed and idempotent. Creating the same relation twice has no effect and produces no duplicates.
|
|
521
|
-
|
|
522
617
|
| Method | Description |
|
|
523
618
|
|--------|-------------|
|
|
524
619
|
| `link(sourceId, relation, targetId): Promise<void>` | Add target to the named relation on source. Reads as "source.relation includes target". |
|
|
@@ -551,89 +646,28 @@ const planets = space.getChildren(sun.id, 'hasPlanet');
|
|
|
551
646
|
const stars = space.getParents(earth.id, 'orbits');
|
|
552
647
|
```
|
|
553
648
|
|
|
554
|
-
|
|
555
|
-
- Relation names are application-defined strings
|
|
556
|
-
- Relations are not stored in object data fields
|
|
557
|
-
- Only relations created via `link()` participate in traversal (`getParents`, `getChildren`) and indexing
|
|
558
|
-
- Storing object IDs inside regular object fields is allowed, but those references are opaque to the system
|
|
559
|
-
|
|
560
|
-
### Space Metadata
|
|
561
|
-
|
|
562
|
-
Store arbitrary data alongside the Space without it being part of the graph content (e.g., viewport state, user preferences).
|
|
563
|
-
|
|
564
|
-
| Method | Description |
|
|
565
|
-
|--------|-------------|
|
|
566
|
-
| `setMetadata(key, value): void` | Set space-level metadata |
|
|
567
|
-
| `getMetadata(key): unknown` | Get metadata value |
|
|
568
|
-
| `getAllMetadata(): Record<string, unknown>` | Get all metadata |
|
|
569
|
-
|
|
570
|
-
### Import/Export
|
|
571
|
-
|
|
572
|
-
Export space data as JSON-LD for backup, portability, or migration:
|
|
573
|
-
|
|
574
|
-
| Method | Description |
|
|
575
|
-
|--------|-------------|
|
|
576
|
-
| `export(): JsonLdDocument` | Export all objects and relations as JSON-LD |
|
|
577
|
-
| `import(data): Promise<void>` | Import JSON-LD into empty space |
|
|
578
|
-
|
|
579
|
-
**Export:**
|
|
580
|
-
```typescript
|
|
581
|
-
const jsonld = space.export();
|
|
582
|
-
// Save to file, send to API, etc.
|
|
583
|
-
const json = JSON.stringify(jsonld, null, 2);
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
**Import:**
|
|
587
|
-
```typescript
|
|
588
|
-
// Space must be empty
|
|
589
|
-
const newSpace = await client.createSpace('Imported Data');
|
|
590
|
-
await newSpace.import(jsonld);
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
The JSON-LD format follows W3C standards and can be processed by standard JSON-LD tools. Objects are exported with their data and relations; space metadata and interaction history are not included.
|
|
594
|
-
|
|
595
|
-
### AI Agent
|
|
649
|
+
### Undo/Redo
|
|
596
650
|
|
|
597
651
|
| Method | Description |
|
|
598
652
|
|--------|-------------|
|
|
599
|
-
| `
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
|
604
|
-
|
|
605
|
-
| `objectIds` | Limit context to specific objects |
|
|
606
|
-
| `responseSchema` | Request structured JSON instead of text summary |
|
|
607
|
-
| `effort` | Effort level: `'QUICK'`, `'STANDARD'` (default), `'REASONING'`, or `'RESEARCH'` |
|
|
608
|
-
| `ephemeral` | If true, don't record in conversation history (useful for tab completion) |
|
|
609
|
-
|
|
610
|
-
**Effort levels:**
|
|
611
|
-
- `QUICK` — Fast responses, read-only (cannot create/update objects or links)
|
|
612
|
-
- `STANDARD` — Default behavior with full capabilities
|
|
613
|
-
- `REASONING` — Extended reasoning for complex tasks
|
|
614
|
-
- `RESEARCH` — Pre-analysis and context gathering (reserved for future use)
|
|
615
|
-
|
|
616
|
-
```typescript
|
|
617
|
-
const { message, objects } = await space.prompt("Create a topic node for the solar system, then child nodes for each planet.");
|
|
618
|
-
console.log(`AI: ${message}`);
|
|
619
|
-
console.log(`Modified ${objects.length} objects:`, objects);
|
|
620
|
-
|
|
621
|
-
const result = await space.prompt("Summarize these articles", { objectIds: ['article-1', 'article-2'] });
|
|
653
|
+
| `checkpoint(label?): Promise<string>` | Call before mutations. Saves current state for undo. |
|
|
654
|
+
| `canUndo(): Promise<boolean>` | Check if undo available |
|
|
655
|
+
| `canRedo(): Promise<boolean>` | Check if redo available |
|
|
656
|
+
| `undo(): Promise<boolean>` | Undo to previous checkpoint |
|
|
657
|
+
| `redo(): Promise<boolean>` | Redo undone action |
|
|
658
|
+
| `clearHistory(): Promise<void>` | Clear undo/redo stack |
|
|
622
659
|
|
|
623
|
-
|
|
624
|
-
const { message } = await space.prompt("What topics are covered?", { effort: 'QUICK' });
|
|
660
|
+
See [Checkpoints & Undo/Redo](#checkpoints--undoredo) for semantics.
|
|
625
661
|
|
|
626
|
-
|
|
627
|
-
await space.prompt("Analyze relationships and reorganize", { effort: 'REASONING' });
|
|
628
|
-
```
|
|
662
|
+
### Space Metadata
|
|
629
663
|
|
|
630
|
-
|
|
664
|
+
Store arbitrary data alongside the Space without it being part of the graph content (e.g., viewport state, user preferences).
|
|
631
665
|
|
|
632
666
|
| Method | Description |
|
|
633
667
|
|--------|-------------|
|
|
634
|
-
| `
|
|
635
|
-
| `
|
|
636
|
-
| `
|
|
668
|
+
| `setMetadata(key, value): void` | Set space-level metadata |
|
|
669
|
+
| `getMetadata(key): unknown` | Get metadata value, or undefined if key not set |
|
|
670
|
+
| `getAllMetadata(): Record<string, unknown>` | Get all metadata |
|
|
637
671
|
|
|
638
672
|
### Media
|
|
639
673
|
|
|
@@ -664,6 +698,46 @@ if (response.contentType.startsWith('image/')) {
|
|
|
664
698
|
}
|
|
665
699
|
```
|
|
666
700
|
|
|
701
|
+
### Import/Export
|
|
702
|
+
|
|
703
|
+
Export space data as JSON-LD for backup, portability, or migration:
|
|
704
|
+
|
|
705
|
+
| Method | Description |
|
|
706
|
+
|--------|-------------|
|
|
707
|
+
| `export(): JsonLdDocument` | Export all objects and relations as JSON-LD |
|
|
708
|
+
| `import(data): Promise<void>` | Import JSON-LD into empty space |
|
|
709
|
+
| `exportArchive(): Promise<Blob>` | Export objects, relations, and media as a zip archive |
|
|
710
|
+
| `importArchive(archive): Promise<void>` | Import from a zip archive into empty space |
|
|
711
|
+
|
|
712
|
+
**Export (data only):**
|
|
713
|
+
```typescript
|
|
714
|
+
const jsonld = space.export();
|
|
715
|
+
const json = JSON.stringify(jsonld, null, 2);
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
**Export (with media):**
|
|
719
|
+
```typescript
|
|
720
|
+
const archive = await space.exportArchive();
|
|
721
|
+
// Save as .zip file
|
|
722
|
+
const url = URL.createObjectURL(archive);
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
**Import (data only):**
|
|
726
|
+
```typescript
|
|
727
|
+
// Space must be empty
|
|
728
|
+
const newSpace = await client.createSpace('Imported Data');
|
|
729
|
+
await newSpace.import(jsonld);
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
**Import (with media):**
|
|
733
|
+
```typescript
|
|
734
|
+
// Space must be empty
|
|
735
|
+
const newSpace = await client.createSpace('Imported Data');
|
|
736
|
+
await newSpace.importArchive(archiveBlob);
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
The JSON-LD format follows W3C standards. The archive format bundles `data.json` (JSON-LD with media URLs rewritten to relative paths) and a `media/` folder containing the actual files. Space metadata and interaction history are not included in either format.
|
|
740
|
+
|
|
667
741
|
### Space Events
|
|
668
742
|
|
|
669
743
|
Semantic events describe what changed. Events fire for both local changes and remote changes.
|
|
@@ -697,20 +771,6 @@ space.on('reset', ({ source }) => void)
|
|
|
697
771
|
space.on('syncError', (error: Error) => void)
|
|
698
772
|
```
|
|
699
773
|
|
|
700
|
-
**Usage pattern:**
|
|
701
|
-
```typescript
|
|
702
|
-
// All UI updates happen in one place, regardless of change source
|
|
703
|
-
space.on('objectUpdated', ({ objectId, object, source }) => {
|
|
704
|
-
renderObject(objectId, object);
|
|
705
|
-
if (source === 'remote_agent') {
|
|
706
|
-
doLayout(); // AI might have added content
|
|
707
|
-
}
|
|
708
|
-
});
|
|
709
|
-
|
|
710
|
-
// Caller just makes the change - event handler does the UI work
|
|
711
|
-
space.updateObject(objectId, { prompt: 'expand this' });
|
|
712
|
-
```
|
|
713
|
-
|
|
714
774
|
### Error Handling
|
|
715
775
|
|
|
716
776
|
AI operations may fail due to rate limiting or other transient errors. Check `error.message` for user-friendly error text:
|
|
@@ -733,8 +793,94 @@ try {
|
|
|
733
793
|
|--------|-------------|
|
|
734
794
|
| `getData(): RoolSpaceData` | Get full space data (internal format) |
|
|
735
795
|
|
|
796
|
+
## Interaction History
|
|
797
|
+
|
|
798
|
+
Each `RoolSpace` instance has a `conversationId` that tracks interaction history for that space. The history records all meaningful interactions (prompts, object changes, links) 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.
|
|
799
|
+
|
|
800
|
+
### What the AI Receives
|
|
801
|
+
|
|
802
|
+
AI operations (`prompt`, `createObject`, `updateObject`, `findObjects`) automatically receive:
|
|
803
|
+
|
|
804
|
+
- **Interaction history** — Previous interactions and their results from this conversation
|
|
805
|
+
- **Recently modified objects** — Objects in the space recently created or changed
|
|
806
|
+
- **Selected objects** — Objects passed via `objectIds` are given primary focus
|
|
807
|
+
|
|
808
|
+
This context flows automatically — no configuration needed. The AI sees enough history to maintain coherent interactions while respecting the `_`-prefixed field hiding rules.
|
|
809
|
+
|
|
810
|
+
### Accessing History
|
|
811
|
+
|
|
812
|
+
```typescript
|
|
813
|
+
// Get interactions for the current conversationId
|
|
814
|
+
const interactions = space.getInteractions();
|
|
815
|
+
// Returns: Interaction[]
|
|
816
|
+
|
|
817
|
+
// Get interactions for a specific conversation ID
|
|
818
|
+
const interactions = space.getInteractionsById('other-conversation-id');
|
|
819
|
+
// Returns: Interaction[]
|
|
820
|
+
|
|
821
|
+
// List all conversation IDs that have interactions
|
|
822
|
+
const conversationIds = space.getConversationIds();
|
|
823
|
+
// Returns: string[]
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
### Conversation Access Methods
|
|
827
|
+
|
|
828
|
+
| Method | Description |
|
|
829
|
+
|--------|-------------|
|
|
830
|
+
| `getInteractions(): Interaction[]` | Get interactions for the current conversationId |
|
|
831
|
+
| `getInteractionsById(id): Interaction[]` | Get interactions for a specific conversation ID |
|
|
832
|
+
| `getConversationIds(): string[]` | List all conversation IDs that have interactions |
|
|
833
|
+
| `clearInteractions(conversationId?): Promise<void>` | Clear interaction history. Defaults to current conversation. |
|
|
834
|
+
|
|
835
|
+
### Listening for Updates
|
|
836
|
+
|
|
837
|
+
```typescript
|
|
838
|
+
space.on('interactionsUpdated', ({ conversationId, source }) => {
|
|
839
|
+
// Interaction history changed - refresh if needed
|
|
840
|
+
const interactions = space.getInteractions();
|
|
841
|
+
renderInteractions(interactions);
|
|
842
|
+
});
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
### Conversation Isolation
|
|
846
|
+
|
|
847
|
+
By default, each call to `openSpace()` or `createSpace()` generates a new `conversationId`. This means:
|
|
848
|
+
- Opening a space twice gives you two independent AI conversation histories
|
|
849
|
+
- Closing and reopening a space starts fresh
|
|
850
|
+
|
|
851
|
+
### Resuming Conversations
|
|
852
|
+
|
|
853
|
+
Pass a `conversationId` to continue a previous conversation:
|
|
854
|
+
|
|
855
|
+
```typescript
|
|
856
|
+
// First conversation - save the conversationId
|
|
857
|
+
const space = await client.openSpace('abc123');
|
|
858
|
+
const savedConversationId = space.conversationId;
|
|
859
|
+
space.close();
|
|
860
|
+
|
|
861
|
+
// Later - resume the same conversation
|
|
862
|
+
const resumedSpace = await client.openSpace('abc123', { conversationId: savedConversationId });
|
|
863
|
+
// AI now has access to conversation history from before
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
**Use cases:**
|
|
867
|
+
- **Page refresh** — Store `conversationId` in localStorage to maintain context across reloads
|
|
868
|
+
- **Multiple conversations** — Maintain and switch between different conversation contexts with the same space
|
|
869
|
+
- **Collaborative conversations** — Share a `conversationId` between users to enable shared AI conversation history
|
|
870
|
+
|
|
871
|
+
**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.
|
|
872
|
+
|
|
873
|
+
Note: Interaction history is truncated to the most recent 50 entries to manage space size.
|
|
874
|
+
|
|
875
|
+
### The ai Field
|
|
876
|
+
|
|
877
|
+
The `ai` field in interactions distinguishes AI-generated responses from synthetic confirmations:
|
|
878
|
+
- `ai: true` — AI processed this operation (prompt, or createObject/updateObject with placeholders)
|
|
879
|
+
- `ai: false` — System confirmation only (e.g., "Linked X to Y", "Created object abc123")
|
|
880
|
+
|
|
881
|
+
### Tool Calls
|
|
736
882
|
|
|
737
|
-
|
|
883
|
+
The `toolCalls` array captures what the AI agent did during execution. Use it to build responsive UIs that show progress while the agent works — the `interactionsUpdated` event fires as each tool completes, letting you display status updates or hints in real-time.
|
|
738
884
|
|
|
739
885
|
## Data Types
|
|
740
886
|
|
|
@@ -762,6 +908,7 @@ interface RoolObjectEntry {
|
|
|
762
908
|
data: RoolObject; // The actual object data
|
|
763
909
|
modifiedAt?: number; // Timestamp of last modification
|
|
764
910
|
modifiedBy?: string; // User ID who last modified
|
|
911
|
+
modifiedByName?: string | null; // Display name at time of modification
|
|
765
912
|
}
|
|
766
913
|
```
|
|
767
914
|
|
|
@@ -788,11 +935,6 @@ interface Interaction {
|
|
|
788
935
|
}
|
|
789
936
|
```
|
|
790
937
|
|
|
791
|
-
The `toolCalls` array captures what the AI agent did during execution. This provides:
|
|
792
|
-
- **Real-time feedback**: Clients receive updates as tools execute (via `interactionsUpdated` event)
|
|
793
|
-
- **Agent memory**: The AI sees its previous tool usage when continuing a conversation
|
|
794
|
-
- **Debugging**: Inspect what tools were called and their results
|
|
795
|
-
|
|
796
938
|
### Info Types
|
|
797
939
|
|
|
798
940
|
```typescript
|
|
@@ -820,9 +962,7 @@ interface PromptOptions {
|
|
|
820
962
|
}
|
|
821
963
|
```
|
|
822
964
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
## What Can You Build With a Space?
|
|
965
|
+
## Patterns & Examples
|
|
826
966
|
|
|
827
967
|
A Rool Space is a persistent, shared world model. Applications project different interaction patterns onto the same core primitives:
|
|
828
968
|
|
|
@@ -877,4 +1017,4 @@ Below are a few representative patterns.
|
|
|
877
1017
|
|
|
878
1018
|
This client is intended for use with the Rool platform and requires an account. Access is currently by invitation.
|
|
879
1019
|
|
|
880
|
-
Proprietary
|
|
1020
|
+
Proprietary — © Lightpost One. All rights reserved.
|