@rool-dev/sdk 2.0.0-dev.4adc2c2 → 2.0.0-dev.66fd5c8
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 +57 -21
- package/dist/agents.d.ts +21 -8
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +263 -28
- package/dist/agents.js.map +1 -1
- package/dist/client.d.ts +3 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +75 -17
- package/dist/client.js.map +1 -1
- package/dist/files.d.ts +9 -3
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +13 -2
- package/dist/files.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/machine.d.ts +4 -2
- package/dist/machine.d.ts.map +1 -1
- package/dist/machine.js +1 -1
- package/dist/machine.js.map +1 -1
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -201,6 +201,8 @@ const path = "/rool-drive/documents/report.pdf";
|
|
|
201
201
|
|
|
202
202
|
const storage = await files.getStorageUsage();
|
|
203
203
|
console.log(storage.usedBytes, storage.availableBytes);
|
|
204
|
+
const { maxUploadBytes } = await files.options(path);
|
|
205
|
+
console.log(maxUploadBytes);
|
|
204
206
|
await files.createDirectory("/rool-drive/documents");
|
|
205
207
|
const written = await files.write(path, reportBlob, {
|
|
206
208
|
contentType: "application/pdf",
|
|
@@ -209,6 +211,11 @@ const written = await files.write(path, reportBlob, {
|
|
|
209
211
|
if (totalBytes) renderUploadProgress(transferredBytes / totalBytes);
|
|
210
212
|
},
|
|
211
213
|
});
|
|
214
|
+
await files.write(
|
|
215
|
+
"/rool-drive/documents/archive.bin",
|
|
216
|
+
() => createArchiveStream(),
|
|
217
|
+
{ contentType: "application/octet-stream" },
|
|
218
|
+
);
|
|
212
219
|
const info = await files.stat(path);
|
|
213
220
|
const documents = await files.list("/rool-drive/documents");
|
|
214
221
|
const response = await files.read(path, {
|
|
@@ -234,7 +241,7 @@ for (const result of deleted) {
|
|
|
234
241
|
}
|
|
235
242
|
```
|
|
236
243
|
|
|
237
|
-
Paths are absolute machine paths under `/space` or `/rool-drive`. `list()` without a path enumerates those storage roots; pass `{ recursive: true }` to enumerate a complete subtree. File and directory metadata has a discriminating `kind` field. Reads return the native `Response` so callers can stream the body. `readMultiple()` hydrates ordered small files in one request and returns an `ok` result with binary-safe bytes and validators, or a per-file HTTP failure. A batch accepts at most 128 paths, 2 MiB per successful file, and 16 MiB across successful files. Writes accept any `BodyInit`, including `Blob` and `ReadableStream`, and return the same complete file metadata as `stat()` and `list()`. `onUploadProgress` reports transferred bytes and includes the total size when it is known; successful completion is confirmed by the `write()` promise. Copy and move operations overwrite by default; pass `{ overwrite: false }` for create-only behavior.
|
|
244
|
+
Paths are absolute machine paths under `/space` or `/rool-drive`. `list()` without a path enumerates those storage roots; pass `{ recursive: true }` to enumerate a complete subtree. File and directory metadata has a discriminating `kind` field. Reads return the native `Response` so callers can stream the body. `readMultiple()` hydrates ordered small files in one request and returns an `ok` result with binary-safe bytes and validators, or a per-file HTTP failure. A batch accepts at most 128 paths, 2 MiB per successful file, and 16 MiB across successful files. Writes accept any `BodyInit`, including `Blob` and `ReadableStream`, and return the same complete file metadata as `stat()` and `list()`. Pass a function that creates a fresh `ReadableStream` when an upload must be replayable after a machine route change; a directly passed stream remains one-shot. `onUploadProgress` reports transferred bytes and includes the total size when it is known; successful completion is confirmed by the `write()` promise. `options(path)` reports `maxUploadBytes` for that path's storage root. Calling `options()` without a path reports whole-DAV capabilities and returns `null` for `maxUploadBytes` because `/space` and `/rool-drive` have different limits. Copy and move operations overwrite by default; pass `{ overwrite: false }` for create-only behavior.
|
|
238
245
|
|
|
239
246
|
`deleteMultiple()` sends independent DAV requests with at most eight in flight. It accepts plain paths and targets carrying their own HTTP preconditions, and returns one ordered success or failure result per target. The requests are not atomic. A directory target recursively deletes its contents, so callers should omit redundant descendants; duplicate and overlapping targets otherwise remain independent and can race.
|
|
240
247
|
|
|
@@ -313,30 +320,29 @@ const defaultAgent = await machine.agents.get("rool");
|
|
|
313
320
|
if (!defaultAgent) throw new Error("Rool agent is unavailable");
|
|
314
321
|
|
|
315
322
|
const conversation = defaultAgent.conversation("research-chat");
|
|
323
|
+
const stopWatching = conversation.watch((view) => {
|
|
324
|
+
renderConversation({
|
|
325
|
+
turns: view.turns,
|
|
326
|
+
output: view.output,
|
|
327
|
+
isRunning: view.isRunning,
|
|
328
|
+
loading: view.loading,
|
|
329
|
+
error: view.error,
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
316
333
|
await conversation.prompt("Explain the result.", { effort: "reasoning" });
|
|
317
334
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
await conversation.follow({
|
|
321
|
-
onEvent: (event) => {
|
|
322
|
-
if (event.type !== "output.delta") return;
|
|
323
|
-
if (event.content.type === "text") {
|
|
324
|
-
renderUnsettledText(event.content.text);
|
|
325
|
-
} else if (event.content.type === "tool_call") {
|
|
326
|
-
showRunningTool(event.content.id, event.content.name);
|
|
327
|
-
} else if (event.content.type === "tool_result") {
|
|
328
|
-
showToolResult(event.content.id, event.content.content);
|
|
329
|
-
}
|
|
330
|
-
},
|
|
331
|
-
});
|
|
332
|
-
renderSettled(await conversation.listTurns());
|
|
335
|
+
// When this conversation leaves the UI:
|
|
336
|
+
stopWatching();
|
|
333
337
|
```
|
|
334
338
|
|
|
335
339
|
`prompt()` starts the conversation's current run and resolves once the server accepts it. The agent runs as a detached job. A conversation can only have one current run; call `cancel()` and wait for `follow()` to finish before prompting again. The `readOnly` option is accepted for compatibility with legacy prompting but currently has no effect.
|
|
336
340
|
|
|
337
|
-
`
|
|
341
|
+
`watch()` is the normal UI API. It fetches only turns after the last turn it has seen, follows the current run, and refreshes the durable turns when that stream ends. `turns` contains saved history through the current user message while a run is active; `output` contains that run's replayed and live output. The first listener starts the work and removing the last listener stops it. Saved turns remain cached on the conversation handle for the next listener.
|
|
342
|
+
|
|
343
|
+
`follow()` is the lower-level streaming API. It performs one `GET` of the conversation's current run. It receives the complete current-run output and then continues with new events until that response ends. Tool calls and their results arrive as `output.delta` events with matching IDs. A tool result contains nested content parts and an optional `error` flag. `follow()` returns `false` when there is no current run. Aborting `follow()` only stops that request; call `cancel()` to stop the detached job.
|
|
338
344
|
|
|
339
|
-
|
|
345
|
+
The SDK uses `conversation_changed` account events to wake active watchers. A watcher also refreshes after prompting, cancellation, stream completion, connection failure, and account event-token replacement.
|
|
340
346
|
|
|
341
347
|
Prompt attachments are existing `/space` or `/rool-drive` paths. Pass a durable user turn's `id` as `replaceTurnId` to replace that message and everything after it. This supports edits and rerolls, including the first message. A replacement gets a new user turn ID; use that ID to edit it again.
|
|
342
348
|
|
|
@@ -354,12 +360,12 @@ await conversation.prompt("Return the number of records.", {
|
|
|
354
360
|
await conversation.follow();
|
|
355
361
|
|
|
356
362
|
const turns = await conversation.listTurns();
|
|
357
|
-
const part = turns.at(-1)?.
|
|
363
|
+
const part = turns.at(-1)?.content[0];
|
|
358
364
|
if (part?.type !== "json") throw new Error("No structured result");
|
|
359
365
|
console.log(part.value); // { count: ... }
|
|
360
366
|
```
|
|
361
367
|
|
|
362
|
-
|
|
368
|
+
A custom agent's `system` field contains instructions added after Rool's built-in machine context. Each conversation can add another instruction layer without changing the agent or its metadata.
|
|
363
369
|
|
|
364
370
|
```typescript
|
|
365
371
|
const researcher = await machine.agents.create("researcher", {
|
|
@@ -369,10 +375,15 @@ const customConversation = await researcher.createConversation({
|
|
|
369
375
|
name: "Climate report",
|
|
370
376
|
visibility: "private",
|
|
371
377
|
});
|
|
378
|
+
await customConversation.replaceInstructions(
|
|
379
|
+
"For this conversation, compare at least two sources.",
|
|
380
|
+
);
|
|
372
381
|
await customConversation.prompt("Investigate this claim.");
|
|
373
382
|
```
|
|
374
383
|
|
|
375
|
-
|
|
384
|
+
`getInstructions()` returns the conversation instructions. `replaceInstructions("")` clears them. Metadata changes do not affect them. New instructions apply to the next run; a run already in progress keeps the instructions it started with.
|
|
385
|
+
|
|
386
|
+
Agents expose `replace()` and `delete()`. Conversations expose instruction and metadata replacement, listing, durable turn reads, rename, and deletion. Listed and fetched conversation metadata includes server-managed ISO 8601 `createdAt` and `updatedAt` timestamps plus `isRunning`, which can drive a running indicator without opening every run stream. Visibility defaults to private. The built-in `rool` agent cannot be replaced or deleted.
|
|
376
387
|
|
|
377
388
|
## Members and invites
|
|
378
389
|
|
|
@@ -427,6 +438,19 @@ const updated = await client.rotateGiftCode(giftId);
|
|
|
427
438
|
|
|
428
439
|
Gift failures are `RoolProblem` errors. `gift_invalid` means the code or gift is unavailable to the caller. `gift_claimed` means it was already claimed.
|
|
429
440
|
|
|
441
|
+
## Speechmatics
|
|
442
|
+
|
|
443
|
+
Voice input transcribes the user's speech with Speechmatics' real-time API. Rool mints a short-lived key without exposing its long-lived provider key; hand the temporary key to the Speechmatics real-time SDK and stream the user's mic audio to it.
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
const { token, expiresAt, ttl } = await client.getSpeechmaticsToken({
|
|
447
|
+
ttl: 300,
|
|
448
|
+
});
|
|
449
|
+
// speechmaticsRealtimeClient.start(token, { transcription_config: { language: "en" } })
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
The key expires after `ttl` seconds (60–3600, default 300). `expiresAt` is the epoch-milliseconds moment the key stops being accepted. A token request fails with `insufficient_credits` when the account's balance is too low and with `speechmatics_unavailable` when Speechmatics cannot issue a key.
|
|
453
|
+
|
|
430
454
|
## API problems
|
|
431
455
|
|
|
432
456
|
Each problem `type` links to its entry below.
|
|
@@ -611,6 +635,18 @@ The gift has already been claimed. A claimed gift cannot be claimed again or giv
|
|
|
611
635
|
|
|
612
636
|
The gift update is empty or contains an invalid note or archived value.
|
|
613
637
|
|
|
638
|
+
<a id="problem-insufficient_credits"></a>
|
|
639
|
+
|
|
640
|
+
### `insufficient_credits`
|
|
641
|
+
|
|
642
|
+
The account's credit balance is too low to mint a speech transcription key. Top up the balance and try again.
|
|
643
|
+
|
|
644
|
+
<a id="problem-speechmatics_unavailable"></a>
|
|
645
|
+
|
|
646
|
+
### `speechmatics_unavailable`
|
|
647
|
+
|
|
648
|
+
Speechmatics could not issue a transcription key right now. Try again shortly.
|
|
649
|
+
|
|
614
650
|
## Development
|
|
615
651
|
|
|
616
652
|
```bash
|
package/dist/agents.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RoolClientEvent } from "./events.js";
|
|
1
2
|
import type { MachineFilePath } from "./files.js";
|
|
2
3
|
export type MachinePromptEffort = "quick" | "standard" | "reasoning" | "research";
|
|
3
4
|
export type MachineAssistantFinish = "stop" | "tool_calls" | "length" | "safety" | "error" | "credits" | "cancelled";
|
|
@@ -24,7 +25,10 @@ export type MachineConversationContentPart = {
|
|
|
24
25
|
type: "json";
|
|
25
26
|
value: unknown;
|
|
26
27
|
};
|
|
27
|
-
export interface
|
|
28
|
+
export interface MachineConversationTurn {
|
|
29
|
+
id: string;
|
|
30
|
+
userId?: string;
|
|
31
|
+
createdAt: string;
|
|
28
32
|
role: MachineConversationTurnRole;
|
|
29
33
|
content: MachineConversationContentPart[];
|
|
30
34
|
request?: Record<string, unknown>;
|
|
@@ -36,13 +40,8 @@ export interface MachineConversationTurnBody {
|
|
|
36
40
|
};
|
|
37
41
|
aside?: boolean;
|
|
38
42
|
}
|
|
39
|
-
export interface MachineConversationTurn {
|
|
40
|
-
id: string;
|
|
41
|
-
userId?: string;
|
|
42
|
-
createdAt: string;
|
|
43
|
-
body: MachineConversationTurnBody;
|
|
44
|
-
}
|
|
45
43
|
export interface MachineAgentDefinition {
|
|
44
|
+
/** Instructions added after Rool's built-in machine context. */
|
|
46
45
|
system: string;
|
|
47
46
|
}
|
|
48
47
|
export type MachineAgentCreateInput = MachineAgentDefinition;
|
|
@@ -95,12 +94,25 @@ export interface MachineConversationFollowOptions extends AgentRequestOptions {
|
|
|
95
94
|
onEvent?: (event: MachineRunEvent) => void;
|
|
96
95
|
}
|
|
97
96
|
export type MachineConversationCancelOptions = AgentRequestOptions;
|
|
97
|
+
export interface MachineConversationView {
|
|
98
|
+
readonly turns: readonly MachineConversationTurn[];
|
|
99
|
+
readonly output: readonly MachineConversationContentPart[];
|
|
100
|
+
readonly isRunning: boolean;
|
|
101
|
+
readonly loading: boolean;
|
|
102
|
+
readonly error: unknown;
|
|
103
|
+
}
|
|
104
|
+
export type MachineConversationListener = (view: MachineConversationView) => void;
|
|
98
105
|
export interface MachineConversation {
|
|
99
106
|
readonly id: string;
|
|
100
107
|
readonly agent: MachineAgent;
|
|
101
108
|
get(options?: AgentRequestOptions): Promise<MachineConversationState | undefined>;
|
|
102
109
|
listTurns(options?: AgentRequestOptions): Promise<MachineConversationTurn[]>;
|
|
110
|
+
watch(listener: MachineConversationListener): () => void;
|
|
103
111
|
replace(metadata: MachineConversationMetadataInput, options?: AgentRequestOptions): Promise<MachineConversationMetadata>;
|
|
112
|
+
/** Return conversation-specific instructions; an empty string means none. */
|
|
113
|
+
getInstructions(options?: AgentRequestOptions): Promise<string>;
|
|
114
|
+
/** Replace conversation-specific instructions; pass an empty string to clear. */
|
|
115
|
+
replaceInstructions(instructions: string, options?: AgentRequestOptions): Promise<string>;
|
|
104
116
|
prompt(text: string, options?: MachineConversationPromptOptions): Promise<void>;
|
|
105
117
|
follow(options?: MachineConversationFollowOptions): Promise<boolean>;
|
|
106
118
|
cancel(options?: MachineConversationCancelOptions): Promise<boolean>;
|
|
@@ -124,7 +136,8 @@ export interface MachineAgents {
|
|
|
124
136
|
interface AgentTransport {
|
|
125
137
|
request(path: string, init?: RequestInit, allowHttpErrors?: boolean): Promise<Response>;
|
|
126
138
|
requestJson<T>(path: string, init?: RequestInit): Promise<T>;
|
|
139
|
+
subscribeEvents(listener: (event: RoolClientEvent) => void): () => void;
|
|
127
140
|
}
|
|
128
|
-
export declare function createMachineAgents(machinePath: string, transport: AgentTransport): MachineAgents;
|
|
141
|
+
export declare function createMachineAgents(machineId: string, machinePath: string, transport: AgentTransport): MachineAgents;
|
|
129
142
|
export {};
|
|
130
143
|
//# sourceMappingURL=agents.d.ts.map
|
package/dist/agents.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD,MAAM,MAAM,mBAAmB,GAC7B,OAAO,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,CAAC;AAClD,MAAM,MAAM,sBAAsB,GAC9B,MAAM,GACN,YAAY,GACZ,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,SAAS,GACT,WAAW,CAAC;AAChB,MAAM,MAAM,2BAA2B,GACrC,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,CAAC;AAC3C,MAAM,MAAM,6BAA6B,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEjE,MAAM,MAAM,8BAA8B,GACtC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5D;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GACD;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,8BAA8B,EAAE,CAAC;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GACD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAErC,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"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"}
|
package/dist/agents.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { throwProblemResponse } from "./problem.js";
|
|
2
2
|
const VALID_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
3
3
|
const MAX_EVENT_BYTES = 1024 * 1024;
|
|
4
|
+
const WATCH_RETRY_MAX_MS = 5_000;
|
|
4
5
|
class MachineAgentState {
|
|
6
|
+
machineId;
|
|
5
7
|
machinePath;
|
|
6
8
|
transport;
|
|
7
9
|
agents = new Map();
|
|
8
|
-
constructor(machinePath, transport) {
|
|
10
|
+
constructor(machineId, machinePath, transport) {
|
|
11
|
+
this.machineId = machineId;
|
|
9
12
|
this.machinePath = machinePath;
|
|
10
13
|
this.transport = transport;
|
|
11
14
|
}
|
|
@@ -73,17 +76,32 @@ class MachineAgentState {
|
|
|
73
76
|
if (!response.ok)
|
|
74
77
|
await throwProblemResponse(response);
|
|
75
78
|
const metadata = (await response.json());
|
|
76
|
-
const
|
|
77
|
-
return { ...metadata, turns };
|
|
79
|
+
const update = await this.readTurnUpdate(agentId, conversationId, undefined, options.signal);
|
|
80
|
+
return { ...metadata, isRunning: update.isRunning, turns: update.turns };
|
|
78
81
|
}
|
|
79
82
|
async listTurns(agentId, conversationId, options = {}) {
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
return (await this.readTurnUpdate(agentId, conversationId, undefined, options.signal)).turns;
|
|
84
|
+
}
|
|
85
|
+
async readTurnUpdate(agentId, conversationId, after, signal) {
|
|
86
|
+
const query = after ? `?${new URLSearchParams({ after }).toString()}` : "";
|
|
87
|
+
return this.transport.requestJson(`${this.conversationPath(agentId, conversationId)}/turns${query}`, { signal });
|
|
88
|
+
}
|
|
89
|
+
subscribeEvents(listener) {
|
|
90
|
+
return this.transport.subscribeEvents(listener);
|
|
82
91
|
}
|
|
83
92
|
replaceConversation(agentId, conversationId, metadata, options = {}) {
|
|
84
93
|
validateMetadata(metadata);
|
|
85
94
|
return this.transport.requestJson(this.conversationPath(agentId, conversationId), jsonRequest("PUT", metadata, options.signal));
|
|
86
95
|
}
|
|
96
|
+
async getConversationInstructions(agentId, conversationId, options = {}) {
|
|
97
|
+
const result = await this.transport.requestJson(`${this.conversationPath(agentId, conversationId)}/instructions`, { signal: options.signal });
|
|
98
|
+
return result.instructions;
|
|
99
|
+
}
|
|
100
|
+
async replaceConversationInstructions(agentId, conversationId, instructions, options = {}) {
|
|
101
|
+
validateInstructions(instructions);
|
|
102
|
+
const result = await this.transport.requestJson(`${this.conversationPath(agentId, conversationId)}/instructions`, jsonRequest("PUT", { instructions }, options.signal));
|
|
103
|
+
return result.instructions;
|
|
104
|
+
}
|
|
87
105
|
async deleteConversation(agent, conversationId, options = {}) {
|
|
88
106
|
await this.transport.request(this.conversationPath(agent.id, conversationId), { method: "DELETE", signal: options.signal });
|
|
89
107
|
agent.forgetConversation(conversationId);
|
|
@@ -230,6 +248,21 @@ class ConversationClient {
|
|
|
230
248
|
agent;
|
|
231
249
|
id;
|
|
232
250
|
state;
|
|
251
|
+
listeners = new Set();
|
|
252
|
+
turns = [];
|
|
253
|
+
output = [];
|
|
254
|
+
isRunning = false;
|
|
255
|
+
loading = true;
|
|
256
|
+
error = null;
|
|
257
|
+
watchController = null;
|
|
258
|
+
unsubscribeEvents = null;
|
|
259
|
+
syncRequested = false;
|
|
260
|
+
syncLoop = null;
|
|
261
|
+
retryTimer = null;
|
|
262
|
+
retryMs = 250;
|
|
263
|
+
followController = null;
|
|
264
|
+
followTask = null;
|
|
265
|
+
followRetryMs = 250;
|
|
233
266
|
constructor(agent, id, state) {
|
|
234
267
|
this.agent = agent;
|
|
235
268
|
this.id = id;
|
|
@@ -241,17 +274,44 @@ class ConversationClient {
|
|
|
241
274
|
listTurns(options = {}) {
|
|
242
275
|
return this.state.listTurns(this.agent.id, this.id, options);
|
|
243
276
|
}
|
|
277
|
+
watch(listener) {
|
|
278
|
+
this.listeners.add(listener);
|
|
279
|
+
listener(this.view());
|
|
280
|
+
if (!this.watchController)
|
|
281
|
+
this.startWatching();
|
|
282
|
+
return () => {
|
|
283
|
+
this.listeners.delete(listener);
|
|
284
|
+
if (this.listeners.size === 0)
|
|
285
|
+
this.stopWatching();
|
|
286
|
+
};
|
|
287
|
+
}
|
|
244
288
|
replace(metadata, options) {
|
|
245
289
|
return this.state.replaceConversation(this.agent.id, this.id, metadata, options);
|
|
246
290
|
}
|
|
247
|
-
|
|
248
|
-
return this.state.
|
|
291
|
+
getInstructions(options) {
|
|
292
|
+
return this.state.getConversationInstructions(this.agent.id, this.id, options);
|
|
293
|
+
}
|
|
294
|
+
replaceInstructions(instructions, options) {
|
|
295
|
+
return this.state.replaceConversationInstructions(this.agent.id, this.id, instructions, options);
|
|
296
|
+
}
|
|
297
|
+
async prompt(text, options = {}) {
|
|
298
|
+
try {
|
|
299
|
+
await this.state.prompt(this.agent.id, this.id, text, options);
|
|
300
|
+
}
|
|
301
|
+
finally {
|
|
302
|
+
this.requestSync();
|
|
303
|
+
}
|
|
249
304
|
}
|
|
250
305
|
follow(options) {
|
|
251
306
|
return this.state.follow(this.agent.id, this.id, options);
|
|
252
307
|
}
|
|
253
|
-
cancel(options) {
|
|
254
|
-
|
|
308
|
+
async cancel(options) {
|
|
309
|
+
try {
|
|
310
|
+
return await this.state.cancel(this.agent.id, this.id, options);
|
|
311
|
+
}
|
|
312
|
+
finally {
|
|
313
|
+
this.requestSync();
|
|
314
|
+
}
|
|
255
315
|
}
|
|
256
316
|
async rename(name, options = {}) {
|
|
257
317
|
const current = await this.get(options);
|
|
@@ -265,25 +325,178 @@ class ConversationClient {
|
|
|
265
325
|
delete(options) {
|
|
266
326
|
return this.state.deleteConversation(this.agent, this.id, options);
|
|
267
327
|
}
|
|
328
|
+
startWatching() {
|
|
329
|
+
const controller = new AbortController();
|
|
330
|
+
this.watchController = controller;
|
|
331
|
+
this.unsubscribeEvents = this.state.subscribeEvents((event) => {
|
|
332
|
+
const matchesConversation = event.type === "conversation_changed" &&
|
|
333
|
+
event.machineId === this.state.machineId &&
|
|
334
|
+
event.agentId === this.agent.id &&
|
|
335
|
+
event.conversationId === this.id;
|
|
336
|
+
if (event.type === "session" || matchesConversation) {
|
|
337
|
+
if (!this.followTask || event.type === "session")
|
|
338
|
+
this.requestSync();
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
this.requestSync();
|
|
342
|
+
}
|
|
343
|
+
stopWatching() {
|
|
344
|
+
this.watchController?.abort();
|
|
345
|
+
this.watchController = null;
|
|
346
|
+
this.followController?.abort();
|
|
347
|
+
this.followController = null;
|
|
348
|
+
this.unsubscribeEvents?.();
|
|
349
|
+
this.unsubscribeEvents = null;
|
|
350
|
+
this.syncRequested = false;
|
|
351
|
+
if (this.retryTimer)
|
|
352
|
+
clearTimeout(this.retryTimer);
|
|
353
|
+
this.retryTimer = null;
|
|
354
|
+
}
|
|
355
|
+
requestSync() {
|
|
356
|
+
const controller = this.watchController;
|
|
357
|
+
if (!controller || controller.signal.aborted)
|
|
358
|
+
return;
|
|
359
|
+
this.syncRequested = true;
|
|
360
|
+
if (this.retryTimer)
|
|
361
|
+
clearTimeout(this.retryTimer);
|
|
362
|
+
this.retryTimer = null;
|
|
363
|
+
if (this.syncLoop)
|
|
364
|
+
return;
|
|
365
|
+
const loop = this.runSyncLoop(controller);
|
|
366
|
+
this.syncLoop = loop;
|
|
367
|
+
const finished = () => {
|
|
368
|
+
if (this.syncLoop === loop)
|
|
369
|
+
this.syncLoop = null;
|
|
370
|
+
const current = this.watchController;
|
|
371
|
+
if (current && this.syncRequested && !current.signal.aborted) {
|
|
372
|
+
this.requestSync();
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
void loop.then(finished, finished);
|
|
376
|
+
}
|
|
377
|
+
async runSyncLoop(controller) {
|
|
378
|
+
while (this.watchController === controller &&
|
|
379
|
+
!controller.signal.aborted &&
|
|
380
|
+
this.syncRequested) {
|
|
381
|
+
this.syncRequested = false;
|
|
382
|
+
try {
|
|
383
|
+
const after = this.turns.at(-1)?.id;
|
|
384
|
+
const update = await this.state.readTurnUpdate(this.agent.id, this.id, after, controller.signal);
|
|
385
|
+
if (controller.signal.aborted)
|
|
386
|
+
return;
|
|
387
|
+
this.turns = update.reset
|
|
388
|
+
? update.turns
|
|
389
|
+
: [...this.turns, ...update.turns];
|
|
390
|
+
this.isRunning = update.isRunning;
|
|
391
|
+
this.loading = false;
|
|
392
|
+
this.error = null;
|
|
393
|
+
this.retryMs = 250;
|
|
394
|
+
if (!this.isRunning || (update.reset && this.followTask)) {
|
|
395
|
+
this.followController?.abort();
|
|
396
|
+
this.output = [];
|
|
397
|
+
}
|
|
398
|
+
this.emit();
|
|
399
|
+
if (this.isRunning)
|
|
400
|
+
this.ensureFollow(controller);
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
if (controller.signal.aborted || isAbortError(error))
|
|
404
|
+
return;
|
|
405
|
+
this.loading = false;
|
|
406
|
+
this.error = error;
|
|
407
|
+
this.emit();
|
|
408
|
+
this.scheduleRetry(controller);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
scheduleRetry(controller) {
|
|
414
|
+
if (this.retryTimer || this.watchController !== controller)
|
|
415
|
+
return;
|
|
416
|
+
const delay = this.retryMs;
|
|
417
|
+
this.retryMs = Math.min(this.retryMs * 2, WATCH_RETRY_MAX_MS);
|
|
418
|
+
this.retryTimer = setTimeout(() => {
|
|
419
|
+
this.retryTimer = null;
|
|
420
|
+
this.requestSync();
|
|
421
|
+
}, delay);
|
|
422
|
+
}
|
|
423
|
+
ensureFollow(watchController) {
|
|
424
|
+
if (this.followTask || this.watchController !== watchController)
|
|
425
|
+
return;
|
|
426
|
+
const controller = new AbortController();
|
|
427
|
+
this.followController = controller;
|
|
428
|
+
this.output = [];
|
|
429
|
+
this.emit();
|
|
430
|
+
const task = this.runFollow(controller);
|
|
431
|
+
this.followTask = task;
|
|
432
|
+
const finished = () => {
|
|
433
|
+
if (this.followTask === task)
|
|
434
|
+
this.followTask = null;
|
|
435
|
+
if (this.followController === controller)
|
|
436
|
+
this.followController = null;
|
|
437
|
+
const current = this.watchController;
|
|
438
|
+
if (current && !current.signal.aborted)
|
|
439
|
+
this.requestSync();
|
|
440
|
+
};
|
|
441
|
+
void task.then(finished, finished);
|
|
442
|
+
}
|
|
443
|
+
async runFollow(controller) {
|
|
444
|
+
let streamFailed = null;
|
|
445
|
+
try {
|
|
446
|
+
await this.state.follow(this.agent.id, this.id, {
|
|
447
|
+
signal: controller.signal,
|
|
448
|
+
onEvent: (event) => {
|
|
449
|
+
if (event.type === "output.delta") {
|
|
450
|
+
this.output = [...this.output, event.content];
|
|
451
|
+
this.error = null;
|
|
452
|
+
this.emit();
|
|
453
|
+
}
|
|
454
|
+
else if (event.type === "error" &&
|
|
455
|
+
event.code === "run_stream_failed") {
|
|
456
|
+
streamFailed = new Error(event.detail);
|
|
457
|
+
}
|
|
458
|
+
},
|
|
459
|
+
});
|
|
460
|
+
if (streamFailed)
|
|
461
|
+
throw streamFailed;
|
|
462
|
+
this.followRetryMs = 250;
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
if (controller.signal.aborted || isAbortError(error))
|
|
466
|
+
return;
|
|
467
|
+
this.error = error;
|
|
468
|
+
this.emit();
|
|
469
|
+
const delay = this.followRetryMs;
|
|
470
|
+
this.followRetryMs = Math.min(this.followRetryMs * 2, WATCH_RETRY_MAX_MS);
|
|
471
|
+
await abortableDelay(delay, controller.signal);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
view() {
|
|
475
|
+
let end = this.turns.length;
|
|
476
|
+
if (this.isRunning) {
|
|
477
|
+
for (let index = this.turns.length - 1; index >= 0; index--) {
|
|
478
|
+
if (this.turns[index].role !== "user")
|
|
479
|
+
continue;
|
|
480
|
+
end = index + 1;
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
turns: this.turns.slice(0, end),
|
|
486
|
+
output: [...this.output],
|
|
487
|
+
isRunning: this.isRunning,
|
|
488
|
+
loading: this.loading,
|
|
489
|
+
error: this.error,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
emit() {
|
|
493
|
+
const view = this.view();
|
|
494
|
+
for (const listener of this.listeners)
|
|
495
|
+
listener(view);
|
|
496
|
+
}
|
|
268
497
|
}
|
|
269
|
-
export function createMachineAgents(machinePath, transport) {
|
|
270
|
-
return new MachineAgentState(machinePath, transport);
|
|
271
|
-
}
|
|
272
|
-
function turnFromWire(turn) {
|
|
273
|
-
return {
|
|
274
|
-
id: turn.id,
|
|
275
|
-
...(turn.userId ? { userId: turn.userId } : {}),
|
|
276
|
-
createdAt: turn.createdAt,
|
|
277
|
-
body: {
|
|
278
|
-
role: turn.role,
|
|
279
|
-
content: turn.content,
|
|
280
|
-
...(turn.request ? { request: turn.request } : {}),
|
|
281
|
-
...(turn.finish ? { finish: turn.finish } : {}),
|
|
282
|
-
...(turn.error ? { error: turn.error } : {}),
|
|
283
|
-
...(turn.usage ? { usage: turn.usage } : {}),
|
|
284
|
-
...(turn.aside !== undefined ? { aside: turn.aside } : {}),
|
|
285
|
-
},
|
|
286
|
-
};
|
|
498
|
+
export function createMachineAgents(machineId, machinePath, transport) {
|
|
499
|
+
return new MachineAgentState(machineId, machinePath, transport);
|
|
287
500
|
}
|
|
288
501
|
function parseRunEvent(line) {
|
|
289
502
|
const value = JSON.parse(line);
|
|
@@ -312,7 +525,7 @@ function parseRunEvent(line) {
|
|
|
312
525
|
}
|
|
313
526
|
function validateDefinition(definition) {
|
|
314
527
|
if (!definition || typeof definition.system !== "string") {
|
|
315
|
-
throw new Error("Agent system
|
|
528
|
+
throw new Error("Agent system instructions must be a string");
|
|
316
529
|
}
|
|
317
530
|
}
|
|
318
531
|
function validateMetadata(metadata) {
|
|
@@ -323,6 +536,11 @@ function validateMetadata(metadata) {
|
|
|
323
536
|
throw new Error("Conversation name must be a string");
|
|
324
537
|
}
|
|
325
538
|
}
|
|
539
|
+
function validateInstructions(instructions) {
|
|
540
|
+
if (typeof instructions !== "string") {
|
|
541
|
+
throw new Error("Conversation instructions must be a string");
|
|
542
|
+
}
|
|
543
|
+
}
|
|
326
544
|
function validateAttachment(path) {
|
|
327
545
|
const supported = path.startsWith("/space/") || path.startsWith("/rool-drive/");
|
|
328
546
|
if (!supported) {
|
|
@@ -345,4 +563,21 @@ function jsonRequest(method, body, signal) {
|
|
|
345
563
|
function randomId() {
|
|
346
564
|
return globalThis.crypto.randomUUID();
|
|
347
565
|
}
|
|
566
|
+
function isAbortError(error) {
|
|
567
|
+
return (error instanceof Error &&
|
|
568
|
+
(error.name === "AbortError" || error.message.includes("was aborted")));
|
|
569
|
+
}
|
|
570
|
+
async function abortableDelay(milliseconds, signal) {
|
|
571
|
+
if (signal.aborted)
|
|
572
|
+
return;
|
|
573
|
+
await new Promise((resolve) => {
|
|
574
|
+
const timeout = setTimeout(finish, milliseconds);
|
|
575
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
576
|
+
function finish() {
|
|
577
|
+
clearTimeout(timeout);
|
|
578
|
+
signal.removeEventListener("abort", finish);
|
|
579
|
+
resolve();
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
}
|
|
348
583
|
//# sourceMappingURL=agents.js.map
|