@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
@@ -1,34 +1,90 @@
1
1
  import type { Log, Model, ToolSpec } from "./types.js";
2
- /** What the main loop is told. */
3
- export declare const MAIN_INSTRUCTIONS = "You are the front desk. You do none of the work and you answer nobody: you say whose a message is, by calling `route` once.\n\nYou are given the pieces of work under way, each by its id and title, and what has come in where no work is listening yet.\n\n- Give it to a piece of work under way only when it points at that work explicitly: it names it, quotes it, or refers to what it is doing, and exactly one piece of work fits. Call `route` with that work's id. Its worker will read it with everything it already knows, and answer where it came in. From then on everything said there goes to that work.\n- Anything else is new work, also when it is on a subject like that of work under way. Call `route` with no `work`, and a `title` of a few words that says what the new work is.\n\nA wrong match stays, so when in doubt it is new work. Your only answer is the call.";
2
+ /** What the main loop is told, before who it speaks as. */
3
+ export declare const MAIN_INSTRUCTIONS = "You are the front desk. You do none of the work: you say whose a message is, answer it yourself when it needs no work, or stop a piece of work it asks to stop.\n\nYou are given the latest pieces of work, each by its id and title, whether it is in a turn or at rest, and a line on what it last said; then the conversation where a message has come in that no work owns.\n\n- Give it to a piece of work only when it points at that work explicitly: it names it, quotes it, or refers to what it is doing or just did, and exactly one piece of work fits. Call `route` with that work's id. Its worker will read it with everything it already knows, and answer where it came in. From then on everything said there goes to that work.\n- Answer it yourself, in plain text and calling nothing, only when it needs no tools and nothing you can't see here: a greeting, thanks, a question about who you are or about the work listed, or a request to stop that no one piece of work in a turn fits. Never say you will do, find, check or look into something: you can't, and saying so starts no work.\n- Stop a piece of work when the message asks to stop it and exactly one piece of work in a turn fits. Call `route` with only `stop`, set to its id, and say in a few words that you stopped it. If none fits, or more than one, stop nothing and call nothing: say what is in a turn, so they can say which.\n- Anything else is new work, also when it is on a subject like that of work there is. Call `route` with no `work`, and a `title` of a few words that says what the new work is.\n\nWhatever you write is said to the person as the reply. So when you give a message to work, old or new, write nothing: the worker answers.\n\nA wrong match stays, and a promise starts nothing: when in doubt whose it is, it is new work.";
4
+ /**
5
+ * What the main loop is told when a message was said to work that is in a turn: the worker has
6
+ * it, and the main loop may only stop that work.
7
+ */
8
+ export declare const HEARD_INSTRUCTIONS = "You are the front desk. You are given one piece of work that is in a turn, by its id and title, and then a message that was just said in a conversation that work follows. Its worker has the message and will read it: you don't answer it, and you give it to nobody.\n\nAct only if the message asks to stop that work. Then call `route` with only `stop`, set to its id, and say in a few words that you stopped it. Otherwise call nothing and write nothing: what you write is said only when you stop it.";
9
+ /** What is said where the main loop called to stop work and wrote nothing: whether a turn was cut. */
10
+ export declare const stopWord: (cut: boolean) => "Nothing was stopped." | "Stopped.";
4
11
  /** The one function the main loop calls. */
5
12
  export declare const ROUTE: ToolSpec;
6
13
  /** A piece of work as the main loop is told of it. */
7
- export interface Underway {
14
+ export interface Listed {
8
15
  id: string;
9
16
  title: string;
17
+ /** Which turn of it is running or waiting for its place, when one is. */
18
+ turn?: number | undefined;
19
+ /** The head of what it last said, if it has said anything. */
20
+ last?: string | undefined;
21
+ /** Whether its last turn failed. */
22
+ failed?: boolean | undefined;
23
+ }
24
+ /** A message of the thread, as the main loop reads it. */
25
+ export interface Said {
26
+ role: "user" | "assistant";
27
+ text: string;
28
+ }
29
+ /**
30
+ * What the main loop decided. `answer` is what it wrote. `placed` is whose what came in is: work
31
+ * it was told of, or new work by its title. `stop` is there when it called to stop work, and
32
+ * then nothing is placed: the turn to cut is the one it was told of, and none when that work was
33
+ * not told of as in a turn.
34
+ */
35
+ export interface Decided {
36
+ answer?: string;
37
+ placed?: {
38
+ work: string;
39
+ } | {
40
+ title: string;
41
+ };
42
+ stop?: {
43
+ work: string;
44
+ turn?: number;
45
+ };
10
46
  }
11
- /** What the main loop decided: work under way that takes the messages, or new work's title. */
12
- export type Routed = {
13
- work: string;
14
- } | {
15
- title: string;
16
- };
17
47
  /** A work's title: the first line of `text` that says anything, in a few words. */
18
48
  export declare const titled: (text: string) => string;
19
- /** What has come in, as it is put to the main loop with the work under way. */
20
- export declare const put: (texts: string[], underway: Underway[]) => string;
49
+ /**
50
+ * How much the main loop is told, enough to place a message: the thread's newest messages and the
51
+ * start of each, the latest pieces of work with the start of what each last said, and the start
52
+ * of who it speaks as.
53
+ */
54
+ export declare const TOLD: {
55
+ messages: number;
56
+ each: number;
57
+ works: number;
58
+ last: number;
59
+ voice: number;
60
+ };
21
61
  export declare function createMain(options: {
22
62
  model: Model;
23
- /** Which model to ask, none meaning the model's own default. */
24
- chosen(): Promise<string | undefined>;
63
+ /**
64
+ * Read for each request: which model to ask, none meaning the model's own default, and who the
65
+ * main loop speaks as when it answers itself.
66
+ */
67
+ request(): Promise<{
68
+ model?: string | undefined;
69
+ voice?: string | undefined;
70
+ }>;
25
71
  log: Log;
26
72
  }): {
27
73
  /**
28
- * Say whose `texts` are, given the work under way: one decision at a time. Nothing is said
29
- * for a decision that is cancelled. A request the model refuses rejects, as it refused.
74
+ * Decide on a thread, given what was said in it and the work there is. `written` is given its
75
+ * text as the model writes it. Nothing is said for a decision that is dropped. A request the
76
+ * model refuses rejects, as it refused.
77
+ */
78
+ decide(said: Said[], works: Listed[], written: (text: string) => void): Promise<Decided | undefined>;
79
+ /**
80
+ * Hear a message that joined a turn of `work`, which has it: the main loop may only stop
81
+ * that turn, and is not told who it speaks as. It gives what to say where the message was
82
+ * said if it says to stop it, and none if it doesn't or the request is dropped. Nothing it
83
+ * writes is said otherwise. A request the model refuses rejects, as it refused.
30
84
  */
31
- route(texts: string[], underway: Underway[]): Promise<Routed | undefined>;
32
- /** Drop the decision being made: its request is cancelled, and it says nothing. */
85
+ hear(text: string, work: Listed): Promise<string | undefined>;
86
+ /** Text the model writes while it answers `requestId`, if that is the request out now. */
87
+ delta(requestId: string, text: string): void;
88
+ /** Drop the request out now: it is cancelled, and it says nothing. */
33
89
  cancel(): Promise<void>;
34
90
  };
@@ -1,111 +1,200 @@
1
1
  /**
2
- * The main loop: one per looper. It is given the messages that have come in where no piece of
3
- * work is listening, and says whose they are: new work, or work under way. It does none of the
4
- * work and answers nobody. It has one function to call, and each decision is one model request
5
- * that holds nothing of the decisions before it.
2
+ * The main loop: one per looper. It is given a thread no piece of work follows, in which a message
3
+ * has come in, and the work there is. It says whose the message is, new work or work there is, or
4
+ * answers it itself when it needs no work, or stops a piece of work the message asks to stop. It
5
+ * is also told of a message said to work that is in a turn, which that work has: then it is given
6
+ * that work and that message, and may only stop that work. It does none of the work, and it has
7
+ * one function to call. Each time is one model request, which holds nothing of those before it
8
+ * but what it is given.
6
9
  */
7
10
  import { head } from "./record.js";
8
- /** What the main loop is told. */
9
- export const MAIN_INSTRUCTIONS = `You are the front desk. You do none of the work and you answer nobody: you say whose a message is, by calling \`route\` once.
11
+ /** What the main loop is told, before who it speaks as. */
12
+ export const MAIN_INSTRUCTIONS = `You are the front desk. You do none of the work: you say whose a message is, answer it yourself when it needs no work, or stop a piece of work it asks to stop.
10
13
 
11
- You are given the pieces of work under way, each by its id and title, and what has come in where no work is listening yet.
14
+ You are given the latest pieces of work, each by its id and title, whether it is in a turn or at rest, and a line on what it last said; then the conversation where a message has come in that no work owns.
12
15
 
13
- - Give it to a piece of work under way only when it points at that work explicitly: it names it, quotes it, or refers to what it is doing, and exactly one piece of work fits. Call \`route\` with that work's id. Its worker will read it with everything it already knows, and answer where it came in. From then on everything said there goes to that work.
14
- - Anything else is new work, also when it is on a subject like that of work under way. Call \`route\` with no \`work\`, and a \`title\` of a few words that says what the new work is.
16
+ - Give it to a piece of work only when it points at that work explicitly: it names it, quotes it, or refers to what it is doing or just did, and exactly one piece of work fits. Call \`route\` with that work's id. Its worker will read it with everything it already knows, and answer where it came in. From then on everything said there goes to that work.
17
+ - Answer it yourself, in plain text and calling nothing, only when it needs no tools and nothing you can't see here: a greeting, thanks, a question about who you are or about the work listed, or a request to stop that no one piece of work in a turn fits. Never say you will do, find, check or look into something: you can't, and saying so starts no work.
18
+ - Stop a piece of work when the message asks to stop it and exactly one piece of work in a turn fits. Call \`route\` with only \`stop\`, set to its id, and say in a few words that you stopped it. If none fits, or more than one, stop nothing and call nothing: say what is in a turn, so they can say which.
19
+ - Anything else is new work, also when it is on a subject like that of work there is. Call \`route\` with no \`work\`, and a \`title\` of a few words that says what the new work is.
15
20
 
16
- A wrong match stays, so when in doubt it is new work. Your only answer is the call.`;
21
+ Whatever you write is said to the person as the reply. So when you give a message to work, old or new, write nothing: the worker answers.
22
+
23
+ A wrong match stays, and a promise starts nothing: when in doubt whose it is, it is new work.`;
24
+ /**
25
+ * What the main loop is told when a message was said to work that is in a turn: the worker has
26
+ * it, and the main loop may only stop that work.
27
+ */
28
+ export const HEARD_INSTRUCTIONS = `You are the front desk. You are given one piece of work that is in a turn, by its id and title, and then a message that was just said in a conversation that work follows. Its worker has the message and will read it: you don't answer it, and you give it to nobody.
29
+
30
+ Act only if the message asks to stop that work. Then call \`route\` with only \`stop\`, set to its id, and say in a few words that you stopped it. Otherwise call nothing and write nothing: what you write is said only when you stop it.`;
31
+ /** What is said where the main loop called to stop work and wrote nothing: whether a turn was cut. */
32
+ export const stopWord = (cut) => (cut ? "Stopped." : "Nothing was stopped.");
33
+ /** How who it speaks as is told after that. */
34
+ const VOICE = "When you answer yourself, you speak as the one described below. What that one is told to do is a worker's to do, not yours:";
17
35
  /** The one function the main loop calls. */
18
36
  export const ROUTE = {
19
37
  name: "route",
20
- description: "Says whose what came in is: a piece of work under way, or new work.",
38
+ description: "Says whose what came in is: a piece of work there is, or new work. Or stops a piece of work.",
21
39
  parameters: {
22
40
  type: "object",
23
41
  properties: {
24
42
  work: {
25
43
  type: "string",
26
- description: "The id of the work under way that it points at. Leave it out for new work.",
44
+ description: "The id of the work that it points at. Leave it out for new work.",
27
45
  },
28
46
  title: {
29
47
  type: "string",
30
48
  description: "For new work: a few words that say what it is.",
31
49
  },
50
+ stop: {
51
+ type: "string",
52
+ description: "The id of the work in a turn to stop. With it, leave the others out.",
53
+ },
32
54
  },
33
55
  },
34
56
  };
35
57
  /** A work's title: the first line of `text` that says anything, in a few words. */
36
58
  export const titled = (text) => head((text.split("\n").find((line) => /\S/.test(line)) ?? "").replace(/\s+/g, " ").trim(), 80);
37
- /** The main loop is put the newest messages waiting, and the start of each: enough to place them. */
38
- const PUT = { messages: 8, each: 4000 };
39
- /** What has come in, as it is put to the main loop with the work under way. */
40
- export const put = (texts, underway) => [
41
- underway.length === 0 ? "No work is under way." : "Work under way:",
42
- ...underway.map(({ id, title }) => `- ${id}: ${title}`),
43
- "",
44
- "What came in:",
45
- texts
46
- .slice(-PUT.messages)
47
- .map((text) => (text.length > PUT.each ? `${head(text, PUT.each)}…` : text))
48
- .join("\n\n"),
49
- ].join("\n");
59
+ /**
60
+ * How much the main loop is told, enough to place a message: the thread's newest messages and the
61
+ * start of each, the latest pieces of work with the start of what each last said, and the start
62
+ * of who it speaks as.
63
+ */
64
+ export const TOLD = { messages: 8, each: 4000, works: 12, last: 200, voice: 100_000 };
65
+ /** The work there is, as the main loop is told of it. */
66
+ const listing = (works) => works.length === 0
67
+ ? "There is no work."
68
+ : [
69
+ "The work there is:",
70
+ ...works.map(({ id, title, turn, last, failed }) => {
71
+ const state = turn !== undefined ? "in a turn" : failed ? "at rest; its last turn failed" : "at rest";
72
+ return `- ${id}: ${title} (${state})${last ? `; it last said: ${last}` : ""}`;
73
+ }),
74
+ ].join("\n");
75
+ /** What the main loop's request holds: the work there is, then the thread as a conversation. */
76
+ const put = (said, works) => [
77
+ { type: "message", role: "user", content: listing(works) },
78
+ ...said.slice(-TOLD.messages).map(({ role, text }) => ({
79
+ type: "message",
80
+ role,
81
+ content: text.length > TOLD.each ? `${head(text, TOLD.each)}…` : text,
82
+ })),
83
+ ];
50
84
  export function createMain(options) {
51
- /** The decision being made now, and how to drop it. */
52
- let deciding;
85
+ /** The request out now, whether it was dropped, and where its text goes as it is written. */
86
+ let asking;
87
+ /**
88
+ * Make one request of the model: what it wrote, and what it called `route` with. `voiced` is
89
+ * whether it is told who it speaks as. None for a request that is dropped, which the model is
90
+ * then not asked, or told to cancel; one the model refuses rejects, as it refused.
91
+ */
92
+ const ask = async (instructions, voiced, input, written) => {
93
+ let drop = () => { };
94
+ const gone = new Promise((resolve) => {
95
+ drop = () => {
96
+ mine.dropped = true;
97
+ resolve(undefined);
98
+ };
99
+ });
100
+ const mine = { drop: () => drop(), written };
101
+ asking = mine;
102
+ try {
103
+ const given = await Promise.race([options.request().then((to) => ({ to })), gone]);
104
+ if (!given || mine.dropped)
105
+ return undefined;
106
+ const { model, voice } = given.to;
107
+ mine.requestId = crypto.randomUUID();
108
+ const asked = options.model.respond({
109
+ requestId: mine.requestId,
110
+ ...(model ? { model } : {}),
111
+ instructions: voiced && voice
112
+ ? `${instructions}\n\n${VOICE}\n\n${head(voice, TOLD.voice)}`
113
+ : instructions,
114
+ input,
115
+ tools: [ROUTE],
116
+ });
117
+ // A model that never answers a request it was told to cancel holds nothing up.
118
+ asked.catch(() => { });
119
+ const reply = await Promise.race([asked, gone]);
120
+ if (!reply || mine.dropped)
121
+ return undefined;
122
+ const text = reply.output
123
+ .flatMap((item) => (item.type === "message" ? [item.content] : []))
124
+ .join("");
125
+ const call = reply.output.find((item) => item.type === "function_call" && item.name === ROUTE.name);
126
+ let routed;
127
+ if (call?.type === "function_call")
128
+ try {
129
+ routed = (JSON.parse(call.arguments) ?? {});
130
+ }
131
+ catch {
132
+ // Arguments that don't read: as a call that says nothing.
133
+ routed = {};
134
+ }
135
+ return { ...(text.trim() ? { text } : {}), routed };
136
+ }
137
+ finally {
138
+ if (asking === mine)
139
+ asking = undefined;
140
+ }
141
+ };
142
+ /** The turn of the work that `stop` names, if it was told of as in a turn. */
143
+ const turnOf = (stop, works) => works.find(({ id }) => id === stop)?.turn;
53
144
  return {
54
145
  /**
55
- * Say whose `texts` are, given the work under way: one decision at a time. Nothing is said
56
- * for a decision that is cancelled. A request the model refuses rejects, as it refused.
146
+ * Decide on a thread, given what was said in it and the work there is. `written` is given its
147
+ * text as the model writes it. Nothing is said for a decision that is dropped. A request the
148
+ * model refuses rejects, as it refused.
57
149
  */
58
- async route(texts, underway) {
59
- let drop = () => { };
60
- const dropped = new Promise((resolve) => {
61
- drop = () => resolve(undefined);
62
- });
63
- const mine = { drop };
64
- deciding = mine;
65
- try {
66
- const chosen = await Promise.race([options.chosen().then((model) => ({ model })), dropped]);
67
- if (!chosen)
68
- return undefined;
69
- mine.requestId = crypto.randomUUID();
70
- const asked = options.model.respond({
71
- requestId: mine.requestId,
72
- ...(chosen.model ? { model: chosen.model } : {}),
73
- instructions: MAIN_INSTRUCTIONS,
74
- input: [{ type: "message", role: "user", content: put(texts, underway) }],
75
- tools: [ROUTE],
76
- });
77
- // A model that never answers a request it was told to cancel holds nothing up.
78
- asked.catch(() => { });
79
- const answer = await Promise.race([asked, dropped]);
80
- if (!answer)
81
- return undefined;
82
- const call = answer.output.find((item) => item.type === "function_call" && item.name === ROUTE.name);
83
- let said = {};
84
- if (call?.type === "function_call")
85
- try {
86
- said = (JSON.parse(call.arguments) ?? {});
87
- }
88
- catch {
89
- // Arguments that don't read: new work, as with no call.
90
- }
91
- if (underway.some(({ id }) => id === said.work))
92
- return { work: said.work };
93
- // Anything else starts new work, titled by what came in if the model gave no title.
94
- if (typeof said.work === "string" && said.work !== "")
95
- options.log.warn("The main loop named work that isn't under way; it is new work.", {
96
- work: head(said.work, 128),
97
- });
98
- const title = titled(typeof said.title === "string" ? said.title : "");
99
- return { title: title || titled(texts.join("\n")) };
100
- }
101
- finally {
102
- if (deciding === mine)
103
- deciding = undefined;
150
+ async decide(said, works, written) {
151
+ const asked = await ask(MAIN_INSTRUCTIONS, true, put(said, works), written);
152
+ if (!asked)
153
+ return undefined;
154
+ const { text, routed } = asked;
155
+ const answered = text === undefined ? {} : { answer: text };
156
+ // Text and no call: it answered, and nothing is placed.
157
+ if (!routed && text !== undefined)
158
+ return answered;
159
+ // A call that names work to stop only stops: nothing is placed.
160
+ if (typeof routed?.stop === "string" && routed.stop !== "") {
161
+ const turn = turnOf(routed.stop, works);
162
+ return {
163
+ ...answered,
164
+ stop: { work: routed.stop, ...(turn === undefined ? {} : { turn }) },
165
+ };
104
166
  }
167
+ if (works.some(({ id }) => id === routed?.work))
168
+ return { ...answered, placed: { work: routed?.work } };
169
+ // Anything else starts new work, titled by what came in if the model gave no title.
170
+ if (typeof routed?.work === "string" && routed.work !== "")
171
+ options.log.warn("The main loop named work there isn't; it is new work.", {
172
+ work: head(routed.work, 128),
173
+ });
174
+ const title = titled(typeof routed?.title === "string" ? routed.title : "");
175
+ const newest = said.findLast(({ role }) => role === "user")?.text ?? "";
176
+ return { ...answered, placed: { title: title || titled(newest) } };
177
+ },
178
+ /**
179
+ * Hear a message that joined a turn of `work`, which has it: the main loop may only stop
180
+ * that turn, and is not told who it speaks as. It gives what to say where the message was
181
+ * said if it says to stop it, and none if it doesn't or the request is dropped. Nothing it
182
+ * writes is said otherwise. A request the model refuses rejects, as it refused.
183
+ */
184
+ async hear(text, work) {
185
+ const asked = await ask(HEARD_INSTRUCTIONS, false, put([{ role: "user", text }], [work]), () => { });
186
+ return asked && turnOf(asked.routed?.stop, [work]) !== undefined
187
+ ? (asked.text ?? stopWord(true))
188
+ : undefined;
189
+ },
190
+ /** Text the model writes while it answers `requestId`, if that is the request out now. */
191
+ delta(requestId, text) {
192
+ if (asking?.requestId === requestId)
193
+ asking.written(text);
105
194
  },
106
- /** Drop the decision being made: its request is cancelled, and it says nothing. */
195
+ /** Drop the request out now: it is cancelled, and it says nothing. */
107
196
  async cancel() {
108
- const cut = deciding;
197
+ const cut = asking;
109
198
  if (!cut)
110
199
  return;
111
200
  cut.drop();
@@ -41,6 +41,7 @@ export declare const Entry: z.ZodObject<{
41
41
  joined: z.ZodOptional<z.ZodLiteral<true>>;
42
42
  ahead: z.ZodOptional<z.ZodNumber>;
43
43
  work: z.ZodOptional<z.ZodString>;
44
+ main: z.ZodOptional<z.ZodNumber>;
44
45
  place: z.ZodOptional<z.ZodNumber>;
45
46
  }, z.core.$strip>;
46
47
  export type Entry = z.output<typeof Entry>;
@@ -97,20 +98,62 @@ export declare function createThread(store: Store, log: Log, limits?: {
97
98
  }): {
98
99
  /** What it holds, read once. */
99
100
  load: () => Promise<Held>;
100
- /**
101
- * Keep a message in a thread already read, at once, under `id` or its own number; `stored`
102
- * settles when it is written and the oldest past the cap are deleted. A tool message the store
103
- * refuses is still kept in memory, since its call has run; any other is taken out again.
104
- */
105
- push(held: Held, entry: Omit<Entry, "id" | "at">, id?: string): {
101
+ push: (held: Held, entry: Omit<Entry, "id" | "at">, id?: string) => {
106
102
  record: Kept;
107
103
  stored: Promise<void>;
108
104
  };
109
105
  /**
110
- * Keep a message as `entry` from now on: at once in memory, then in the store. A write the
111
- * store refuses leaves it as it was.
106
+ * What was said in it, as a conversation: each message and each reply the main loop gave, in
107
+ * the order they were answered; and the number it holds up to.
108
+ */
109
+ said(): Promise<{
110
+ said: {
111
+ role: "user" | "assistant";
112
+ text: string;
113
+ }[];
114
+ upTo: number;
115
+ }>;
116
+ /**
117
+ * Keep, under `id`, a reply the main loop gave after it had read up to `upTo`. `late` says
118
+ * whether a message was said after that, which this reply doesn't answer.
119
+ */
120
+ answer(text: string, upTo: number, id: string): Promise<{
121
+ entry: {
122
+ id: string;
123
+ role: "assistant" | "tool" | "user";
124
+ text?: string | undefined;
125
+ at: string;
126
+ items: ({
127
+ type: "message";
128
+ role: "assistant" | "user";
129
+ content: string;
130
+ } | {
131
+ type: "function_call";
132
+ callId: string;
133
+ name: string;
134
+ arguments: string;
135
+ } | {
136
+ type: "function_call_output";
137
+ callId: string;
138
+ output: string;
139
+ })[];
140
+ state?: "done" | "refused" | undefined;
141
+ joined?: true | undefined;
142
+ ahead?: number | undefined;
143
+ work?: string | undefined;
144
+ main?: number | undefined;
145
+ place?: number | undefined;
146
+ };
147
+ late: boolean;
148
+ }>;
149
+ /**
150
+ * Hold a message as `entry` from now on, in memory: `store` writes it as it then is, and
151
+ * `undo` puts it back as it was.
112
152
  */
113
- rewrite(held: Held, record: Kept, entry: Entry): Promise<void>;
153
+ change(held: Held, record: Kept, entry: Entry): {
154
+ store: () => Promise<unknown>;
155
+ undo: () => void;
156
+ };
114
157
  /** Messages before `before`, oldest first, as many as fit `limit` and `PAGE_BUDGET`. */
115
158
  page(before: string | undefined, limit: number): Promise<{
116
159
  messages: Shown[];
@@ -121,8 +164,8 @@ export type Thread = ReturnType<typeof createThread>;
121
164
  /**
122
165
  * A piece of work's record: its messages across the threads it follows, in the order it took them.
123
166
  * Its id is the name of its own thread, whose messages are its unless they name another work; in
124
- * any other thread, the messages that name it are. A message's place is its number in its own
125
- * thread until the work follows a second thread, and a count kept with each message after. A
167
+ * any other thread, the messages that name it are. A message's place is where it stands in its
168
+ * own thread until the work follows a second thread, and a count kept with each message after. A
126
169
  * message waiting in a thread it follows, said before the work had the thread, names no work yet:
127
170
  * the work takes it, in the order it was said, before it keeps anything else there.
128
171
  */
@@ -136,24 +179,39 @@ export declare function createRecord(options: {
136
179
  /**
137
180
  * Keep a message of its in `thread`, one of those it follows, after any waiting there. `read`
138
181
  * is given for what came of a model request that had read it up to that place: when a user
139
- * message was said since, it is kept as `ahead` of it.
182
+ * message was said since, or the turn was stopped by saying so, it is kept as `ahead` of
183
+ * that.
140
184
  */
141
- append(entry: Omit<Entry, "id" | "at" | "work" | "place" | "ahead">, at: {
185
+ append: (entry: Omit<Entry, "id" | "at" | "work" | "place" | "ahead">, at: {
142
186
  thread: string;
143
187
  id?: string;
144
188
  read?: number;
145
- }): Promise<{
189
+ }) => Promise<{
190
+ entry: Entry;
191
+ thread: string;
192
+ }>;
193
+ /**
194
+ * Keep in `thread` what the main loop said as it stopped a turn of this work. It stands after
195
+ * all the thread holds, and like a message said in that turn, the work reads it after
196
+ * whatever the turn still writes.
197
+ */
198
+ stopLine: (text: string, thread: string) => Promise<{
146
199
  entry: Entry;
147
200
  thread: string;
148
201
  }>;
149
202
  /**
150
203
  * Take the messages waiting in `thread`, which it has come to follow, as its own: `joined`,
151
- * when a turn of its is running. It says how many there were to answer: in its own thread,
152
- * where nothing needs taking, those said since it last did anything.
204
+ * 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,
205
+ * where nothing needs taking, those said since it last did anything itself.
153
206
  */
154
207
  adopt(thread: string, joined: boolean): Promise<number>;
155
- /** Whether it holds anything but what was said to it: it has had a turn that did something. */
208
+ /**
209
+ * Whether it has had a turn that did something: it holds more than what was said to it and
210
+ * what the main loop said.
211
+ */
156
212
  begun(): Promise<boolean>;
213
+ /** What it last said, if anything. */
214
+ last(): Promise<string | undefined>;
157
215
  /** Whether it has no message. */
158
216
  empty(): Promise<boolean>;
159
217
  /** The thread it answers in now. */
@@ -161,7 +219,7 @@ export declare function createRecord(options: {
161
219
  /** Its first user message, for a title. */
162
220
  opening(): Promise<string>;
163
221
  /**
164
- * The conversation to send: the newest exchanges (each from the user message that starts a turn
222
+ * The conversation to send: the newest exchanges (each from a user message that is not `joined`
165
223
  * on) that fit the budget, and always the latest two, so "go on" after a long turn still has
166
224
  * that turn. When those two are over the budget, their oldest function arguments and outputs
167
225
  * are left out, each call still paired with an output. A message said during a turn comes after