@agentshouse/kit 0.2.1-c15804a0fa28d → 0.2.1-c23bd93c02c83
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 +32 -31
- package/dist/acp.js +16 -93
- package/dist/agents.js +153 -44
- package/dist/api.js +34 -2
- package/dist/app-git.js +48 -0
- package/dist/bootstrap.json +4 -4
- package/dist/bridge.js +35 -56
- package/dist/chatgpt.js +32 -0
- package/dist/clis.js +137 -65
- package/dist/codex-main.js +58 -0
- package/dist/conversations.js +352 -244
- package/dist/copies.js +157 -116
- package/dist/end.js +23 -0
- package/dist/enrol-main.js +7 -1
- package/dist/events.js +524 -0
- package/dist/files.js +21 -1
- package/dist/git.js +10 -8
- package/dist/home.js +96 -7
- package/dist/house-cli.js +291 -0
- package/dist/house-main.js +7 -159
- package/dist/instructions.js +10 -32
- package/dist/kit-cli.js +68 -0
- package/dist/kit-main.js +8 -19
- package/dist/login.js +118 -103
- package/dist/logout.js +14 -13
- package/dist/parts.js +82 -0
- package/dist/placement.js +30 -0
- package/dist/refusals.js +12 -0
- package/dist/relay.js +12 -2
- package/dist/resident.js +84 -32
- package/dist/scope.js +203 -0
- package/dist/secret-input.js +4 -1
- package/dist/service-main.js +9 -0
- package/dist/service.js +33 -0
- package/dist/shell.js +3 -0
- package/dist/sign-in.js +34 -9
- package/dist/skills.js +81 -12
- package/dist/stream.js +87 -11
- package/dist/transcription.js +57 -9
- package/dist/update.js +30 -6
- package/dist/wire.js +6 -0
- package/dist/work.js +346 -0
- package/how-we-work.md +13 -3
- package/package.json +3 -2
- package/skills/ask-matt/SKILL.md +17 -11
- package/skills/ask-matt/agents/openai.yaml +1 -0
- package/skills/code-review/SKILL.md +5 -5
- package/skills/codebase-design/DESIGN-IT-TWICE.md +1 -1
- package/skills/codebase-design/SKILL.md +0 -2
- package/skills/diagnosing-bugs/SKILL.md +2 -4
- package/skills/domain-modeling/{CONTEXT-FORMAT.md → GLOSSARY-FORMAT.md} +10 -10
- package/skills/domain-modeling/SKILL.md +12 -14
- package/skills/grill-me/SKILL.md +1 -2
- package/skills/grill-me/agents/openai.yaml +1 -0
- package/skills/grill-with-docs/SKILL.md +1 -2
- package/skills/grill-with-docs/agents/openai.yaml +1 -0
- package/skills/grilling/SKILL.md +2 -2
- package/skills/handoff/SKILL.md +2 -3
- package/skills/handoff/agents/openai.yaml +1 -0
- package/skills/implement/SKILL.md +5 -4
- package/skills/implement/agents/openai.yaml +1 -0
- package/skills/implement-spec/SKILL.md +40 -0
- package/skills/implement-spec/agents/openai.yaml +5 -0
- package/skills/improve-codebase-architecture/SKILL.md +6 -7
- package/skills/improve-codebase-architecture/agents/openai.yaml +1 -0
- package/skills/pr/CREDITS.md +3 -0
- package/skills/pr/LICENSE +21 -0
- package/skills/pr/SKILL.md +170 -0
- package/skills/pr/agents/openai.yaml +3 -0
- package/skills/prototype/SKILL.md +0 -2
- package/skills/research/SKILL.md +0 -2
- package/skills/retro/LICENSE +21 -0
- package/skills/retro/SKILL.md +44 -0
- package/skills/retro/agents/openai.yaml +5 -0
- package/skills/tdd/SKILL.md +2 -4
- package/skills/teach/SKILL.md +3 -4
- package/skills/teach/agents/openai.yaml +1 -0
- package/skills/to-questionnaire/SKILL.md +1 -2
- package/skills/to-questionnaire/agents/openai.yaml +1 -0
- package/skills/to-spec/SKILL.md +1 -2
- package/skills/to-spec/agents/openai.yaml +1 -0
- package/skills/to-tickets/SKILL.md +3 -4
- package/skills/to-tickets/agents/openai.yaml +1 -0
- package/skills/triage/AGENT-BRIEF.md +207 -0
- package/skills/triage/LICENSE +21 -0
- package/skills/triage/OUT-OF-SCOPE.md +105 -0
- package/skills/triage/SKILL.md +112 -0
- package/skills/triage/agents/openai.yaml +5 -0
- package/skills/wait-what/SKILL.md +2 -3
- package/skills/wait-what/agents/openai.yaml +1 -0
- package/skills/wayfinder/SKILL.md +5 -6
- package/skills/wayfinder/agents/openai.yaml +1 -0
- package/skills/wizard/LICENSE +21 -0
- package/skills/wizard/SKILL.md +44 -0
- package/skills/wizard/agents/openai.yaml +3 -0
- package/skills/wizard/template.sh +223 -0
- package/skills/writing-for-agents/SKILL.md +0 -2
- package/dist/blocks.js +0 -51
- package/skills/resolving-merge-conflicts/SKILL.md +0 -16
- package/skills/resolving-merge-conflicts/agents/openai.yaml +0 -3
- /package/skills/{resolving-merge-conflicts → implement-spec}/LICENSE +0 -0
package/dist/conversations.js
CHANGED
|
@@ -1,19 +1,47 @@
|
|
|
1
1
|
import { client, } from '@agentclientprotocol/sdk';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
3
3
|
import { mkdir } from 'node:fs/promises';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { unlessAborted } from './abort.js';
|
|
6
|
-
import {
|
|
7
|
-
import { blocksOf, firstChange } from './blocks.js';
|
|
6
|
+
import { startAdapter } from './acp.js';
|
|
8
7
|
import { openBridge } from './bridge.js';
|
|
9
8
|
import { CLIS } from './clis.js';
|
|
10
|
-
import {
|
|
9
|
+
import { answeredFailure, readerFor } from './events.js';
|
|
10
|
+
import { placeFiles, uploadBytes } from './files.js';
|
|
11
11
|
import { agentBase } from './home.js';
|
|
12
12
|
import { instructions } from './instructions.js';
|
|
13
|
+
import { Parts } from './parts.js';
|
|
14
|
+
import { killMarked, Scope } from './scope.js';
|
|
13
15
|
import { holdSecretInput } from './secret-input.js';
|
|
14
16
|
import { createHowWeWork } from './skills.js';
|
|
15
17
|
import { transcribe } from './transcription.js';
|
|
18
|
+
import { workFor } from './work.js';
|
|
16
19
|
const KILLED = 'the conversation was killed';
|
|
20
|
+
const REFUSED = 'the CLI refused the prompt';
|
|
21
|
+
const NO_CONNECTION = "the Kit could not open this conversation's connection";
|
|
22
|
+
const NO_START = "the Agent's CLI did not start";
|
|
23
|
+
const NO_SESSION = "the Agent's CLI did not open a session";
|
|
24
|
+
const NO_RESUME = "the Agent's CLI could not resume this conversation";
|
|
25
|
+
const NO_SETTINGS = "the Agent's CLI refused the Agent's model, mode or effort";
|
|
26
|
+
class NotStarted extends Error {
|
|
27
|
+
}
|
|
28
|
+
async function step(reason, work) {
|
|
29
|
+
try {
|
|
30
|
+
return await work;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
throw new NotStarted(reason, { cause: error });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const EXIT_PLAN_MODE = {
|
|
37
|
+
parse(value) {
|
|
38
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
39
|
+
throw new Error('_x.ai/exit_plan_mode needs an object');
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
export const CONVERSATION_INPUTS = new Set(['open', 'message', 'interrupt', 'kill', 'option', 'answer']);
|
|
17
45
|
function causeOf(error) {
|
|
18
46
|
return error instanceof Error ? error.message : String(error);
|
|
19
47
|
}
|
|
@@ -23,34 +51,44 @@ function answerMessage(request, response) {
|
|
|
23
51
|
function logged(error) {
|
|
24
52
|
process.stderr.write(`kit: ${causeOf(error)}\n`);
|
|
25
53
|
}
|
|
26
|
-
function
|
|
27
|
-
|
|
54
|
+
function refusal(conversation, error) {
|
|
55
|
+
if (conversation.kills > 0)
|
|
56
|
+
return KILLED;
|
|
57
|
+
const cause = error instanceof NotStarted ? `${error.message}: ${causeOf(error.cause)}` : causeOf(error);
|
|
58
|
+
logged(new Error(`conversation ${conversation.id} did not start: ${cause}`));
|
|
59
|
+
return causeOf(error);
|
|
60
|
+
}
|
|
61
|
+
function marker(bridge) {
|
|
62
|
+
return `HOUSE_BRIDGE=${bridge.env.HOUSE_BRIDGE}`;
|
|
28
63
|
}
|
|
29
64
|
class Turn {
|
|
30
65
|
id = randomUUID();
|
|
31
|
-
|
|
32
|
-
segment = null;
|
|
66
|
+
parts;
|
|
33
67
|
admitted = false;
|
|
34
|
-
notes = [];
|
|
35
|
-
sent = [];
|
|
36
|
-
unsentFrom = null;
|
|
37
|
-
draftSequence = 0;
|
|
38
|
-
planSequence = 0;
|
|
39
|
-
entries = null;
|
|
40
68
|
questions = 0;
|
|
41
69
|
secrets = 0;
|
|
42
|
-
|
|
70
|
+
prompted = false;
|
|
43
71
|
ended = false;
|
|
72
|
+
images = 0;
|
|
73
|
+
uploads = [];
|
|
74
|
+
constructor(emit) {
|
|
75
|
+
this.parts = new Parts(emit);
|
|
76
|
+
}
|
|
44
77
|
}
|
|
45
78
|
class Conversation {
|
|
46
79
|
id;
|
|
47
80
|
running = null;
|
|
81
|
+
session = null;
|
|
48
82
|
turn = null;
|
|
49
83
|
commands = null;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
84
|
+
context = null;
|
|
85
|
+
leftovers = new Set();
|
|
86
|
+
waiting = [];
|
|
87
|
+
sending = null;
|
|
88
|
+
subagents = new Map();
|
|
89
|
+
held = [];
|
|
53
90
|
opening = null;
|
|
91
|
+
prompted = null;
|
|
54
92
|
kills = 0;
|
|
55
93
|
killed = new AbortController();
|
|
56
94
|
interrupts = 0;
|
|
@@ -77,8 +115,11 @@ export class Conversations {
|
|
|
77
115
|
questions = new Map();
|
|
78
116
|
carrying = 0;
|
|
79
117
|
reporting = 0;
|
|
118
|
+
watched = false;
|
|
80
119
|
constructor(kit) {
|
|
81
120
|
this.kit = kit;
|
|
121
|
+
// A process or a durable schedule ends without a report, so the Kit looks at both again every two seconds.
|
|
122
|
+
setInterval(() => this.look(), 2_000);
|
|
82
123
|
}
|
|
83
124
|
input(input) {
|
|
84
125
|
this.carrying++;
|
|
@@ -88,14 +129,17 @@ export class Conversations {
|
|
|
88
129
|
if (input.kind === 'kill') {
|
|
89
130
|
conversation.kills++;
|
|
90
131
|
conversation.killed.abort(new Error(KILLED));
|
|
132
|
+
for (const waiting of conversation.waiting.splice(0))
|
|
133
|
+
waiting.settle({ refused: KILLED });
|
|
91
134
|
this.halt(conversation);
|
|
92
135
|
}
|
|
93
136
|
if (input.kind === 'interrupt') {
|
|
94
137
|
conversation.interrupts++;
|
|
95
138
|
conversation.interrupted.abort();
|
|
96
139
|
}
|
|
97
|
-
const
|
|
98
|
-
conversation.queue =
|
|
140
|
+
const handled = conversation.queue.then(() => this.carry(conversation, input));
|
|
141
|
+
conversation.queue = handled.catch(() => undefined);
|
|
142
|
+
const work = handled.then(({ ack }) => ack);
|
|
99
143
|
this.acks.set(input.input_id, work);
|
|
100
144
|
return work;
|
|
101
145
|
})();
|
|
@@ -113,15 +157,39 @@ export class Conversations {
|
|
|
113
157
|
for (const conversation of this.conversations.values()) {
|
|
114
158
|
const turn = conversation.turn;
|
|
115
159
|
const asking = turn !== null && turn.questions > 0 && turn.secrets === 0;
|
|
116
|
-
if (conversation.
|
|
160
|
+
if (conversation.open > (asking ? 1 : 0))
|
|
117
161
|
return false;
|
|
118
162
|
}
|
|
119
|
-
|
|
163
|
+
const conversations = [...this.conversations.values()];
|
|
164
|
+
if (conversations.some((conversation) => conversation.running?.work.busy()))
|
|
165
|
+
return false;
|
|
166
|
+
return !conversations.some((conversation) => conversation.running?.scope.working() || [...conversation.leftovers].some((scope) => scope.working()));
|
|
167
|
+
}
|
|
168
|
+
quiet() {
|
|
169
|
+
return this.idle() && [...this.conversations.values()].every((conversation) => conversation.open === 0);
|
|
170
|
+
}
|
|
171
|
+
look() {
|
|
172
|
+
for (const conversation of this.conversations.values()) {
|
|
173
|
+
for (const scope of conversation.leftovers) {
|
|
174
|
+
if (!scope.empty())
|
|
175
|
+
continue;
|
|
176
|
+
scope.close();
|
|
177
|
+
conversation.leftovers.delete(scope);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
this.kit.changed();
|
|
120
181
|
}
|
|
121
182
|
opened() {
|
|
183
|
+
this.watched = false;
|
|
184
|
+
}
|
|
185
|
+
watching(watched) {
|
|
186
|
+
const resend = watched && !this.watched;
|
|
187
|
+
this.watched = watched;
|
|
188
|
+
if (!resend)
|
|
189
|
+
return;
|
|
122
190
|
for (const conversation of this.conversations.values()) {
|
|
123
191
|
if (conversation.turn !== null)
|
|
124
|
-
conversation.turn
|
|
192
|
+
this.replay(conversation, conversation.turn);
|
|
125
193
|
}
|
|
126
194
|
}
|
|
127
195
|
conversation(id) {
|
|
@@ -134,7 +202,7 @@ export class Conversations {
|
|
|
134
202
|
}
|
|
135
203
|
async carry(conversation, input) {
|
|
136
204
|
if (input.kind === 'open')
|
|
137
|
-
return this.open(conversation, input);
|
|
205
|
+
return { ack: Promise.resolve(await this.open(conversation, input)) };
|
|
138
206
|
if (input.kind === 'message')
|
|
139
207
|
return this.message(conversation, input);
|
|
140
208
|
if (input.kind === 'interrupt') {
|
|
@@ -144,6 +212,7 @@ export class Conversations {
|
|
|
144
212
|
}
|
|
145
213
|
if (input.kind === 'kill') {
|
|
146
214
|
await this.kill(conversation);
|
|
215
|
+
await conversation.sending;
|
|
147
216
|
if (--conversation.kills === 0)
|
|
148
217
|
conversation.killed = new AbortController();
|
|
149
218
|
}
|
|
@@ -152,54 +221,91 @@ export class Conversations {
|
|
|
152
221
|
if (input.kind === 'answer') {
|
|
153
222
|
const question = this.questions.get(String(input.interaction_id));
|
|
154
223
|
if (question === undefined) {
|
|
155
|
-
|
|
224
|
+
const text = answerMessage(input.request, input.response);
|
|
225
|
+
return this.message(conversation, { ...input, text, files: [], first: false });
|
|
156
226
|
}
|
|
157
227
|
question.answer(input.response);
|
|
158
228
|
}
|
|
159
|
-
return {};
|
|
229
|
+
return { ack: Promise.resolve({}) };
|
|
160
230
|
}
|
|
161
231
|
async open(conversation, input) {
|
|
162
232
|
if (conversation.running !== null)
|
|
163
233
|
return {};
|
|
164
234
|
try {
|
|
165
|
-
return { provider_session_id: await this.start(conversation, await this.route(conversation, input.agent_id), null) };
|
|
235
|
+
return { provider_session_id: await this.start(conversation, await this.route(conversation, input.agent_id), null, true) };
|
|
166
236
|
}
|
|
167
237
|
catch (error) {
|
|
168
|
-
return { refused:
|
|
238
|
+
return { refused: refusal(conversation, error) };
|
|
169
239
|
}
|
|
170
240
|
}
|
|
171
241
|
async message(conversation, input) {
|
|
172
|
-
|
|
173
|
-
|
|
242
|
+
const content = [{ type: 'text', text: String(input.text) }];
|
|
243
|
+
let route;
|
|
174
244
|
try {
|
|
175
|
-
|
|
245
|
+
route = await this.route(conversation, input.agent_id);
|
|
176
246
|
const files = input.files;
|
|
177
247
|
const paths = await placeFiles(this.kit.house, route.working_directory, files, conversation.killed.signal);
|
|
178
248
|
const lines = await this.transcribed(conversation, files, paths);
|
|
249
|
+
if (lines.length > 0)
|
|
250
|
+
content.push({ type: 'text', text: lines.join('\n') });
|
|
251
|
+
if (conversation.kills > 0)
|
|
252
|
+
throw new Error(KILLED);
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
return { ack: Promise.resolve({ refused: refusal(conversation, error) }) };
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
ack: new Promise((settle) => {
|
|
259
|
+
conversation.waiting.push({ input, route, content, settle });
|
|
260
|
+
this.next(conversation);
|
|
261
|
+
}),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
next(conversation) {
|
|
265
|
+
if (conversation.sending !== null || conversation.open > 0)
|
|
266
|
+
return;
|
|
267
|
+
const waiting = conversation.waiting.shift();
|
|
268
|
+
if (waiting === undefined)
|
|
269
|
+
return;
|
|
270
|
+
conversation.sending = this.send(conversation, waiting).finally(() => {
|
|
271
|
+
conversation.sending = null;
|
|
272
|
+
this.next(conversation);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
async send(conversation, waiting) {
|
|
276
|
+
const { input } = waiting;
|
|
277
|
+
let ack = {};
|
|
278
|
+
let started = false;
|
|
279
|
+
const prompt = [];
|
|
280
|
+
try {
|
|
179
281
|
if (conversation.running === null) {
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
282
|
+
const stored = conversation.session ?? input.provider_session_id;
|
|
283
|
+
const empty = input.first === true && conversation.prompted !== stored;
|
|
284
|
+
started = (await this.start(conversation, waiting.route, stored, empty)) !== stored;
|
|
285
|
+
}
|
|
286
|
+
const session = conversation.running.sessionId;
|
|
287
|
+
if (started || (input.first === true && session !== input.provider_session_id)) {
|
|
288
|
+
ack = { provider_session_id: session };
|
|
183
289
|
}
|
|
184
290
|
if (input.first === true) {
|
|
185
291
|
prompt.push({
|
|
186
292
|
type: 'text',
|
|
187
|
-
text: await instructions(conversation.running.bridge, route.base_instructions, await this.kit.copies.mapping()),
|
|
293
|
+
text: await instructions(conversation.running.bridge, waiting.route.base_instructions, await this.kit.copies.mapping()),
|
|
188
294
|
});
|
|
189
295
|
}
|
|
190
|
-
prompt.push({ type: 'text', text: String(input.text) });
|
|
191
|
-
if (lines.length > 0)
|
|
192
|
-
prompt.push({ type: 'text', text: lines.join('\n') });
|
|
193
296
|
if (conversation.kills > 0)
|
|
194
297
|
throw new Error(KILLED);
|
|
195
298
|
}
|
|
196
299
|
catch (error) {
|
|
197
|
-
if (
|
|
300
|
+
if (started) {
|
|
301
|
+
conversation.session = null;
|
|
198
302
|
await this.kill(conversation);
|
|
199
|
-
|
|
303
|
+
}
|
|
304
|
+
waiting.settle({ refused: refusal(conversation, error) });
|
|
305
|
+
return;
|
|
200
306
|
}
|
|
201
|
-
this.prompt(conversation, prompt);
|
|
202
|
-
|
|
307
|
+
this.prompt(conversation, [...prompt, ...waiting.content]);
|
|
308
|
+
waiting.settle(ack);
|
|
203
309
|
}
|
|
204
310
|
async transcribed(conversation, files, paths) {
|
|
205
311
|
const lines = [];
|
|
@@ -209,7 +315,7 @@ export class Conversations {
|
|
|
209
315
|
continue;
|
|
210
316
|
try {
|
|
211
317
|
const text = await transcribe(path, AbortSignal.any([conversation.killed.signal, conversation.interrupted.signal]));
|
|
212
|
-
if (text !== '')
|
|
318
|
+
if (text !== null && text !== '')
|
|
213
319
|
lines.push(`Transcript: ${text}`);
|
|
214
320
|
}
|
|
215
321
|
catch (error) {
|
|
@@ -229,11 +335,13 @@ export class Conversations {
|
|
|
229
335
|
}
|
|
230
336
|
halt(conversation) {
|
|
231
337
|
if (conversation.opening !== null)
|
|
232
|
-
|
|
338
|
+
killMarked(marker(conversation.opening));
|
|
339
|
+
for (const scope of conversation.leftovers)
|
|
340
|
+
scope.kill();
|
|
233
341
|
if (conversation.running === null)
|
|
234
342
|
return;
|
|
235
343
|
conversation.running.killed = true;
|
|
236
|
-
|
|
344
|
+
conversation.running.scope.kill();
|
|
237
345
|
}
|
|
238
346
|
async kill(conversation) {
|
|
239
347
|
const running = conversation.running;
|
|
@@ -245,6 +353,7 @@ export class Conversations {
|
|
|
245
353
|
if (running === null)
|
|
246
354
|
return;
|
|
247
355
|
try {
|
|
356
|
+
running.reader.requested(option, value);
|
|
248
357
|
const answered = await running.adapter.connection.agent.request('session/set_config_option', {
|
|
249
358
|
sessionId: running.sessionId,
|
|
250
359
|
configId: option,
|
|
@@ -253,6 +362,7 @@ export class Conversations {
|
|
|
253
362
|
this.options(conversation, answered.configOptions);
|
|
254
363
|
}
|
|
255
364
|
catch (error) {
|
|
365
|
+
running.reader.refused(option);
|
|
256
366
|
logged(error);
|
|
257
367
|
}
|
|
258
368
|
}
|
|
@@ -269,39 +379,46 @@ export class Conversations {
|
|
|
269
379
|
}
|
|
270
380
|
return route;
|
|
271
381
|
}
|
|
272
|
-
async start(conversation, route, sessionId) {
|
|
382
|
+
async start(conversation, route, sessionId, empty) {
|
|
383
|
+
const reader = readerFor(route.kind);
|
|
384
|
+
reader.session = sessionId;
|
|
273
385
|
const app = client({ name: '@agentshouse/kit' })
|
|
274
|
-
.onNotification('session/update', ({ params }) => this.update(conversation, params))
|
|
275
|
-
.onNotification(TURN_STARTED, (params) => params, () => void this.started(conversation))
|
|
276
|
-
.onNotification(TURN_ENDED, (params) => params, () => this.ended(conversation))
|
|
277
386
|
.onRequest('session/request_permission', ({ params, signal }) => this.ask(conversation, 'session/request_permission', params, false, signal))
|
|
278
|
-
.onRequest('elicitation/create', ({ params, signal }) => this.ask(conversation, 'elicitation/create', params, asksSecret(params), signal))
|
|
387
|
+
.onRequest('elicitation/create', ({ params, signal }) => this.ask(conversation, 'elicitation/create', params, asksSecret(params), signal))
|
|
388
|
+
.onRequest('_x.ai/exit_plan_mode', EXIT_PLAN_MODE, ({ params, signal }) => this.ask(conversation, '_x.ai/exit_plan_mode', params, false, signal));
|
|
279
389
|
const cli = await unlessAborted(this.kit.agents.cli(route.kind), conversation.killed.signal);
|
|
280
|
-
const bridge = await openBridge(this.kit.house, this.kit.copies, conversation.id, conversation.killed.signal);
|
|
390
|
+
const bridge = await step(NO_CONNECTION, openBridge(this.kit.house, this.kit.copies, conversation.id, conversation.killed.signal));
|
|
281
391
|
if (conversation.kills > 0) {
|
|
282
392
|
bridge.close();
|
|
283
393
|
throw new Error(KILLED);
|
|
284
394
|
}
|
|
285
395
|
conversation.opening = bridge;
|
|
396
|
+
const work = workFor(route.kind, route.working_directory, () => this.kit.changed());
|
|
397
|
+
const scope = new Scope(marker(bridge), randomUUID(), cli);
|
|
286
398
|
try {
|
|
287
399
|
await unlessAborted(createHowWeWork(bridge), conversation.killed.signal);
|
|
288
|
-
|
|
400
|
+
await CLIS[route.kind].allowHouse(bridge.env.PATH);
|
|
401
|
+
const streamed = await step(NO_CONNECTION, work.open(cli));
|
|
402
|
+
const adapter = await step(NO_START, startAdapter(route.kind, cli, route.working_directory, app, (message) => {
|
|
403
|
+
work.read(message);
|
|
404
|
+
this.read(conversation, reader, message);
|
|
405
|
+
}, { ...bridge.env, ...streamed }, undefined, scope));
|
|
289
406
|
void adapter.exited.then(() => bridge.close());
|
|
290
407
|
const agent = adapter.connection.agent;
|
|
291
408
|
const cwd = route.working_directory;
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
sessionId: opened.sessionId,
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const options = await this.launchSettings(running, route, opened.configOptions ?? []);
|
|
409
|
+
const _meta = CLIS[route.kind].sessionMeta(bridge.env.HOUSE_BRIDGE, conversation.id);
|
|
410
|
+
const resumed = sessionId === null ? null : await this.resume(conversation, route.kind, adapter, { sessionId, cwd, mcpServers: [], _meta }, empty);
|
|
411
|
+
const opened = resumed ?? (await step(NO_SESSION, agent.request('session/new', { cwd, mcpServers: [], _meta })));
|
|
412
|
+
reader.session = opened.sessionId;
|
|
413
|
+
if (opened.sessionId !== sessionId && CLIS[route.kind].renames) {
|
|
414
|
+
const prompt = [{ type: 'text', text: `/rename ${conversation.id}` }];
|
|
415
|
+
await step(NO_SESSION, agent.request('session/prompt', { sessionId: opened.sessionId, prompt }));
|
|
416
|
+
}
|
|
417
|
+
const running = { adapter, bridge, sessionId: opened.sessionId, killed: false, reader, work, scope };
|
|
418
|
+
const options = await step(NO_SETTINGS, this.launchSettings(running, route, opened.configOptions ?? []));
|
|
419
|
+
void work.settled().then(() => scope.record());
|
|
304
420
|
conversation.running = running;
|
|
421
|
+
conversation.session = running.sessionId;
|
|
305
422
|
this.kit.send({ type: 'process', conversation_id: conversation.id, running: true });
|
|
306
423
|
if (conversation.commands !== null)
|
|
307
424
|
this.commands(conversation, conversation.commands);
|
|
@@ -310,7 +427,9 @@ export class Conversations {
|
|
|
310
427
|
return running.sessionId;
|
|
311
428
|
}
|
|
312
429
|
catch (error) {
|
|
313
|
-
|
|
430
|
+
scope.kill();
|
|
431
|
+
work.close();
|
|
432
|
+
conversation.leftovers.add(scope);
|
|
314
433
|
bridge.close();
|
|
315
434
|
throw conversation.kills > 0 ? new Error(KILLED) : error;
|
|
316
435
|
}
|
|
@@ -318,9 +437,22 @@ export class Conversations {
|
|
|
318
437
|
conversation.opening = null;
|
|
319
438
|
}
|
|
320
439
|
}
|
|
440
|
+
async resume(conversation, kind, adapter, request, empty) {
|
|
441
|
+
try {
|
|
442
|
+
return { ...(await adapter.connection.agent.request('session/resume', request)), sessionId: request.sessionId };
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
if (!empty || conversation.kills > 0 || !CLIS[kind].unknownSession(error)) {
|
|
446
|
+
throw new NotStarted(NO_RESUME, { cause: error });
|
|
447
|
+
}
|
|
448
|
+
logged(new Error(`conversation ${conversation.id} opens a new session: ${NO_RESUME}: ${causeOf(error)}`));
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
321
452
|
async launchSettings(running, route, offered) {
|
|
322
453
|
let options = offered;
|
|
323
454
|
const settings = [
|
|
455
|
+
['mode', route.mode ?? CLIS[route.kind].fullAccess],
|
|
324
456
|
['model', route.model],
|
|
325
457
|
['thought_level', route.effort],
|
|
326
458
|
];
|
|
@@ -328,6 +460,7 @@ export class Conversations {
|
|
|
328
460
|
if (value === null)
|
|
329
461
|
continue;
|
|
330
462
|
const option = options.find((candidate) => candidate.category === category);
|
|
463
|
+
running.reader.requested(option?.id ?? category, value);
|
|
331
464
|
const answered = await running.adapter.connection.agent.request('session/set_config_option', {
|
|
332
465
|
sessionId: running.sessionId,
|
|
333
466
|
configId: option?.id ?? category,
|
|
@@ -341,19 +474,23 @@ export class Conversations {
|
|
|
341
474
|
const running = conversation.running;
|
|
342
475
|
if (running?.adapter !== adapter)
|
|
343
476
|
return;
|
|
477
|
+
this.detach(conversation, running);
|
|
478
|
+
if (conversation.turn !== null) {
|
|
479
|
+
this.end(conversation, conversation.turn, { failed: running.killed ? KILLED : cause });
|
|
480
|
+
}
|
|
481
|
+
this.next(conversation);
|
|
482
|
+
this.kit.changed();
|
|
483
|
+
}
|
|
484
|
+
detach(conversation, running) {
|
|
485
|
+
running.work.close();
|
|
486
|
+
conversation.leftovers.add(running.scope);
|
|
344
487
|
conversation.running = null;
|
|
345
488
|
conversation.commands = null;
|
|
346
|
-
conversation.jobs.clear();
|
|
347
|
-
conversation.queued.length = 0;
|
|
348
489
|
for (const [id, question] of this.questions) {
|
|
349
490
|
if (question.conversation === conversation)
|
|
350
491
|
this.questions.delete(id);
|
|
351
492
|
}
|
|
352
493
|
this.kit.send({ type: 'process', conversation_id: conversation.id, running: false });
|
|
353
|
-
if (conversation.turn !== null) {
|
|
354
|
-
this.end(conversation, conversation.turn, { failed: running.killed ? KILLED : cause });
|
|
355
|
-
}
|
|
356
|
-
this.kit.changed();
|
|
357
494
|
}
|
|
358
495
|
commands(conversation, commands) {
|
|
359
496
|
conversation.commands = commands;
|
|
@@ -368,7 +505,7 @@ export class Conversations {
|
|
|
368
505
|
}
|
|
369
506
|
deliver(conversation, turn, event, body) {
|
|
370
507
|
this.reporting++;
|
|
371
|
-
const delivered = conversation.reports.then(() => this.kit.house.deliver(`/kit/conversations/${conversation.id}/turns/${turn.id}/${event}`, body));
|
|
508
|
+
const delivered = conversation.reports.then(async () => this.kit.house.deliver(`/kit/conversations/${conversation.id}/turns/${turn.id}/${event}`, await body));
|
|
372
509
|
conversation.reports = delivered
|
|
373
510
|
.catch(() => undefined)
|
|
374
511
|
.finally(() => {
|
|
@@ -377,119 +514,89 @@ export class Conversations {
|
|
|
377
514
|
});
|
|
378
515
|
return delivered;
|
|
379
516
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
.then(() => this.admitted(conversation, turn))
|
|
385
|
-
.catch(logged);
|
|
386
|
-
return turn;
|
|
517
|
+
write(conversation, turn, operation) {
|
|
518
|
+
if (!turn.admitted || turn.ended || !this.watched)
|
|
519
|
+
return;
|
|
520
|
+
this.kit.send({ type: 'turn', conversation_id: conversation.id, turn_id: turn.id, ...operation });
|
|
387
521
|
}
|
|
388
|
-
|
|
389
|
-
turn.
|
|
390
|
-
|
|
391
|
-
this.kit.send(note);
|
|
392
|
-
if (turn.text !== '') {
|
|
393
|
-
turn.unsentFrom = 0;
|
|
394
|
-
this.draft(conversation, turn);
|
|
395
|
-
}
|
|
396
|
-
if (turn.entries !== null)
|
|
397
|
-
this.plan(conversation, turn);
|
|
522
|
+
replay(conversation, turn) {
|
|
523
|
+
for (const operation of turn.parts.operations())
|
|
524
|
+
this.write(conversation, turn, operation);
|
|
398
525
|
}
|
|
399
526
|
begin(conversation) {
|
|
400
|
-
const turn = this.
|
|
527
|
+
const turn = new Turn((operation) => this.write(conversation, turn, operation));
|
|
528
|
+
conversation.open++;
|
|
529
|
+
void this.deliver(conversation, turn, 'started', {})
|
|
530
|
+
.then(() => {
|
|
531
|
+
turn.admitted = true;
|
|
532
|
+
this.replay(conversation, turn);
|
|
533
|
+
})
|
|
534
|
+
.catch(logged);
|
|
401
535
|
conversation.turn = turn;
|
|
536
|
+
if (conversation.context !== null)
|
|
537
|
+
turn.parts.measure(conversation.context);
|
|
538
|
+
for (const part of conversation.held.splice(0))
|
|
539
|
+
turn.parts.start(part);
|
|
402
540
|
this.kit.changed();
|
|
403
541
|
return turn;
|
|
404
542
|
}
|
|
405
|
-
|
|
406
|
-
if (turn.ended)
|
|
407
|
-
return;
|
|
408
|
-
const segment = turn.segment;
|
|
409
|
-
turn.segment = null;
|
|
410
|
-
if (segment?.phase === 'note')
|
|
411
|
-
this.note(conversation, turn, segment.text);
|
|
412
|
-
else if (segment !== null)
|
|
413
|
-
this.answer(conversation, turn, segment.text);
|
|
414
|
-
this.end(conversation, turn, { text: turn.text });
|
|
415
|
-
}
|
|
416
|
-
end(conversation, turn, outcome) {
|
|
543
|
+
end(conversation, turn, failed, notice = null) {
|
|
417
544
|
if (turn.ended)
|
|
418
545
|
return;
|
|
546
|
+
if (conversation.turn === turn) {
|
|
547
|
+
for (const event of conversation.running?.reader.closeTurn() ?? [])
|
|
548
|
+
this.apply(conversation, turn, event);
|
|
549
|
+
}
|
|
550
|
+
if (notice !== null)
|
|
551
|
+
turn.parts.start({ type: 'marker', marker: 'retry', text: notice });
|
|
419
552
|
turn.ended = true;
|
|
420
553
|
conversation.open--;
|
|
421
554
|
if (conversation.turn === turn)
|
|
422
555
|
conversation.turn = null;
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
556
|
+
const body = Promise.allSettled(turn.uploads).then(() => ({
|
|
557
|
+
parts: turn.parts.saved(),
|
|
558
|
+
context: turn.parts.context,
|
|
559
|
+
...failed,
|
|
560
|
+
}));
|
|
561
|
+
void this.deliver(conversation, turn, 'ended', body).catch(logged);
|
|
562
|
+
this.next(conversation);
|
|
427
563
|
this.kit.changed();
|
|
428
564
|
}
|
|
429
|
-
failed(conversation, cause) {
|
|
430
|
-
if (conversation.open > 0)
|
|
431
|
-
conversation.late.push(cause);
|
|
432
|
-
else
|
|
433
|
-
this.end(conversation, this.report(conversation), { failed: cause });
|
|
434
|
-
}
|
|
435
565
|
prompt(conversation, prompt) {
|
|
436
566
|
const running = conversation.running;
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
if (
|
|
446
|
-
this.
|
|
447
|
-
|
|
448
|
-
|
|
567
|
+
const turn = this.begin(conversation);
|
|
568
|
+
turn.prompted = true;
|
|
569
|
+
conversation.prompted = running.sessionId;
|
|
570
|
+
running.adapter.connection.agent.request('session/prompt', { sessionId: running.sessionId, prompt }).then((answer) => {
|
|
571
|
+
const notice = answeredFailure(answer);
|
|
572
|
+
const failed = notice ?? (answer.stopReason === 'refusal' ? REFUSED : null);
|
|
573
|
+
this.end(conversation, turn, failed === null ? null : { failed }, notice);
|
|
574
|
+
}, async (error) => {
|
|
575
|
+
if (running.adapter.connection.signal.aborted) {
|
|
576
|
+
this.end(conversation, turn, { failed: running.killed ? KILLED : await running.adapter.exited });
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
if (conversation.open === 1 && conversation.running === running) {
|
|
580
|
+
running.scope.kill();
|
|
581
|
+
this.detach(conversation, running);
|
|
582
|
+
}
|
|
583
|
+
this.end(conversation, turn, { failed: causeOf(error) });
|
|
449
584
|
}
|
|
450
585
|
});
|
|
451
586
|
}
|
|
452
|
-
attach(turn, sent) {
|
|
453
|
-
turn.prompt = sent;
|
|
454
|
-
sent.turn = turn;
|
|
455
|
-
}
|
|
456
|
-
answered(conversation, sent, failed) {
|
|
457
|
-
const turn = sent.turn;
|
|
458
|
-
if (turn === null) {
|
|
459
|
-
const queued = conversation.queued.indexOf(sent);
|
|
460
|
-
if (queued < 0)
|
|
461
|
-
return;
|
|
462
|
-
conversation.queued.splice(queued, 1);
|
|
463
|
-
if (failed !== null)
|
|
464
|
-
this.failed(conversation, failed);
|
|
465
|
-
}
|
|
466
|
-
else if (failed !== null) {
|
|
467
|
-
if (turn.ended)
|
|
468
|
-
this.failed(conversation, failed);
|
|
469
|
-
else
|
|
470
|
-
this.end(conversation, turn, { failed });
|
|
471
|
-
}
|
|
472
|
-
else if (turn.prompt === sent) {
|
|
473
|
-
this.finish(conversation, turn);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
587
|
started(conversation) {
|
|
477
|
-
|
|
478
|
-
return conversation.turn;
|
|
479
|
-
const turn = this.begin(conversation);
|
|
480
|
-
const queued = conversation.queued.shift();
|
|
481
|
-
if (queued !== undefined)
|
|
482
|
-
this.attach(turn, queued);
|
|
483
|
-
return turn;
|
|
588
|
+
return conversation.turn ?? this.begin(conversation);
|
|
484
589
|
}
|
|
485
590
|
ended(conversation) {
|
|
486
591
|
const turn = conversation.turn;
|
|
487
592
|
if (turn === null)
|
|
488
593
|
return;
|
|
489
|
-
if (turn.
|
|
490
|
-
this.
|
|
594
|
+
if (!turn.prompted) {
|
|
595
|
+
this.end(conversation, turn, null);
|
|
491
596
|
}
|
|
492
597
|
else {
|
|
598
|
+
for (const event of conversation.running?.reader.closeTurn() ?? [])
|
|
599
|
+
this.apply(conversation, turn, event);
|
|
493
600
|
conversation.turn = null;
|
|
494
601
|
this.kit.changed();
|
|
495
602
|
}
|
|
@@ -538,101 +645,102 @@ export class Conversations {
|
|
|
538
645
|
if (content !== null)
|
|
539
646
|
this.questions.get(interactionId)?.answer({ action: 'accept', content });
|
|
540
647
|
}
|
|
541
|
-
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
this.chunk(conversation, this.started(conversation), update, update.content.text);
|
|
545
|
-
}
|
|
546
|
-
else if (update.sessionUpdate === 'plan') {
|
|
547
|
-
const turn = this.started(conversation);
|
|
548
|
-
this.close(conversation, turn);
|
|
549
|
-
turn.entries = update.entries;
|
|
550
|
-
this.plan(conversation, turn);
|
|
551
|
-
}
|
|
552
|
-
else if (update.sessionUpdate === 'tool_call') {
|
|
553
|
-
this.close(conversation, this.started(conversation));
|
|
554
|
-
}
|
|
555
|
-
else if (update.sessionUpdate === 'agent_thought_chunk') {
|
|
556
|
-
const turn = this.started(conversation);
|
|
557
|
-
if (turn.segment?.messageId !== (update.messageId ?? null))
|
|
558
|
-
this.close(conversation, turn);
|
|
559
|
-
}
|
|
560
|
-
else if (update.sessionUpdate === 'available_commands_update') {
|
|
561
|
-
this.commands(conversation, update.availableCommands);
|
|
562
|
-
}
|
|
563
|
-
else if (update.sessionUpdate === 'config_option_update') {
|
|
564
|
-
this.options(conversation, update.configOptions);
|
|
565
|
-
}
|
|
648
|
+
read(conversation, reader, message) {
|
|
649
|
+
for (const event of reader.read(message))
|
|
650
|
+
this.event(conversation, event);
|
|
566
651
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
652
|
+
event(conversation, event) {
|
|
653
|
+
switch (event.kind) {
|
|
654
|
+
case 'started':
|
|
655
|
+
this.started(conversation);
|
|
656
|
+
return;
|
|
657
|
+
case 'ended':
|
|
658
|
+
this.ended(conversation);
|
|
659
|
+
return;
|
|
660
|
+
case 'commands':
|
|
661
|
+
this.commands(conversation, event.commands);
|
|
662
|
+
return;
|
|
663
|
+
case 'options':
|
|
664
|
+
this.options(conversation, event.options);
|
|
665
|
+
return;
|
|
666
|
+
case 'context':
|
|
667
|
+
conversation.context = { used: event.used, window: event.window };
|
|
668
|
+
conversation.turn?.parts.measure(conversation.context);
|
|
669
|
+
return;
|
|
670
|
+
case 'subagent-done':
|
|
671
|
+
this.subagentDone(conversation, event.id, event.result);
|
|
672
|
+
return;
|
|
673
|
+
case 'marker':
|
|
674
|
+
case 'image':
|
|
675
|
+
if (conversation.turn !== null)
|
|
676
|
+
break;
|
|
677
|
+
if (event.kind === 'marker' && event.marker === 'job')
|
|
678
|
+
conversation.held.push({ type: 'marker', marker: 'job', text: event.text });
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
this.apply(conversation, this.started(conversation), event);
|
|
573
682
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
683
|
+
apply(conversation, turn, event) {
|
|
684
|
+
switch (event.kind) {
|
|
685
|
+
case 'text':
|
|
686
|
+
case 'reasoning':
|
|
687
|
+
turn.parts.write(event.kind, event.id, event.text);
|
|
688
|
+
return;
|
|
689
|
+
case 'command':
|
|
690
|
+
turn.parts.command();
|
|
691
|
+
return;
|
|
692
|
+
case 'subagent': {
|
|
693
|
+
const index = turn.parts.start({ type: 'subagent', description: event.description, status: 'running', result: null });
|
|
694
|
+
conversation.subagents.set(event.id, { description: event.description, turn, index });
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
case 'message':
|
|
698
|
+
turn.parts.start({ type: 'message', command: event.command, target: event.target });
|
|
699
|
+
return;
|
|
700
|
+
case 'marker':
|
|
701
|
+
turn.parts.start({ type: 'marker', marker: event.marker, text: event.text });
|
|
702
|
+
return;
|
|
703
|
+
case 'plan':
|
|
704
|
+
turn.parts.plan(event.entries);
|
|
705
|
+
return;
|
|
706
|
+
case 'image':
|
|
707
|
+
turn.uploads.push(this.image(conversation, turn, turn.parts.reserve(), event.data, event.mediaType));
|
|
708
|
+
this.kit.changed();
|
|
709
|
+
return;
|
|
582
710
|
}
|
|
583
|
-
turn.segment ??= { messageId, phase, text: '' };
|
|
584
|
-
turn.segment.text += text;
|
|
585
711
|
}
|
|
586
|
-
|
|
587
|
-
const
|
|
588
|
-
if (
|
|
712
|
+
subagentDone(conversation, id, result) {
|
|
713
|
+
const subagent = conversation.subagents.get(id);
|
|
714
|
+
if (subagent === undefined)
|
|
589
715
|
return;
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
note(conversation, turn, text) {
|
|
594
|
-
if (text.trim() === '')
|
|
716
|
+
conversation.subagents.delete(id);
|
|
717
|
+
if (subagent.turn === conversation.turn) {
|
|
718
|
+
subagent.turn.parts.done(subagent.index, result);
|
|
595
719
|
return;
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
720
|
+
}
|
|
721
|
+
const part = { type: 'subagent', description: subagent.description, status: 'done', result };
|
|
722
|
+
if (conversation.turn === null)
|
|
723
|
+
conversation.held.push(part);
|
|
599
724
|
else
|
|
600
|
-
turn.
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
});
|
|
617
|
-
if (sent) {
|
|
618
|
-
turn.draftSequence++;
|
|
619
|
-
turn.sent = blocks;
|
|
620
|
-
turn.unsentFrom = null;
|
|
725
|
+
conversation.turn.parts.start(part);
|
|
726
|
+
}
|
|
727
|
+
async image(conversation, turn, reserved, data, mediaType) {
|
|
728
|
+
const bytes = Buffer.from(data, 'base64');
|
|
729
|
+
const name = `image-${++turn.images}.${mediaType.split('/')[1]}`;
|
|
730
|
+
try {
|
|
731
|
+
const declared = (await this.deliver(conversation, turn, 'images', {
|
|
732
|
+
name,
|
|
733
|
+
media_type: mediaType,
|
|
734
|
+
bytes: bytes.length,
|
|
735
|
+
sha256: createHash('sha256').update(bytes).digest('hex'),
|
|
736
|
+
}));
|
|
737
|
+
const saved = declared.upload === undefined ? declared : await uploadBytes(declared.upload, bytes);
|
|
738
|
+
if (saved.save.status !== 'saved')
|
|
739
|
+
throw new Error(saved.save.failure ?? saved.save.status);
|
|
740
|
+
turn.parts.arrive({ type: 'image', file: saved.version, name, media_type: mediaType }, reserved);
|
|
621
741
|
}
|
|
622
|
-
|
|
623
|
-
turn.
|
|
742
|
+
catch (error) {
|
|
743
|
+
turn.parts.arrive({ type: 'marker', marker: 'retry', text: `${name} was not saved: ${causeOf(error)}` }, reserved);
|
|
624
744
|
}
|
|
625
745
|
}
|
|
626
|
-
plan(conversation, turn) {
|
|
627
|
-
const sent = this.kit.send({
|
|
628
|
-
type: 'plan',
|
|
629
|
-
conversation_id: conversation.id,
|
|
630
|
-
turn_id: turn.id,
|
|
631
|
-
sequence: turn.planSequence + 1,
|
|
632
|
-
from: 0,
|
|
633
|
-
steps: turn.entries.map((entry) => ({ label: entry.content, priority: entry.priority, status: entry.status })),
|
|
634
|
-
});
|
|
635
|
-
if (sent)
|
|
636
|
-
turn.planSequence++;
|
|
637
|
-
}
|
|
638
746
|
}
|