@agentshouse/kit 0.2.1-cbdf76b9a90a3 → 0.2.1-cc6246211e00c
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 +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} +26 -23
- package/dist/conversations.js +149 -85
- package/dist/copies.js +15 -29
- package/dist/end.js +3 -3
- package/dist/events.js +2 -2
- 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 +40 -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 +61 -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}`),
|
|
@@ -218,7 +218,7 @@ export class LocalCopies {
|
|
|
218
218
|
async mapping() {
|
|
219
219
|
await this.answering;
|
|
220
220
|
return [...this.copies.values()]
|
|
221
|
-
.filter((copy) => copy.
|
|
221
|
+
.filter((copy) => copy.unfollowed === undefined)
|
|
222
222
|
.map((copy) => `${rootOf(copy.room_handle)}: ${copy.repository}`)
|
|
223
223
|
.sort();
|
|
224
224
|
}
|
|
@@ -250,30 +250,24 @@ export class LocalCopies {
|
|
|
250
250
|
return this.holding([...new Set([...held().map((copy) => copy.key), ...synced.keys()])].sort(), async () => {
|
|
251
251
|
const said = [];
|
|
252
252
|
let refused = false;
|
|
253
|
+
const mark = refreshing ? 'said' : 'unsaid';
|
|
253
254
|
for (const copy of held()) {
|
|
254
|
-
|
|
255
|
-
if (returned && copy.erasing === undefined)
|
|
255
|
+
if (synced.has(copy.key) || copy.unfollowed === 'said' || copy.unfollowed === mark)
|
|
256
256
|
continue;
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
said.push(`${rootOf(copy.room_handle)}:
|
|
260
|
-
}
|
|
261
|
-
catch (error) {
|
|
262
|
-
refused = true;
|
|
263
|
-
said.push(`${rootOf(copy.room_handle)}: ${causeOf(error)}`);
|
|
264
|
-
}
|
|
257
|
+
await this.save({ ...copy, unfollowed: mark });
|
|
258
|
+
if (refreshing)
|
|
259
|
+
said.push(`${rootOf(copy.room_handle)}: no longer followed; kept as it is`);
|
|
265
260
|
}
|
|
266
261
|
const current = [];
|
|
267
262
|
for (const [key, handle] of synced) {
|
|
268
263
|
try {
|
|
269
264
|
const held = this.copies.get(key);
|
|
270
|
-
if (held?.erasing)
|
|
271
|
-
continue;
|
|
272
265
|
if (held === undefined || !(await exists(held.repository))) {
|
|
273
266
|
said.push(await this.materialize(key, handle));
|
|
274
267
|
continue;
|
|
275
268
|
}
|
|
276
|
-
|
|
269
|
+
const { unfollowed, ...followed } = held;
|
|
270
|
+
current.push(unfollowed === undefined && held.room_handle === handle ? held : await this.save({ ...followed, room_handle: handle }));
|
|
277
271
|
}
|
|
278
272
|
catch (error) {
|
|
279
273
|
refused = true;
|
|
@@ -298,8 +292,6 @@ export class LocalCopies {
|
|
|
298
292
|
return;
|
|
299
293
|
await this.exclusive(key, async () => {
|
|
300
294
|
const copy = this.copies.get(key);
|
|
301
|
-
if (copy === undefined || copy.erasing)
|
|
302
|
-
return;
|
|
303
295
|
let moves = copy.moves;
|
|
304
296
|
for (const { from, to } of report.moves) {
|
|
305
297
|
const held = moves.find((move) => move.worktree === report.worktree && move.to === from);
|
|
@@ -355,12 +347,6 @@ export class LocalCopies {
|
|
|
355
347
|
this.copies.set(copy.key, copy);
|
|
356
348
|
return copy;
|
|
357
349
|
}
|
|
358
|
-
async erase(copy) {
|
|
359
|
-
await this.save({ ...copy, erasing: true });
|
|
360
|
-
await rm(copy.repository, { recursive: true, force: true });
|
|
361
|
-
await rm(recordOf(copy.key), { force: true });
|
|
362
|
-
this.copies.delete(copy.key);
|
|
363
|
-
}
|
|
364
350
|
async read(handle) {
|
|
365
351
|
const prefixes = [rootOf(handle)];
|
|
366
352
|
const discovery = await this.house.post('/kit/door/discover', { prefixes });
|
|
@@ -402,7 +388,7 @@ export class LocalCopies {
|
|
|
402
388
|
throw new Error('House serves nothing of it to copy');
|
|
403
389
|
const repository = join(localCopies(), 'rooms', read.anchor);
|
|
404
390
|
if (await exists(repository))
|
|
405
|
-
throw new Error(`${repository} already exists and
|
|
391
|
+
throw new Error(`${repository} already exists and Kit leaves it as it is`);
|
|
406
392
|
const staging = join(dirname(repository), `.${randomUUID()}`);
|
|
407
393
|
await mkdir(staging, { recursive: true });
|
|
408
394
|
let house;
|
|
@@ -560,8 +546,6 @@ export class LocalCopies {
|
|
|
560
546
|
}
|
|
561
547
|
async pushed(key, worktree, selector, caller) {
|
|
562
548
|
let copy = this.copies.get(key);
|
|
563
|
-
if (copy === undefined || copy.erasing)
|
|
564
|
-
return outside(worktree);
|
|
565
549
|
if (copy.push?.state === 'submitted')
|
|
566
550
|
return this.settle(copy, caller);
|
|
567
551
|
const selected = await run(worktree, ['rev-parse', '--verify', '--quiet', '--end-of-options', `${selector}^{commit}`]);
|
|
@@ -676,8 +660,10 @@ export class LocalCopies {
|
|
|
676
660
|
}
|
|
677
661
|
if (outcome.refused.code === 'expired')
|
|
678
662
|
return this.recovered(copy);
|
|
679
|
-
const rebased = rebaseFixes(outcome.refused);
|
|
680
663
|
let settled = await this.save(without(copy));
|
|
664
|
+
if (settled.unfollowed !== undefined)
|
|
665
|
+
return { refused: true, text: outcome.refused.text };
|
|
666
|
+
const rebased = rebaseFixes(outcome.refused);
|
|
681
667
|
try {
|
|
682
668
|
settled = (await this.caughtUp([settled], true))[0];
|
|
683
669
|
}
|
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);
|
package/dist/events.js
CHANGED
|
@@ -2,7 +2,7 @@ const COMMAND = { shown: 'command' };
|
|
|
2
2
|
const HIDDEN = { shown: 'hidden' };
|
|
3
3
|
const FINISHED = new Set(['completed', 'failed']);
|
|
4
4
|
export const RUNNING_JOB = new Set(['running', 'paused']);
|
|
5
|
-
const
|
|
5
|
+
const MESSAGE_TARGETS = { send_routine_request: 'access', cancel_routine_request: 'request', send_mail: 'request' };
|
|
6
6
|
function words(line) {
|
|
7
7
|
const found = [];
|
|
8
8
|
let word = null;
|
|
@@ -48,7 +48,7 @@ export function houseMessage(line) {
|
|
|
48
48
|
catch {
|
|
49
49
|
return null;
|
|
50
50
|
}
|
|
51
|
-
const argument =
|
|
51
|
+
const argument = MESSAGE_TARGETS[call.command];
|
|
52
52
|
if (argument === undefined)
|
|
53
53
|
return null;
|
|
54
54
|
const target = call.arguments?.[argument];
|