@agentorg/cli 0.0.8 → 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 (31) 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 +17 -8
  13. package/dist/node_modules/@agentorg/looper/dist/src/loop.js +80 -41
  14. package/dist/node_modules/@agentorg/looper/dist/src/looper.d.ts +39 -5
  15. package/dist/node_modules/@agentorg/looper/dist/src/looper.js +290 -102
  16. package/dist/node_modules/@agentorg/looper/dist/src/main.d.ts +73 -17
  17. package/dist/node_modules/@agentorg/looper/dist/src/main.js +166 -77
  18. package/dist/node_modules/@agentorg/looper/dist/src/record.d.ts +76 -18
  19. package/dist/node_modules/@agentorg/looper/dist/src/record.js +190 -111
  20. package/dist/node_modules/@agentorg/shell/dist/app.css +255 -276
  21. package/dist/node_modules/@agentorg/shell/dist/app.js +144 -147
  22. package/dist/node_modules/assistant/dist/src/chats.d.ts +10 -5
  23. package/dist/node_modules/assistant/dist/src/chats.js +5 -4
  24. package/dist/node_modules/assistant/dist/src/engine.js +10 -4
  25. package/dist/node_modules/chatgpt/dist/src/account.js +1 -1
  26. package/dist/src/gateway.js +0 -3
  27. package/package.json +1 -1
  28. package/dist/node_modules/@agentorg/interfaces/dist/src/folders.d.ts +0 -19
  29. package/dist/node_modules/@agentorg/interfaces/dist/src/folders.js +0 -25
  30. package/dist/src/folders.d.ts +0 -2
  31. package/dist/src/folders.js +0 -45
@@ -17,18 +17,30 @@ export const Entry = z.object({
17
17
  items: z.array(Item),
18
18
  /** For a tool message whose call ran: how it ended. */
19
19
  state: z.enum(["done", "refused"]).optional(),
20
- /** For a user message said while a turn was running: it is part of that turn. */
20
+ /**
21
+ * For a user message said while a turn was running, or while a stopped turn was still writing:
22
+ * it is part of that turn. Also for what the main loop said in a thread, taken with such
23
+ * messages or said as it stopped the turn: it stays in its place among them.
24
+ */
21
25
  joined: z.literal(true).optional(),
22
26
  /**
23
27
  * For what came of a model request that had read its work up to this place, when a user message
24
- * was said after that: the model reads it ahead of that message, which it doesn't answer.
28
+ * was said after that, or the main loop said it stopped the turn: the model reads it ahead of
29
+ * those, which it doesn't answer.
25
30
  */
26
31
  ahead: z.number().optional(),
27
32
  /** The piece of work it belongs to, when that isn't its thread's own. */
28
33
  work: z.string().optional(),
34
+ /**
35
+ * For what the main loop said itself, which no piece of work said: the number in its thread it
36
+ * stands after, ahead of any said after that. For a reply that is the number it had read up to;
37
+ * for what it said as it stopped a piece of work, all the thread held then.
38
+ */
39
+ main: z.number().optional(),
29
40
  /**
30
41
  * Its place in its work's order, once that work follows more than one thread. Without it, its
31
- * place is its number in its thread.
42
+ * place is where it stands in its thread: its number, or for what the main loop said, right
43
+ * after the number that names.
32
44
  */
33
45
  place: z.number().optional(),
34
46
  });
@@ -109,6 +121,17 @@ export const userEntry = (text) => ({
109
121
  role: "user",
110
122
  items: [{ type: "message", role: "user", content: text }],
111
123
  });
124
+ /** What the main loop said, as it is kept: it stands after `after`, a number in its thread. */
125
+ const mainEntry = (text, after) => ({
126
+ role: "assistant",
127
+ main: after,
128
+ items: [{ type: "message", role: "assistant", content: text }],
129
+ });
130
+ /**
131
+ * Where a message stands in its thread: its number, but what the main loop said stands right
132
+ * after the number it names, ahead of a message said while it answered.
133
+ */
134
+ const orderOf = ({ seq, entry }) => (entry.main === undefined ? seq : entry.main + 0.5);
112
135
  /** A thread's messages in a store, by their numbers. */
113
136
  export function createThread(store, log, limits = { keep: KEEP, keepBytes: KEEP_BYTES }) {
114
137
  let loaded;
@@ -154,76 +177,97 @@ export function createThread(store, log, limits = { keep: KEEP, keepBytes: KEEP_
154
177
  });
155
178
  return attempt;
156
179
  };
180
+ /**
181
+ * Keep a message in a thread already read, at once, under `id` or its own number; `stored`
182
+ * settles when it is written and the oldest past the cap are deleted. A tool message the store
183
+ * refuses is still kept in memory, since its call has run; any other is taken out again.
184
+ */
185
+ const push = (held, entry, id) => {
186
+ const seq = ++held.last;
187
+ const key = keyOf(seq);
188
+ const record = {
189
+ seq,
190
+ key,
191
+ entry: kept({
192
+ id: id ?? key.slice(MESSAGES.length),
193
+ at: new Date().toISOString(),
194
+ ...entry,
195
+ }),
196
+ bytes: 0,
197
+ writing: entry.role === "user",
198
+ };
199
+ record.bytes = size(record.entry);
200
+ held.records.push(record);
201
+ held.bytes += record.bytes;
202
+ const stored = (async () => {
203
+ try {
204
+ await store.set({ key, value: record.entry });
205
+ record.writing = false;
206
+ }
207
+ catch (error) {
208
+ if (entry.role !== "tool") {
209
+ held.records.splice(held.records.indexOf(record), 1);
210
+ held.bytes -= record.bytes;
211
+ throw error;
212
+ }
213
+ log.error("A tool message couldn't be stored; it is kept until a restart.", {
214
+ error: String(error),
215
+ });
216
+ }
217
+ while (held.records.length > 1 &&
218
+ (held.records.length > limits.keep || held.bytes > limits.keepBytes)) {
219
+ const oldest = held.records.shift();
220
+ held.bytes -= oldest.bytes;
221
+ await store.delete({ key: oldest.key }).catch((error) => log.warn("An old message couldn't be deleted.", {
222
+ key: oldest.key,
223
+ error: String(error),
224
+ }));
225
+ }
226
+ })();
227
+ return { record, stored };
228
+ };
157
229
  return {
158
230
  /** What it holds, read once. */
159
231
  load,
232
+ push,
160
233
  /**
161
- * Keep a message in a thread already read, at once, under `id` or its own number; `stored`
162
- * settles when it is written and the oldest past the cap are deleted. A tool message the store
163
- * refuses is still kept in memory, since its call has run; any other is taken out again.
234
+ * What was said in it, as a conversation: each message and each reply the main loop gave, in
235
+ * the order they were answered; and the number it holds up to.
164
236
  */
165
- push(held, entry, id) {
166
- const seq = ++held.last;
167
- const key = keyOf(seq);
168
- const record = {
169
- seq,
170
- key,
171
- entry: kept({
172
- id: id ?? key.slice(MESSAGES.length),
173
- at: new Date().toISOString(),
174
- ...entry,
175
- }),
176
- bytes: 0,
177
- writing: entry.role === "user",
178
- };
179
- record.bytes = size(record.entry);
180
- held.records.push(record);
181
- held.bytes += record.bytes;
182
- const stored = (async () => {
183
- try {
184
- await store.set({ key, value: record.entry });
185
- record.writing = false;
186
- }
187
- catch (error) {
188
- if (entry.role !== "tool") {
189
- held.records.splice(held.records.indexOf(record), 1);
190
- held.bytes -= record.bytes;
191
- throw error;
192
- }
193
- log.error("A tool message couldn't be stored; it is kept until a restart.", {
194
- error: String(error),
195
- });
196
- }
197
- while (held.records.length > 1 &&
198
- (held.records.length > limits.keep || held.bytes > limits.keepBytes)) {
199
- const oldest = held.records.shift();
200
- held.bytes -= oldest.bytes;
201
- await store.delete({ key: oldest.key }).catch((error) => log.warn("An old message couldn't be deleted.", {
202
- key: oldest.key,
203
- error: String(error),
204
- }));
205
- }
206
- })();
207
- return { record, stored };
237
+ async said() {
238
+ const held = await load();
239
+ const said = held.records
240
+ .filter(({ entry }) => entry.role !== "tool")
241
+ .sort((a, b) => orderOf(a) - orderOf(b))
242
+ .map(({ entry }) => ({ role: entry.role, text: textOf(entry) }));
243
+ return { said, upTo: held.last };
244
+ },
245
+ /**
246
+ * Keep, under `id`, a reply the main loop gave after it had read up to `upTo`. `late` says
247
+ * whether a message was said after that, which this reply doesn't answer.
248
+ */
249
+ async answer(text, upTo, id) {
250
+ const held = await load();
251
+ const { record, stored } = push(held, mainEntry(text, upTo), id);
252
+ await stored;
253
+ const late = held.records.some(({ seq, entry }) => seq > upTo && entry.role === "user");
254
+ return { entry: record.entry, late };
208
255
  },
209
256
  /**
210
- * Keep a message as `entry` from now on: at once in memory, then in the store. A write the
211
- * store refuses leaves it as it was.
257
+ * Hold a message as `entry` from now on, in memory: `store` writes it as it then is, and
258
+ * `undo` puts it back as it was.
212
259
  */
213
- async rewrite(held, record, entry) {
260
+ change(held, record, entry) {
214
261
  const was = { entry: record.entry, bytes: record.bytes };
215
262
  const swap = (to) => {
216
263
  held.bytes += to.bytes - record.bytes;
217
264
  Object.assign(record, to);
218
265
  };
219
266
  swap({ entry, bytes: size(entry) });
220
- try {
221
- await store.set({ key: record.key, value: entry });
222
- }
223
- catch (error) {
224
- swap(was);
225
- throw error;
226
- }
267
+ return {
268
+ store: () => store.set({ key: record.key, value: record.entry }),
269
+ undo: () => swap(was),
270
+ };
227
271
  },
228
272
  /** Messages before `before`, oldest first, as many as fit `limit` and `PAGE_BUDGET`. */
229
273
  async page(before, limit) {
@@ -250,14 +294,14 @@ export function createThread(store, log, limits = { keep: KEEP, keepBytes: KEEP_
250
294
  /**
251
295
  * A piece of work's record: its messages across the threads it follows, in the order it took them.
252
296
  * Its id is the name of its own thread, whose messages are its unless they name another work; in
253
- * any other thread, the messages that name it are. A message's place is its number in its own
254
- * thread until the work follows a second thread, and a count kept with each message after. A
297
+ * any other thread, the messages that name it are. A message's place is where it stands in its
298
+ * own thread until the work follows a second thread, and a count kept with each message after. A
255
299
  * message waiting in a thread it follows, said before the work had the thread, names no work yet:
256
300
  * the work takes it, in the order it was said, before it keeps anything else there.
257
301
  */
258
302
  export function createRecord(options) {
259
303
  const { id } = options;
260
- const placeOf = (record) => record.entry.place ?? record.seq;
304
+ const placeOf = (record) => record.entry.place ?? orderOf(record);
261
305
  const mine = (thread, record) => record.entry.work === undefined ? thread === id : record.entry.work === id;
262
306
  /** Every thread it follows, read: what each holds, as it changes. */
263
307
  const load = async () => {
@@ -272,10 +316,7 @@ export function createRecord(options) {
272
316
  */
273
317
  const own = (held) => {
274
318
  const records = [...held].flatMap(([thread, { records }]) => records.flatMap((record) => (mine(thread, record) ? [{ thread, record }] : [])));
275
- // One thread's messages are in order already.
276
- if (held.size > 1)
277
- records.sort((a, b) => placeOf(a.record) - placeOf(b.record));
278
- return records;
319
+ return records.sort((a, b) => placeOf(a.record) - placeOf(b.record));
279
320
  };
280
321
  /** The highest place among its messages. */
281
322
  const topOf = (records) => {
@@ -284,63 +325,93 @@ export function createRecord(options) {
284
325
  };
285
326
  /**
286
327
  * Take the messages waiting in `thread` as its own, after all it holds: at once in memory, and
287
- * `written` settles when each is stored, or rejects with one that isn't and stays waiting. Like
288
- * a message being written, the model is sent none of them until all are stored.
328
+ * `written` settles when each is stored, or rejects with the first that isn't, which stays
329
+ * waiting with those after it. Like a message being written, the model is sent none of them
330
+ * until that has settled.
289
331
  */
290
332
  const take = (held, thread, joined) => {
291
333
  const from = held.get(thread);
292
- const waiting = thread === id ? [] : from.records.filter(({ entry }) => !entry.work);
334
+ const waiting = thread === id
335
+ ? []
336
+ : from.records.filter(({ entry }) => !entry.work).sort((a, b) => orderOf(a) - orderOf(b));
293
337
  let top = topOf(own(held));
294
- const writes = waiting.map((record) => options.thread(thread).rewrite(from, record, {
295
- ...record.entry,
296
- work: id,
297
- place: ++top,
298
- ...(joined && record.entry.role === "user" ? { joined: true } : {}),
299
- }));
300
- for (const record of waiting)
338
+ // Each gets its place in the order it was said and answered: at once in memory.
339
+ const changes = waiting.map((record) => {
301
340
  record.writing = true;
302
- const written = Promise.allSettled(writes).then((all) => {
303
- for (const record of waiting)
304
- record.writing = false;
305
- const refused = all.find((one) => one.status === "rejected");
306
- if (refused)
307
- throw refused.reason;
341
+ return options.thread(thread).change(from, record, {
342
+ ...record.entry,
343
+ work: id,
344
+ place: ++top,
345
+ ...(joined ? { joined: true } : {}),
346
+ });
308
347
  });
348
+ // Stored one after another in that order, so what a crash or a refusal leaves taken is the
349
+ // start of them: the rest stay waiting, and are taken after it.
350
+ const written = (async () => {
351
+ try {
352
+ for (const [i, change] of changes.entries())
353
+ await change.store().catch((error) => {
354
+ for (const rest of changes.slice(i))
355
+ rest.undo();
356
+ throw error;
357
+ });
358
+ }
359
+ finally {
360
+ for (const record of waiting)
361
+ record.writing = false;
362
+ }
363
+ })();
309
364
  return { taken: waiting.length, written };
310
365
  };
311
- /** Whether a user message of its follows the place `read`: one `stored` already, when asked. */
312
- const saidAfter = (records, read, stored = false) => records.some(({ record }) => record.entry.role === "user" && placeOf(record) > read && !(stored && record.writing));
366
+ /** Whether a user message of its, stored already, follows the place `read`. */
367
+ const saidAfter = (records, read) => records.some(({ record }) => record.entry.role === "user" && placeOf(record) > read && !record.writing);
368
+ /**
369
+ * Whether something the model reads after what a request made stands past the place `read`,
370
+ * which that request had read up to: a user message, or what the main loop said as it stopped
371
+ * the turn.
372
+ */
373
+ const heldBack = (records, read) => records.some(({ record }) => placeOf(record) > read && (record.entry.role === "user" || record.entry.joined === true));
313
374
  /** The thread of its newest user message: where it answers. Its own, with none. */
314
375
  const whereOf = (records) => records.findLast(({ record }) => record.entry.role === "user")?.thread ?? id;
376
+ /** Keep an entry of its in `thread`, made once nothing waits there, from what the thread holds. */
377
+ const keep = async (thread, make, at) => {
378
+ const held = await load();
379
+ const from = held.get(thread);
380
+ // What waits there is taken and stored first, so this is never kept ahead of it: it joins
381
+ // a turn if this does, or comes of one. If the store won't take it, nothing is kept.
382
+ const joins = make(from).joined === true || at.read !== undefined;
383
+ await take(held, thread, joins).written;
384
+ // From here to the push nothing waits, so its place and what is ahead of it are decided
385
+ // with the record as it stands.
386
+ const records = own(held);
387
+ const { record, stored } = options.thread(thread).push(from, {
388
+ ...make(from),
389
+ ...(thread === id ? {} : { work: id }),
390
+ ...(held.size > 1 ? { place: topOf(records) + 1 } : {}),
391
+ ...(at.read !== undefined && heldBack(records, at.read) ? { ahead: at.read } : {}),
392
+ }, at.id);
393
+ await stored;
394
+ return { entry: record.entry, thread };
395
+ };
315
396
  return {
316
397
  id,
317
398
  /**
318
399
  * Keep a message of its in `thread`, one of those it follows, after any waiting there. `read`
319
400
  * is given for what came of a model request that had read it up to that place: when a user
320
- * message was said since, it is kept as `ahead` of it.
401
+ * message was said since, or the turn was stopped by saying so, it is kept as `ahead` of
402
+ * that.
321
403
  */
322
- async append(entry, at) {
323
- const held = await load();
324
- const { thread } = at;
325
- // What waits there is taken and stored first, so this is never kept ahead of it: it joins
326
- // a turn if this does, or comes of one. If the store won't take it, nothing is kept.
327
- await take(held, thread, entry.joined === true || at.read !== undefined).written;
328
- // From here to the push nothing waits, so its place and what is ahead of it are decided
329
- // with the record as it stands.
330
- const records = own(held);
331
- const { record, stored } = options.thread(thread).push(held.get(thread), {
332
- ...entry,
333
- ...(thread === id ? {} : { work: id }),
334
- ...(held.size > 1 ? { place: topOf(records) + 1 } : {}),
335
- ...(at.read !== undefined && saidAfter(records, at.read) ? { ahead: at.read } : {}),
336
- }, at.id);
337
- await stored;
338
- return { entry: record.entry, thread };
339
- },
404
+ append: (entry, at) => keep(at.thread, () => entry, at),
405
+ /**
406
+ * Keep in `thread` what the main loop said as it stopped a turn of this work. It stands after
407
+ * all the thread holds, and like a message said in that turn, the work reads it after
408
+ * whatever the turn still writes.
409
+ */
410
+ stopLine: (text, thread) => keep(thread, (from) => ({ ...mainEntry(text, from.last), joined: true }), {}),
340
411
  /**
341
412
  * Take the messages waiting in `thread`, which it has come to follow, as its own: `joined`,
342
- * when a turn of its is running. It says how many there were to answer: in its own thread,
343
- * where nothing needs taking, those said since it last did anything.
413
+ * when a turn of its is running or a stopped one is still writing. It says how many there were to answer: in its own thread,
414
+ * where nothing needs taking, those said since it last did anything itself.
344
415
  */
345
416
  async adopt(thread, joined) {
346
417
  const held = await load();
@@ -349,12 +420,20 @@ export function createRecord(options) {
349
420
  if (thread !== id)
350
421
  return taken;
351
422
  const records = own(held);
352
- const done = records.findLastIndex(({ record }) => record.entry.role !== "user");
353
- return records.length - 1 - done;
423
+ const done = records.findLastIndex(({ record: { entry } }) => entry.role !== "user" && entry.main === undefined);
424
+ return records.slice(done + 1).filter(({ record }) => record.entry.role === "user").length;
354
425
  },
355
- /** Whether it holds anything but what was said to it: it has had a turn that did something. */
426
+ /**
427
+ * Whether it has had a turn that did something: it holds more than what was said to it and
428
+ * what the main loop said.
429
+ */
356
430
  async begun() {
357
- return own(await load()).some(({ record }) => record.entry.role !== "user");
431
+ return own(await load()).some(({ record: { entry } }) => entry.role !== "user" && entry.main === undefined);
432
+ },
433
+ /** What it last said, if anything. */
434
+ async last() {
435
+ const said = own(await load()).findLast(({ record: { entry } }) => entry.role === "assistant" && entry.main === undefined);
436
+ return said ? textOf(said.record.entry) : undefined;
358
437
  },
359
438
  /** Whether it has no message. */
360
439
  async empty() {
@@ -370,7 +449,7 @@ export function createRecord(options) {
370
449
  return first ? textOf(first.record.entry) : "";
371
450
  },
372
451
  /**
373
- * The conversation to send: the newest exchanges (each from the user message that starts a turn
452
+ * The conversation to send: the newest exchanges (each from a user message that is not `joined`
374
453
  * on) that fit the budget, and always the latest two, so "go on" after a long turn still has
375
454
  * that turn. When those two are over the budget, their oldest function arguments and outputs
376
455
  * are left out, each call still paired with an output. A message said during a turn comes after
@@ -427,7 +506,7 @@ export function createRecord(options) {
427
506
  },
428
507
  /** Whether a user message of its has been stored after the place `read`, which `input` gave. */
429
508
  async saidAfter(read) {
430
- return saidAfter(own(await load()), read, true);
509
+ return saidAfter(own(await load()), read);
431
510
  },
432
511
  };
433
512
  }