@agentshouse/kit 0.2.1-ce3d2bb67177c → 0.2.1-ce47f621f90e1
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 +17 -17
- package/dist/acp.js +57 -6
- package/dist/agents.js +54 -31
- package/dist/authority-main.js +2 -2
- package/dist/bridge.js +1 -1
- package/dist/building.js +50 -0
- package/dist/calls.js +11 -3
- package/dist/clis.js +33 -39
- package/dist/connection.js +10 -30
- package/dist/conversations.js +149 -47
- package/dist/end.js +8 -2
- package/dist/events.js +29 -8
- package/dist/files.js +33 -17
- package/dist/git.js +2 -2
- package/dist/home.js +0 -7
- package/dist/house-cli.js +27 -55
- package/dist/house-main.js +3 -3
- package/dist/instructions.js +29 -9
- package/dist/kit-cli.js +2 -2
- package/dist/kit-main.js +3 -3
- package/dist/login.js +3 -1
- package/dist/mcp.js +2 -0
- package/dist/placement.js +10 -18
- package/dist/relay.js +21 -0
- package/dist/resident.js +19 -9
- package/dist/scope.js +12 -4
- package/dist/scripts.js +255 -0
- package/dist/sign-in.js +3 -2
- package/dist/silence.js +28 -0
- package/dist/skills.js +41 -96
- package/dist/transcription.js +32 -23
- package/dist/update.js +109 -120
- package/dist/wire.js +23 -5
- package/package.json +19 -20
- package/dist/bootstrap.json +0 -24
- package/dist/clis-main.js +0 -23
- 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/connection.js
CHANGED
|
@@ -3,40 +3,13 @@ import { HouseRefusal, retryDelay } from './api.js';
|
|
|
3
3
|
import { end, REPLACED, signInAgain } from './end.js';
|
|
4
4
|
import { readEnrolment } from './home.js';
|
|
5
5
|
import { relayed } from './relay.js';
|
|
6
|
-
import {
|
|
6
|
+
import { Silence } from './silence.js';
|
|
7
|
+
import { endUnserved, KIT_WIRE, unserved } from './wire.js';
|
|
7
8
|
const PATH = '/kit/stream';
|
|
8
9
|
const SUBPROTOCOL = `house.kit.stream.${KIT_WIRE}`;
|
|
9
10
|
const REFUSAL_HEADER = 'x-house-refusal';
|
|
10
11
|
// House beats each Kit connection every thirty seconds, so two silent beats mean this Kit process cannot hear House.
|
|
11
12
|
export const CONNECTION_SILENCE_MS = 60_000;
|
|
12
|
-
const SYSTEM_CONNECTION_CLOCK = {
|
|
13
|
-
later: (delay, callback) => setTimeout(callback, delay),
|
|
14
|
-
clear: (timer) => clearTimeout(timer),
|
|
15
|
-
};
|
|
16
|
-
export class ConnectionSilence {
|
|
17
|
-
timer = null;
|
|
18
|
-
silent;
|
|
19
|
-
clock;
|
|
20
|
-
interval;
|
|
21
|
-
constructor(silent, clock = SYSTEM_CONNECTION_CLOCK, interval = CONNECTION_SILENCE_MS) {
|
|
22
|
-
this.silent = silent;
|
|
23
|
-
this.clock = clock;
|
|
24
|
-
this.interval = interval;
|
|
25
|
-
}
|
|
26
|
-
heard() {
|
|
27
|
-
this.stop();
|
|
28
|
-
this.timer = this.clock.later(this.interval, () => {
|
|
29
|
-
this.timer = null;
|
|
30
|
-
this.silent();
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
stop() {
|
|
34
|
-
if (this.timer === null)
|
|
35
|
-
return;
|
|
36
|
-
this.clock.clear(this.timer);
|
|
37
|
-
this.timer = null;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
13
|
function connectionUrl(house) {
|
|
41
14
|
const url = new URL(PATH, house);
|
|
42
15
|
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
@@ -58,21 +31,28 @@ subscribe('undici:request:headers', (message) => {
|
|
|
58
31
|
});
|
|
59
32
|
export function holdConnection(handlers) {
|
|
60
33
|
let connection = null;
|
|
34
|
+
let socket = null;
|
|
61
35
|
let failures = 0;
|
|
62
36
|
const connect = async () => {
|
|
63
37
|
const { house, credential } = (await readEnrolment());
|
|
38
|
+
if (socket !== null || unserved())
|
|
39
|
+
return;
|
|
64
40
|
refused = null;
|
|
65
41
|
const opening = new WebSocket(relayed(connectionUrl(house)), {
|
|
66
42
|
protocols: [SUBPROTOCOL],
|
|
67
43
|
headers: { authorization: `Bearer ${credential}` },
|
|
68
44
|
});
|
|
45
|
+
socket = opening;
|
|
69
46
|
let opened = false;
|
|
70
47
|
let silence = null;
|
|
71
48
|
const closed = (event) => {
|
|
72
49
|
silence?.stop();
|
|
73
50
|
silence = null;
|
|
51
|
+
if (socket === opening)
|
|
52
|
+
socket = null;
|
|
74
53
|
if (connection === opening)
|
|
75
54
|
connection = null;
|
|
55
|
+
handlers.closed();
|
|
76
56
|
process.stderr.write(`kit: connection closed ${event.code} ${event.reason}\n`);
|
|
77
57
|
if (event.reason === 'replaced')
|
|
78
58
|
end(REPLACED);
|
|
@@ -91,7 +71,7 @@ export function holdConnection(handlers) {
|
|
|
91
71
|
opened = true;
|
|
92
72
|
failures = 0;
|
|
93
73
|
connection = opening;
|
|
94
|
-
silence = new
|
|
74
|
+
silence = new Silence(CONNECTION_SILENCE_MS, () => {
|
|
95
75
|
if (connection !== opening)
|
|
96
76
|
return;
|
|
97
77
|
connection = null;
|
package/dist/conversations.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { client,
|
|
1
|
+
import { client, } from '@agentclientprotocol/sdk';
|
|
2
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
3
|
import { mkdir } from 'node:fs/promises';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { unlessAborted } from './abort.js';
|
|
6
|
-
import { startAdapter } from './acp.js';
|
|
6
|
+
import { signedOut, startAdapter } from './acp.js';
|
|
7
7
|
import { finalRefusal, HouseRefusal } from './api.js';
|
|
8
8
|
import { openBridge } from './bridge.js';
|
|
9
9
|
import { CLIS } from './clis.js';
|
|
@@ -16,10 +16,8 @@ import { refusalOf } from './refusals.js';
|
|
|
16
16
|
import { relayed } from './relay.js';
|
|
17
17
|
import { killMarked, Scope } from './scope.js';
|
|
18
18
|
import { holdSecretInput } from './secret-input.js';
|
|
19
|
-
import { createHowWeWork } from './skills.js';
|
|
20
19
|
import { transcribe } from './transcription.js';
|
|
21
20
|
import { workFor } from './work.js';
|
|
22
|
-
const SIGNED_OUT = RequestError.authRequired().code;
|
|
23
21
|
const KILLED = 'the conversation was killed';
|
|
24
22
|
const INTERRUPTED = 'the conversation was interrupted';
|
|
25
23
|
const REFUSED = 'the CLI refused the prompt';
|
|
@@ -35,6 +33,7 @@ const NO_SESSION = "the Agent's CLI did not open a session";
|
|
|
35
33
|
const NO_RESUME = "the Agent's CLI could not resume this conversation";
|
|
36
34
|
const NOT_SIGNED_IN = "the Agent's CLI is not signed in; sign it in on the Agent's machine";
|
|
37
35
|
const NO_SETTINGS = "the Agent's CLI refused the Agent's model, mode or effort";
|
|
36
|
+
const UNSTEERED = "the Agent's CLI did not take the message into its running turn";
|
|
38
37
|
class NotStarted extends Error {
|
|
39
38
|
}
|
|
40
39
|
class ResumeRefused extends NotStarted {
|
|
@@ -47,9 +46,6 @@ async function step(reason, work) {
|
|
|
47
46
|
throw new NotStarted(typeof reason === 'string' ? reason : reason(error), { cause: error });
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
|
-
function signedOut(error) {
|
|
51
|
-
return typeof error === 'object' && error !== null && error.code === SIGNED_OUT;
|
|
52
|
-
}
|
|
53
49
|
function unopened(reason) {
|
|
54
50
|
return (error) => (signedOut(error) ? NOT_SIGNED_IN : reason);
|
|
55
51
|
}
|
|
@@ -66,7 +62,7 @@ const EXIT_PLAN_MODE = {
|
|
|
66
62
|
return value;
|
|
67
63
|
},
|
|
68
64
|
};
|
|
69
|
-
export const CONVERSATION_METHODS = new Set(['open', 'message', 'interrupt', 'kill', 'option', 'answer']);
|
|
65
|
+
export const CONVERSATION_METHODS = new Set(['open', 'message', 'interrupt', 'kill', 'done', 'option', 'answer']);
|
|
70
66
|
function causeOf(error) {
|
|
71
67
|
return error instanceof Error ? error.message : String(error);
|
|
72
68
|
}
|
|
@@ -74,13 +70,15 @@ function answeredWith(error) {
|
|
|
74
70
|
return { failure: signedOut(error) ? 'signed_out' : 'error', cause: causeOf(error) };
|
|
75
71
|
}
|
|
76
72
|
function exitedWith(running, cause) {
|
|
77
|
-
|
|
73
|
+
if (running.killed)
|
|
74
|
+
return { failure: 'killed', cause: KILLED };
|
|
75
|
+
return { failure: running.adapter.silent() ? 'silent' : 'exited', cause };
|
|
78
76
|
}
|
|
79
77
|
function answerMessage(request, response) {
|
|
80
78
|
return `The User answered a question you asked earlier.\nQuestion: ${JSON.stringify(request)}\nAnswer: ${JSON.stringify(response)}`;
|
|
81
79
|
}
|
|
82
|
-
async function sentPart(url, bytes) {
|
|
83
|
-
const answer = await fetch(relayed(url), { method: 'PUT', body: bytes });
|
|
80
|
+
async function sentPart(url, bytes, headers) {
|
|
81
|
+
const answer = await fetch(relayed(url), { method: 'PUT', headers, body: bytes });
|
|
84
82
|
await answer.arrayBuffer();
|
|
85
83
|
if (!answer.ok)
|
|
86
84
|
throw storeRefusal('upload', answer.status);
|
|
@@ -104,12 +102,16 @@ function audioOf(result) {
|
|
|
104
102
|
function marker(bridge) {
|
|
105
103
|
return `HOUSE_BRIDGE=${bridge.env.HOUSE_BRIDGE}`;
|
|
106
104
|
}
|
|
105
|
+
function steers(adapter) {
|
|
106
|
+
return adapter.initialized._meta?.steering?.supported === true;
|
|
107
|
+
}
|
|
107
108
|
class Turn {
|
|
108
109
|
id = randomUUID();
|
|
109
110
|
parts;
|
|
110
111
|
questions = 0;
|
|
111
112
|
secrets = 0;
|
|
112
113
|
prompted = false;
|
|
114
|
+
steered = false;
|
|
113
115
|
taken = null;
|
|
114
116
|
ended = false;
|
|
115
117
|
images = 0;
|
|
@@ -129,6 +131,7 @@ class Conversation {
|
|
|
129
131
|
context = null;
|
|
130
132
|
leftovers = new Set();
|
|
131
133
|
waiting = [];
|
|
134
|
+
done = false;
|
|
132
135
|
sending = null;
|
|
133
136
|
subagents = new Map();
|
|
134
137
|
held = [];
|
|
@@ -140,7 +143,7 @@ class Conversation {
|
|
|
140
143
|
killed = new AbortController();
|
|
141
144
|
interrupts = 0;
|
|
142
145
|
interrupted = new AbortController();
|
|
143
|
-
|
|
146
|
+
turns = new Set();
|
|
144
147
|
queue = Promise.resolve();
|
|
145
148
|
reports = Promise.resolve();
|
|
146
149
|
starting = null;
|
|
@@ -196,7 +199,7 @@ export class Conversations {
|
|
|
196
199
|
for (const conversation of this.conversations.values()) {
|
|
197
200
|
const turn = conversation.turn;
|
|
198
201
|
const asking = turn !== null && turn.questions > 0 && turn.secrets === 0;
|
|
199
|
-
if (conversation.
|
|
202
|
+
if (conversation.turns.size > (asking ? 1 : 0))
|
|
200
203
|
return false;
|
|
201
204
|
}
|
|
202
205
|
const conversations = [...this.conversations.values()];
|
|
@@ -205,7 +208,7 @@ export class Conversations {
|
|
|
205
208
|
return !conversations.some((conversation) => conversation.running?.scope.working() || [...conversation.leftovers].some((scope) => scope.working()));
|
|
206
209
|
}
|
|
207
210
|
quiet() {
|
|
208
|
-
return this.idle() && [...this.conversations.values()].every((conversation) => conversation.
|
|
211
|
+
return this.idle() && [...this.conversations.values()].every((conversation) => conversation.turns.size === 0);
|
|
209
212
|
}
|
|
210
213
|
look() {
|
|
211
214
|
for (const conversation of this.conversations.values()) {
|
|
@@ -221,8 +224,10 @@ export class Conversations {
|
|
|
221
224
|
opened() {
|
|
222
225
|
this.watched = false;
|
|
223
226
|
for (const conversation of this.conversations.values()) {
|
|
224
|
-
if (conversation.running !== null)
|
|
227
|
+
if (conversation.running !== null) {
|
|
225
228
|
this.facts(conversation);
|
|
229
|
+
this.subagentsChanged(conversation);
|
|
230
|
+
}
|
|
226
231
|
else if (conversation.exitUntold)
|
|
227
232
|
this.process(conversation, false);
|
|
228
233
|
if (conversation.untold || conversation.told !== null)
|
|
@@ -249,9 +254,14 @@ export class Conversations {
|
|
|
249
254
|
}
|
|
250
255
|
async carry(conversation, call) {
|
|
251
256
|
if (call.kind === 'open')
|
|
252
|
-
return
|
|
257
|
+
return this.open(conversation, call);
|
|
253
258
|
if (call.kind === 'message')
|
|
254
259
|
return this.message(conversation, call);
|
|
260
|
+
if (call.kind === 'done') {
|
|
261
|
+
conversation.done = true;
|
|
262
|
+
this.next(conversation);
|
|
263
|
+
return { result: Promise.resolve({}) };
|
|
264
|
+
}
|
|
255
265
|
await this.launched(conversation);
|
|
256
266
|
if (call.kind === 'interrupt') {
|
|
257
267
|
if (--conversation.interrupts === 0)
|
|
@@ -276,11 +286,17 @@ export class Conversations {
|
|
|
276
286
|
}
|
|
277
287
|
return { result: Promise.resolve({}) };
|
|
278
288
|
}
|
|
279
|
-
open(conversation, call) {
|
|
280
|
-
|
|
281
|
-
return Promise.resolve({});
|
|
289
|
+
async open(conversation, call) {
|
|
290
|
+
await this.stop(conversation);
|
|
282
291
|
const launch = conversation.launch ?? this.launch(conversation, this.route(conversation, call.agent_id), null, true);
|
|
283
|
-
return
|
|
292
|
+
return {
|
|
293
|
+
result: launch.session.then((session) => ({ provider_session_id: session }), (error) => ({ refused: refusal(conversation, error) })),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
async stop(conversation) {
|
|
297
|
+
const running = conversation.running;
|
|
298
|
+
running?.scope.kill();
|
|
299
|
+
await running?.adapter.exited;
|
|
284
300
|
}
|
|
285
301
|
launch(conversation, route, sessionId, empty) {
|
|
286
302
|
let opened;
|
|
@@ -319,7 +335,10 @@ export class Conversations {
|
|
|
319
335
|
return read;
|
|
320
336
|
}
|
|
321
337
|
async message(conversation, call) {
|
|
322
|
-
const content = [
|
|
338
|
+
const content = [
|
|
339
|
+
...(typeof call.recent === 'string' ? [{ type: 'text', text: call.recent }] : []),
|
|
340
|
+
{ type: 'text', text: String(call.text) },
|
|
341
|
+
];
|
|
323
342
|
let route;
|
|
324
343
|
let instructing = null;
|
|
325
344
|
try {
|
|
@@ -337,21 +356,71 @@ export class Conversations {
|
|
|
337
356
|
return { result: Promise.resolve({ refused: refusal(conversation, error) }) };
|
|
338
357
|
}
|
|
339
358
|
conversation.preparing = null;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
359
|
+
const { promise, resolve: settle } = Promise.withResolvers();
|
|
360
|
+
const waiting = { call, route, content, instructing, settle };
|
|
361
|
+
if (call.at_once === true && conversation.turns.size > 0)
|
|
362
|
+
await this.atOnce(conversation, waiting);
|
|
363
|
+
else
|
|
364
|
+
conversation.waiting.push(waiting);
|
|
365
|
+
this.next(conversation);
|
|
366
|
+
return { result: promise };
|
|
367
|
+
}
|
|
368
|
+
async atOnce(conversation, waiting) {
|
|
369
|
+
while (conversation.sending !== null)
|
|
370
|
+
await conversation.sending;
|
|
371
|
+
const running = conversation.running;
|
|
372
|
+
if (running !== null && conversation.turns.size > 0 && steers(running.adapter)) {
|
|
373
|
+
conversation.sending = this.steer(conversation, running, waiting);
|
|
374
|
+
await conversation.sending;
|
|
375
|
+
conversation.sending = null;
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
this.ahead(conversation, waiting);
|
|
379
|
+
if (conversation.turns.size > 0)
|
|
380
|
+
await this.cancel(conversation);
|
|
381
|
+
}
|
|
382
|
+
ahead(conversation, waiting) {
|
|
383
|
+
const behind = conversation.waiting.findIndex((held) => held.call.at_once !== true);
|
|
384
|
+
conversation.waiting.splice(behind === -1 ? conversation.waiting.length : behind, 0, waiting);
|
|
385
|
+
}
|
|
386
|
+
async steer(conversation, running, waiting) {
|
|
387
|
+
let outcome;
|
|
388
|
+
try {
|
|
389
|
+
({ outcome } = await running.adapter.connection.agent.request('_session/steering', {
|
|
390
|
+
sessionId: running.sessionId,
|
|
391
|
+
prompt: waiting.content,
|
|
392
|
+
_meta: { steering: { idleBehavior: 'promptRequired' } },
|
|
393
|
+
}));
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
outcome = error;
|
|
397
|
+
}
|
|
398
|
+
if (outcome === 'promptRequired') {
|
|
399
|
+
this.ahead(conversation, waiting);
|
|
400
|
+
}
|
|
401
|
+
else if (outcome === 'injected' || outcome === 'startedNewTurn') {
|
|
402
|
+
if (outcome === 'injected') {
|
|
403
|
+
for (const turn of conversation.turns)
|
|
404
|
+
turn.steered ||= turn.prompted;
|
|
405
|
+
}
|
|
406
|
+
this.kit.calls.keepResult(String(waiting.call.input_id), {});
|
|
407
|
+
waiting.settle({});
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
const cause = outcome instanceof Error ? outcome : new Error(`the CLI answered ${JSON.stringify(outcome)}`);
|
|
411
|
+
waiting.settle({ refused: refusal(conversation, new NotStarted(UNSTEERED, { cause })) });
|
|
412
|
+
}
|
|
346
413
|
}
|
|
347
414
|
next(conversation) {
|
|
348
|
-
if (conversation.sending !== null || conversation.
|
|
415
|
+
if (conversation.sending !== null || conversation.turns.size > 0)
|
|
349
416
|
return;
|
|
350
417
|
const waiting = conversation.waiting.shift();
|
|
351
418
|
if (waiting === undefined) {
|
|
352
419
|
const step = conversation.preparing ?? (conversation.launch === null ? null : 'session');
|
|
353
420
|
if (conversation.starting !== step)
|
|
354
421
|
this.starts(conversation, step);
|
|
422
|
+
if (conversation.done && conversation.launch === null)
|
|
423
|
+
void this.stop(conversation).catch(logged);
|
|
355
424
|
return;
|
|
356
425
|
}
|
|
357
426
|
conversation.sending = this.send(conversation, waiting).finally(() => {
|
|
@@ -492,7 +561,11 @@ export class Conversations {
|
|
|
492
561
|
void bridging.then((bridge) => bridge.close(), () => undefined);
|
|
493
562
|
}
|
|
494
563
|
async interrupt(conversation, turnId) {
|
|
495
|
-
if (conversation.
|
|
564
|
+
if ([...conversation.turns].some((turn) => turn.id === turnId))
|
|
565
|
+
await this.cancel(conversation);
|
|
566
|
+
}
|
|
567
|
+
async cancel(conversation) {
|
|
568
|
+
if (conversation.running === null)
|
|
496
569
|
return;
|
|
497
570
|
await conversation.running.adapter.connection.agent.notify('session/cancel', {
|
|
498
571
|
sessionId: conversation.running.sessionId,
|
|
@@ -564,7 +637,6 @@ export class Conversations {
|
|
|
564
637
|
const scope = new Scope(marker(bridge), randomUUID(), cli);
|
|
565
638
|
let adapter = null;
|
|
566
639
|
try {
|
|
567
|
-
await unlessAborted(createHowWeWork(bridge), conversation.killed.signal);
|
|
568
640
|
bridged(bridge);
|
|
569
641
|
await CLIS[route.kind].allowHouse(bridge.env.PATH);
|
|
570
642
|
const streamed = await step(NO_CONNECTION, work.open(cli));
|
|
@@ -587,6 +659,7 @@ export class Conversations {
|
|
|
587
659
|
const options = await step(NO_SETTINGS, this.launchSettings(running, route, opened.configOptions ?? []));
|
|
588
660
|
void work.settled().then(() => scope.record());
|
|
589
661
|
conversation.running = running;
|
|
662
|
+
adapter.turn(conversation.turns.size > 0);
|
|
590
663
|
conversation.session = running.sessionId;
|
|
591
664
|
conversation.options = options;
|
|
592
665
|
this.facts(conversation);
|
|
@@ -653,6 +726,10 @@ export class Conversations {
|
|
|
653
726
|
conversation.running = null;
|
|
654
727
|
conversation.commands = null;
|
|
655
728
|
conversation.options = null;
|
|
729
|
+
if (conversation.subagents.size > 0) {
|
|
730
|
+
conversation.subagents.clear();
|
|
731
|
+
this.subagentsChanged(conversation);
|
|
732
|
+
}
|
|
656
733
|
for (const [id, question] of this.questions) {
|
|
657
734
|
if (question.conversation === conversation)
|
|
658
735
|
this.questions.delete(id);
|
|
@@ -669,6 +746,10 @@ export class Conversations {
|
|
|
669
746
|
if (conversation.options !== null)
|
|
670
747
|
this.options(conversation, conversation.options);
|
|
671
748
|
}
|
|
749
|
+
subagentsChanged(conversation) {
|
|
750
|
+
const subagents = [...conversation.subagents.values()].map(({ description, activity }) => ({ description, activity }));
|
|
751
|
+
this.kit.send({ type: 'subagents', conversation_id: conversation.id, subagents });
|
|
752
|
+
}
|
|
672
753
|
commands(conversation, commands) {
|
|
673
754
|
conversation.commands = commands;
|
|
674
755
|
if (conversation.running === null)
|
|
@@ -703,7 +784,8 @@ export class Conversations {
|
|
|
703
784
|
}
|
|
704
785
|
begin(conversation) {
|
|
705
786
|
const turn = new Turn((operation) => this.write(conversation, turn, operation));
|
|
706
|
-
conversation.
|
|
787
|
+
conversation.turns.add(turn);
|
|
788
|
+
conversation.running?.adapter.turn(true);
|
|
707
789
|
void this.deliver(conversation, turn, 'started', {}).catch(logged);
|
|
708
790
|
conversation.turn = turn;
|
|
709
791
|
if (conversation.context !== null)
|
|
@@ -725,7 +807,8 @@ export class Conversations {
|
|
|
725
807
|
if (notice !== null)
|
|
726
808
|
turn.parts.start({ type: 'marker', marker: 'retry', text: notice });
|
|
727
809
|
turn.ended = true;
|
|
728
|
-
conversation.
|
|
810
|
+
conversation.turns.delete(turn);
|
|
811
|
+
conversation.running?.adapter.turn(conversation.turns.size > 0);
|
|
729
812
|
if (conversation.turn === turn)
|
|
730
813
|
conversation.turn = null;
|
|
731
814
|
const body = Promise.allSettled(turn.uploads).then(() => ({
|
|
@@ -758,7 +841,7 @@ export class Conversations {
|
|
|
758
841
|
}
|
|
759
842
|
else {
|
|
760
843
|
this.took(turn);
|
|
761
|
-
if (conversation.
|
|
844
|
+
if (conversation.turns.size === 1 && conversation.turns.has(turn) && conversation.running === running) {
|
|
762
845
|
running.adapter.retire();
|
|
763
846
|
this.detach(conversation, running);
|
|
764
847
|
}
|
|
@@ -771,19 +854,22 @@ export class Conversations {
|
|
|
771
854
|
this.took(turn);
|
|
772
855
|
return turn;
|
|
773
856
|
}
|
|
774
|
-
ended(conversation) {
|
|
857
|
+
ended(conversation, stop) {
|
|
775
858
|
const turn = conversation.turn;
|
|
776
859
|
if (turn === null)
|
|
777
860
|
return;
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
this.apply(conversation, turn, event);
|
|
784
|
-
conversation.turn = null;
|
|
785
|
-
this.kit.changed();
|
|
861
|
+
const held = stop !== null && !turn.steered && [...conversation.subagents.values()].some((subagent) => subagent.turn === turn);
|
|
862
|
+
if (!turn.prompted || held) {
|
|
863
|
+
this.took(turn);
|
|
864
|
+
this.end(conversation, turn, stop === 'refusal' ? { failure: 'refused', cause: REFUSED } : null);
|
|
865
|
+
return;
|
|
786
866
|
}
|
|
867
|
+
if (stop !== null)
|
|
868
|
+
return;
|
|
869
|
+
for (const event of conversation.running?.reader.closeTurn() ?? [])
|
|
870
|
+
this.apply(conversation, turn, event);
|
|
871
|
+
conversation.turn = null;
|
|
872
|
+
this.kit.changed();
|
|
787
873
|
}
|
|
788
874
|
async ask(conversation, method, params, secret, signal) {
|
|
789
875
|
const turn = this.started(conversation);
|
|
@@ -839,7 +925,7 @@ export class Conversations {
|
|
|
839
925
|
this.started(conversation);
|
|
840
926
|
return;
|
|
841
927
|
case 'ended':
|
|
842
|
-
this.ended(conversation);
|
|
928
|
+
this.ended(conversation, event.stop);
|
|
843
929
|
return;
|
|
844
930
|
case 'commands':
|
|
845
931
|
this.commands(conversation, event.commands);
|
|
@@ -854,6 +940,9 @@ export class Conversations {
|
|
|
854
940
|
case 'subagent-done':
|
|
855
941
|
this.subagentDone(conversation, event.id, event.result);
|
|
856
942
|
return;
|
|
943
|
+
case 'subagent-activity':
|
|
944
|
+
this.subagentActivity(conversation, event.id, event.activity);
|
|
945
|
+
return;
|
|
857
946
|
case 'marker':
|
|
858
947
|
case 'image':
|
|
859
948
|
if (conversation.turn !== null)
|
|
@@ -880,7 +969,8 @@ export class Conversations {
|
|
|
880
969
|
return;
|
|
881
970
|
case 'subagent': {
|
|
882
971
|
const index = turn.parts.start({ type: 'subagent', description: event.description, status: 'running', result: null });
|
|
883
|
-
conversation.subagents.set(event.id, { description: event.description, turn, index });
|
|
972
|
+
conversation.subagents.set(event.id, { description: event.description, turn, index, activity: null });
|
|
973
|
+
this.subagentsChanged(conversation);
|
|
884
974
|
return;
|
|
885
975
|
}
|
|
886
976
|
case 'message':
|
|
@@ -903,6 +993,7 @@ export class Conversations {
|
|
|
903
993
|
if (subagent === undefined)
|
|
904
994
|
return;
|
|
905
995
|
conversation.subagents.delete(id);
|
|
996
|
+
this.subagentsChanged(conversation);
|
|
906
997
|
if (subagent.turn === conversation.turn) {
|
|
907
998
|
subagent.turn.parts.done(subagent.index, result);
|
|
908
999
|
return;
|
|
@@ -913,20 +1004,31 @@ export class Conversations {
|
|
|
913
1004
|
else
|
|
914
1005
|
conversation.turn.parts.start(part);
|
|
915
1006
|
}
|
|
1007
|
+
subagentActivity(conversation, id, activity) {
|
|
1008
|
+
const subagent = conversation.subagents.get(id);
|
|
1009
|
+
if (subagent === undefined || subagent.activity === activity)
|
|
1010
|
+
return;
|
|
1011
|
+
subagent.activity = activity;
|
|
1012
|
+
this.subagentsChanged(conversation);
|
|
1013
|
+
}
|
|
916
1014
|
async image(conversation, turn, reserved, data, mediaType) {
|
|
917
1015
|
const bytes = Buffer.from(data, 'base64');
|
|
918
1016
|
const name = `image-${++turn.images}.${mediaType.split('/')[1]}`;
|
|
1017
|
+
const sha256 = createHash('sha256').update(bytes).digest('hex');
|
|
919
1018
|
try {
|
|
920
1019
|
const declared = (await this.deliver(conversation, turn, 'images', {
|
|
921
1020
|
name,
|
|
922
1021
|
media_type: mediaType,
|
|
923
1022
|
bytes: bytes.length,
|
|
924
|
-
sha256
|
|
1023
|
+
sha256,
|
|
925
1024
|
}));
|
|
926
1025
|
const saved = await uploaded(declared.upload, {
|
|
1026
|
+
name,
|
|
927
1027
|
bytes: bytes.length,
|
|
928
|
-
|
|
929
|
-
|
|
1028
|
+
sha256,
|
|
1029
|
+
read: (start, end) => [bytes.subarray(start, end)],
|
|
1030
|
+
send: (url, start, end, headers) => sentPart(url, bytes.subarray(start, end), headers),
|
|
1031
|
+
finish: (upload, partSha256) => this.kit.house.deliver('/kit/uploads/finish', { upload, part_sha256: partSha256 }),
|
|
930
1032
|
});
|
|
931
1033
|
turn.parts.arrive({ type: 'image', file: saved.path, name, media_type: mediaType }, reserved);
|
|
932
1034
|
}
|
package/dist/end.js
CHANGED
|
@@ -14,9 +14,15 @@ export function end(reason) {
|
|
|
14
14
|
export function restart(reason) {
|
|
15
15
|
exit(reason, 1);
|
|
16
16
|
}
|
|
17
|
-
export function
|
|
17
|
+
export function updateLine(lead) {
|
|
18
18
|
const command = installCommand();
|
|
19
|
-
|
|
19
|
+
return command === null ? `${lead}; install it again.` : `${lead}. Update it: ${command}`;
|
|
20
|
+
}
|
|
21
|
+
export function updateRequired() {
|
|
22
|
+
end(updateLine('This Kit is too old for House'));
|
|
23
|
+
}
|
|
24
|
+
export function unknownMethod(kind) {
|
|
25
|
+
return updateLine(`This Kit does not know ${kind}`);
|
|
20
26
|
}
|
|
21
27
|
export function newerThanHouse(release, command) {
|
|
22
28
|
end(`Kit ${KIT_RELEASE} is newer than this Building supports (Kit ${release}). Install Kit ${release}: ${command}`);
|
package/dist/events.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
const AUTONOMOUS_ORIGINS = new Set(['task-notification', 'peer', 'coordinator', 'observer', 'observer-activity']);
|
|
1
2
|
const COMMAND = { shown: 'command' };
|
|
2
3
|
const HIDDEN = { shown: 'hidden' };
|
|
3
4
|
const FINISHED = new Set(['completed', 'failed']);
|
|
4
5
|
export const RUNNING_JOB = new Set(['running', 'paused']);
|
|
5
|
-
const MESSAGE_TARGETS =
|
|
6
|
+
const MESSAGE_TARGETS = new Map([
|
|
7
|
+
['send_agent_function_request', ['agent_function', 'agent']],
|
|
8
|
+
['cancel_agent_function_request', ['request']],
|
|
9
|
+
['send_mail', ['conversation', 'request']],
|
|
10
|
+
]);
|
|
6
11
|
function words(line) {
|
|
7
12
|
const found = [];
|
|
8
13
|
let word = null;
|
|
@@ -48,11 +53,13 @@ export function houseMessage(line) {
|
|
|
48
53
|
catch {
|
|
49
54
|
return null;
|
|
50
55
|
}
|
|
51
|
-
|
|
52
|
-
if (argument === undefined)
|
|
56
|
+
if (call === null)
|
|
53
57
|
return null;
|
|
54
|
-
const
|
|
55
|
-
|
|
58
|
+
const targets = MESSAGE_TARGETS.get(call.command);
|
|
59
|
+
if (targets === undefined)
|
|
60
|
+
return null;
|
|
61
|
+
const target = targets.map((argument) => call.arguments?.[argument]).find((value) => typeof value === 'string');
|
|
62
|
+
return { shown: 'message', command: call.command, target: target ?? null };
|
|
56
63
|
}
|
|
57
64
|
function decidedByLine(command, final) {
|
|
58
65
|
const line = typeof command === 'string' ? command : Array.isArray(command) ? command.at(-1) : undefined;
|
|
@@ -269,6 +276,7 @@ class Reader {
|
|
|
269
276
|
class ClaudeReader extends Reader {
|
|
270
277
|
watchedOptions = { model: 'model' };
|
|
271
278
|
tasks = new Map();
|
|
279
|
+
stop = null;
|
|
272
280
|
own(message) {
|
|
273
281
|
if (message.params?.update?._meta?.claudeCode?.parentToolUseId !== undefined)
|
|
274
282
|
return [];
|
|
@@ -276,7 +284,9 @@ class ClaudeReader extends Reader {
|
|
|
276
284
|
}
|
|
277
285
|
update(update) {
|
|
278
286
|
if (update.sessionUpdate === 'usage_update' && update._meta?.['_claude/origin'] !== undefined) {
|
|
279
|
-
|
|
287
|
+
const stop = this.stop;
|
|
288
|
+
this.stop = null;
|
|
289
|
+
return [...super.update(update), { kind: 'ended', stop }];
|
|
280
290
|
}
|
|
281
291
|
if (update.sessionUpdate === 'session_info_update' && update._meta?.jetbrains?.air?.sessionFailure !== undefined) {
|
|
282
292
|
return [{ kind: 'marker', marker: 'retry', text: update._meta.jetbrains.air.sessionFailure.title }];
|
|
@@ -287,6 +297,13 @@ class ClaudeReader extends Reader {
|
|
|
287
297
|
if (message.method !== '_claude/sdkMessage')
|
|
288
298
|
return [];
|
|
289
299
|
const sdk = message.params.message;
|
|
300
|
+
if (sdk.type === 'result') {
|
|
301
|
+
if (AUTONOMOUS_ORIGINS.has(sdk.origin?.kind))
|
|
302
|
+
this.stop = null;
|
|
303
|
+
else
|
|
304
|
+
this.stop = sdk.stop_reason === 'refusal' ? 'refusal' : sdk.is_error === true ? null : 'end_turn';
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
290
307
|
switch (sdk.subtype) {
|
|
291
308
|
case 'task_started':
|
|
292
309
|
if (sdk.tool_use_id !== undefined)
|
|
@@ -294,6 +311,10 @@ class ClaudeReader extends Reader {
|
|
|
294
311
|
return [];
|
|
295
312
|
case 'task_notification':
|
|
296
313
|
return sdk.tool_use_id === undefined ? [] : this.finish(sdk.tool_use_id, sdk.summary ?? null);
|
|
314
|
+
case 'task_progress':
|
|
315
|
+
if (!this.subagents.has(sdk.tool_use_id) || this.done.has(sdk.tool_use_id) || typeof sdk.description !== 'string')
|
|
316
|
+
return [];
|
|
317
|
+
return [{ kind: 'subagent-activity', id: sdk.tool_use_id, activity: sdk.description }];
|
|
297
318
|
default:
|
|
298
319
|
return [];
|
|
299
320
|
}
|
|
@@ -356,7 +377,7 @@ class CodexReader extends Reader {
|
|
|
356
377
|
if (codex?.threadStatus?.type === 'active')
|
|
357
378
|
return [{ kind: 'started' }];
|
|
358
379
|
if (codex?.threadStatus?.type === 'idle')
|
|
359
|
-
return [{ kind: 'ended' }];
|
|
380
|
+
return [{ kind: 'ended', stop: null }];
|
|
360
381
|
if (codex?.error !== undefined)
|
|
361
382
|
return [{ kind: 'marker', marker: 'retry', text: codex.error.message ?? '' }];
|
|
362
383
|
return [];
|
|
@@ -429,7 +450,7 @@ class GrokReader extends Reader {
|
|
|
429
450
|
return (message.params.runningPromptId ?? null) !== null && session === this.session ? [{ kind: 'started' }] : [];
|
|
430
451
|
}
|
|
431
452
|
if (message.params?.update?.sessionUpdate === 'turn_completed') {
|
|
432
|
-
return session === this.session ? [{ kind: 'ended' }] : [];
|
|
453
|
+
return session === this.session ? [{ kind: 'ended', stop: null }] : [];
|
|
433
454
|
}
|
|
434
455
|
if (message.method === '_x.ai/models/update') {
|
|
435
456
|
this.offered(message.params);
|