@agentorg/cli 0.0.7 → 0.0.9
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 +3 -3
- package/dist/node_modules/@agentorg/computer/dist/types/generated/schema.d.ts +3 -0
- package/dist/node_modules/@agentorg/computer/package.json +1 -1
- package/dist/node_modules/@agentorg/computer-connector/dist/src/provider.js +3 -3
- package/dist/node_modules/@agentorg/computer-connector/dist/src/unit.d.ts +1 -1
- package/dist/node_modules/@agentorg/computer-connector/dist/src/unit.js +34 -30
- package/dist/node_modules/@agentorg/computer-connector/package.json +1 -1
- package/dist/node_modules/@agentorg/interfaces/dist/src/index.d.ts +0 -1
- package/dist/node_modules/@agentorg/interfaces/dist/src/index.js +0 -1
- package/dist/node_modules/@agentorg/interfaces/dist/src/manager.d.ts +1 -1
- package/dist/node_modules/@agentorg/interfaces/dist/src/manager.js +1 -1
- package/dist/node_modules/@agentorg/looper/dist/src/loop.d.ts +40 -17
- package/dist/node_modules/@agentorg/looper/dist/src/loop.js +142 -69
- package/dist/node_modules/@agentorg/looper/dist/src/looper.d.ts +62 -18
- package/dist/node_modules/@agentorg/looper/dist/src/looper.js +588 -108
- package/dist/node_modules/@agentorg/looper/dist/src/main.d.ts +90 -0
- package/dist/node_modules/@agentorg/looper/dist/src/main.js +205 -0
- package/dist/node_modules/@agentorg/looper/dist/src/record.d.ts +156 -16
- package/dist/node_modules/@agentorg/looper/dist/src/record.js +292 -58
- package/dist/node_modules/@agentorg/manager/dist/src/control.d.ts +1 -1
- package/dist/node_modules/@agentorg/manager/dist/src/control.js +1 -1
- package/dist/node_modules/@agentorg/shell/dist/app.css +290 -375
- package/dist/node_modules/@agentorg/shell/dist/app.js +141 -144
- package/dist/node_modules/assistant/dist/src/chats.d.ts +14 -7
- package/dist/node_modules/assistant/dist/src/chats.js +24 -25
- package/dist/node_modules/assistant/dist/src/engine.js +11 -5
- package/dist/node_modules/chatgpt/dist/src/account.js +1 -1
- package/dist/src/gateway.js +0 -3
- package/package.json +1 -1
- package/dist/node_modules/@agentorg/interfaces/dist/src/folders.d.ts +0 -19
- package/dist/node_modules/@agentorg/interfaces/dist/src/folders.js +0 -25
- package/dist/src/folders.d.ts +0 -2
- package/dist/src/folders.js +0 -45
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
* A loop: a turn sends its record and the tools to the model, runs each function the model calls,
|
|
3
3
|
* and sends the results back, until the model answers without calling anything, or the turn has
|
|
4
4
|
* made its `MAX_CALLS` calls. A message said while a turn runs is recorded at once, and the turn
|
|
5
|
-
* takes it at its next model request.
|
|
5
|
+
* takes it at its next model request. A loop is one piece of work's: its messages come in the
|
|
6
|
+
* threads the work follows, and what comes of a request is kept in the thread of the newest
|
|
7
|
+
* message that request read.
|
|
6
8
|
*/
|
|
7
|
-
import { argumentsOf, head, messageOf } from "./record.js";
|
|
9
|
+
import { argumentsOf, head, messageOf, userEntry } from "./record.js";
|
|
8
10
|
import { capped, runTool } from "./tools.js";
|
|
9
11
|
import { codeOf, Refusal, } from "./types.js";
|
|
10
12
|
/** A brake on a turn, not a budget: nothing approves a call, and the person can stop it. */
|
|
@@ -17,14 +19,23 @@ const CUT_OFF = {
|
|
|
17
19
|
export function createLoop(options) {
|
|
18
20
|
const { model, record, out, log } = options;
|
|
19
21
|
let turn;
|
|
20
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* A stopped turn that had begun to run and hasn't ended: it may still write the result of its
|
|
24
|
+
* running call, or the reply it was keeping. What is kept meanwhile is kept as joined, so the
|
|
25
|
+
* model reads it after that; and the next turn reads the record once it has ended.
|
|
26
|
+
*/
|
|
21
27
|
let draining;
|
|
28
|
+
/**
|
|
29
|
+
* That turn's place, the loop's while it still writes. A turn started meanwhile asks for none:
|
|
30
|
+
* when the stopped turn has ended, its place goes to that turn, or is given back.
|
|
31
|
+
*/
|
|
32
|
+
let lingering;
|
|
22
33
|
/** Settles when the latest turn's loop has ended. */
|
|
23
34
|
let lastEnded;
|
|
24
35
|
/** The latest status, for a view that asks. */
|
|
25
36
|
let latest = { state: "idle" };
|
|
26
|
-
/**
|
|
27
|
-
let
|
|
37
|
+
/** The starts made here. After the first, nothing cut off earlier is left to go on with. */
|
|
38
|
+
let turns = 0;
|
|
28
39
|
/** Counts the cancels, so that what was asked before one can tell when it is taken after it. */
|
|
29
40
|
let cancels = 0;
|
|
30
41
|
/** What is being taken now: the next thing asked is taken after it. */
|
|
@@ -55,7 +66,11 @@ export function createLoop(options) {
|
|
|
55
66
|
if (current.cancelled)
|
|
56
67
|
return;
|
|
57
68
|
log.error("A turn failed.", { error: String(error) });
|
|
58
|
-
|
|
69
|
+
// Every thread it took a message in, and the one it was answering in, is told.
|
|
70
|
+
const detail = codeOf(error);
|
|
71
|
+
for (const thread of new Set([...current.took, current.thread ?? record.id]))
|
|
72
|
+
out.failed(thread, detail);
|
|
73
|
+
status(current, { state: "error", detail });
|
|
59
74
|
})
|
|
60
75
|
.finally(() => {
|
|
61
76
|
current.place?.leave();
|
|
@@ -65,9 +80,9 @@ export function createLoop(options) {
|
|
|
65
80
|
.catch((error) => log.error("A turn's status failed.", { error: String(error) }));
|
|
66
81
|
lastEnded = current.ended;
|
|
67
82
|
};
|
|
68
|
-
const show = (entry) => out.message(messageOf(entry));
|
|
83
|
+
const show = ({ entry, thread }) => out.message(thread, messageOf(entry));
|
|
69
84
|
/** A call starting: shown as running, under the id its record will have when it ends. */
|
|
70
|
-
const running = (call, id) => out.message({
|
|
85
|
+
const running = (call, id, thread) => out.message(thread, {
|
|
71
86
|
id,
|
|
72
87
|
role: "tool",
|
|
73
88
|
text: `${call.name}: running`,
|
|
@@ -78,7 +93,7 @@ export function createLoop(options) {
|
|
|
78
93
|
* Record a function call with its output, keeping every call paired, as the API needs. Its end
|
|
79
94
|
* is shown even if the record can't be written, so no call is left shown as running.
|
|
80
95
|
*/
|
|
81
|
-
const answered = async (call, result, id, read) => {
|
|
96
|
+
const answered = async (call, result, id, read, thread) => {
|
|
82
97
|
const entry = {
|
|
83
98
|
role: "tool",
|
|
84
99
|
text: `${call.name}: ${result.ok ? "done" : result.code}`,
|
|
@@ -93,10 +108,10 @@ export function createLoop(options) {
|
|
|
93
108
|
],
|
|
94
109
|
};
|
|
95
110
|
try {
|
|
96
|
-
show(await record.append(entry, id, read));
|
|
111
|
+
show(await record.append(entry, { thread, id, read }));
|
|
97
112
|
}
|
|
98
113
|
catch (error) {
|
|
99
|
-
out.message(messageOf({
|
|
114
|
+
out.message(thread, messageOf({
|
|
100
115
|
...entry,
|
|
101
116
|
id,
|
|
102
117
|
at: new Date().toISOString(),
|
|
@@ -109,19 +124,20 @@ export function createLoop(options) {
|
|
|
109
124
|
* Note calls that won't run, in one message however many there are. They never ran, so the
|
|
110
125
|
* record leaves them out: what the model is sent next holds only calls with their outputs.
|
|
111
126
|
*/
|
|
112
|
-
const skipped = async (calls, code) => {
|
|
127
|
+
const skipped = async (calls, code, thread) => {
|
|
113
128
|
if (calls.length === 0)
|
|
114
129
|
return;
|
|
115
130
|
const text = calls.length === 1 ? `${calls[0]?.name}: ${code}` : `${calls.length} calls: ${code}`;
|
|
116
|
-
show(await record.append({ role: "tool", text, items: [] }));
|
|
131
|
+
show(await record.append({ role: "tool", text, items: [] }, { thread }));
|
|
117
132
|
};
|
|
118
133
|
async function run(current) {
|
|
119
134
|
let calls = 0;
|
|
120
135
|
for (;;) {
|
|
121
136
|
const { instructions, tools, model: chosen } = await options.request();
|
|
122
|
-
const { items: input, read } = await record.input();
|
|
137
|
+
const { items: input, read, thread } = await record.input();
|
|
123
138
|
if (current.cancelled)
|
|
124
139
|
return;
|
|
140
|
+
current.thread = thread;
|
|
125
141
|
const requestId = crypto.randomUUID();
|
|
126
142
|
const replyId = crypto.randomUUID();
|
|
127
143
|
current.requestId = requestId;
|
|
@@ -147,7 +163,7 @@ export function createLoop(options) {
|
|
|
147
163
|
show(await record.append({
|
|
148
164
|
role: "assistant",
|
|
149
165
|
items: texts.map((content) => ({ type: "message", role: "assistant", content })),
|
|
150
|
-
}, replyId, read));
|
|
166
|
+
}, { thread, id: replyId, read }));
|
|
151
167
|
if (called.length === 0) {
|
|
152
168
|
if (current.cancelled)
|
|
153
169
|
return;
|
|
@@ -174,7 +190,7 @@ export function createLoop(options) {
|
|
|
174
190
|
for (const [i, call] of called.entries()) {
|
|
175
191
|
if (current.cancelled || calls >= MAX_CALLS) {
|
|
176
192
|
const rest = called.slice(i);
|
|
177
|
-
await skipped(rest, current.cancelled ? "cancelled" : "limit");
|
|
193
|
+
await skipped(rest, current.cancelled ? "cancelled" : "limit", thread);
|
|
178
194
|
break;
|
|
179
195
|
}
|
|
180
196
|
calls++;
|
|
@@ -182,9 +198,9 @@ export function createLoop(options) {
|
|
|
182
198
|
// A call that has started runs to its end, and its real result is kept: it may have
|
|
183
199
|
// taken effect.
|
|
184
200
|
const id = crypto.randomUUID();
|
|
185
|
-
running(call, id);
|
|
201
|
+
running(call, id, thread);
|
|
186
202
|
const result = await runTool(tools, call.name, call.arguments);
|
|
187
|
-
await answered(call, result, id, read);
|
|
203
|
+
await answered(call, result, id, read, thread);
|
|
188
204
|
}
|
|
189
205
|
if (current.cancelled)
|
|
190
206
|
return;
|
|
@@ -193,53 +209,56 @@ export function createLoop(options) {
|
|
|
193
209
|
status(current, { state: "thinking" });
|
|
194
210
|
}
|
|
195
211
|
}
|
|
196
|
-
/**
|
|
212
|
+
/**
|
|
213
|
+
* A turn from its place to its end: it waits for the place, and for what a stopped turn before
|
|
214
|
+
* it still writes, already shown as thinking.
|
|
215
|
+
*/
|
|
197
216
|
async function placed(current) {
|
|
198
217
|
status(current, { state: "thinking" });
|
|
218
|
+
await draining;
|
|
199
219
|
await current.place?.entered;
|
|
200
220
|
current.waiting = false;
|
|
201
221
|
if (current.cancelled)
|
|
202
222
|
return;
|
|
223
|
+
// Where it answers until a request says: so a failure before one names its thread.
|
|
224
|
+
current.thread = await record.where();
|
|
203
225
|
await run(current);
|
|
204
226
|
}
|
|
205
227
|
/**
|
|
206
|
-
* Take a turn's place, or its turn in the line for one, before anything of it is kept.
|
|
207
|
-
*
|
|
228
|
+
* Take a turn's place, or its turn in the line for one, before anything of it is kept. While a
|
|
229
|
+
* stopped turn of this work still writes, the turn takes none: it gets that turn's, so a piece
|
|
230
|
+
* of work holds one place. A turn cancelled already takes none: it runs nothing.
|
|
208
231
|
*/
|
|
209
232
|
const enter = (current, begun) => {
|
|
210
233
|
if (current.cancelled)
|
|
211
234
|
return;
|
|
212
|
-
|
|
235
|
+
if (!lingering)
|
|
236
|
+
current.place = options.enter(begun);
|
|
213
237
|
current.waiting = true;
|
|
214
238
|
};
|
|
215
|
-
const said = async (text, joined) => {
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
...(joined ? { joined: true } : {}),
|
|
220
|
-
});
|
|
221
|
-
show(entry);
|
|
222
|
-
return { messageId: entry.id };
|
|
239
|
+
const said = async (text, thread, joined) => {
|
|
240
|
+
const kept = await record.append({ ...userEntry(text), ...(joined ? { joined: true } : {}) }, { thread });
|
|
241
|
+
show(kept);
|
|
242
|
+
return { messageId: kept.entry.id };
|
|
223
243
|
};
|
|
224
244
|
/**
|
|
225
|
-
* Start a turn: `open` takes its place and keeps what opens it, then its loop runs.
|
|
226
|
-
* gives nothing, no turn starts. A turn `cancelled` before it started keeps what
|
|
227
|
-
* runs nothing.
|
|
245
|
+
* Start a turn: `open` takes its place and keeps what opens it, at once, then its loop runs.
|
|
246
|
+
* When `open` gives nothing, no turn starts. A turn `cancelled` before it started keeps what
|
|
247
|
+
* opens it and runs nothing.
|
|
228
248
|
*/
|
|
229
249
|
const start = async (cancelled, open) => {
|
|
230
|
-
const current = { cancelled };
|
|
250
|
+
const current = { id: ++turns, cancelled, took: new Set() };
|
|
231
251
|
if (!cancelled)
|
|
232
252
|
turn = current;
|
|
233
|
-
began = true;
|
|
234
253
|
try {
|
|
235
|
-
// A cancelled turn's last call lands in the record before anything new does.
|
|
236
|
-
await draining;
|
|
237
|
-
draining = undefined;
|
|
238
254
|
const begun = await open(current);
|
|
239
255
|
if (begun)
|
|
240
256
|
begin(current);
|
|
241
|
-
else
|
|
242
|
-
|
|
257
|
+
else {
|
|
258
|
+
current.place?.leave();
|
|
259
|
+
if (turn === current)
|
|
260
|
+
turn = undefined;
|
|
261
|
+
}
|
|
243
262
|
return begun?.opened;
|
|
244
263
|
}
|
|
245
264
|
catch (error) {
|
|
@@ -251,37 +270,66 @@ export function createLoop(options) {
|
|
|
251
270
|
};
|
|
252
271
|
return {
|
|
253
272
|
/**
|
|
254
|
-
* Say a message
|
|
255
|
-
* running or waiting for its place,
|
|
256
|
-
*
|
|
273
|
+
* Say a message in a thread the work follows. It is recorded and said at once. In a turn,
|
|
274
|
+
* running or waiting for its place, the turn takes it at its next model request: `turn` is
|
|
275
|
+
* then that turn's number. With no turn it starts one, whose progress is said through `out`.
|
|
257
276
|
*/
|
|
258
|
-
send: (text) => inOrder(async (cancelled) => {
|
|
259
|
-
|
|
260
|
-
|
|
277
|
+
send: (text, thread) => inOrder(async (cancelled) => {
|
|
278
|
+
const into = turn;
|
|
279
|
+
if (into) {
|
|
280
|
+
into.took.add(thread);
|
|
281
|
+
return { ...(await said(text, thread, true)), turn: into.id };
|
|
282
|
+
}
|
|
261
283
|
return (await start(cancelled, async (current) => {
|
|
262
|
-
enter(current,
|
|
263
|
-
|
|
284
|
+
enter(current, await record.begun());
|
|
285
|
+
current.took.add(thread);
|
|
286
|
+
return { opened: await said(text, thread, draining !== undefined) };
|
|
264
287
|
}));
|
|
265
288
|
}),
|
|
266
289
|
/**
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
* a
|
|
270
|
-
|
|
290
|
+
* Take the messages waiting in `thread`, which the work has come to follow, as its own, and
|
|
291
|
+
* answer them: in the turn running, or in one this starts. With none waiting no turn starts.
|
|
292
|
+
* When a cancel came first nothing is taken, and it says so with `false`.
|
|
293
|
+
*/
|
|
294
|
+
adopt: (thread) => inOrder(async (cancelled) => {
|
|
295
|
+
if (cancelled)
|
|
296
|
+
return false;
|
|
297
|
+
if (turn) {
|
|
298
|
+
turn.took.add(thread);
|
|
299
|
+
await record.adopt(thread, true);
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
await start(false, async (current) => {
|
|
303
|
+
enter(current, await record.begun());
|
|
304
|
+
current.took.add(thread);
|
|
305
|
+
if ((await record.adopt(thread, draining !== undefined)) > 0)
|
|
306
|
+
return { opened: undefined };
|
|
307
|
+
return undefined;
|
|
308
|
+
});
|
|
309
|
+
return true;
|
|
310
|
+
}),
|
|
311
|
+
/**
|
|
312
|
+
* Go on with a turn that was cut off before it ended, as by a restart: the thread it was
|
|
313
|
+
* answering in shows one line saying so, and the model is told once to check what is already
|
|
314
|
+
* done. Nothing goes on in work with nothing kept, in work that has had a turn since the loop
|
|
315
|
+
* was made, or when a cancel comes first.
|
|
271
316
|
*/
|
|
272
317
|
resume: () => inOrder(async (cancelled) => {
|
|
273
|
-
if (
|
|
318
|
+
if (turns > 0 || cancelled)
|
|
274
319
|
return;
|
|
275
320
|
await start(false, async (current) => {
|
|
276
321
|
if ((await record.empty()) || current.cancelled)
|
|
277
322
|
return undefined;
|
|
278
323
|
enter(current, true);
|
|
279
324
|
current.resumed = true;
|
|
325
|
+
// In the thread of the message it was answering.
|
|
326
|
+
const thread = await record.where();
|
|
327
|
+
current.took.add(thread);
|
|
280
328
|
show(await record.append({
|
|
281
329
|
role: "tool",
|
|
282
330
|
text: CUT_OFF.shown,
|
|
283
331
|
items: [{ type: "message", role: "user", content: CUT_OFF.told }],
|
|
284
|
-
}));
|
|
332
|
+
}, { thread }));
|
|
285
333
|
return { opened: undefined };
|
|
286
334
|
});
|
|
287
335
|
}),
|
|
@@ -289,44 +337,69 @@ export function createLoop(options) {
|
|
|
289
337
|
resumed: () => turn?.resumed === true,
|
|
290
338
|
/** The status now. */
|
|
291
339
|
state: () => latest,
|
|
340
|
+
/** The number of the turn running or waiting for its place, if one is. */
|
|
341
|
+
turn: () => turn?.id,
|
|
292
342
|
/**
|
|
293
|
-
* Stop the turn now
|
|
294
|
-
*
|
|
295
|
-
*
|
|
343
|
+
* Stop the turn now, or with `only`, the turn of that number if it is still the one: its
|
|
344
|
+
* model request is cancelled and no further call starts. A call already running finishes,
|
|
345
|
+
* and its result is kept; a new turn may be started at once, and reads the record after
|
|
346
|
+
* that. A turn waiting for its place never starts. It says whether it cut a turn. `line` is
|
|
347
|
+
* what the main loop says as it stops it, kept in that thread in order with what was asked
|
|
348
|
+
* before.
|
|
296
349
|
*/
|
|
297
|
-
async cancel() {
|
|
350
|
+
async cancel(only, line) {
|
|
351
|
+
const current = turn;
|
|
352
|
+
if (only !== undefined && current?.id !== only)
|
|
353
|
+
return false;
|
|
298
354
|
// Counted with no turn too: a message asked but not yet taken starts none after this.
|
|
299
355
|
cancels++;
|
|
300
|
-
const current = turn;
|
|
301
356
|
if (!current)
|
|
302
|
-
return;
|
|
357
|
+
return false;
|
|
303
358
|
current.cancelled = true;
|
|
304
359
|
turn = undefined;
|
|
305
360
|
// A turn that will write nothing more gives its place back now: one still waiting for it,
|
|
306
|
-
// one the model is answering, or one waiting for its end. Any other
|
|
307
|
-
// has written what it will
|
|
308
|
-
// or the result of its running call.
|
|
361
|
+
// one the model is answering, or one waiting for its end. Any other's place is kept until
|
|
362
|
+
// it has written what it will: the reply it is storing, or the result of its running call.
|
|
309
363
|
const quiet = current.requestId !== undefined || current.ending === true;
|
|
310
364
|
if (current.waiting || quiet)
|
|
311
365
|
current.place?.leave();
|
|
312
|
-
if (
|
|
313
|
-
|
|
366
|
+
else if (current.ended) {
|
|
367
|
+
const still = current.ended;
|
|
368
|
+
draining = still;
|
|
369
|
+
lingering = current.place;
|
|
370
|
+
current.place = undefined;
|
|
371
|
+
// Once that has ended nothing is being written by a stopped turn any more: its place
|
|
372
|
+
// goes to the turn that waited for this, or back.
|
|
373
|
+
still.finally(() => {
|
|
374
|
+
if (draining !== still)
|
|
375
|
+
return;
|
|
376
|
+
draining = undefined;
|
|
377
|
+
if (turn?.waiting && !turn.place)
|
|
378
|
+
turn.place = lingering;
|
|
379
|
+
else
|
|
380
|
+
lingering?.leave();
|
|
381
|
+
lingering = undefined;
|
|
382
|
+
});
|
|
383
|
+
}
|
|
314
384
|
say({ state: "idle" });
|
|
315
|
-
|
|
316
|
-
|
|
385
|
+
await Promise.all([
|
|
386
|
+
current.requestId && model.cancel({ requestId: current.requestId }).catch(() => { }),
|
|
387
|
+
line && inOrder(async () => show(await record.stopLine(line.text, line.thread))),
|
|
388
|
+
]);
|
|
389
|
+
return true;
|
|
317
390
|
},
|
|
318
391
|
/** Settles when the latest turn has written all it will. */
|
|
319
392
|
settled: async () => {
|
|
320
393
|
await lastEnded;
|
|
321
394
|
},
|
|
322
|
-
/** Settles when a stopped turn
|
|
395
|
+
/** Settles when a stopped turn has written what it will: at once, if none is writing. */
|
|
323
396
|
drained: async () => {
|
|
324
397
|
await draining;
|
|
325
398
|
},
|
|
326
399
|
/** Streamed text of the model request running now, for the reply it becomes. */
|
|
327
400
|
delta(requestId, text) {
|
|
328
401
|
if (turn?.requestId === requestId && turn.replyId)
|
|
329
|
-
out.delta(turn.replyId, text);
|
|
402
|
+
out.delta(turn.thread ?? record.id, turn.replyId, text);
|
|
330
403
|
},
|
|
331
404
|
};
|
|
332
405
|
}
|
|
@@ -1,14 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The looper: the loops of whoever hosts it. Messages come in on threads, which are names its host
|
|
3
|
+
* gives; a thread's messages are kept under `thread/<name>/` in the store. What answers them is a
|
|
4
|
+
* piece of work, with a loop of its own, or, when a message needs no work, the main loop.
|
|
5
|
+
*
|
|
6
|
+
* Intake is code. A message in a thread that a piece of work follows is given to that work. One in
|
|
7
|
+
* a thread nothing follows is kept, and the main loop decides on the thread: it answers itself, and
|
|
8
|
+
* the thread stays no work's; or it says whose the message is, new work, which has the thread's
|
|
9
|
+
* name as its id, or work there is, in a turn or at rest, which then follows that thread too. The
|
|
10
|
+
* main loop makes one decision at a time, and one for a thread on all that waits in it. A piece
|
|
11
|
+
* of work answers in the thread of the newest message it has read.
|
|
12
|
+
*
|
|
13
|
+
* One piece of work is stopped by saying so. The main loop may answer a message in a thread
|
|
14
|
+
* nothing follows by stopping the turn of work it was told of; and it is told of each message that
|
|
15
|
+
* joins a turn of the work its thread follows, beside that work getting it, to stop that turn if
|
|
16
|
+
* the message asks. The turn is cut and the work is at rest. What the main loop says of it is
|
|
17
|
+
* said where the message was: in the work's own record when it joined the turn, for the work to
|
|
18
|
+
* read next, and otherwise in the asking thread alone, which stays no work's.
|
|
19
|
+
*
|
|
20
|
+
* Turns run side by side, each in a place; the main loop takes none. There are `PLACES` of them,
|
|
21
|
+
* and one more that only work already begun (it has done something) may take, so new work never
|
|
22
|
+
* keeps a message from work that is under way. A turn that finds no place waits for one, in the
|
|
23
|
+
* order it came. While begun work keeps five turns running, new work goes on waiting. `WAITING`
|
|
24
|
+
* turns may wait; past that a message that would start a turn is refused `busy`.
|
|
25
|
+
*
|
|
26
|
+
* A piece of work is marked in the store while a turn of it hasn't ended, and a thread while no
|
|
27
|
+
* work follows it and it holds messages. A looper that closes under a turn or a decision, or goes
|
|
28
|
+
* without closing, leaves the mark, and `resume` on the same store goes on with it, once. A turn
|
|
29
|
+
* that ends, fails or is stopped leaves no mark; a thread the main loop answered, couldn't place,
|
|
30
|
+
* or was stopped over, waits for its next message, and the main loop is put what it holds.
|
|
31
|
+
*/
|
|
1
32
|
import { type Log, type Model, type Shown, type Status, type Store, type Tool } from "./types.js";
|
|
2
|
-
/** The turns that run at once, not counting the one more for
|
|
33
|
+
/** The turns that run at once, not counting the one more for work already begun. */
|
|
3
34
|
export declare const PLACES = 4;
|
|
4
35
|
/** The turns that may wait for a place at once. */
|
|
5
36
|
export declare const WAITING = 16;
|
|
6
|
-
/** Where a looper
|
|
37
|
+
/** Where a looper says what it does. */
|
|
7
38
|
export interface LooperOut {
|
|
39
|
+
/** A message, with the thread it is shown in. */
|
|
8
40
|
message(thread: string, message: Shown): void;
|
|
9
41
|
/** Text of the reply `id` as the model writes it. */
|
|
10
42
|
delta(thread: string, id: string, text: string): void;
|
|
11
|
-
|
|
43
|
+
/** Whether anything is at work, the main loop included: said when it changes. */
|
|
44
|
+
working(on: boolean): void;
|
|
45
|
+
/** A message in `thread` got no answer, and why. */
|
|
46
|
+
failed(thread: string, problem: {
|
|
47
|
+
code: string;
|
|
48
|
+
message: string;
|
|
49
|
+
}): void;
|
|
12
50
|
}
|
|
13
51
|
/**
|
|
14
52
|
* Make a looper. Nothing is read or run until a thread is named: a message sent in it, or its
|
|
@@ -19,12 +57,15 @@ export declare function createLooper(options: {
|
|
|
19
57
|
model: Model;
|
|
20
58
|
/**
|
|
21
59
|
* Read for each model request, since any of it may change between two: what the model is told,
|
|
22
|
-
* what it can call, and which model to ask, none meaning the model's own default.
|
|
60
|
+
* what it can call, and which model to ask, none meaning the model's own default. `voice` is
|
|
61
|
+
* who the main loop speaks as when it answers a message itself: what the host's own model is
|
|
62
|
+
* told of itself, with nothing of what it can call.
|
|
23
63
|
*/
|
|
24
64
|
request(): Promise<{
|
|
25
65
|
instructions: string;
|
|
26
66
|
tools: Tool[];
|
|
27
67
|
model?: string | undefined;
|
|
68
|
+
voice?: string | undefined;
|
|
28
69
|
}>;
|
|
29
70
|
out: LooperOut;
|
|
30
71
|
/** Whether it starts stopped: no turn starts until `start`. */
|
|
@@ -32,38 +73,41 @@ export declare function createLooper(options: {
|
|
|
32
73
|
log: Log;
|
|
33
74
|
}): {
|
|
34
75
|
/**
|
|
35
|
-
* Say a message in a thread. It is recorded and said at once.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
76
|
+
* Say a message in a thread. It is recorded and said at once. In a thread a piece of work
|
|
77
|
+
* follows it is that work's: with no turn of it running it starts one, which waits for a place
|
|
78
|
+
* if none is free, and a turn running or waiting takes it at its next model request. In a
|
|
79
|
+
* thread nothing follows, the main loop answers it or says whose it is first. Refused `busy`
|
|
80
|
+
* while `WAITING` turns already wait and it would start one more, or while the looper is
|
|
81
|
+
* stopped; and `unavailable` once it has closed.
|
|
39
82
|
*/
|
|
40
83
|
send(thread: string, text: string): Promise<{
|
|
41
84
|
messageId: string;
|
|
42
85
|
}>;
|
|
43
|
-
/** Stop a
|
|
44
|
-
cancel: (
|
|
45
|
-
/** What a
|
|
46
|
-
state: (
|
|
86
|
+
/** Stop a piece of work's turn now. A call already running finishes, and its result is kept. */
|
|
87
|
+
cancel: (work: string) => Promise<undefined>;
|
|
88
|
+
/** What a piece of work's loop is doing. */
|
|
89
|
+
state: (work: string) => Status;
|
|
47
90
|
/** A thread's messages before `before`, oldest first, as many as fit `limit`. */
|
|
48
91
|
page: (thread: string, before: string | undefined, limit: number) => Promise<{
|
|
49
92
|
messages: Shown[];
|
|
50
93
|
more: boolean;
|
|
51
94
|
}>;
|
|
52
95
|
/**
|
|
53
|
-
* Stop: every turn is cut, running or waiting,
|
|
54
|
-
* running finishes first, so this answers when nothing more will
|
|
55
|
-
* as they are, to be read.
|
|
96
|
+
* Stop: every turn is cut, running or waiting, the main loop is cut too, and nothing starts
|
|
97
|
+
* until `start`. A call already running finishes first, so this answers when nothing more will
|
|
98
|
+
* be written. The records stay as they are, to be read. What was cut off earlier no longer
|
|
99
|
+
* goes on, and messages waiting for a piece of work wait for their thread's next.
|
|
56
100
|
*/
|
|
57
101
|
stop(): Promise<void>;
|
|
58
102
|
/** Take messages again. The turns `stop` cut stay cut. */
|
|
59
103
|
start(): void;
|
|
60
104
|
resume: () => Promise<void>;
|
|
61
105
|
/**
|
|
62
|
-
* Close for good: every turn is cut, and none starts after. Each stays marked,
|
|
63
|
-
* in whoever opens the store next.
|
|
106
|
+
* Close for good: every turn is cut, and none starts after. Each stays marked, as does each
|
|
107
|
+
* thread where messages wait, for `resume` in whoever opens the store next.
|
|
64
108
|
*/
|
|
65
109
|
close(): Promise<void>;
|
|
66
|
-
/** Settles when every
|
|
110
|
+
/** Settles when every piece of work's latest turn has written all it will. */
|
|
67
111
|
settled(): Promise<void>;
|
|
68
112
|
/** Text the model writes while it answers `requestId`, for the reply it becomes. */
|
|
69
113
|
delta(requestId: string, text: string): void;
|