@rool-dev/sdk 0.12.5 → 2.0.0-dev.0745af4

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.
Files changed (112) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +63 -951
  3. package/assets/rool-logo.svg +5 -0
  4. package/assets/rool-wordmark-dark.svg +7 -0
  5. package/assets/rool-wordmark.svg +7 -0
  6. package/dist/agents.d.ts +143 -0
  7. package/dist/agents.d.ts.map +1 -0
  8. package/dist/agents.js +583 -0
  9. package/dist/agents.js.map +1 -0
  10. package/dist/auth-base.d.ts +43 -88
  11. package/dist/auth-base.d.ts.map +1 -1
  12. package/dist/auth-base.js +178 -302
  13. package/dist/auth-base.js.map +1 -1
  14. package/dist/auth-browser.d.ts +7 -25
  15. package/dist/auth-browser.d.ts.map +1 -1
  16. package/dist/auth-browser.js +45 -82
  17. package/dist/auth-browser.js.map +1 -1
  18. package/dist/auth-native.d.ts +9 -56
  19. package/dist/auth-native.d.ts.map +1 -1
  20. package/dist/auth-native.js +88 -177
  21. package/dist/auth-native.js.map +1 -1
  22. package/dist/auth-node.d.ts +32 -36
  23. package/dist/auth-node.d.ts.map +1 -1
  24. package/dist/auth-node.js +338 -265
  25. package/dist/auth-node.js.map +1 -1
  26. package/dist/auth-url.d.ts +6 -0
  27. package/dist/auth-url.d.ts.map +1 -0
  28. package/dist/auth-url.js +30 -0
  29. package/dist/auth-url.js.map +1 -0
  30. package/dist/client.d.ts +37 -290
  31. package/dist/client.d.ts.map +1 -1
  32. package/dist/client.js +331 -635
  33. package/dist/client.js.map +1 -1
  34. package/dist/events.d.ts +40 -0
  35. package/dist/events.d.ts.map +1 -0
  36. package/dist/events.js +175 -0
  37. package/dist/events.js.map +1 -0
  38. package/dist/files.d.ts +176 -0
  39. package/dist/files.d.ts.map +1 -0
  40. package/dist/files.js +857 -0
  41. package/dist/files.js.map +1 -0
  42. package/dist/index.d.ts +16 -19
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +7 -20
  45. package/dist/index.js.map +1 -1
  46. package/dist/machine.d.ts +51 -0
  47. package/dist/machine.d.ts.map +1 -0
  48. package/dist/machine.js +142 -0
  49. package/dist/machine.js.map +1 -0
  50. package/dist/problem.d.ts +11 -0
  51. package/dist/problem.d.ts.map +1 -0
  52. package/dist/problem.js +20 -0
  53. package/dist/problem.js.map +1 -0
  54. package/dist/structured.d.ts +84 -0
  55. package/dist/structured.d.ts.map +1 -0
  56. package/dist/structured.js +405 -0
  57. package/dist/structured.js.map +1 -0
  58. package/dist/types.d.ts +133 -520
  59. package/dist/types.d.ts.map +1 -1
  60. package/package.json +24 -22
  61. package/dist/auth.d.ts +0 -99
  62. package/dist/auth.d.ts.map +0 -1
  63. package/dist/auth.js +0 -217
  64. package/dist/auth.js.map +0 -1
  65. package/dist/client-info.d.ts +0 -18
  66. package/dist/client-info.d.ts.map +0 -1
  67. package/dist/client-info.js +0 -25
  68. package/dist/client-info.js.map +0 -1
  69. package/dist/event-emitter.d.ts +0 -45
  70. package/dist/event-emitter.d.ts.map +0 -1
  71. package/dist/event-emitter.js +0 -86
  72. package/dist/event-emitter.js.map +0 -1
  73. package/dist/graphql.d.ts +0 -90
  74. package/dist/graphql.d.ts.map +0 -1
  75. package/dist/graphql.js +0 -471
  76. package/dist/graphql.js.map +0 -1
  77. package/dist/logger.d.ts +0 -13
  78. package/dist/logger.d.ts.map +0 -1
  79. package/dist/logger.js +0 -17
  80. package/dist/logger.js.map +0 -1
  81. package/dist/path.d.ts +0 -6
  82. package/dist/path.d.ts.map +0 -1
  83. package/dist/path.js +0 -47
  84. package/dist/path.js.map +0 -1
  85. package/dist/reroute.d.ts +0 -22
  86. package/dist/reroute.d.ts.map +0 -1
  87. package/dist/reroute.js +0 -70
  88. package/dist/reroute.js.map +0 -1
  89. package/dist/rest.d.ts +0 -54
  90. package/dist/rest.d.ts.map +0 -1
  91. package/dist/rest.js +0 -171
  92. package/dist/rest.js.map +0 -1
  93. package/dist/router.d.ts +0 -20
  94. package/dist/router.d.ts.map +0 -1
  95. package/dist/router.js +0 -54
  96. package/dist/router.js.map +0 -1
  97. package/dist/space-session.d.ts +0 -30
  98. package/dist/space-session.d.ts.map +0 -1
  99. package/dist/space-session.js +0 -83
  100. package/dist/space-session.js.map +0 -1
  101. package/dist/space.d.ts +0 -161
  102. package/dist/space.d.ts.map +0 -1
  103. package/dist/space.js +0 -618
  104. package/dist/space.js.map +0 -1
  105. package/dist/subscription.d.ts +0 -36
  106. package/dist/subscription.d.ts.map +0 -1
  107. package/dist/subscription.js +0 -419
  108. package/dist/subscription.js.map +0 -1
  109. package/dist/webdav.d.ts +0 -171
  110. package/dist/webdav.d.ts.map +0 -1
  111. package/dist/webdav.js +0 -481
  112. package/dist/webdav.js.map +0 -1
package/README.md CHANGED
@@ -1,15 +1,10 @@
1
- # Rool SDK
1
+ <p align="center" class="sdk-brand"><a href="https://rool.dev"><img class="sdk-brand-logo" src="./assets/rool-logo.svg" alt="Rool logo" width="44" height="44"><picture><source media="(prefers-color-scheme: dark)" srcset="./assets/rool-wordmark-dark.svg"><img class="sdk-brand-wordmark" src="./assets/rool-wordmark.svg" alt="Rool" width="128"></picture></a></p>
2
2
 
3
- TypeScript SDK for programmatic access to [Rool Machines](https://rool.dev) — private AI computers holding objects, files, conversations, and memory, with AI-assisted editing built in.
3
+ <h1 align="center" class="sdk-title">TypeScript SDK</h1>
4
4
 
5
- > **Naming note:** the SDK predates the Rool Machine branding and calls a Machine a **space** (`createSpace`, `RoolSpace`, `/space/...` paths). The API keeps this legacy naming until v2; read "space" as "Machine" throughout.
5
+ The official TypeScript SDK for building apps and automations with [Rool Machines](https://rool.dev).
6
6
 
7
- Core primitives:
8
-
9
- - **Spaces** — Machines: containers for objects, schema, metadata, conversations, collaborators, and files.
10
- - **Conversations** — independent interaction histories in a space.
11
- - **Objects** — JSON records addressed by object paths such as `/space/article/welcome.json`.
12
- - **Files** — user-visible files stored under `/rool-drive/...` through WebDAV.
7
+ A Rool Machine is a persistent cloud VM with snapshots, files, and embedded AI agents. The SDK provides one typed interface for authentication, machine management, file access, agent conversations, and live updates.
13
8
 
14
9
  ## Installation
15
10
 
@@ -17,991 +12,108 @@ Core primitives:
17
12
  npm install @rool-dev/sdk
18
13
  ```
19
14
 
20
- ## Quick Start
21
-
22
- ```typescript
23
- import { RoolClient } from '@rool-dev/sdk';
24
-
25
- async function main() {
26
- const client = new RoolClient();
27
-
28
- if (!(await client.initialize())) {
29
- await client.login('My App');
30
- // Browser auth redirects away. Run startup again after the auth callback.
31
- return;
32
- }
33
-
34
- const space = await client.createSpace('Solar System');
35
- const conversation = space.conversation('main');
36
-
37
- await space.createCollection('body', [
38
- { name: 'name', type: { kind: 'string' } },
39
- { name: 'mass', type: { kind: 'string' } },
40
- { name: 'radius', type: { kind: 'string' } },
41
- { name: 'orbits', type: { kind: 'maybe', inner: { kind: 'ref' } } },
42
- ]);
43
-
44
- const { object: sun } = await space.putObject('/space/body/sun.json', {
45
- name: 'Sun',
46
- mass: '1 solar mass',
47
- radius: '696,340 km',
48
- });
49
-
50
- const { object: earth } = await space.putObject('/space/body/earth.json', {
51
- name: 'Earth',
52
- mass: '1 Earth mass',
53
- radius: '6,371 km',
54
- orbits: sun.path,
55
- });
56
-
57
- const { message, objects } = await conversation.prompt(
58
- 'Add the other planets in our solar system, each referencing the Sun.'
59
- );
60
-
61
- console.log(message);
62
- console.log(`Modified ${objects.length} objects`);
63
-
64
- const loadedEarth = await space.getObject(earth.path);
65
- console.log(loadedEarth?.body.name);
66
-
67
- space.close();
68
- }
69
-
70
- void main();
71
- ```
72
-
73
- ## Paths and Resource URIs
74
-
75
- Most SDK methods take plain path strings:
76
-
77
- - Object paths: `/space/<collection>/<name>.json` (exactly three segments; no dotfile collection or object names)
78
- - File paths: `/rool-drive/<path/to/file>`
79
-
80
- `rool-machine:/...` URIs are the user-facing/canonical form for resource references in prompt attachments and interaction history. The exported helpers normalize between these forms when you need them.
81
-
82
- ```typescript
83
- import { machinePath, machineUri, isObjectPath } from '@rool-dev/sdk';
84
-
85
- machinePath('rool-machine:/rool-drive/docs/read%20me.md');
86
- // '/rool-drive/docs/read me.md'
87
-
88
- machineUri('/space/article/welcome.json');
89
- // 'rool-machine:/space/article/welcome.json'
90
-
91
- isObjectPath('/space/article/welcome.json'); // true
92
- ```
93
-
94
- Object APIs require full object paths. References between objects are ordinary body fields containing object paths:
95
-
96
- ```typescript
97
- {
98
- path: '/space/body/earth.json',
99
- body: { name: 'Earth', orbits: '/space/body/sun.json' },
100
- }
101
- ```
102
-
103
- ## Authentication
104
-
105
- ### Browser
106
-
107
- The default auth provider stores tokens in browser storage and redirects to the Rool auth page.
108
-
109
- ```typescript
110
- async function start() {
111
- const client = new RoolClient();
112
-
113
- if (!(await client.initialize())) {
114
- await client.login('My App');
115
- // Browser auth redirects; stop startup until the callback reloads the app.
116
- return;
117
- }
118
-
119
- // Use the authenticated client here.
120
- }
121
-
122
- void start();
123
- ```
124
-
125
- ### Node.js
15
+ ## Connect from Node.js
126
16
 
127
- Use the Node auth provider for CLIs and scripts. It stores endpoint-scoped credentials under `~/.config/rool/` by default (for example, `credentials-<hash>.json`) and opens a browser for login.
17
+ `NodeAuth` opens the user's browser for login, stores their credentials locally, and refreshes them when needed.
128
18
 
129
19
  ```typescript
130
- import { RoolClient } from '@rool-dev/sdk';
131
- import { NodeAuthProvider } from '@rool-dev/sdk/node';
20
+ import { RoolClient } from "@rool-dev/sdk";
21
+ import { NodeAuth } from "@rool-dev/sdk/node";
132
22
 
133
- const client = new RoolClient({ authProvider: new NodeAuthProvider() });
134
- let authenticated = await client.initialize();
135
-
136
- if (!authenticated) {
137
- await client.login('My CLI Tool');
138
- // Re-run initialize after the non-redirect login to hydrate currentUser,
139
- // user storage, and client-level event subscriptions.
140
- authenticated = await client.initialize();
23
+ const auth = new NodeAuth();
24
+ if (!(await auth.initialize())) {
25
+ await auth.login("My app");
141
26
  }
142
27
 
143
- if (!authenticated) throw new Error('Login required');
144
- ```
145
-
146
- ### Native (Capacitor, Cordova, Tauri, ...)
147
-
148
- Use the native PKCE provider for JS app shells that sign in through an external
149
- system browser. `login()`/`signup()` open the auth server's `/authorize` page
150
- via the `openExternal` callback you supply; when the deep link returns, feed it
151
- to `client.handleAuthRedirect(url)` from your platform's deep-link handler. The
152
- code is exchanged for a session at `/token` and tokens are stored like the
153
- browser provider.
154
-
155
- ```typescript
156
- import { RoolClient, NativePkceAuthProvider } from '@rool-dev/sdk';
157
- import { Browser } from '@capacitor/browser';
158
- import { App } from '@capacitor/app';
159
-
160
28
  const client = new RoolClient({
161
- authProvider: new NativePkceAuthProvider({
162
- redirectUri: 'roolandroidauth://auth/callback', // must match the server allowlist
163
- defaultProvider: 'google', // 'google' | 'apple'
164
- openExternal: (url) => Browser.open({ url }),
165
- }),
166
- });
167
-
168
- // Complete sign-in when the OS hands the app its deep link.
169
- App.addListener('appUrlOpen', async ({ url }) => {
170
- if (await client.handleAuthRedirect(url)) {
171
- await Browser.close();
172
- // Now authenticated — refresh your UI.
173
- }
29
+ getTokens: auth.getTokens,
30
+ onAuthInvalidated: auth.logout,
174
31
  });
175
-
176
- if (!(await client.initialize())) {
177
- // Opens the system browser; completion arrives via the listener above.
178
- await client.login('My App'); // pass { provider: 'apple' } to override the default
179
- }
180
- ```
181
-
182
- #### Email + password and magic links (native)
183
-
184
- The native provider also supports email/password sign-in and magic links —
185
- no system browser, the server returns the token set as JSON directly.
186
-
187
- ```typescript
188
- // Password sign-in
189
- const result = await client.signInWithPassword(email, password);
190
- if (result.status === 'signed_in') {
191
- // authenticated — refresh your UI
192
- } else {
193
- // status === 'verify_required': the email isn't verified yet and the server
194
- // has emailed a magic link. Tell the user to check their inbox.
195
- }
196
-
197
- // Or request a magic link explicitly
198
- await client.requestMagicLink(email);
199
32
  ```
200
33
 
201
- The magic link carries a `?verify=<token>` param; complete sign-in by passing
202
- that token to `client.verify(token)` once the link lands back in the app.
203
-
204
- > **⚠️ Magic links open the website, not the app, until Universal Links / App
205
- > Links are configured.** The emailed link is an `https://` URL for the Rool
206
- > web app. On native, an `https` link only re-opens your app if you've set up
207
- > [iOS Universal Links](https://developer.apple.com/ios/universal-links/) /
208
- > [Android App Links](https://developer.android.com/training/app-links) for that
209
- > domain (custom-scheme deep links don't apply to email links). **Without that
210
- > setup the magic link completes sign-in in the browser/website, not in the
211
- > native app** — so for now treat magic links on native as a website hand-off,
212
- > and prefer password or social sign-in for an in-app experience.
213
-
214
- ### Auth API
215
-
216
- | Method | Description |
217
- | --- | --- |
218
- | `initialize(): Promise<boolean>` | Call on startup. Initializes auth, refreshes user/storage state, and starts client events when authenticated. |
219
- | `login(appName, params?): Promise<void>` | Start login flow. |
220
- | `signup(appName, params?): Promise<void>` | Start signup flow. |
221
- | `verify(token): Promise<boolean>` | Complete email verification token flow; returns `false` when the active auth provider does not implement verification. |
222
- | `handleAuthRedirect(url): Promise<boolean>` | Complete a native PKCE sign-in from a deep-link callback URL. Returns `false` when the active auth provider does not implement it. |
223
- | `signInWithPassword(email, password): Promise<PasswordSignInResult>` | Email + password sign-in (native provider). Resolves `{ status: 'signed_in' }`, or `{ status: 'verify_required' }` when the email is unverified (a magic link was emailed). Rejects on bad credentials. |
224
- | `requestMagicLink(email): Promise<void>` | Email the user a magic sign-in link (native provider). See the caveat below — on native the link opens the **website**, not the app, until Universal Links / App Links are configured. |
225
- | `logout(): void` | Clear auth state and close open spaces. |
226
- | `isAuthenticated(): Promise<boolean>` | Whether credentials are held locally. No network call — a server outage does not read as logged out. |
227
- | `getAuthUser(): AuthUser` | Return auth identity decoded from the token. |
228
- | `setPassword(password): Promise<void>` | Set/change password and sign out the user's other sessions. |
229
- | `requestEmailChange(newEmail): Promise<void>` | Start an email address change. The server mails a confirmation link to the new address; the change applies when that link is clicked. |
230
-
231
- ### Changing email
232
-
233
- `requestEmailChange` starts the flow; nothing changes until the user clicks the confirmation link mailed to the new address. After confirmation the current session no longer belongs to the account — the next API call fails with a 401, which ends the session via `authStateChanged(false)`. Have the user sign in again with the new address.
34
+ The package also includes:
234
35
 
235
- ```typescript
236
- import { EmailChangeError } from '@rool-dev/sdk';
237
-
238
- try {
239
- await client.requestEmailChange('new@example.com');
240
- // Tell the user to check the new address's inbox.
241
- } catch (error) {
242
- if (error instanceof EmailChangeError) {
243
- showFormError(error.message); // user-facing message
244
- }
245
- }
246
- ```
36
+ - `BrowserAuth` for web apps using browser redirects and browser storage.
37
+ - `NativeAuth` for mobile apps that open sign-in in the system browser and return through a deep link. It also supports passwords and magic links.
247
38
 
248
- Refusals throw `EmailChangeError` with a `code` and a user-facing `message`. Codes worth branching on:
39
+ ## Put a machine to work
249
40
 
250
- | Code | Meaning |
251
- | --- | --- |
252
- | `invalid_email` | The new address is not a valid email. |
253
- | `same_email` | The new address is the account's current address. |
254
- | `email_in_use` | Another account already uses the new address. |
255
- | `send_failed` | The confirmation email could not be sent. |
256
-
257
- ### Offline behavior
258
-
259
- A temporarily unreachable server never reads as "logged out". `initialize()` reports authentication from stored credentials, so on an offline start it can return `true` while `currentUser` is still `null` and user storage is empty — the SDK keeps reconnecting in the background and hydrates both automatically once the server is reachable, emitting `currentUserChanged`. Only a hard auth failure ends the session — an invalid or expired refresh token, or a 401 from the API (the identity was retired, e.g. after an email change, or the account deleted) — via `authStateChanged(false)`.
260
-
261
- ## Spaces and Conversations
262
-
263
- Open a space to receive live events and manage objects, schema, metadata, collaborators, file storage, and conversations. Use a conversation handle for conversation metadata and AI.
41
+ This example creates a machine, gives an agent a job, uploads a receipt, and asks the agent to file it. The file API writes to the machine's WebDAV storage, so the agent and the app see the same files.
264
42
 
265
43
  ```typescript
266
- const space = await client.openSpace('space-id');
267
-
268
- const conversation = space.conversation('main');
269
- space.on('filesChanged', () => console.log('files changed'));
270
-
271
- await conversation.prompt('Summarize this space');
272
- ```
44
+ import { readFile } from "node:fs/promises";
273
45
 
274
- Conversation IDs must be 1–32 characters and contain only letters, numbers, `_`, and `-`.
46
+ const created = await client.createMachine({ name: "Receipts" });
47
+ const machine = client.machine(created.id);
275
48
 
276
- ## Object Operations
277
-
278
- Objects are JSON files under `/space`. Create the collection before writing objects in it.
279
-
280
- ```typescript
281
- await space.createCollection('article', [
282
- { name: 'title', type: { kind: 'string' } },
283
- { name: 'status', type: { kind: 'string' } },
284
- ]);
285
-
286
- // Create or replace an exact object path
287
- const { object } = await space.putObject('/space/article/welcome.json', {
288
- title: 'Welcome',
289
- status: 'draft',
290
- });
291
-
292
- // Patch fields; null or undefined deletes a field
293
- await space.patchObject(object.path, {
294
- data: { status: 'published', obsoleteField: null },
49
+ const agent = await machine.agents.create("receipt-filer", {
50
+ system:
51
+ "File receipt attachments under /rool-drive/receipts. " +
52
+ "Choose useful subfolders based on the receipt's contents.",
295
53
  });
296
-
297
- // Read one or many objects
298
- await space.getObject('/space/article/welcome.json');
299
- await space.getObjects([
300
- '/space/article/welcome.json',
301
- '/space/article/intro.json',
302
- ]);
303
-
304
- // Rename or move an object
305
- await space.moveObject(
306
- '/space/article/welcome.json',
307
- '/space/article/hello-world.json'
308
- );
309
-
310
- // Delete objects
311
- await space.deleteObjects(['/space/article/hello-world.json']);
312
- ```
313
-
314
- | Method | Description |
315
- | --- | --- |
316
- | `getObject(path): Promise<RoolObject | undefined>` | Fetch one object by object path. |
317
- | `getObjects(paths): Promise<GetObjectsResult>` | Fetch objects in bulk; returns `objects` and `missing`. |
318
- | `space.putObject(path, body): Promise<{ object, message }>` | Create or replace an object at an exact path. |
319
- | `space.patchObject(path, { data }): Promise<{ object, message }>` | Patch an object's body; `null`/`undefined` deletes fields. |
320
- | `space.moveObject(from, to, options?): Promise<{ object, message }>` | Rename or relocate an object; `options.body` can replace the body after moving. |
321
- | `space.deleteObjects(paths): Promise<void>` | Delete object files. |
322
-
323
- ## AI Agent
324
-
325
- `prompt()` invokes the AI agent. The agent can inspect space context and, unless `readOnly` or a read-only effort is used, create/modify/move/delete objects.
326
-
327
- ```typescript
328
- const { message, objects } = await conversation.prompt(
329
- 'Create a topic node for the solar system, then child nodes for each planet.'
330
- );
331
-
332
- console.log(message);
333
- console.log(objects.map((object) => object.path));
334
- ```
335
-
336
- ### Prompt Options
337
-
338
- | Option | Description |
339
- | --- | --- |
340
- | `responseSchema` | Request structured JSON text matching a JSON-schema-like shape. |
341
- | `effort` | `'QUICK'` (fast/read-only), `'STANDARD'` (default), `'REASONING'`, or `'RESEARCH'`. |
342
- | `ephemeral` | Do not record the prompt in interaction history. |
343
- | `readOnly` | Disable mutation tools. |
344
- | `parentInteractionId` | Conversation-tree parent. Omit to fetch and continue from the current default leaf; pass `null` for a new root branch. |
345
- | `attachments` | Existing object/file paths or `rool-machine:/...` URIs, plus local files (`File`, `Blob`, or `{ data, contentType, filename? }`). |
346
- | `signal` | AbortSignal used to request that the server stop an in-flight prompt. |
347
- | `eventName` | Optional telemetry event name. Defaults to `'prompt_user'`. |
348
-
349
- ```typescript
350
- // Read-only quick question
351
- await conversation.prompt('What topics are covered?', {
352
- effort: 'QUICK', // fast/read-only
54
+ const conversation = await agent.createConversation({
55
+ name: "Receipt inbox",
56
+ visibility: "private",
353
57
  });
354
58
 
355
- // Focus on existing objects and files
356
- await conversation.prompt('Compare these resources', {
357
- attachments: [
358
- '/space/article/intro.json',
359
- 'rool-machine:/rool-drive/docs/report.pdf',
360
- ],
59
+ const receiptPath = "/rool-drive/receipts/unsorted/receipt.pdf";
60
+ await machine.files.write(receiptPath, await readFile("./receipt.pdf"), {
61
+ contentType: "application/pdf",
62
+ createParents: true,
361
63
  });
362
64
 
363
- // Upload a local file as an attachment
364
- await conversation.prompt('Describe this image', {
365
- attachments: [fileInput.files![0]],
65
+ await conversation.prompt("File this receipt.", {
66
+ attachments: [receiptPath],
366
67
  });
367
-
368
- // Structured response
369
- const { message } = await conversation.prompt('Categorize these items', {
370
- responseSchema: {
371
- type: 'object',
372
- properties: {
373
- categories: { type: 'array', items: { type: 'string' } },
374
- summary: { type: 'string' },
375
- },
68
+ await conversation.follow({
69
+ onEvent(event) {
70
+ if (event.type === "output.delta" && event.content.type === "text") {
71
+ process.stdout.write(event.content.text);
72
+ }
376
73
  },
377
74
  });
378
- const result = JSON.parse(message);
379
-
380
- // Stop a long prompt with a signal (when the caller holds the controller)
381
- const ac = new AbortController();
382
- const promptPromise = conversation.prompt('Do a deep analysis', {
383
- effort: 'RESEARCH',
384
- signal: ac.signal,
385
- });
386
- ac.abort(); // asks the server to stop the in-flight interaction
387
- await promptPromise;
388
75
  ```
389
76
 
390
- ### Stopping a conversation
77
+ Prompting is asynchronous: `prompt()` returns when Rool accepts the work, while the agent keeps running on the machine. An app can follow the current run, leave, and reconnect later. UI clients can instead watch a conversation and receive a current view as its saved turns and live output change.
391
78
 
392
- Use `signal` when the same call site cancels the prompt. When the Stop button
393
- lives elsewhere — a different component, after a reload, or a prompt another
394
- client started — stop the conversation itself. A conversation processes one
395
- run at a time, so no interaction ID is needed. Stopping is best-effort: the
396
- server halts the agent loop and closes the stream, but an LLM turn already in
397
- flight keeps generating server-side and is billed.
79
+ ## Give agents data your app understands
398
80
 
399
- ```typescript
400
- // Stop whatever is running in a conversation. Returns whether anything was
401
- // actually running.
402
- await space.stopConversation('thread-42');
403
-
404
- // Conversation handles stop their own running work.
405
- const thread = space.conversation('thread-42');
406
- await thread.stop();
407
- ```
408
-
409
- | Method | Description |
410
- | --- | --- |
411
- | `stopConversation(conversationId): Promise<boolean>` | Stop whatever is running in a conversation. |
412
- | `conversation.stop(): Promise<boolean>` | Stop this conversation's running work. |
413
-
414
- ## Conversations
415
-
416
- Use `space.conversation(id)` for an imperative API scoped to one conversation. The handle retains no conversation contents or branch cursor; fetch contents when needed. With no explicit parent, `prompt()` fetches the current conversation and continues from its default leaf.
81
+ A machine can hold records as well as documents. A collection defines the fields in each record, and Rool rejects writes that do not match. For example, an app can keep an index alongside the receipt files:
417
82
 
418
83
  ```typescript
419
- import { conversationBranch, defaultConversationLeaf } from '@rool-dev/sdk';
420
-
421
- const thread = space.conversation('thread-42');
422
- await thread.prompt('Hello from another thread');
423
- await thread.setSystemInstruction('Answer in haiku');
424
-
425
- const conversation = await thread.get();
426
- const leaf = defaultConversationLeaf(conversation);
427
- const branch = conversationBranch(conversation, leaf);
428
- ```
429
-
430
- | Method/property | Description |
431
- | --- | --- |
432
- | `space.conversation(id): ConversationHandle` | Get a stateless conversation-scoped API handle. |
433
- | `conversation.get(): Promise<Conversation | null>` | Fetch current conversation contents. |
434
- | `conversation.prompt(text, options?)` | Prompt the conversation. Pass `parentInteractionId` to choose a branch explicitly. |
435
- | `conversation.setSystemInstruction(value)` | Set or clear the system instruction. |
436
- | `conversation.rename(name)` / `conversation.delete()` / `conversation.stop()` | Manage the scoped conversation. |
437
- | `listConversations(): Promise<ConversationMeta[]>` | Fetch the conversation roster. |
438
- | `createConversation(agent, visibility): Promise<string>` | Create a conversation under an agent; returns the server-minted conversation ID. |
439
- | `deleteConversation(id): Promise<void>` | Delete a conversation by ID. |
440
-
441
- ### Agents
442
-
443
- Conversations belong to an agent. Every space has the stock agent `rool`; spaces can also host custom agents. `createConversation` creates a conversation under any agent — including `rool` — with a server-minted ID and the visibility you choose; prompt the returned ID through a normal handle. Stock (`rool`) conversations can also spring into existence from a client-minted ID on first prompt (as in the examples above); custom-agent conversations are only created explicitly.
444
-
445
- Each conversation has a visibility: `'shared'` (every space member), `'private'` (only you), or `'temporary'` (private, and auto-deleted after sitting idle). A conversation's agent and visibility are reported on its `ConversationMeta`.
446
-
447
- ```typescript
448
- const agents = await space.listAgents(); // always includes 'rool'
449
-
450
- const id = await space.createConversation('research-bot', 'private');
451
- await space.conversation(id).prompt('Hello');
452
-
453
- await space.deleteAgent('research-bot'); // removes the agent and all its conversations
454
- ```
455
-
456
- | Method | Description |
457
- | --- | --- |
458
- | `listAgents(): Promise<string[]>` | The space's agents. Always includes the stock agent `rool`. |
459
- | `createConversation(agent, visibility): Promise<string>` | Create a conversation under an agent with the given visibility. |
460
- | `deleteAgent(agent): Promise<void>` | Delete a custom agent and all its conversations. The stock agent `rool` cannot be deleted. |
461
-
462
- ## Schema and Metadata
463
-
464
- Collections define the schema visible to the AI agent. Hidden body fields whose names start with `_` are useful for app/UI state that should not be considered by AI.
465
-
466
- ```typescript
467
- await space.createCollection('article', {
468
- schemaOrgType: 'Article',
84
+ await machine.collections.create("receipts", {
469
85
  fields: [
470
- { name: 'title', type: { kind: 'string' } },
471
- { name: 'status', type: { kind: 'enum', values: ['draft', 'published'] } },
472
- { name: 'tags', type: { kind: 'array', inner: { kind: 'string' } } },
473
- { name: 'author', type: { kind: 'ref' } },
86
+ { name: "vendor", type: { kind: "string" } },
87
+ { name: "amount", type: { kind: "number" } },
88
+ { name: "currency", type: { kind: "string" } },
89
+ { name: "document", type: { kind: "string" } },
474
90
  ],
475
91
  });
476
92
 
477
- const schema = await space.readSchema();
478
-
479
- await space.alterCollection('article', [
480
- { name: 'title', type: { kind: 'string' } },
481
- { name: 'status', type: { kind: 'string' } },
482
- ]);
483
-
484
- await space.writeMeta({ viewport: { x: 0, y: 0, zoom: 1 } });
485
- const meta = await space.readMeta();
486
- ```
487
-
488
- | Method | Description |
489
- | --- | --- |
490
- | `readSchema(): Promise<SpaceSchema>` | Collection definitions, read from `/space/<name>/.schema.json`. |
491
- | `createCollection(name, fieldsOrDef, options?): Promise<CollectionDef>` | Create a collection. |
492
- | `alterCollection(name, fieldsOrDef, options?): Promise<CollectionDef>` | Replace a collection definition. |
493
- | `dropCollection(name): Promise<void>` | Remove a collection and its object directory. |
494
- | `readMeta(): Promise<Record<string, unknown>>` | Read metadata from `/space/.meta.json`. |
495
- | `writeMeta(meta): Promise<void>` | Write the full metadata blob to `/space/.meta.json`. |
496
-
497
- Field kinds: `string`, `number`, `boolean`, `ref`, `enum`, `literal`, `array`, and `maybe`.
498
-
499
- ## Undo/Redo
500
-
501
- Undo/redo works over the whole space. Checkpoints are managed automatically by the server, so you don't need to create them yourself — just call `undo()`/`redo()`.
502
-
503
- ```typescript
504
- await space.deleteObjects(['/space/article/welcome.json']);
505
-
506
- if (await space.canUndo()) {
507
- await space.undo();
508
- }
509
- ```
510
-
511
- | Method | Description |
512
- | --- | --- |
513
- | `canUndo(): Promise<boolean>` | Check whether undo is available. |
514
- | `canRedo(): Promise<boolean>` | Check whether redo is available. |
515
- | `undo(): Promise<boolean>` | Restore the latest checkpoint. |
516
- | `redo(): Promise<boolean>` | Reapply undone work. |
517
-
518
- Undo/redo availability is scoped to the space.
519
-
520
- ## File Storage and WebDAV
521
-
522
- Every space has authenticated WebDAV storage. WebDAV methods take SDK machine paths such as `/space/...`, `/rool-drive/...`, or `/` for the root collection.
523
-
524
- ```typescript
525
- const webdav = space.webdav;
526
-
527
- await webdav.mkcol('/rool-drive/docs');
528
- await webdav.put('/rool-drive/docs/readme.md', '# Hello', {
529
- contentType: 'text/markdown',
530
- ifNoneMatch: '*',
531
- });
532
-
533
- const listing = await webdav.propfind('/rool-drive/docs', {
534
- depth: '1',
535
- props: ['displayname', 'getcontentlength', 'getcontenttype', 'getetag'],
536
- });
537
-
538
- const response = await webdav.get('/rool-drive/docs/readme.md');
539
- console.log(await response.text());
540
-
541
- const file = await space.fetchPath('/rool-drive/docs/readme.md');
542
- console.log(file.headers.get('Content-Type'));
543
-
544
- ```
545
-
546
- ### Real-time file sync
547
-
548
- Object and file changes are announced at the space level. Use WebDAV `syncCollection()` to reconcile changes.
549
-
550
- ```typescript
551
- let token: string | null = null;
552
-
553
- async function syncFiles() {
554
- const result = await space.webdav.syncCollection('/', {
555
- token,
556
- level: 'infinite',
557
- props: ['displayname', 'getetag', 'getlastmodified', 'resourcetype'],
558
- });
559
- token = result.token;
560
- updateFileTree(result.responses);
561
- }
562
-
563
- space.on('filesChanged', syncFiles);
564
- space.on('filesReset', () => {
565
- token = null;
566
- void syncFiles();
93
+ await machine.objects.create("/space/receipts/cafe.json", {
94
+ vendor: "Cafe",
95
+ amount: 12,
96
+ currency: "EUR",
97
+ document: receiptPath,
567
98
  });
568
-
569
- await syncFiles();
570
- ```
571
-
572
- | Method | Description |
573
- | --- | --- |
574
- | `webdav.href(path)` / `webdav.url(path)` | Return WebDAV href/URL for an absolute SDK path. |
575
- | `webdav.options(path)` | Send `OPTIONS`. |
576
- | `webdav.propfind(path, options)` | Read properties/list collections. `depth` is required. |
577
- | `webdav.syncCollection(path, options)` | WebDAV `REPORT sync-collection`; returns changed responses and next token. |
578
- | `webdav.get(path, options?)` / `webdav.head(path)` | Read a file; `get` supports byte ranges. |
579
- | `webdav.put(path, body, options?)` | Write a file/object at an exact path. Parent collection must exist unless `createParents: true` is passed, which creates missing parent collections atomically (intended for `/rool-drive` paths). |
580
- | `webdav.mkcol(path)` | Create one collection. |
581
- | `webdav.copy(source, destination, options?)` | Copy a file or collection. |
582
- | `webdav.move(source, destination, options?)` | Move a file or collection. |
583
- | `webdav.delete(path, options?)` | Delete a file or collection. |
584
- | `webdav.lock(path, options)` / `refreshLock(path, token)` / `unlock(token)` | WebDAV write locks. |
585
- | `webdav.request(method, path, init?)` | Raw authenticated WebDAV request. |
586
- | `space.fetchPath(path, options?)` | Fetch a `/rool-drive/...` file path or `rool-machine:` file URI. |
587
-
588
- High-level WebDAV methods that validate response status throw `WebDAVError` with `status`, `statusText`, and `body`; raw `request()` and `options()` return `Response`.
589
-
590
- ## Collaboration
591
-
592
- New members join a space by redeeming an invite. Owners and admins mint invites; the returned `url` contains the secret token and is only available at mint time.
593
-
594
- ```typescript
595
- // Shareable invite link
596
- const invite = await space.createInvite('editor', { expiresInDays: 7 });
597
- console.log(invite.url);
598
-
599
- // Email-guarded invite: single-use, locked to that address, sent by mail
600
- const emailed = await space.createInvite('viewer', { email: 'colleague@example.com' });
601
- if (emailed.emailStatus !== 'sent') {
602
- // Mail did not go out (e.g. no mail provider configured) — share emailed.url yourself
603
- }
604
-
605
- // Manage outstanding invites
606
- const invites = await space.listInvites();
607
- await space.revokeInvite(invites[0].inviteId);
608
-
609
- // Change an existing member's role, or remove them
610
- await space.setUserRole(userId, 'admin');
611
- await space.removeUser(userId);
612
- ```
613
-
614
- On the join page, look up the invite before sign-in and redeem it once authenticated:
615
-
616
- ```typescript
617
- const preview = await client.previewInvite(token); // no auth required
618
- console.log(preview.spaceName, preview.role, preview.inviterName);
619
-
620
- const result = await client.redeemInvite(token);
621
- console.log(result.spaceId, result.status); // 'joined' | 'upgraded' | 'already_member'
622
- ```
623
-
624
- Invalid, expired, revoked, exhausted, or email-mismatched invites throw `InviteError` with a `code` of `'INVITE_INVALID' | 'INVITE_EXPIRED' | 'INVITE_REVOKED' | 'INVITE_EXHAUSTED' | 'INVITE_EMAIL_MISMATCH'`.
625
-
626
- ```typescript
627
- import { InviteError } from '@rool-dev/sdk';
628
-
629
- try {
630
- await client.redeemInvite(token);
631
- } catch (error) {
632
- if (error instanceof InviteError && error.code === 'INVITE_EXPIRED') {
633
- // Ask for a fresh invite
634
- }
635
- }
636
- ```
637
-
638
- Roles (invites grant `admin`, `editor`, or `viewer` — never `owner`):
639
-
640
- | Role | Capabilities |
641
- | --- | --- |
642
- | `owner` | Full control. |
643
- | `admin` | Editor capabilities plus user/link management. |
644
- | `editor` | Create, modify, move, and delete objects/files. |
645
- | `viewer` | Read-only access. |
646
-
647
- ## Gifts
648
-
649
- A gift is something of value carried by a short code, e.g. `K7M2-9QRT`. Users are issued gifts server-side and give them away in their own words; whoever claims one gets what it holds. Claiming your own gift is a legitimate use. A gift is unclaimed or claimed, and claiming is single-use.
650
-
651
- What a gift grants is in its `gift` field, discriminated by `kind`. Every gift also carries a server-rendered `description` such as `"10,000 AI credits"`, so UI can display any gift without knowing its kind. Prefer `description` unless you need the numbers.
652
-
653
- Narrow on `kind` before reading any other payload field. Kinds are added over time, and un-narrowed access becomes a compile error when the next one lands.
654
-
655
- Users hold no gifts by default, so `listGifts()` returning an empty list is the normal state for most accounts, not an error.
656
-
657
- ```typescript
658
- // The current user's gifts, spent and unspent
659
- const { gifts } = await client.listGifts();
660
- for (const gift of gifts) {
661
- console.log(gift.code, gift.url, gift.description, gift.claimedAt);
662
- }
663
99
  ```
664
100
 
665
- On the claim page, look up the gift before sign-up and claim it once authenticated:
666
-
667
- ```typescript
668
- const preview = await client.previewGift(code); // no auth required
669
- console.log(`${preview.holderName} gave you ${preview.description}`);
101
+ The record is also a normal JSON file at `/space/receipts/cafe.json`. The app can work with it through `machine.objects`, while agents and programs inside the machine can use normal file tools. Changes from either side enter the same watched file tree.
670
102
 
671
- const { gift } = await client.claimGift(code);
672
- if (gift.kind === 'credits') console.log(`+${gift.credits} credits`);
673
- ```
103
+ Collections can power task lists, catalogues, contact records, or a memory view with links between people, projects, and notes. The app gets predictable fields for its UI without hiding the data from the agent.
674
104
 
675
- Codes are case-insensitive, and the dash is optional on input.
676
-
677
- When a gift is claimed, `claimedByName` carries the claimer's display name — the reciprocal of the claim page showing the holder's name to the claimer. The claim page tells the claimer this will happen.
678
-
679
- A holder can annotate a gift with a note ("sent this to Peter") and archive it to hide it from their default view. Both are bookkeeping only: an archived gift's code still claims.
680
-
681
- A holder can also rotate a gift's code: a fresh code is minted and the old one stops working. Use it to take back a code that was given away but never claimed — the gift itself is untouched.
682
-
683
- ```typescript
684
- await client.updateGift(gift.id, { note: 'sent to Peter' });
685
- await client.updateGift(gift.id, { archived: true });
686
- await client.updateGift(gift.id, { note: null }); // clear the note
687
-
688
- const updated = await client.rotateGiftCode(gift.id);
689
- console.log(updated.code, updated.url); // the old code and link are dead
690
- ```
105
+ ## What the SDK handles
691
106
 
692
- Failed gift operations throw `GiftError` with a `code`:
107
+ - **Machines:** create, configure, duplicate, checkpoint, share, and delete persistent VMs.
108
+ - **Files:** upload, stream, move, and watch files under `/space` and `/rool-drive`.
109
+ - **Agents:** define agents, keep conversation history, attach machine files, stream runs, and request structured output.
110
+ - **Live state:** learn when account data changes, keep a file tree current, and stream conversation updates.
111
+ - **Shared app data:** define records that apps, agents, and programs inside the machine can all read and edit.
693
112
 
694
- | Code | Meaning |
695
- | --- | --- |
696
- | `gift_invalid` | No gift matches that code. |
697
- | `gift_claimed` | The gift has already been claimed. |
113
+ Long-running work belongs to the machine rather than the client connection. File changes made by the app, a user, or an agent enter the same synchronized file tree, while account events tell the app when to fetch fresh account or machine data.
698
114
 
699
- ```typescript
700
- import { GiftError } from '@rool-dev/sdk';
701
-
702
- try {
703
- await client.claimGift(code);
704
- } catch (error) {
705
- if (error instanceof GiftError && error.code === 'gift_claimed') {
706
- // Offer a plain signup instead
707
- }
708
- }
709
- ```
710
-
711
- ## RoolClient API
712
-
713
- ### Constructor config
714
-
715
- ```typescript
716
- const client = new RoolClient({
717
- apiUrl: 'https://api.rool.dev',
718
- authUrl: 'https://rool.dev/auth',
719
- graphqlUrl: 'https://api.rool.dev/graphql',
720
- client: {
721
- appName: 'com.example.app',
722
- appVersion: '1.4.2',
723
- osVersion: 'iOS 17.5',
724
- },
725
- logger: console,
726
- });
727
- ```
728
-
729
- `apiUrl` defaults to `https://api.rool.dev`; `authUrl` is derived by stripping the `api.` hostname prefix unless provided. `baseUrl` is still accepted as a deprecated alias for `apiUrl`. Pass `authProvider` for Node.js, Electron, or custom auth flows.
730
-
731
- `client` is optional application identity sent on requests alongside the SDK package name/version. Compatibility is based only on the SDK version.
732
-
733
- | Method/property | Description |
734
- | --- | --- |
735
- | `currentUser: CurrentUser | null` | Cached user profile from initialization/fetch. |
736
- | `getCurrentUser(): Promise<CurrentUser>` | Fetch current user. |
737
- | `updateCurrentUser(input): Promise<CurrentUser>` | Update `name`, `slug`, or `marketingOptIn`. |
738
- | `deleteCurrentUser(): Promise<void>` | Mark account for deletion and log out. |
739
- | `previewInvite(token): Promise<InvitePreview>` | Look up an invite link without redeeming it. No auth required. |
740
- | `redeemInvite(token): Promise<InviteRedeemResult>` | Redeem an invite, joining (or upgrading in) its space. |
741
- | `previewGift(code): Promise<GiftPreview>` | Look up a gift without claiming it. No auth required. |
742
- | `claimGift(code): Promise<GiftClaimResult>` | Claim a gift, granting what it holds to the current account. |
743
- | `listGifts(): Promise<GiftList>` | The current user's gifts, spent and unspent. |
744
- | `updateGift(giftId, changes): Promise<Gift>` | Set or clear a gift's note, or change its archived state. |
745
- | `rotateGiftCode(giftId): Promise<Gift>` | Mint a new code for a gift; the old code and link stop working. |
746
- | `listSpaces(): Promise<RoolSpaceInfo[]>` | List accessible spaces. |
747
- | `openSpace(id): Promise<RoolSpace>` | Open/cached live space handle. |
748
- | `createSpace(name): Promise<RoolSpace>` | Create and open a space. |
749
- | `duplicateSpace(sourceId, name): Promise<RoolSpace>` | Duplicate a space. |
750
- | `deleteSpace(id): Promise<void>` | Permanently delete a space. |
751
- | `importArchive(name, archive): Promise<RoolSpace>` | Import a zip archive as a new space. |
752
- | `getUserStorage<T>(key): T | undefined` | Sync read from user-storage cache. |
753
- | `setUserStorage(key, value): void` | Update user storage; `null`/`undefined` deletes. |
754
- | `getAllUserStorage(): Record<string, unknown>` | Copy all cached user storage. |
755
- | `reportEvent(event, url?): void` | Fire-and-forget telemetry event. |
756
- | `destroy(): void` | Close subscriptions, spaces, auth resources, and listeners. |
757
- | `generateId(): string` | Generate a unique ID suitable for conversation IDs. |
758
-
759
- ### Client events
760
-
761
- ```typescript
762
- client.on('authStateChanged', (authenticated) => void 0);
763
- client.on('currentUserChanged', (user) => void 0); // CurrentUser | null; null on sign-out
764
- client.on('spaceAdded', (space) => void 0);
765
- client.on('spaceRemoved', (spaceId) => void 0);
766
- client.on('spaceRenamed', (spaceId, newName) => void 0);
767
- client.on('userStorageChanged', ({ key, value, source }) => void 0);
768
- client.on('connectionStateChanged', (state) => void 0);
769
- client.on('error', (error, context) => void 0);
770
- client.on('serverInfoChanged', (info) => void 0);
771
- client.on('unsupported', (info) => void 0); // SDK older than server minimum
772
- ```
773
-
774
- ## RoolSpace API
775
-
776
- Properties: `id`, `name`, `role`, `memberCount`, `openSpaceResult`, `route`, `webdav`.
777
-
778
- | Method | Description |
779
- | --- | --- |
780
- | `conversation(conversationId): ConversationHandle` | Get a stateless conversation-scoped handle. |
781
- | `getObject`, `getObjects` | Read object data. |
782
- | `listConversations`, `createConversation`, `deleteConversation` | Fetch, create, and delete conversations. |
783
- | `listAgents`, `deleteAgent` | List the space's agents and delete a custom agent. |
784
- | `readMeta`, `writeMeta`, `readSchema` | Read and write WebDAV-backed metadata and schema. |
785
- | `canUndo`, `canRedo`, `undo`, `redo` | Space history controls. |
786
- | `stopConversation(conversationId): Promise<boolean>` | Stop whatever is running in a conversation. |
787
- | `fetch(url, init?): Promise<Response>` | Proxy an external HTTP request through the server to bypass browser CORS. |
788
- | `close(): void` | Stop the space subscription. |
789
- | `rename(newName): Promise<void>` | Rename the space. |
790
- | `delete(): Promise<void>` | Permanently delete the space. |
791
- | `listUsers(): Promise<SpaceMember[]>` | List collaborators. |
792
- | `setUserRole(userId, role): Promise<void>` | Change an existing member's role. |
793
- | `removeUser(userId): Promise<void>` | Remove collaborator. |
794
- | `createInvite(role, options?): Promise<SpaceInviteCreated>` | Mint an invite link; `options` takes `email`, `expiresInDays`, `maxUses`. |
795
- | `listInvites(): Promise<SpaceInvite[]>` | List currently redeemable invites. |
796
- | `revokeInvite(inviteId): Promise<boolean>` | Revoke an invite so its link stops working. |
797
- | `exportArchive(): Promise<Blob>` | Export a space archive. |
798
- | `refresh(): Promise<OpenSpaceResult>` | Refresh identity, access, member count, and conversation metadata. |
799
- | `fetchPath(path, options?): Promise<Response>` | Fetch a `/rool-drive/...` file. |
800
-
801
- Events:
802
-
803
- ```typescript
804
- space.on('conversationUpdated', ({ conversationId, conversation, source, timestamp }) => void 0);
805
- space.on('syncError', (error) => void 0);
806
- space.on('filesChanged', ({ spaceId, source, timestamp }) => void 0);
807
- space.on('filesReset', ({ spaceId, source, timestamp }) => void 0);
808
- space.on('connectionStateChanged', (state) => void 0);
809
- ```
810
- ## Import/Export
811
-
812
- ```typescript
813
- const archive = await space.exportArchive();
814
- const imported = await client.importArchive('Imported Data', archive);
815
- ```
816
-
817
- Archives include objects, metadata, conversations, and file storage.
818
-
819
- ## Data Types
820
-
821
- ```typescript
822
- // Outcome of signInWithPassword. 'verify_required' means the account's email
823
- // isn't verified yet and the server has emailed a magic link.
824
- type PasswordSignInResult = { status: 'signed_in' | 'verify_required' };
825
-
826
- type FieldType =
827
- | { kind: 'string' }
828
- | { kind: 'number' }
829
- | { kind: 'boolean' }
830
- | { kind: 'array'; inner?: FieldType }
831
- | { kind: 'maybe'; inner: FieldType }
832
- | { kind: 'enum'; values: string[] }
833
- | { kind: 'literal'; value: string | number | boolean }
834
- | { kind: 'ref' };
835
-
836
- interface FieldDef {
837
- name: string;
838
- type: FieldType;
839
- }
840
-
841
- interface CollectionDef {
842
- fields: FieldDef[];
843
- schemaOrgType?: string;
844
- }
845
-
846
- type SpaceSchema = Record<string, CollectionDef>;
847
-
848
- interface RoolObject {
849
- path: string;
850
- body: Record<string, unknown>;
851
- }
852
-
853
- interface GetObjectsResult {
854
- objects: RoolObject[];
855
- missing: string[];
856
- }
857
-
858
- // Who may see a conversation: 'shared' is every space member; 'private' is
859
- // owner-only; 'temporary' is private plus auto-delete once it sits idle.
860
- type ConversationVisibility = 'shared' | 'private' | 'temporary';
861
-
862
- // Lightweight conversation roster entry (no interaction bodies), returned by
863
- // openSpace/listConversations. `updatedAt` drives last-activity display.
864
- interface ConversationMeta {
865
- id: string;
866
- agent: string; // owning agent ('rool' is the stock agent)
867
- visibility: ConversationVisibility;
868
- name: string | null;
869
- systemInstruction: string | null;
870
- createdAt: number;
871
- createdBy: string;
872
- interactionCount: number;
873
- updatedAt: number;
874
- }
875
-
876
- type InviteRole = 'admin' | 'editor' | 'viewer';
877
-
878
- interface SpaceInvite {
879
- inviteId: string;
880
- spaceId: string;
881
- role: InviteRole;
882
- email: string | null;
883
- createdBy: string;
884
- createdAt: string;
885
- expiresAt: string;
886
- maxUses: number | null;
887
- useCount: number;
888
- }
889
-
890
- // Outcome of the invite email send. Null when no email was involved (open link).
891
- // The invite is always minted and its `url` is usable regardless of this value;
892
- // only the email delivery is reflected here.
893
- // - 'sent': email dispatched
894
- // - 'not_configured': server has no mail provider (local dev)
895
- // - 'failed': provider rejected the send
896
- // - 'cooldown': a recent invite to this same address was already emailed
897
- // - 'rate_limited': the inviter hit their daily email-invite cap
898
- // Treat unknown values as not sent.
899
- type InviteEmailStatus =
900
- | 'sent'
901
- | 'not_configured'
902
- | 'failed'
903
- | 'cooldown'
904
- | 'rate_limited'
905
- | (string & {});
906
-
907
- interface SpaceInviteCreated {
908
- inviteId: string;
909
- spaceId: string;
910
- role: InviteRole;
911
- email: string | null;
912
- expiresAt: string;
913
- maxUses: number | null;
914
- url: string; // contains the secret token; only available at mint time
915
- emailStatus: InviteEmailStatus | null;
916
- }
917
-
918
- interface InvitePreview {
919
- spaceId: string;
920
- spaceName: string;
921
- role: InviteRole;
922
- email: string | null;
923
- inviterName: string | null;
924
- }
925
-
926
- interface InviteRedeemResult {
927
- spaceId: string;
928
- role: RoolUserRole;
929
- status: 'joined' | 'upgraded' | 'already_member';
930
- }
931
-
932
- type GiftPayload = { kind: 'credits'; credits: number };
933
-
934
- interface Gift {
935
- id: string;
936
- code: string; // display form, e.g. "K7M2-9QRT"
937
- url: string; // claim URL carrying the code
938
- gift: GiftPayload;
939
- description: string; // e.g. "10,000 AI credits"
940
- claimedAt: string | null; // null while unspent
941
- claimedByName: string | null; // claimer's display name, when they have one
942
- createdAt: string;
943
- note: string | null; // the holder's own reminder
944
- archivedAt: string | null; // set when hidden; the code still claims
945
- }
946
-
947
- interface GiftUpdate {
948
- note?: string | null; // undefined leaves as-is, null clears
949
- archived?: boolean;
950
- }
951
-
952
- interface GiftList {
953
- gifts: Gift[];
954
- }
955
-
956
- interface GiftPreview {
957
- holderName: string | null;
958
- gift: GiftPayload;
959
- description: string;
960
- }
961
-
962
- interface GiftClaimResult {
963
- gift: GiftPayload;
964
- description: string;
965
- }
966
-
967
- type PromptAttachment =
968
- | File
969
- | Blob
970
- | { data: string; contentType: string; filename?: string }
971
- | string;
972
-
973
- type PromptEffort = 'QUICK' | 'STANDARD' | 'REASONING' | 'RESEARCH';
974
-
975
- interface PromptOptions {
976
- responseSchema?: Record<string, unknown>;
977
- effort?: PromptEffort;
978
- parentInteractionId?: string | null;
979
- ephemeral?: boolean;
980
- readOnly?: boolean;
981
- attachments?: PromptAttachment[];
982
- signal?: AbortSignal;
983
- eventName?: string;
984
- }
985
-
986
- type InteractionStatus = 'pending' | 'streaming' | 'done' | 'error';
987
-
988
- interface Interaction {
989
- id: string;
990
- parentId: string | null;
991
- timestamp: number;
992
- userId: string;
993
- userName?: string | null;
994
- operation: 'prompt' | 'putObject' | 'patchObject' | 'moveObject' | 'deleteObjects' | 'deletePaths' | string;
995
- input: string;
996
- output: string | null;
997
- status: InteractionStatus;
998
- ai: boolean;
999
- modifiedObjectPaths: string[];
1000
- toolCalls: ToolCall[];
1001
- attachments?: string[];
1002
- }
1003
- ```
115
+ The package exports its public TypeScript types, so editor autocomplete shows the detailed options and results. See [docs.rool.dev](https://docs.rool.dev/) for the published documentation.
1004
116
 
1005
117
  ## License
1006
118
 
1007
- MIT - see [LICENSE](../../LICENSE) for details.
119
+ MIT see [LICENSE](./LICENSE).