@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.
- 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 +17 -8
- package/dist/node_modules/@agentorg/looper/dist/src/loop.js +80 -41
- package/dist/node_modules/@agentorg/looper/dist/src/looper.d.ts +39 -5
- package/dist/node_modules/@agentorg/looper/dist/src/looper.js +290 -102
- package/dist/node_modules/@agentorg/looper/dist/src/main.d.ts +73 -17
- package/dist/node_modules/@agentorg/looper/dist/src/main.js +166 -77
- package/dist/node_modules/@agentorg/looper/dist/src/record.d.ts +76 -18
- package/dist/node_modules/@agentorg/looper/dist/src/record.js +190 -111
- package/dist/node_modules/@agentorg/shell/dist/app.css +255 -276
- package/dist/node_modules/@agentorg/shell/dist/app.js +144 -147
- package/dist/node_modules/assistant/dist/src/chats.d.ts +10 -5
- package/dist/node_modules/assistant/dist/src/chats.js +5 -4
- package/dist/node_modules/assistant/dist/src/engine.js +10 -4
- 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
|
@@ -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
|
|
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
|
|
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
|
-
/**
|
|
20
|
-
|
|
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
|
-
/**
|
|
24
|
-
|
|
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
|
-
*
|
|
29
|
-
* for a decision that is
|
|
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
|
-
|
|
32
|
-
/**
|
|
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
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
|
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
|
|
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
|
|
14
|
-
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.
|
|
49
|
-
|
|
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
|
|
52
|
-
let
|
|
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
|
-
*
|
|
56
|
-
* for a decision that is
|
|
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
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
|
195
|
+
/** Drop the request out now: it is cancelled, and it says nothing. */
|
|
107
196
|
async cancel() {
|
|
108
|
-
const cut =
|
|
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
|
-
*
|
|
111
|
-
*
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
})
|
|
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
|
-
/**
|
|
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
|
|
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
|