@agentshouse/kit 0.2.1-cf6326dfa5c3e → 0.2.1-cf6a92e13f031
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 +22 -22
- package/dist/acp.js +2 -2
- package/dist/agents.js +2 -2
- package/dist/api.js +5 -5
- package/dist/bootstrap.json +4 -4
- package/dist/bridge.js +1 -1
- package/dist/{inputs.js → calls.js} +20 -20
- package/dist/clis.js +6 -3
- package/dist/{stream.js → connection.js} +30 -27
- package/dist/conversations.js +220 -105
- package/dist/copies.js +15 -29
- package/dist/end.js +6 -2
- package/dist/events.js +2 -2
- package/dist/files.js +123 -37
- package/dist/git.js +1 -1
- package/dist/house-cli.js +81 -86
- package/dist/instructions.js +28 -8
- package/dist/kit-cli.js +2 -2
- package/dist/login.js +1 -3
- package/dist/mcp.js +4 -2
- package/dist/placement.js +11 -2
- package/dist/resident.js +38 -32
- package/dist/scope.js +3 -0
- package/dist/service.js +1 -1
- package/dist/sign-in.js +19 -19
- package/dist/skills.js +12 -93
- package/dist/transcription.js +51 -20
- package/dist/update.js +32 -32
- package/dist/web-wizard.js +100 -0
- package/dist/wire.js +19 -3
- package/package.json +6 -6
- package/how-we-work.md +0 -33
- package/skills/ask-matt/LICENSE +0 -21
- package/skills/ask-matt/PHASE-BOUNDARIES.md +0 -55
- package/skills/ask-matt/SKILL.md +0 -97
- package/skills/ask-matt/agents/openai.yaml +0 -5
- package/skills/code-review/LICENSE +0 -21
- package/skills/code-review/SKILL.md +0 -89
- package/skills/code-review/agents/openai.yaml +0 -3
- package/skills/codebase-design/DEEPENING.md +0 -37
- package/skills/codebase-design/DESIGN-IT-TWICE.md +0 -44
- package/skills/codebase-design/LICENSE +0 -21
- package/skills/codebase-design/SKILL.md +0 -114
- package/skills/codebase-design/agents/openai.yaml +0 -3
- package/skills/diagnosing-bugs/LICENSE +0 -21
- package/skills/diagnosing-bugs/SKILL.md +0 -138
- package/skills/diagnosing-bugs/agents/openai.yaml +0 -3
- package/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +0 -44
- package/skills/domain-modeling/ADR-FORMAT.md +0 -47
- package/skills/domain-modeling/GLOSSARY-FORMAT.md +0 -60
- package/skills/domain-modeling/LICENSE +0 -21
- package/skills/domain-modeling/SKILL.md +0 -74
- package/skills/domain-modeling/agents/openai.yaml +0 -3
- package/skills/grill-me/LICENSE +0 -21
- package/skills/grill-me/SKILL.md +0 -7
- package/skills/grill-me/agents/openai.yaml +0 -5
- package/skills/grill-with-docs/LICENSE +0 -21
- package/skills/grill-with-docs/SKILL.md +0 -7
- package/skills/grill-with-docs/agents/openai.yaml +0 -5
- package/skills/grilling/LICENSE +0 -21
- package/skills/grilling/SKILL.md +0 -30
- package/skills/grilling/agents/openai.yaml +0 -3
- package/skills/handoff/LICENSE +0 -21
- package/skills/handoff/SKILL.md +0 -16
- package/skills/handoff/agents/openai.yaml +0 -5
- package/skills/implement/LICENSE +0 -21
- package/skills/implement/SKILL.md +0 -17
- package/skills/implement/agents/openai.yaml +0 -5
- package/skills/implement-spec/LICENSE +0 -21
- package/skills/implement-spec/SKILL.md +0 -40
- package/skills/implement-spec/agents/openai.yaml +0 -5
- package/skills/improve-codebase-architecture/HTML-REPORT.md +0 -123
- package/skills/improve-codebase-architecture/LICENSE +0 -21
- package/skills/improve-codebase-architecture/SKILL.md +0 -71
- package/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/skills/pr/CREDITS.md +0 -3
- package/skills/pr/LICENSE +0 -21
- package/skills/pr/SKILL.md +0 -170
- package/skills/pr/agents/openai.yaml +0 -3
- package/skills/prototype/LICENSE +0 -21
- package/skills/prototype/LOGIC.md +0 -67
- package/skills/prototype/SKILL.md +0 -26
- package/skills/prototype/UI.md +0 -112
- package/skills/prototype/agents/openai.yaml +0 -3
- package/skills/research/LICENSE +0 -21
- package/skills/research/SKILL.md +0 -12
- package/skills/research/agents/openai.yaml +0 -3
- package/skills/retro/LICENSE +0 -21
- package/skills/retro/SKILL.md +0 -44
- package/skills/retro/agents/openai.yaml +0 -5
- package/skills/tdd/LICENSE +0 -21
- package/skills/tdd/SKILL.md +0 -38
- package/skills/tdd/agents/openai.yaml +0 -3
- package/skills/tdd/mocking.md +0 -59
- package/skills/tdd/tests.md +0 -77
- package/skills/teach/GLOSSARY-FORMAT.md +0 -35
- package/skills/teach/LEARNING-RECORD-FORMAT.md +0 -46
- package/skills/teach/LICENSE +0 -21
- package/skills/teach/MISSION-FORMAT.md +0 -31
- package/skills/teach/RESOURCES-FORMAT.md +0 -32
- package/skills/teach/SKILL.md +0 -140
- package/skills/teach/agents/openai.yaml +0 -5
- package/skills/to-questionnaire/LICENSE +0 -21
- package/skills/to-questionnaire/SKILL.md +0 -54
- package/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/skills/to-spec/LICENSE +0 -21
- package/skills/to-spec/SKILL.md +0 -75
- package/skills/to-spec/agents/openai.yaml +0 -5
- package/skills/to-tickets/LICENSE +0 -21
- package/skills/to-tickets/SKILL.md +0 -105
- package/skills/to-tickets/agents/openai.yaml +0 -5
- package/skills/triage/AGENT-BRIEF.md +0 -207
- package/skills/triage/LICENSE +0 -21
- package/skills/triage/OUT-OF-SCOPE.md +0 -105
- package/skills/triage/SKILL.md +0 -112
- package/skills/triage/agents/openai.yaml +0 -5
- package/skills/wait-what/LICENSE +0 -21
- package/skills/wait-what/SKILL.md +0 -7
- package/skills/wait-what/agents/openai.yaml +0 -5
- package/skills/wayfinder/LICENSE +0 -21
- package/skills/wayfinder/SKILL.md +0 -128
- package/skills/wayfinder/agents/openai.yaml +0 -5
- package/skills/wizard/LICENSE +0 -21
- package/skills/wizard/SKILL.md +0 -44
- package/skills/wizard/agents/openai.yaml +0 -3
- package/skills/wizard/template.sh +0 -223
- package/skills/writing-for-agents/LICENSE +0 -21
- package/skills/writing-for-agents/SKILL-MECHANICS.md +0 -22
- package/skills/writing-for-agents/SKILL.md +0 -81
- package/skills/writing-for-agents/agents/openai.yaml +0 -3
package/dist/conversations.js
CHANGED
|
@@ -4,22 +4,23 @@ import { mkdir } from 'node:fs/promises';
|
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { unlessAborted } from './abort.js';
|
|
6
6
|
import { startAdapter } from './acp.js';
|
|
7
|
-
import { finalRefusal } from './api.js';
|
|
7
|
+
import { finalRefusal, HouseRefusal } from './api.js';
|
|
8
8
|
import { openBridge } from './bridge.js';
|
|
9
9
|
import { CLIS } from './clis.js';
|
|
10
10
|
import { answeredFailure, readerFor } from './events.js';
|
|
11
|
-
import {
|
|
11
|
+
import { inspectedFile, linkedPaths, storeRefusal, uploaded } from './files.js';
|
|
12
12
|
import { agentBase } from './home.js';
|
|
13
13
|
import { instructions } from './instructions.js';
|
|
14
14
|
import { Parts } from './parts.js';
|
|
15
15
|
import { refusalOf } from './refusals.js';
|
|
16
|
+
import { relayed } from './relay.js';
|
|
16
17
|
import { killMarked, Scope } from './scope.js';
|
|
17
18
|
import { holdSecretInput } from './secret-input.js';
|
|
18
|
-
import { createHowWeWork } from './skills.js';
|
|
19
19
|
import { transcribe } from './transcription.js';
|
|
20
20
|
import { workFor } from './work.js';
|
|
21
21
|
const SIGNED_OUT = RequestError.authRequired().code;
|
|
22
22
|
const KILLED = 'the conversation was killed';
|
|
23
|
+
const INTERRUPTED = 'the conversation was interrupted';
|
|
23
24
|
const REFUSED = 'the CLI refused the prompt';
|
|
24
25
|
const NO_CONNECTION = "the Agent's computer could not open this conversation; try again";
|
|
25
26
|
const GONE = 'House no longer holds this conversation; start a new one';
|
|
@@ -35,6 +36,8 @@ const NOT_SIGNED_IN = "the Agent's CLI is not signed in; sign it in on the Agent
|
|
|
35
36
|
const NO_SETTINGS = "the Agent's CLI refused the Agent's model, mode or effort";
|
|
36
37
|
class NotStarted extends Error {
|
|
37
38
|
}
|
|
39
|
+
class ResumeRefused extends NotStarted {
|
|
40
|
+
}
|
|
38
41
|
async function step(reason, work) {
|
|
39
42
|
try {
|
|
40
43
|
return await work;
|
|
@@ -62,7 +65,7 @@ const EXIT_PLAN_MODE = {
|
|
|
62
65
|
return value;
|
|
63
66
|
},
|
|
64
67
|
};
|
|
65
|
-
export const
|
|
68
|
+
export const CONVERSATION_METHODS = new Set(['open', 'message', 'interrupt', 'kill', 'option', 'answer']);
|
|
66
69
|
function causeOf(error) {
|
|
67
70
|
return error instanceof Error ? error.message : String(error);
|
|
68
71
|
}
|
|
@@ -75,16 +78,28 @@ function exitedWith(running, cause) {
|
|
|
75
78
|
function answerMessage(request, response) {
|
|
76
79
|
return `The User answered a question you asked earlier.\nQuestion: ${JSON.stringify(request)}\nAnswer: ${JSON.stringify(response)}`;
|
|
77
80
|
}
|
|
81
|
+
async function sentPart(url, bytes) {
|
|
82
|
+
const answer = await fetch(relayed(url), { method: 'PUT', body: bytes });
|
|
83
|
+
await answer.arrayBuffer();
|
|
84
|
+
if (!answer.ok)
|
|
85
|
+
throw storeRefusal('upload', answer.status);
|
|
86
|
+
}
|
|
78
87
|
function logged(error) {
|
|
79
88
|
process.stderr.write(`kit: ${causeOf(error)}\n`);
|
|
80
89
|
}
|
|
81
90
|
function refusal(conversation, error) {
|
|
82
91
|
if (conversation.kills > 0)
|
|
83
92
|
return KILLED;
|
|
84
|
-
const cause = error instanceof
|
|
93
|
+
const cause = error instanceof Error && error.cause !== undefined ? `${error.message}: ${causeOf(error.cause)}` : causeOf(error);
|
|
85
94
|
logged(new Error(`conversation ${conversation.id} did not start: ${cause}`));
|
|
86
95
|
return causeOf(error);
|
|
87
96
|
}
|
|
97
|
+
function audioOf(result) {
|
|
98
|
+
if (result.isError === true)
|
|
99
|
+
return null;
|
|
100
|
+
const file = inspectedFile(result.content.map((content) => content.text).join('\n'));
|
|
101
|
+
return file?.media_type?.startsWith('audio/') === true ? file : null;
|
|
102
|
+
}
|
|
88
103
|
function marker(bridge) {
|
|
89
104
|
return `HOUSE_BRIDGE=${bridge.env.HOUSE_BRIDGE}`;
|
|
90
105
|
}
|
|
@@ -108,6 +123,8 @@ class Conversation {
|
|
|
108
123
|
session = null;
|
|
109
124
|
turn = null;
|
|
110
125
|
commands = null;
|
|
126
|
+
options = null;
|
|
127
|
+
exitUntold = false;
|
|
111
128
|
context = null;
|
|
112
129
|
leftovers = new Set();
|
|
113
130
|
waiting = [];
|
|
@@ -115,6 +132,8 @@ class Conversation {
|
|
|
115
132
|
subagents = new Map();
|
|
116
133
|
held = [];
|
|
117
134
|
opening = null;
|
|
135
|
+
bridge = null;
|
|
136
|
+
launch = null;
|
|
118
137
|
prompted = null;
|
|
119
138
|
kills = 0;
|
|
120
139
|
killed = new AbortController();
|
|
@@ -151,23 +170,23 @@ export class Conversations {
|
|
|
151
170
|
// A process or a durable schedule ends without a report, so the Kit looks at both again every two seconds.
|
|
152
171
|
setInterval(() => this.look(), 2_000);
|
|
153
172
|
}
|
|
154
|
-
|
|
155
|
-
void this.kit.
|
|
156
|
-
const conversation = this.conversation(
|
|
157
|
-
if (
|
|
173
|
+
call(call) {
|
|
174
|
+
void this.kit.calls.carry(call.input_id, () => {
|
|
175
|
+
const conversation = this.conversation(call.conversation_id);
|
|
176
|
+
if (call.kind === 'kill') {
|
|
158
177
|
conversation.kills++;
|
|
159
178
|
conversation.killed.abort(new Error(KILLED));
|
|
160
179
|
for (const waiting of conversation.waiting.splice(0))
|
|
161
180
|
waiting.settle({ refused: KILLED });
|
|
162
181
|
this.halt(conversation);
|
|
163
182
|
}
|
|
164
|
-
if (
|
|
183
|
+
if (call.kind === 'interrupt') {
|
|
165
184
|
conversation.interrupts++;
|
|
166
185
|
conversation.interrupted.abort();
|
|
167
186
|
}
|
|
168
|
-
const handled = conversation.queue.then(() => this.carry(conversation,
|
|
187
|
+
const handled = conversation.queue.then(() => this.carry(conversation, call));
|
|
169
188
|
conversation.queue = handled.catch(() => undefined);
|
|
170
|
-
return handled.then(async ({
|
|
189
|
+
return handled.then(async ({ result }) => ({ result: await result }));
|
|
171
190
|
});
|
|
172
191
|
}
|
|
173
192
|
idle() {
|
|
@@ -201,6 +220,10 @@ export class Conversations {
|
|
|
201
220
|
opened() {
|
|
202
221
|
this.watched = false;
|
|
203
222
|
for (const conversation of this.conversations.values()) {
|
|
223
|
+
if (conversation.running !== null)
|
|
224
|
+
this.facts(conversation);
|
|
225
|
+
else if (conversation.exitUntold)
|
|
226
|
+
this.process(conversation, false);
|
|
204
227
|
if (conversation.untold || conversation.told !== null)
|
|
205
228
|
this.retell(conversation);
|
|
206
229
|
}
|
|
@@ -223,71 +246,99 @@ export class Conversations {
|
|
|
223
246
|
}
|
|
224
247
|
return conversation;
|
|
225
248
|
}
|
|
226
|
-
async carry(conversation,
|
|
227
|
-
if (
|
|
228
|
-
return {
|
|
229
|
-
if (
|
|
230
|
-
return this.message(conversation,
|
|
231
|
-
|
|
249
|
+
async carry(conversation, call) {
|
|
250
|
+
if (call.kind === 'open')
|
|
251
|
+
return { result: this.open(conversation, call) };
|
|
252
|
+
if (call.kind === 'message')
|
|
253
|
+
return this.message(conversation, call);
|
|
254
|
+
await this.launched(conversation);
|
|
255
|
+
if (call.kind === 'interrupt') {
|
|
232
256
|
if (--conversation.interrupts === 0)
|
|
233
257
|
conversation.interrupted = new AbortController();
|
|
234
|
-
await this.interrupt(conversation, String(
|
|
258
|
+
await this.interrupt(conversation, String(call.turn_id));
|
|
235
259
|
}
|
|
236
|
-
if (
|
|
260
|
+
if (call.kind === 'kill') {
|
|
237
261
|
await this.kill(conversation);
|
|
238
262
|
await conversation.sending;
|
|
239
263
|
if (--conversation.kills === 0)
|
|
240
264
|
conversation.killed = new AbortController();
|
|
241
265
|
}
|
|
242
|
-
if (
|
|
243
|
-
await this.option(conversation, String(
|
|
244
|
-
if (
|
|
245
|
-
const question = this.questions.get(String(
|
|
266
|
+
if (call.kind === 'option')
|
|
267
|
+
await this.option(conversation, String(call.option), call.value);
|
|
268
|
+
if (call.kind === 'answer') {
|
|
269
|
+
const question = this.questions.get(String(call.interaction_id));
|
|
246
270
|
if (question === undefined) {
|
|
247
|
-
const text = answerMessage(
|
|
248
|
-
return this.message(conversation, { ...
|
|
271
|
+
const text = answerMessage(call.request, call.response);
|
|
272
|
+
return this.message(conversation, { ...call, text, first: false, message_id: null });
|
|
249
273
|
}
|
|
250
|
-
question.answer(
|
|
274
|
+
question.answer(call.response);
|
|
251
275
|
}
|
|
252
|
-
return {
|
|
276
|
+
return { result: Promise.resolve({}) };
|
|
253
277
|
}
|
|
254
|
-
|
|
278
|
+
open(conversation, call) {
|
|
255
279
|
if (conversation.running !== null)
|
|
256
|
-
return {};
|
|
257
|
-
this.
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
280
|
+
return Promise.resolve({});
|
|
281
|
+
const launch = conversation.launch ?? this.launch(conversation, this.route(conversation, call.agent_id), null, true);
|
|
282
|
+
return launch.session.then((session) => ({ provider_session_id: session }), (error) => ({ refused: refusal(conversation, error) }));
|
|
283
|
+
}
|
|
284
|
+
launch(conversation, route, sessionId, empty) {
|
|
285
|
+
let opened;
|
|
286
|
+
let failed;
|
|
287
|
+
const bridge = new Promise((resolve, reject) => {
|
|
288
|
+
opened = resolve;
|
|
289
|
+
failed = reject;
|
|
290
|
+
});
|
|
291
|
+
bridge.catch(() => undefined);
|
|
292
|
+
const session = this.start(conversation, route, sessionId, empty, opened);
|
|
293
|
+
session.catch(failed);
|
|
294
|
+
const launch = { bridge, session };
|
|
295
|
+
conversation.launch = launch;
|
|
296
|
+
void session
|
|
297
|
+
.catch(() => undefined)
|
|
298
|
+
.then(() => {
|
|
299
|
+
if (conversation.launch !== launch)
|
|
300
|
+
return;
|
|
301
|
+
conversation.launch = null;
|
|
302
|
+
this.closeIdleBridge(conversation);
|
|
303
|
+
this.next(conversation);
|
|
304
|
+
});
|
|
305
|
+
this.next(conversation);
|
|
306
|
+
return launch;
|
|
267
307
|
}
|
|
268
|
-
async
|
|
269
|
-
|
|
308
|
+
async launched(conversation) {
|
|
309
|
+
while (conversation.launch !== null)
|
|
310
|
+
await conversation.launch.session.catch(() => undefined);
|
|
311
|
+
}
|
|
312
|
+
instructed(conversation, route) {
|
|
313
|
+
const bridge = conversation.running === null ? conversation.launch?.bridge : Promise.resolve(conversation.running.bridge);
|
|
314
|
+
if (bridge === undefined)
|
|
315
|
+
return null;
|
|
316
|
+
const read = Promise.all([bridge, this.kit.copies.mapping()]).then(([opened, copies]) => instructions(opened, route.base_instructions, copies));
|
|
317
|
+
read.catch(() => undefined);
|
|
318
|
+
return read;
|
|
319
|
+
}
|
|
320
|
+
async message(conversation, call) {
|
|
321
|
+
const content = [{ type: 'text', text: String(call.text) }];
|
|
270
322
|
let route;
|
|
323
|
+
let instructing = null;
|
|
271
324
|
try {
|
|
272
|
-
route = await this.route(conversation,
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const lines = await this.transcribed(conversation, files, paths);
|
|
278
|
-
if (lines.length > 0)
|
|
279
|
-
content.push({ type: 'text', text: lines.join('\n') });
|
|
325
|
+
route = await this.route(conversation, call.agent_id);
|
|
326
|
+
if (call.first === true)
|
|
327
|
+
instructing = this.instructed(conversation, route);
|
|
328
|
+
if (call.message_id !== null)
|
|
329
|
+
content.push(...(await this.transcribed(conversation, String(call.message_id), String(call.text))));
|
|
280
330
|
if (conversation.kills > 0)
|
|
281
331
|
throw new Error(KILLED);
|
|
282
332
|
}
|
|
283
333
|
catch (error) {
|
|
334
|
+
this.closeIdleBridge(conversation);
|
|
284
335
|
this.prepares(conversation, null);
|
|
285
|
-
return {
|
|
336
|
+
return { result: Promise.resolve({ refused: refusal(conversation, error) }) };
|
|
286
337
|
}
|
|
287
338
|
conversation.preparing = null;
|
|
288
339
|
return {
|
|
289
|
-
|
|
290
|
-
conversation.waiting.push({
|
|
340
|
+
result: new Promise((settle) => {
|
|
341
|
+
conversation.waiting.push({ call, route, content, instructing, settle });
|
|
291
342
|
this.next(conversation);
|
|
292
343
|
}),
|
|
293
344
|
};
|
|
@@ -297,8 +348,9 @@ export class Conversations {
|
|
|
297
348
|
return;
|
|
298
349
|
const waiting = conversation.waiting.shift();
|
|
299
350
|
if (waiting === undefined) {
|
|
300
|
-
|
|
301
|
-
|
|
351
|
+
const step = conversation.preparing ?? (conversation.launch === null ? null : 'session');
|
|
352
|
+
if (conversation.starting !== step)
|
|
353
|
+
this.starts(conversation, step);
|
|
302
354
|
return;
|
|
303
355
|
}
|
|
304
356
|
conversation.sending = this.send(conversation, waiting).finally(() => {
|
|
@@ -307,26 +359,30 @@ export class Conversations {
|
|
|
307
359
|
});
|
|
308
360
|
}
|
|
309
361
|
async send(conversation, waiting) {
|
|
310
|
-
const {
|
|
311
|
-
let
|
|
362
|
+
const { call } = waiting;
|
|
363
|
+
let result = {};
|
|
312
364
|
let started = false;
|
|
365
|
+
let instructing = waiting.instructing;
|
|
313
366
|
const prompt = [];
|
|
314
367
|
try {
|
|
315
368
|
if (conversation.running === null) {
|
|
316
369
|
this.starts(conversation, 'session');
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
370
|
+
await this.launched(conversation);
|
|
371
|
+
}
|
|
372
|
+
if (conversation.running === null) {
|
|
373
|
+
const stored = conversation.session ?? call.provider_session_id;
|
|
374
|
+
const empty = call.first === true && conversation.prompted !== stored;
|
|
375
|
+
const launch = this.launch(conversation, Promise.resolve(waiting.route), stored, empty);
|
|
376
|
+
if (call.first === true)
|
|
377
|
+
instructing = this.instructed(conversation, waiting.route);
|
|
378
|
+
started = (await launch.session) !== stored;
|
|
320
379
|
}
|
|
321
380
|
const session = conversation.running.sessionId;
|
|
322
|
-
if (started || (
|
|
323
|
-
|
|
381
|
+
if (started || (call.first === true && session !== call.provider_session_id)) {
|
|
382
|
+
result = { provider_session_id: session };
|
|
324
383
|
}
|
|
325
|
-
if (
|
|
326
|
-
prompt.push({
|
|
327
|
-
type: 'text',
|
|
328
|
-
text: await instructions(conversation.running.bridge, waiting.route.base_instructions, await this.kit.copies.mapping()),
|
|
329
|
-
});
|
|
384
|
+
if (call.first === true) {
|
|
385
|
+
prompt.push({ type: 'text', text: await (instructing ?? this.instructed(conversation, waiting.route)) });
|
|
330
386
|
}
|
|
331
387
|
if (conversation.kills > 0)
|
|
332
388
|
throw new Error(KILLED);
|
|
@@ -337,12 +393,14 @@ export class Conversations {
|
|
|
337
393
|
conversation.session = null;
|
|
338
394
|
await this.kill(conversation);
|
|
339
395
|
}
|
|
340
|
-
|
|
396
|
+
const refused = refusal(conversation, error);
|
|
397
|
+
const resumeRefused = call.kind === 'message' && conversation.kills === 0 && error instanceof ResumeRefused;
|
|
398
|
+
waiting.settle(resumeRefused ? { refused, resume_refused: true } : { refused });
|
|
341
399
|
return;
|
|
342
400
|
}
|
|
343
401
|
this.starts(conversation, null);
|
|
344
|
-
this.prompt(conversation, [...prompt, ...waiting.content], () => this.kit.
|
|
345
|
-
waiting.settle(
|
|
402
|
+
this.prompt(conversation, [...prompt, ...waiting.content], () => this.kit.calls.keepResult(String(call.input_id), result));
|
|
403
|
+
waiting.settle(result);
|
|
346
404
|
}
|
|
347
405
|
starts(conversation, step) {
|
|
348
406
|
conversation.starting = step;
|
|
@@ -372,25 +430,65 @@ export class Conversations {
|
|
|
372
430
|
retell(conversation) {
|
|
373
431
|
conversation.untold = !this.kit.send({ type: 'starting', conversation_id: conversation.id, step: conversation.told });
|
|
374
432
|
}
|
|
375
|
-
async transcribed(conversation,
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
433
|
+
async transcribed(conversation, messageId, text) {
|
|
434
|
+
const paths = linkedPaths(text);
|
|
435
|
+
if (paths.length === 0)
|
|
436
|
+
return [];
|
|
437
|
+
const killed = conversation.killed.signal;
|
|
438
|
+
const interrupted = conversation.interrupted.signal;
|
|
439
|
+
const transcribing = AbortSignal.any([killed, interrupted]);
|
|
440
|
+
let signal = killed;
|
|
441
|
+
const blocks = [];
|
|
442
|
+
try {
|
|
443
|
+
const bridge = await step(unbridged, unlessAborted(this.bridged(conversation), killed));
|
|
444
|
+
const read = async (path) => audioOf(await step(unbridged, unlessAborted(bridge.tool('inspect', { path }), signal)));
|
|
445
|
+
for (const path of paths) {
|
|
446
|
+
let answered = await read(path);
|
|
447
|
+
if (answered === null)
|
|
448
|
+
continue;
|
|
449
|
+
signal = transcribing;
|
|
450
|
+
this.prepares(conversation, 'transcription');
|
|
451
|
+
const said = await transcribe(async () => {
|
|
452
|
+
const address = answered ?? (await read(path));
|
|
453
|
+
answered = null;
|
|
454
|
+
if (address === null)
|
|
455
|
+
throw new Error(`${path} is no longer an audio file`);
|
|
456
|
+
return address;
|
|
457
|
+
}, signal);
|
|
458
|
+
const transcript = `/kit/conversations/${conversation.id}/messages/${messageId}/transcripts`;
|
|
459
|
+
await step(unbridged, unlessAborted(this.kit.house.deliver(transcript, { path, text: said }, () => !signal.aborted), signal));
|
|
460
|
+
blocks.push({ type: 'text', text: `${path}\nTranscript: ${said}` });
|
|
391
461
|
}
|
|
392
462
|
}
|
|
393
|
-
|
|
463
|
+
catch (error) {
|
|
464
|
+
if (signal === transcribing && interrupted.aborted && conversation.kills === 0)
|
|
465
|
+
throw new NotStarted(INTERRUPTED, { cause: error });
|
|
466
|
+
throw error;
|
|
467
|
+
}
|
|
468
|
+
return blocks;
|
|
469
|
+
}
|
|
470
|
+
bridged(conversation) {
|
|
471
|
+
if (conversation.bridge === null) {
|
|
472
|
+
const bridging = openBridge(this.kit.house, this.kit.copies, conversation.id, conversation.killed.signal);
|
|
473
|
+
conversation.bridge = bridging;
|
|
474
|
+
bridging.catch(() => {
|
|
475
|
+
if (conversation.bridge === bridging)
|
|
476
|
+
conversation.bridge = null;
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
return conversation.bridge;
|
|
480
|
+
}
|
|
481
|
+
closeBridge(conversation, bridging, bridge) {
|
|
482
|
+
bridge.close();
|
|
483
|
+
if (conversation.bridge === bridging)
|
|
484
|
+
conversation.bridge = null;
|
|
485
|
+
}
|
|
486
|
+
closeIdleBridge(conversation) {
|
|
487
|
+
const bridging = conversation.bridge;
|
|
488
|
+
if (bridging === null || conversation.running !== null || conversation.launch !== null)
|
|
489
|
+
return;
|
|
490
|
+
conversation.bridge = null;
|
|
491
|
+
void bridging.then((bridge) => bridge.close(), () => undefined);
|
|
394
492
|
}
|
|
395
493
|
async interrupt(conversation, turnId) {
|
|
396
494
|
if (conversation.running === null || conversation.turn?.id !== turnId)
|
|
@@ -445,7 +543,8 @@ export class Conversations {
|
|
|
445
543
|
}
|
|
446
544
|
return route;
|
|
447
545
|
}
|
|
448
|
-
async start(conversation,
|
|
546
|
+
async start(conversation, routed, sessionId, empty, bridged) {
|
|
547
|
+
const route = await routed;
|
|
449
548
|
const reader = readerFor(route.kind);
|
|
450
549
|
reader.session = sessionId;
|
|
451
550
|
const app = client({ name: '@agentshouse/kit' })
|
|
@@ -453,9 +552,10 @@ export class Conversations {
|
|
|
453
552
|
.onRequest('elicitation/create', ({ params, signal }) => this.ask(conversation, 'elicitation/create', params, asksSecret(params), signal))
|
|
454
553
|
.onRequest('_x.ai/exit_plan_mode', EXIT_PLAN_MODE, ({ params, signal }) => this.ask(conversation, '_x.ai/exit_plan_mode', params, false, signal));
|
|
455
554
|
const cli = await unlessAborted(this.kit.agents.cli(route.kind), conversation.killed.signal);
|
|
456
|
-
const
|
|
555
|
+
const bridging = this.bridged(conversation);
|
|
556
|
+
const bridge = await step(unbridged, bridging);
|
|
457
557
|
if (conversation.kills > 0) {
|
|
458
|
-
|
|
558
|
+
this.closeBridge(conversation, bridging, bridge);
|
|
459
559
|
throw new Error(KILLED);
|
|
460
560
|
}
|
|
461
561
|
conversation.opening = bridge;
|
|
@@ -463,14 +563,14 @@ export class Conversations {
|
|
|
463
563
|
const scope = new Scope(marker(bridge), randomUUID(), cli);
|
|
464
564
|
let adapter = null;
|
|
465
565
|
try {
|
|
466
|
-
|
|
566
|
+
bridged(bridge);
|
|
467
567
|
await CLIS[route.kind].allowHouse(bridge.env.PATH);
|
|
468
568
|
const streamed = await step(NO_CONNECTION, work.open(cli));
|
|
469
569
|
adapter = await step(NO_START, startAdapter(route.kind, cli, route.working_directory, app, (message) => {
|
|
470
570
|
work.read(message);
|
|
471
571
|
this.read(conversation, reader, message);
|
|
472
572
|
}, { ...bridge.env, ...streamed }, undefined, scope));
|
|
473
|
-
void adapter.exited.then(() =>
|
|
573
|
+
void adapter.exited.then(() => this.closeBridge(conversation, bridging, bridge));
|
|
474
574
|
const agent = adapter.connection.agent;
|
|
475
575
|
const cwd = route.working_directory;
|
|
476
576
|
const _meta = CLIS[route.kind].sessionMeta(bridge.env.HOUSE_BRIDGE, conversation.id);
|
|
@@ -481,15 +581,13 @@ export class Conversations {
|
|
|
481
581
|
const prompt = [{ type: 'text', text: `/rename ${conversation.id}` }];
|
|
482
582
|
await step(unopened(NO_SESSION), agent.request('session/prompt', { sessionId: opened.sessionId, prompt }));
|
|
483
583
|
}
|
|
484
|
-
const running = { adapter, bridge, sessionId: opened.sessionId, killed: false, reader, work, scope };
|
|
584
|
+
const running = { adapter, bridging, bridge, sessionId: opened.sessionId, killed: false, reader, work, scope };
|
|
485
585
|
const options = await step(NO_SETTINGS, this.launchSettings(running, route, opened.configOptions ?? []));
|
|
486
586
|
void work.settled().then(() => scope.record());
|
|
487
587
|
conversation.running = running;
|
|
488
588
|
conversation.session = running.sessionId;
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
this.commands(conversation, conversation.commands);
|
|
492
|
-
this.options(conversation, options);
|
|
589
|
+
conversation.options = options;
|
|
590
|
+
this.facts(conversation);
|
|
493
591
|
void adapter.lost.then((cause) => this.exited(conversation, running, cause));
|
|
494
592
|
return running.sessionId;
|
|
495
593
|
}
|
|
@@ -497,7 +595,7 @@ export class Conversations {
|
|
|
497
595
|
adapter?.retire();
|
|
498
596
|
work.close();
|
|
499
597
|
conversation.leftovers.add(scope);
|
|
500
|
-
|
|
598
|
+
this.closeBridge(conversation, bridging, bridge);
|
|
501
599
|
throw conversation.kills > 0 ? new Error(KILLED) : error;
|
|
502
600
|
}
|
|
503
601
|
finally {
|
|
@@ -510,7 +608,7 @@ export class Conversations {
|
|
|
510
608
|
}
|
|
511
609
|
catch (error) {
|
|
512
610
|
if (!empty || conversation.kills > 0 || !CLIS[kind].unknownSession(error)) {
|
|
513
|
-
throw new NotStarted(
|
|
611
|
+
throw signedOut(error) ? new NotStarted(NOT_SIGNED_IN, { cause: error }) : new ResumeRefused(NO_RESUME, { cause: error });
|
|
514
612
|
}
|
|
515
613
|
logged(new Error(`conversation ${conversation.id} opens a new session: ${NO_RESUME}: ${causeOf(error)}`));
|
|
516
614
|
return null;
|
|
@@ -546,15 +644,28 @@ export class Conversations {
|
|
|
546
644
|
this.kit.changed();
|
|
547
645
|
}
|
|
548
646
|
detach(conversation, running) {
|
|
647
|
+
if (conversation.bridge === running.bridging)
|
|
648
|
+
conversation.bridge = null;
|
|
549
649
|
running.work.close();
|
|
550
650
|
conversation.leftovers.add(running.scope);
|
|
551
651
|
conversation.running = null;
|
|
552
652
|
conversation.commands = null;
|
|
653
|
+
conversation.options = null;
|
|
553
654
|
for (const [id, question] of this.questions) {
|
|
554
655
|
if (question.conversation === conversation)
|
|
555
656
|
this.questions.delete(id);
|
|
556
657
|
}
|
|
557
|
-
this.
|
|
658
|
+
this.process(conversation, false);
|
|
659
|
+
}
|
|
660
|
+
process(conversation, running) {
|
|
661
|
+
conversation.exitUntold = !this.kit.send({ type: 'process', conversation_id: conversation.id, running }) && !running;
|
|
662
|
+
}
|
|
663
|
+
facts(conversation) {
|
|
664
|
+
this.process(conversation, true);
|
|
665
|
+
if (conversation.commands !== null)
|
|
666
|
+
this.commands(conversation, conversation.commands);
|
|
667
|
+
if (conversation.options !== null)
|
|
668
|
+
this.options(conversation, conversation.options);
|
|
558
669
|
}
|
|
559
670
|
commands(conversation, commands) {
|
|
560
671
|
conversation.commands = commands;
|
|
@@ -565,6 +676,7 @@ export class Conversations {
|
|
|
565
676
|
options(conversation, options) {
|
|
566
677
|
if (conversation.running === null)
|
|
567
678
|
return;
|
|
679
|
+
conversation.options = options;
|
|
568
680
|
this.kit.send({ type: 'options', conversation_id: conversation.id, options });
|
|
569
681
|
}
|
|
570
682
|
deliver(conversation, turn, event, body) {
|
|
@@ -809,13 +921,16 @@ export class Conversations {
|
|
|
809
921
|
bytes: bytes.length,
|
|
810
922
|
sha256: createHash('sha256').update(bytes).digest('hex'),
|
|
811
923
|
}));
|
|
812
|
-
const saved =
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
924
|
+
const saved = await uploaded(declared.upload, {
|
|
925
|
+
bytes: bytes.length,
|
|
926
|
+
send: (url, start, end) => sentPart(url, bytes.subarray(start, end)),
|
|
927
|
+
finish: (upload) => this.kit.house.deliver('/kit/uploads/finish', { upload }),
|
|
928
|
+
});
|
|
929
|
+
turn.parts.arrive({ type: 'image', file: saved.path, name, media_type: mediaType }, reserved);
|
|
816
930
|
}
|
|
817
931
|
catch (error) {
|
|
818
|
-
|
|
932
|
+
const cause = error instanceof HouseRefusal ? (refusalOf(error.text)?.text ?? causeOf(error)) : causeOf(error);
|
|
933
|
+
turn.parts.arrive({ type: 'marker', marker: 'retry', text: `${name} was not saved: ${cause}` }, reserved);
|
|
819
934
|
}
|
|
820
935
|
}
|
|
821
936
|
}
|