@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.
Files changed (33) hide show
  1. package/README.md +3 -3
  2. package/dist/node_modules/@agentorg/computer/dist/types/generated/schema.d.ts +3 -0
  3. package/dist/node_modules/@agentorg/computer/package.json +1 -1
  4. package/dist/node_modules/@agentorg/computer-connector/dist/src/provider.js +3 -3
  5. package/dist/node_modules/@agentorg/computer-connector/dist/src/unit.d.ts +1 -1
  6. package/dist/node_modules/@agentorg/computer-connector/dist/src/unit.js +34 -30
  7. package/dist/node_modules/@agentorg/computer-connector/package.json +1 -1
  8. package/dist/node_modules/@agentorg/interfaces/dist/src/index.d.ts +0 -1
  9. package/dist/node_modules/@agentorg/interfaces/dist/src/index.js +0 -1
  10. package/dist/node_modules/@agentorg/interfaces/dist/src/manager.d.ts +1 -1
  11. package/dist/node_modules/@agentorg/interfaces/dist/src/manager.js +1 -1
  12. package/dist/node_modules/@agentorg/looper/dist/src/loop.d.ts +40 -17
  13. package/dist/node_modules/@agentorg/looper/dist/src/loop.js +142 -69
  14. package/dist/node_modules/@agentorg/looper/dist/src/looper.d.ts +62 -18
  15. package/dist/node_modules/@agentorg/looper/dist/src/looper.js +588 -108
  16. package/dist/node_modules/@agentorg/looper/dist/src/main.d.ts +90 -0
  17. package/dist/node_modules/@agentorg/looper/dist/src/main.js +205 -0
  18. package/dist/node_modules/@agentorg/looper/dist/src/record.d.ts +156 -16
  19. package/dist/node_modules/@agentorg/looper/dist/src/record.js +292 -58
  20. package/dist/node_modules/@agentorg/manager/dist/src/control.d.ts +1 -1
  21. package/dist/node_modules/@agentorg/manager/dist/src/control.js +1 -1
  22. package/dist/node_modules/@agentorg/shell/dist/app.css +290 -375
  23. package/dist/node_modules/@agentorg/shell/dist/app.js +141 -144
  24. package/dist/node_modules/assistant/dist/src/chats.d.ts +14 -7
  25. package/dist/node_modules/assistant/dist/src/chats.js +24 -25
  26. package/dist/node_modules/assistant/dist/src/engine.js +11 -5
  27. package/dist/node_modules/chatgpt/dist/src/account.js +1 -1
  28. package/dist/src/gateway.js +0 -3
  29. package/package.json +1 -1
  30. package/dist/node_modules/@agentorg/interfaces/dist/src/folders.d.ts +0 -19
  31. package/dist/node_modules/@agentorg/interfaces/dist/src/folders.js +0 -25
  32. package/dist/src/folders.d.ts +0 -2
  33. 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
- /** A cancelled turn whose running call hasn't settled: the next turn waits for its records. */
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
- /** Set once a turn has started here: nothing that was cut off earlier is left to go on with. */
27
- let began = false;
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
- status(current, { state: "error", detail: codeOf(error) });
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
- /** A turn from its place to its end: it waits for the place already shown as thinking. */
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. A turn
207
- * cancelled already takes none: it runs nothing.
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
- current.place = options.enter(begun);
235
+ if (!lingering)
236
+ current.place = options.enter(begun);
213
237
  current.waiting = true;
214
238
  };
215
- const said = async (text, joined) => {
216
- const entry = await record.append({
217
- role: "user",
218
- items: [{ type: "message", role: "user", content: text }],
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. When `open`
226
- * gives nothing, no turn starts. A turn `cancelled` before it started keeps what opens it and
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 if (turn === current)
242
- turn = undefined;
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. With no turn it starts one, whose progress is said through `out`. In a turn,
255
- * running or waiting for its place, it is recorded and said at once, and the turn takes it at
256
- * its next model request.
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
- if (turn)
260
- return said(text, true);
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, !(await record.empty()));
263
- return { opened: await said(text, false) };
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
- * Go on with a turn that was cut off before it ended, as by a restart: the thread shows one
268
- * line saying so, and the model is told once to check what is already done. Nothing goes on in
269
- * a thread with nothing kept, in one that has had a turn since the loop was made, or when a
270
- * cancel comes first.
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 (began || cancelled)
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: its model request is cancelled, no further call starts, and a new turn
294
- * may begin at once. A call already running finishes, and its result is kept. A turn waiting
295
- * for its place never starts.
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 keeps its place until it
307
- // has written what it will, and what comes next waits for that: the reply it is storing,
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 (!quiet)
313
- draining = current.ended ?? draining;
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
- if (current.requestId)
316
- await model.cancel({ requestId: current.requestId }).catch(() => { });
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's running call has ended: at once, if none is running. */
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 a thread already begun. */
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's loops say what they do, each with the thread it is said in. */
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
- status(thread: string, status: Status): void;
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. With no turn in the thread it
36
- * starts one, which waits for a place if none is free; a turn running or waiting takes it at
37
- * its next model request. Refused `busy` while `WAITING` turns already wait and it would start
38
- * one more, or while the looper is stopped; and `unavailable` once it has closed.
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 thread's turn now. A call already running finishes, and its result is kept. */
44
- cancel: (thread: string) => Promise<void | undefined>;
45
- /** What a thread's loop is doing. */
46
- state: (thread: string) => Status;
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, and none starts until `start`. A call already
54
- * running finishes first, so this answers when nothing more will be written. The records stay
55
- * as they are, to be read. A turn that was cut off earlier no longer goes on.
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, for `resume`
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 thread's latest turn has written all it will. */
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;