@agentshouse/kit 0.2.1-cb8dc89b78e07 → 0.2.1-cb997daca91ed
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 +20 -20
- package/dist/acp.js +4 -3
- package/dist/agents.js +2 -2
- package/dist/api.js +1 -1
- package/dist/authority-main.js +2 -2
- package/dist/bridge.js +1 -1
- package/dist/building.js +46 -0
- package/dist/{inputs.js → calls.js} +20 -20
- package/dist/clis-main.js +2 -2
- package/dist/clis.js +38 -15
- package/dist/{stream.js → connection.js} +40 -20
- package/dist/conversations.js +149 -85
- package/dist/copies.js +4 -4
- package/dist/end.js +3 -3
- package/dist/files.js +139 -37
- package/dist/git.js +3 -3
- package/dist/house-cli.js +95 -87
- package/dist/house-main.js +3 -3
- package/dist/instructions.js +29 -9
- package/dist/kit-cli.js +4 -4
- package/dist/kit-main.js +3 -3
- package/dist/login.js +2 -4
- package/dist/mcp.js +4 -2
- package/dist/relay.js +21 -0
- package/dist/resident.js +43 -34
- package/dist/service.js +1 -1
- package/dist/sign-in.js +22 -21
- package/dist/skills.js +42 -97
- package/dist/transcription.js +83 -43
- package/dist/update.js +73 -35
- package/dist/web-wizard.js +101 -0
- package/dist/wire.js +1 -1
- package/package.json +20 -21
- package/dist/bootstrap.json +0 -24
- 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, headers) {
|
|
82
|
+
const answer = await fetch(relayed(url), { method: 'PUT', headers, 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
|
}
|
|
@@ -117,6 +132,7 @@ class Conversation {
|
|
|
117
132
|
subagents = new Map();
|
|
118
133
|
held = [];
|
|
119
134
|
opening = null;
|
|
135
|
+
bridge = null;
|
|
120
136
|
launch = null;
|
|
121
137
|
prompted = null;
|
|
122
138
|
kills = 0;
|
|
@@ -154,23 +170,23 @@ export class Conversations {
|
|
|
154
170
|
// A process or a durable schedule ends without a report, so the Kit looks at both again every two seconds.
|
|
155
171
|
setInterval(() => this.look(), 2_000);
|
|
156
172
|
}
|
|
157
|
-
|
|
158
|
-
void this.kit.
|
|
159
|
-
const conversation = this.conversation(
|
|
160
|
-
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') {
|
|
161
177
|
conversation.kills++;
|
|
162
178
|
conversation.killed.abort(new Error(KILLED));
|
|
163
179
|
for (const waiting of conversation.waiting.splice(0))
|
|
164
180
|
waiting.settle({ refused: KILLED });
|
|
165
181
|
this.halt(conversation);
|
|
166
182
|
}
|
|
167
|
-
if (
|
|
183
|
+
if (call.kind === 'interrupt') {
|
|
168
184
|
conversation.interrupts++;
|
|
169
185
|
conversation.interrupted.abort();
|
|
170
186
|
}
|
|
171
|
-
const handled = conversation.queue.then(() => this.carry(conversation,
|
|
187
|
+
const handled = conversation.queue.then(() => this.carry(conversation, call));
|
|
172
188
|
conversation.queue = handled.catch(() => undefined);
|
|
173
|
-
return handled.then(async ({
|
|
189
|
+
return handled.then(async ({ result }) => ({ result: await result }));
|
|
174
190
|
});
|
|
175
191
|
}
|
|
176
192
|
idle() {
|
|
@@ -230,39 +246,39 @@ export class Conversations {
|
|
|
230
246
|
}
|
|
231
247
|
return conversation;
|
|
232
248
|
}
|
|
233
|
-
async carry(conversation,
|
|
234
|
-
if (
|
|
235
|
-
return {
|
|
236
|
-
if (
|
|
237
|
-
return this.message(conversation,
|
|
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);
|
|
238
254
|
await this.launched(conversation);
|
|
239
|
-
if (
|
|
255
|
+
if (call.kind === 'interrupt') {
|
|
240
256
|
if (--conversation.interrupts === 0)
|
|
241
257
|
conversation.interrupted = new AbortController();
|
|
242
|
-
await this.interrupt(conversation, String(
|
|
258
|
+
await this.interrupt(conversation, String(call.turn_id));
|
|
243
259
|
}
|
|
244
|
-
if (
|
|
260
|
+
if (call.kind === 'kill') {
|
|
245
261
|
await this.kill(conversation);
|
|
246
262
|
await conversation.sending;
|
|
247
263
|
if (--conversation.kills === 0)
|
|
248
264
|
conversation.killed = new AbortController();
|
|
249
265
|
}
|
|
250
|
-
if (
|
|
251
|
-
await this.option(conversation, String(
|
|
252
|
-
if (
|
|
253
|
-
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));
|
|
254
270
|
if (question === undefined) {
|
|
255
|
-
const text = answerMessage(
|
|
256
|
-
return this.message(conversation, { ...
|
|
271
|
+
const text = answerMessage(call.request, call.response);
|
|
272
|
+
return this.message(conversation, { ...call, text, first: false, message_id: null });
|
|
257
273
|
}
|
|
258
|
-
question.answer(
|
|
274
|
+
question.answer(call.response);
|
|
259
275
|
}
|
|
260
|
-
return {
|
|
276
|
+
return { result: Promise.resolve({}) };
|
|
261
277
|
}
|
|
262
|
-
open(conversation,
|
|
278
|
+
open(conversation, call) {
|
|
263
279
|
if (conversation.running !== null)
|
|
264
280
|
return Promise.resolve({});
|
|
265
|
-
const launch = conversation.launch ?? this.launch(conversation, this.route(conversation,
|
|
281
|
+
const launch = conversation.launch ?? this.launch(conversation, this.route(conversation, call.agent_id), null, true);
|
|
266
282
|
return launch.session.then((session) => ({ provider_session_id: session }), (error) => ({ refused: refusal(conversation, error) }));
|
|
267
283
|
}
|
|
268
284
|
launch(conversation, route, sessionId, empty) {
|
|
@@ -283,6 +299,7 @@ export class Conversations {
|
|
|
283
299
|
if (conversation.launch !== launch)
|
|
284
300
|
return;
|
|
285
301
|
conversation.launch = null;
|
|
302
|
+
this.closeIdleBridge(conversation);
|
|
286
303
|
this.next(conversation);
|
|
287
304
|
});
|
|
288
305
|
this.next(conversation);
|
|
@@ -300,32 +317,28 @@ export class Conversations {
|
|
|
300
317
|
read.catch(() => undefined);
|
|
301
318
|
return read;
|
|
302
319
|
}
|
|
303
|
-
async message(conversation,
|
|
304
|
-
const content = [{ type: 'text', text: String(
|
|
320
|
+
async message(conversation, call) {
|
|
321
|
+
const content = [{ type: 'text', text: String(call.text) }];
|
|
305
322
|
let route;
|
|
306
323
|
let instructing = null;
|
|
307
324
|
try {
|
|
308
|
-
route = await this.route(conversation,
|
|
309
|
-
if (
|
|
325
|
+
route = await this.route(conversation, call.agent_id);
|
|
326
|
+
if (call.first === true)
|
|
310
327
|
instructing = this.instructed(conversation, route);
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
this.prepares(conversation, 'files');
|
|
314
|
-
const paths = await placeFiles(this.kit.house, route.working_directory, files, conversation.killed.signal);
|
|
315
|
-
const lines = await this.transcribed(conversation, files, paths);
|
|
316
|
-
if (lines.length > 0)
|
|
317
|
-
content.push({ type: 'text', text: lines.join('\n') });
|
|
328
|
+
if (call.message_id !== null)
|
|
329
|
+
content.push(...(await this.transcribed(conversation, String(call.message_id), String(call.text))));
|
|
318
330
|
if (conversation.kills > 0)
|
|
319
331
|
throw new Error(KILLED);
|
|
320
332
|
}
|
|
321
333
|
catch (error) {
|
|
334
|
+
this.closeIdleBridge(conversation);
|
|
322
335
|
this.prepares(conversation, null);
|
|
323
|
-
return {
|
|
336
|
+
return { result: Promise.resolve({ refused: refusal(conversation, error) }) };
|
|
324
337
|
}
|
|
325
338
|
conversation.preparing = null;
|
|
326
339
|
return {
|
|
327
|
-
|
|
328
|
-
conversation.waiting.push({
|
|
340
|
+
result: new Promise((settle) => {
|
|
341
|
+
conversation.waiting.push({ call, route, content, instructing, settle });
|
|
329
342
|
this.next(conversation);
|
|
330
343
|
}),
|
|
331
344
|
};
|
|
@@ -346,8 +359,8 @@ export class Conversations {
|
|
|
346
359
|
});
|
|
347
360
|
}
|
|
348
361
|
async send(conversation, waiting) {
|
|
349
|
-
const {
|
|
350
|
-
let
|
|
362
|
+
const { call } = waiting;
|
|
363
|
+
let result = {};
|
|
351
364
|
let started = false;
|
|
352
365
|
let instructing = waiting.instructing;
|
|
353
366
|
const prompt = [];
|
|
@@ -357,18 +370,18 @@ export class Conversations {
|
|
|
357
370
|
await this.launched(conversation);
|
|
358
371
|
}
|
|
359
372
|
if (conversation.running === null) {
|
|
360
|
-
const stored = conversation.session ??
|
|
361
|
-
const empty =
|
|
373
|
+
const stored = conversation.session ?? call.provider_session_id;
|
|
374
|
+
const empty = call.first === true && conversation.prompted !== stored;
|
|
362
375
|
const launch = this.launch(conversation, Promise.resolve(waiting.route), stored, empty);
|
|
363
|
-
if (
|
|
376
|
+
if (call.first === true)
|
|
364
377
|
instructing = this.instructed(conversation, waiting.route);
|
|
365
378
|
started = (await launch.session) !== stored;
|
|
366
379
|
}
|
|
367
380
|
const session = conversation.running.sessionId;
|
|
368
|
-
if (started || (
|
|
369
|
-
|
|
381
|
+
if (started || (call.first === true && session !== call.provider_session_id)) {
|
|
382
|
+
result = { provider_session_id: session };
|
|
370
383
|
}
|
|
371
|
-
if (
|
|
384
|
+
if (call.first === true) {
|
|
372
385
|
prompt.push({ type: 'text', text: await (instructing ?? this.instructed(conversation, waiting.route)) });
|
|
373
386
|
}
|
|
374
387
|
if (conversation.kills > 0)
|
|
@@ -380,12 +393,14 @@ export class Conversations {
|
|
|
380
393
|
conversation.session = null;
|
|
381
394
|
await this.kill(conversation);
|
|
382
395
|
}
|
|
383
|
-
|
|
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 });
|
|
384
399
|
return;
|
|
385
400
|
}
|
|
386
401
|
this.starts(conversation, null);
|
|
387
|
-
this.prompt(conversation, [...prompt, ...waiting.content], () => this.kit.
|
|
388
|
-
waiting.settle(
|
|
402
|
+
this.prompt(conversation, [...prompt, ...waiting.content], () => this.kit.calls.keepResult(String(call.input_id), result));
|
|
403
|
+
waiting.settle(result);
|
|
389
404
|
}
|
|
390
405
|
starts(conversation, step) {
|
|
391
406
|
conversation.starting = step;
|
|
@@ -415,25 +430,65 @@ export class Conversations {
|
|
|
415
430
|
retell(conversation) {
|
|
416
431
|
conversation.untold = !this.kit.send({ type: 'starting', conversation_id: conversation.id, step: conversation.told });
|
|
417
432
|
}
|
|
418
|
-
async transcribed(conversation,
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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}` });
|
|
434
461
|
}
|
|
435
462
|
}
|
|
436
|
-
|
|
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);
|
|
437
492
|
}
|
|
438
493
|
async interrupt(conversation, turnId) {
|
|
439
494
|
if (conversation.running === null || conversation.turn?.id !== turnId)
|
|
@@ -497,9 +552,10 @@ export class Conversations {
|
|
|
497
552
|
.onRequest('elicitation/create', ({ params, signal }) => this.ask(conversation, 'elicitation/create', params, asksSecret(params), signal))
|
|
498
553
|
.onRequest('_x.ai/exit_plan_mode', EXIT_PLAN_MODE, ({ params, signal }) => this.ask(conversation, '_x.ai/exit_plan_mode', params, false, signal));
|
|
499
554
|
const cli = await unlessAborted(this.kit.agents.cli(route.kind), conversation.killed.signal);
|
|
500
|
-
const
|
|
555
|
+
const bridging = this.bridged(conversation);
|
|
556
|
+
const bridge = await step(unbridged, bridging);
|
|
501
557
|
if (conversation.kills > 0) {
|
|
502
|
-
|
|
558
|
+
this.closeBridge(conversation, bridging, bridge);
|
|
503
559
|
throw new Error(KILLED);
|
|
504
560
|
}
|
|
505
561
|
conversation.opening = bridge;
|
|
@@ -507,7 +563,6 @@ export class Conversations {
|
|
|
507
563
|
const scope = new Scope(marker(bridge), randomUUID(), cli);
|
|
508
564
|
let adapter = null;
|
|
509
565
|
try {
|
|
510
|
-
await unlessAborted(createHowWeWork(bridge), conversation.killed.signal);
|
|
511
566
|
bridged(bridge);
|
|
512
567
|
await CLIS[route.kind].allowHouse(bridge.env.PATH);
|
|
513
568
|
const streamed = await step(NO_CONNECTION, work.open(cli));
|
|
@@ -515,7 +570,7 @@ export class Conversations {
|
|
|
515
570
|
work.read(message);
|
|
516
571
|
this.read(conversation, reader, message);
|
|
517
572
|
}, { ...bridge.env, ...streamed }, undefined, scope));
|
|
518
|
-
void adapter.exited.then(() =>
|
|
573
|
+
void adapter.exited.then(() => this.closeBridge(conversation, bridging, bridge));
|
|
519
574
|
const agent = adapter.connection.agent;
|
|
520
575
|
const cwd = route.working_directory;
|
|
521
576
|
const _meta = CLIS[route.kind].sessionMeta(bridge.env.HOUSE_BRIDGE, conversation.id);
|
|
@@ -526,7 +581,7 @@ export class Conversations {
|
|
|
526
581
|
const prompt = [{ type: 'text', text: `/rename ${conversation.id}` }];
|
|
527
582
|
await step(unopened(NO_SESSION), agent.request('session/prompt', { sessionId: opened.sessionId, prompt }));
|
|
528
583
|
}
|
|
529
|
-
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 };
|
|
530
585
|
const options = await step(NO_SETTINGS, this.launchSettings(running, route, opened.configOptions ?? []));
|
|
531
586
|
void work.settled().then(() => scope.record());
|
|
532
587
|
conversation.running = running;
|
|
@@ -540,7 +595,7 @@ export class Conversations {
|
|
|
540
595
|
adapter?.retire();
|
|
541
596
|
work.close();
|
|
542
597
|
conversation.leftovers.add(scope);
|
|
543
|
-
|
|
598
|
+
this.closeBridge(conversation, bridging, bridge);
|
|
544
599
|
throw conversation.kills > 0 ? new Error(KILLED) : error;
|
|
545
600
|
}
|
|
546
601
|
finally {
|
|
@@ -553,7 +608,7 @@ export class Conversations {
|
|
|
553
608
|
}
|
|
554
609
|
catch (error) {
|
|
555
610
|
if (!empty || conversation.kills > 0 || !CLIS[kind].unknownSession(error)) {
|
|
556
|
-
throw new NotStarted(
|
|
611
|
+
throw signedOut(error) ? new NotStarted(NOT_SIGNED_IN, { cause: error }) : new ResumeRefused(NO_RESUME, { cause: error });
|
|
557
612
|
}
|
|
558
613
|
logged(new Error(`conversation ${conversation.id} opens a new session: ${NO_RESUME}: ${causeOf(error)}`));
|
|
559
614
|
return null;
|
|
@@ -589,6 +644,8 @@ export class Conversations {
|
|
|
589
644
|
this.kit.changed();
|
|
590
645
|
}
|
|
591
646
|
detach(conversation, running) {
|
|
647
|
+
if (conversation.bridge === running.bridging)
|
|
648
|
+
conversation.bridge = null;
|
|
592
649
|
running.work.close();
|
|
593
650
|
conversation.leftovers.add(running.scope);
|
|
594
651
|
conversation.running = null;
|
|
@@ -857,20 +914,27 @@ export class Conversations {
|
|
|
857
914
|
async image(conversation, turn, reserved, data, mediaType) {
|
|
858
915
|
const bytes = Buffer.from(data, 'base64');
|
|
859
916
|
const name = `image-${++turn.images}.${mediaType.split('/')[1]}`;
|
|
917
|
+
const sha256 = createHash('sha256').update(bytes).digest('hex');
|
|
860
918
|
try {
|
|
861
919
|
const declared = (await this.deliver(conversation, turn, 'images', {
|
|
862
920
|
name,
|
|
863
921
|
media_type: mediaType,
|
|
864
922
|
bytes: bytes.length,
|
|
865
|
-
sha256
|
|
923
|
+
sha256,
|
|
866
924
|
}));
|
|
867
|
-
const saved =
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
925
|
+
const saved = await uploaded(declared.upload, {
|
|
926
|
+
name,
|
|
927
|
+
bytes: bytes.length,
|
|
928
|
+
sha256,
|
|
929
|
+
read: (start, end) => [bytes.subarray(start, end)],
|
|
930
|
+
send: (url, start, end, headers) => sentPart(url, bytes.subarray(start, end), headers),
|
|
931
|
+
finish: (upload, partSha256) => this.kit.house.deliver('/kit/uploads/finish', { upload, part_sha256: partSha256 }),
|
|
932
|
+
});
|
|
933
|
+
turn.parts.arrive({ type: 'image', file: saved.path, name, media_type: mediaType }, reserved);
|
|
871
934
|
}
|
|
872
935
|
catch (error) {
|
|
873
|
-
|
|
936
|
+
const cause = error instanceof HouseRefusal ? (refusalOf(error.text)?.text ?? causeOf(error)) : causeOf(error);
|
|
937
|
+
turn.parts.arrive({ type: 'marker', marker: 'retry', text: `${name} was not saved: ${cause}` }, reserved);
|
|
874
938
|
}
|
|
875
939
|
}
|
|
876
940
|
}
|
package/dist/copies.js
CHANGED
|
@@ -11,7 +11,7 @@ import { changed } from './translate.js';
|
|
|
11
11
|
import { relayed } from './relay.js';
|
|
12
12
|
const BRANCH = 'main';
|
|
13
13
|
const PROVENANCE = '_provenance';
|
|
14
|
-
const IDENTITY = { GIT_COMMITTER_NAME: '
|
|
14
|
+
const IDENTITY = { GIT_COMMITTER_NAME: 'Kit', GIT_COMMITTER_EMAIL: 'house-kit@localhost' };
|
|
15
15
|
function causeOf(error) {
|
|
16
16
|
return error instanceof Error ? error.message : String(error);
|
|
17
17
|
}
|
|
@@ -133,7 +133,7 @@ async function ignoreRules(repository, writable, copy) {
|
|
|
133
133
|
].map(pattern);
|
|
134
134
|
const granted = readOnly.filter((path) => !protectedPath(path)).map(pattern);
|
|
135
135
|
await ruled(join(repository, '.gitignore'), [
|
|
136
|
-
'#
|
|
136
|
+
'# Kit writes this file and .ignore. The paths below are read-only House',
|
|
137
137
|
'# content: Git does not track them, and House refuses a change to them even',
|
|
138
138
|
'# when this file is edited or a path is force-added.',
|
|
139
139
|
'/.gitignore',
|
|
@@ -148,7 +148,7 @@ async function ignoreRules(repository, writable, copy) {
|
|
|
148
148
|
'',
|
|
149
149
|
].join('\n'));
|
|
150
150
|
await ruled(join(repository, '.ignore'), [
|
|
151
|
-
'#
|
|
151
|
+
'# Kit writes this file so native search still reads the read-only House',
|
|
152
152
|
'# content that .gitignore keeps out of Git.',
|
|
153
153
|
`!${PROVENANCE}`,
|
|
154
154
|
...[...house, ...granted].map((rule) => `!${rule}`),
|
|
@@ -388,7 +388,7 @@ export class LocalCopies {
|
|
|
388
388
|
throw new Error('House serves nothing of it to copy');
|
|
389
389
|
const repository = join(localCopies(), 'rooms', read.anchor);
|
|
390
390
|
if (await exists(repository))
|
|
391
|
-
throw new Error(`${repository} already exists and
|
|
391
|
+
throw new Error(`${repository} already exists and Kit leaves it as it is`);
|
|
392
392
|
const staging = join(dirname(repository), `.${randomUUID()}`);
|
|
393
393
|
await mkdir(staging, { recursive: true });
|
|
394
394
|
let house;
|
package/dist/end.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { KIT_RELEASE } from './clis.js';
|
|
2
2
|
import { forgetRejected } from './home.js';
|
|
3
3
|
import { installCommand } from './placement.js';
|
|
4
4
|
import { killDescendants } from './scope.js';
|
|
5
|
-
export const REPLACED = 'another Kit started for this Environment';
|
|
5
|
+
export const REPLACED = 'another Kit process started for this Environment';
|
|
6
6
|
function exit(reason, status) {
|
|
7
7
|
process.stderr.write(`kit: ${reason}\n`);
|
|
8
8
|
killDescendants();
|
|
@@ -19,7 +19,7 @@ export function updateRequired() {
|
|
|
19
19
|
end(command === null ? 'This Kit is too old for House; install it again.' : `This Kit is too old for House. Update it: ${command}`);
|
|
20
20
|
}
|
|
21
21
|
export function newerThanHouse(release, command) {
|
|
22
|
-
end(`Kit ${
|
|
22
|
+
end(`Kit ${KIT_RELEASE} is newer than this Building supports (Kit ${release}). Install Kit ${release}: ${command}`);
|
|
23
23
|
}
|
|
24
24
|
export async function signInAgain(rejected) {
|
|
25
25
|
await forgetRejected(rejected);
|