@agentshouse/kit 0.2.1-c84a4fccf2835 → 0.2.1-c8850697c8230
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 +28 -28
- package/dist/acp.js +90 -19
- package/dist/agents.js +86 -38
- package/dist/api.js +5 -5
- package/dist/authority-main.js +2 -2
- package/dist/bridge.js +1 -1
- package/dist/building.js +46 -0
- package/dist/calls.js +71 -0
- package/dist/clis.js +58 -22
- package/dist/{stream.js → connection.js} +45 -52
- package/dist/conversations.js +429 -147
- package/dist/copies.js +53 -51
- package/dist/end.js +14 -4
- package/dist/events.js +29 -8
- package/dist/files.js +139 -37
- package/dist/git.js +3 -3
- package/dist/home.js +0 -7
- 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 +4 -4
- package/dist/mcp.js +6 -2
- package/dist/placement.js +11 -2
- package/dist/relay.js +21 -0
- package/dist/resident.js +46 -38
- package/dist/scope.js +15 -4
- package/dist/scripts.js +255 -0
- package/dist/service.js +23 -1
- package/dist/sign-in.js +22 -21
- package/dist/silence.js +28 -0
- package/dist/skills.js +42 -97
- package/dist/transcription.js +83 -43
- package/dist/translate.js +2 -8
- package/dist/update.js +79 -37
- package/dist/wire.js +19 -3
- package/package.json +20 -21
- package/dist/bootstrap.json +0 -24
- package/dist/clis-main.js +0 -23
- package/dist/inputs.js +0 -37
- 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/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
|
}
|
|
@@ -58,6 +58,9 @@ function expiredAuthority(error) {
|
|
|
58
58
|
return undefined;
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
function grouped(items, size) {
|
|
62
|
+
return items.flatMap((_, index) => (index % size === 0 ? [items.slice(index, index + size)] : []));
|
|
63
|
+
}
|
|
61
64
|
function covers(path, prefix) {
|
|
62
65
|
return prefix === '' || path === prefix || path.startsWith(`${prefix}/`);
|
|
63
66
|
}
|
|
@@ -130,7 +133,7 @@ async function ignoreRules(repository, writable, copy) {
|
|
|
130
133
|
].map(pattern);
|
|
131
134
|
const granted = readOnly.filter((path) => !protectedPath(path)).map(pattern);
|
|
132
135
|
await ruled(join(repository, '.gitignore'), [
|
|
133
|
-
'#
|
|
136
|
+
'# Kit writes this file and .ignore. The paths below are read-only House',
|
|
134
137
|
'# content: Git does not track them, and House refuses a change to them even',
|
|
135
138
|
'# when this file is edited or a path is force-added.',
|
|
136
139
|
'/.gitignore',
|
|
@@ -145,7 +148,7 @@ async function ignoreRules(repository, writable, copy) {
|
|
|
145
148
|
'',
|
|
146
149
|
].join('\n'));
|
|
147
150
|
await ruled(join(repository, '.ignore'), [
|
|
148
|
-
'#
|
|
151
|
+
'# Kit writes this file so native search still reads the read-only House',
|
|
149
152
|
'# content that .gitignore keeps out of Git.',
|
|
150
153
|
`!${PROVENANCE}`,
|
|
151
154
|
...[...house, ...granted].map((rule) => `!${rule}`),
|
|
@@ -215,53 +218,56 @@ export class LocalCopies {
|
|
|
215
218
|
async mapping() {
|
|
216
219
|
await this.answering;
|
|
217
220
|
return [...this.copies.values()]
|
|
218
|
-
.filter((copy) => copy.
|
|
221
|
+
.filter((copy) => copy.unfollowed === undefined)
|
|
219
222
|
.map((copy) => `${rootOf(copy.room_handle)}: ${copy.repository}`)
|
|
220
223
|
.sort();
|
|
221
224
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
225
|
+
connected() {
|
|
226
|
+
return this.selected();
|
|
227
|
+
}
|
|
228
|
+
async changed(rooms) {
|
|
229
|
+
if (rooms.length > 0)
|
|
230
|
+
await this.selected(rooms);
|
|
226
231
|
}
|
|
227
232
|
fetch() {
|
|
228
233
|
return this.answered(() => this.fetched(true));
|
|
229
234
|
}
|
|
235
|
+
async selected(rooms) {
|
|
236
|
+
const copied = await this.answered(() => this.fetched(false, rooms));
|
|
237
|
+
if (copied.refused)
|
|
238
|
+
logged(copied.text);
|
|
239
|
+
}
|
|
230
240
|
answered(work) {
|
|
231
241
|
const answered = this.answering.then(work);
|
|
232
242
|
this.answering = answered.catch(() => undefined);
|
|
233
243
|
return answered;
|
|
234
244
|
}
|
|
235
|
-
async fetched(refreshing) {
|
|
236
|
-
const { local_copy: answer } = await this.house.post('/kit/local-copy/selection', {});
|
|
245
|
+
async fetched(refreshing, rooms) {
|
|
246
|
+
const { local_copy: answer } = await this.house.post('/kit/local-copy/selection', rooms === undefined ? {} : { rooms });
|
|
237
247
|
const synced = new Map(answer.rooms.map((room) => [`rooms/${room.room_ref}`, room.room_handle]));
|
|
238
|
-
|
|
248
|
+
const named = rooms === undefined ? null : new Set(rooms.map((room) => `rooms/${room}`));
|
|
249
|
+
const held = () => [...this.copies.values()].filter((copy) => named === null || named.has(copy.key));
|
|
250
|
+
return this.holding([...new Set([...held().map((copy) => copy.key), ...synced.keys()])].sort(), async () => {
|
|
239
251
|
const said = [];
|
|
240
252
|
let refused = false;
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (
|
|
253
|
+
const mark = refreshing ? 'said' : 'unsaid';
|
|
254
|
+
for (const copy of held()) {
|
|
255
|
+
if (synced.has(copy.key) || copy.unfollowed === 'said' || copy.unfollowed === mark)
|
|
244
256
|
continue;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
said.push(`${rootOf(copy.room_handle)}:
|
|
248
|
-
}
|
|
249
|
-
catch (error) {
|
|
250
|
-
refused = true;
|
|
251
|
-
said.push(`${rootOf(copy.room_handle)}: ${causeOf(error)}`);
|
|
252
|
-
}
|
|
257
|
+
await this.save({ ...copy, unfollowed: mark });
|
|
258
|
+
if (refreshing)
|
|
259
|
+
said.push(`${rootOf(copy.room_handle)}: no longer followed; kept as it is`);
|
|
253
260
|
}
|
|
254
261
|
const current = [];
|
|
255
262
|
for (const [key, handle] of synced) {
|
|
256
263
|
try {
|
|
257
264
|
const held = this.copies.get(key);
|
|
258
|
-
if (held?.erasing)
|
|
259
|
-
continue;
|
|
260
265
|
if (held === undefined || !(await exists(held.repository))) {
|
|
261
266
|
said.push(await this.materialize(key, handle));
|
|
262
267
|
continue;
|
|
263
268
|
}
|
|
264
|
-
|
|
269
|
+
const { unfollowed, ...followed } = held;
|
|
270
|
+
current.push(unfollowed === undefined && held.room_handle === handle ? held : await this.save({ ...followed, room_handle: handle }));
|
|
265
271
|
}
|
|
266
272
|
catch (error) {
|
|
267
273
|
refused = true;
|
|
@@ -269,13 +275,12 @@ export class LocalCopies {
|
|
|
269
275
|
}
|
|
270
276
|
}
|
|
271
277
|
if (refreshing && current.length > 0) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
catch (error) {
|
|
278
|
+
const failed = await this.refreshed(current).then(() => null, houseCause);
|
|
279
|
+
for (const copy of current)
|
|
280
|
+
said.push(...moved(copy, this.copies.get(copy.key)));
|
|
281
|
+
if (failed !== null) {
|
|
277
282
|
refused = true;
|
|
278
|
-
said.push(
|
|
283
|
+
said.push(failed);
|
|
279
284
|
}
|
|
280
285
|
}
|
|
281
286
|
return { refused, text: said.length === 0 ? 'Nothing new from House.' : said.join('\n') };
|
|
@@ -287,8 +292,6 @@ export class LocalCopies {
|
|
|
287
292
|
return;
|
|
288
293
|
await this.exclusive(key, async () => {
|
|
289
294
|
const copy = this.copies.get(key);
|
|
290
|
-
if (copy === undefined || copy.erasing)
|
|
291
|
-
return;
|
|
292
295
|
let moves = copy.moves;
|
|
293
296
|
for (const { from, to } of report.moves) {
|
|
294
297
|
const held = moves.find((move) => move.worktree === report.worktree && move.to === from);
|
|
@@ -344,12 +347,6 @@ export class LocalCopies {
|
|
|
344
347
|
this.copies.set(copy.key, copy);
|
|
345
348
|
return copy;
|
|
346
349
|
}
|
|
347
|
-
async erase(copy) {
|
|
348
|
-
await this.save({ ...copy, erasing: true });
|
|
349
|
-
await rm(copy.repository, { recursive: true, force: true });
|
|
350
|
-
await rm(recordOf(copy.key), { force: true });
|
|
351
|
-
this.copies.delete(copy.key);
|
|
352
|
-
}
|
|
353
350
|
async read(handle) {
|
|
354
351
|
const prefixes = [rootOf(handle)];
|
|
355
352
|
const discovery = await this.house.post('/kit/door/discover', { prefixes });
|
|
@@ -391,7 +388,7 @@ export class LocalCopies {
|
|
|
391
388
|
throw new Error('House serves nothing of it to copy');
|
|
392
389
|
const repository = join(localCopies(), 'rooms', read.anchor);
|
|
393
390
|
if (await exists(repository))
|
|
394
|
-
throw new Error(`${repository} already exists and
|
|
391
|
+
throw new Error(`${repository} already exists and Kit leaves it as it is`);
|
|
395
392
|
const staging = join(dirname(repository), `.${randomUUID()}`);
|
|
396
393
|
await mkdir(staging, { recursive: true });
|
|
397
394
|
let house;
|
|
@@ -450,19 +447,24 @@ export class LocalCopies {
|
|
|
450
447
|
return next;
|
|
451
448
|
}
|
|
452
449
|
async refreshed(copies) {
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
});
|
|
456
|
-
const
|
|
450
|
+
const discover = (group) => this.house.post('/kit/door/discover', { prefixes: group.map((copy) => rootOf(copy.room_handle)) });
|
|
451
|
+
const [first, ...rest] = copies;
|
|
452
|
+
const { bounds, authorities } = await discover([first]);
|
|
453
|
+
for (const group of grouped(rest, bounds.max_prefixes))
|
|
454
|
+
authorities.push(...(await discover(group)).authorities);
|
|
455
|
+
const unchanged = [];
|
|
457
456
|
for (const copy of copies) {
|
|
458
|
-
const authority =
|
|
457
|
+
const authority = authorities.find((entry) => entry.room_ref === copy.room_ref);
|
|
459
458
|
if (authority !== undefined &&
|
|
460
459
|
JSON.stringify([authority.operations.write, authority.protected_paths]) !== JSON.stringify([copy.write, copy.protected])) {
|
|
461
|
-
|
|
460
|
+
await this.save(await this.received(copy));
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
unchanged.push(copy);
|
|
462
464
|
}
|
|
463
465
|
}
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
+
for (const group of grouped(unchanged, bounds.max_prefixes))
|
|
467
|
+
await this.caughtUp(group, false);
|
|
466
468
|
}
|
|
467
469
|
async caughtUp(copies, whole) {
|
|
468
470
|
const caught = new Map(copies.map((copy) => [copy.key, copy]));
|
|
@@ -544,8 +546,6 @@ export class LocalCopies {
|
|
|
544
546
|
}
|
|
545
547
|
async pushed(key, worktree, selector, caller) {
|
|
546
548
|
let copy = this.copies.get(key);
|
|
547
|
-
if (copy === undefined || copy.erasing)
|
|
548
|
-
return outside(worktree);
|
|
549
549
|
if (copy.push?.state === 'submitted')
|
|
550
550
|
return this.settle(copy, caller);
|
|
551
551
|
const selected = await run(worktree, ['rev-parse', '--verify', '--quiet', '--end-of-options', `${selector}^{commit}`]);
|
|
@@ -660,8 +660,10 @@ export class LocalCopies {
|
|
|
660
660
|
}
|
|
661
661
|
if (outcome.refused.code === 'expired')
|
|
662
662
|
return this.recovered(copy);
|
|
663
|
-
const rebased = rebaseFixes(outcome.refused);
|
|
664
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);
|
|
665
667
|
try {
|
|
666
668
|
settled = (await this.caughtUp([settled], true))[0];
|
|
667
669
|
}
|
package/dist/end.js
CHANGED
|
@@ -1,21 +1,31 @@
|
|
|
1
|
+
import { KIT_RELEASE } from './clis.js';
|
|
1
2
|
import { forgetRejected } from './home.js';
|
|
2
3
|
import { installCommand } from './placement.js';
|
|
3
4
|
import { killDescendants } from './scope.js';
|
|
4
|
-
export const REPLACED = 'another Kit started for this Environment';
|
|
5
|
+
export const REPLACED = 'another Kit process started for this Environment';
|
|
5
6
|
function exit(reason, status) {
|
|
6
7
|
process.stderr.write(`kit: ${reason}\n`);
|
|
7
8
|
killDescendants();
|
|
8
9
|
process.exit(status);
|
|
9
10
|
}
|
|
10
11
|
export function end(reason) {
|
|
11
|
-
exit(reason, 0);
|
|
12
|
+
exit(reason, process.env.HOUSE_KIT_RESIDENT === String(process.pid) ? 0 : 1);
|
|
12
13
|
}
|
|
13
14
|
export function restart(reason) {
|
|
14
15
|
exit(reason, 1);
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
+
function updateLine(lead) {
|
|
17
18
|
const command = installCommand();
|
|
18
|
-
|
|
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}`);
|
|
26
|
+
}
|
|
27
|
+
export function newerThanHouse(release, command) {
|
|
28
|
+
end(`Kit ${KIT_RELEASE} is newer than this Building supports (Kit ${release}). Install Kit ${release}: ${command}`);
|
|
19
29
|
}
|
|
20
30
|
export async function signInAgain(rejected) {
|
|
21
31
|
await forgetRejected(rejected);
|
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
|
|
6
|
+
const MESSAGE_TARGETS = new Map([
|
|
7
|
+
['send_routine_request', ['routine', 'agent']],
|
|
8
|
+
['cancel_routine_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);
|
package/dist/files.js
CHANGED
|
@@ -1,42 +1,144 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { createWriteStream } from 'node:fs';
|
|
3
|
+
import { mkdir, rename, rm } from 'node:fs/promises';
|
|
4
|
+
import { basename, dirname, join } from 'node:path';
|
|
5
|
+
import { Readable, Transform } from 'node:stream';
|
|
6
|
+
import { pipeline } from 'node:stream/promises';
|
|
7
|
+
import { parseDocument } from 'yaml';
|
|
8
|
+
import { retryDelay } from './api.js';
|
|
9
|
+
import { Unsettled } from './mcp.js';
|
|
5
10
|
import { relayed } from './relay.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
const STALLED_ROUNDS = 8;
|
|
12
|
+
const PART_CHECKSUM = 'x-amz-checksum-sha256';
|
|
13
|
+
const LINK = /\]\((\/rooms\/[^/)\s]+\/[^)\s]+)\)/gu;
|
|
14
|
+
export function linkedPaths(text) {
|
|
15
|
+
return [...new Set(Array.from(text.matchAll(LINK), (link) => link[1]))];
|
|
16
|
+
}
|
|
17
|
+
export function inspectedFile(answer) {
|
|
18
|
+
const document = parseDocument(answer);
|
|
19
|
+
if (document.errors.length > 0)
|
|
20
|
+
return null;
|
|
21
|
+
const inspected = document.toJS();
|
|
22
|
+
if (typeof inspected?.download !== 'string')
|
|
23
|
+
return null;
|
|
24
|
+
return {
|
|
25
|
+
download: inspected.download,
|
|
26
|
+
sha256: String(inspected.sha256),
|
|
27
|
+
media_type: typeof inspected.media_type === 'string' ? inspected.media_type : null,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function paused(milliseconds) {
|
|
31
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
32
|
+
}
|
|
33
|
+
export function storeRefusal(act, status) {
|
|
34
|
+
return new Error(`the ${act} answered ${status}; ${act} the file again`);
|
|
35
|
+
}
|
|
36
|
+
async function partDigests(uploading, partBytes) {
|
|
37
|
+
const whole = createHash('sha256');
|
|
38
|
+
const digests = [];
|
|
39
|
+
for (let start = 0; digests.length === 0 || start < uploading.bytes; start += partBytes) {
|
|
40
|
+
const end = Math.min(start + partBytes, uploading.bytes);
|
|
41
|
+
const digest = createHash('sha256');
|
|
42
|
+
for await (const chunk of uploading.read(start, end)) {
|
|
43
|
+
digest.update(chunk);
|
|
44
|
+
whole.update(chunk);
|
|
45
|
+
}
|
|
46
|
+
digests.push(digest.digest());
|
|
47
|
+
}
|
|
48
|
+
if (whole.digest('hex') !== uploading.sha256)
|
|
49
|
+
throw new Error(`${uploading.name} changed while it was uploaded; upload it again`);
|
|
50
|
+
return digests;
|
|
51
|
+
}
|
|
52
|
+
export async function uploaded(declared, uploading) {
|
|
53
|
+
const partBytes = declared.part_bytes;
|
|
54
|
+
const digests = await partDigests(uploading, partBytes);
|
|
55
|
+
const partSha256 = digests.map((digest) => digest.toString('hex'));
|
|
56
|
+
let answer = await finishing(uploading, declared.id, partSha256);
|
|
57
|
+
let idle = 0;
|
|
58
|
+
let stalled = 0;
|
|
59
|
+
while ('upload' in answer) {
|
|
60
|
+
const { id, parts } = answer.upload;
|
|
61
|
+
let sent = 0;
|
|
62
|
+
let failure = null;
|
|
63
|
+
for (const { part, url } of parts ?? []) {
|
|
64
|
+
const start = (part - 1) * partBytes;
|
|
65
|
+
try {
|
|
66
|
+
await uploading.send(url, start, Math.min(start + partBytes, uploading.bytes), {
|
|
67
|
+
[PART_CHECKSUM]: digests[part - 1].toString('base64'),
|
|
68
|
+
});
|
|
69
|
+
sent++;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
failure = error;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (sent > 0) {
|
|
77
|
+
idle = 0;
|
|
78
|
+
stalled = 0;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
if (failure !== null && ++stalled >= STALLED_ROUNDS)
|
|
82
|
+
throw failure;
|
|
83
|
+
// A round that sent no part, its store unreachable, is followed by the next finishing call on the jittered curve.
|
|
84
|
+
await paused(retryDelay(idle++));
|
|
85
|
+
}
|
|
86
|
+
answer = await finishing(uploading, id, partSha256);
|
|
87
|
+
}
|
|
88
|
+
return answer;
|
|
89
|
+
}
|
|
90
|
+
async function finishing(uploading, upload, partSha256) {
|
|
91
|
+
for (let unsettled = 1;; unsettled++) {
|
|
92
|
+
try {
|
|
93
|
+
return await uploading.finish(upload, partSha256);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
if (!(error instanceof Unsettled) || unsettled === STALLED_ROUNDS)
|
|
21
97
|
throw error;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
await writeFile(path, verified(Buffer.from(await answer.arrayBuffer()), file.sha256, file.name));
|
|
26
|
-
paths.push(path);
|
|
98
|
+
}
|
|
99
|
+
// A finishing call House did not settle, unreached or refused short of final, is sent again on the jittered curve as a conversation resends it, so a dropped connection or a House restart resumes the upload instead of ending it.
|
|
100
|
+
await paused(retryDelay(unsettled - 1));
|
|
27
101
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
102
|
+
}
|
|
103
|
+
async function drained(body) {
|
|
104
|
+
for await (const _chunk of body)
|
|
105
|
+
;
|
|
106
|
+
}
|
|
107
|
+
export async function downloaded(address, fetching, destination, name) {
|
|
108
|
+
for (let asked = 1;; asked++) {
|
|
109
|
+
const { download, sha256 } = await address();
|
|
110
|
+
const answer = await fetching(download);
|
|
111
|
+
if (answer.status === 403 && asked === 1) {
|
|
112
|
+
await drained(answer.body);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (answer.status !== 200) {
|
|
116
|
+
await drained(answer.body);
|
|
117
|
+
throw storeRefusal('download', answer.status);
|
|
118
|
+
}
|
|
119
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
120
|
+
const partial = join(dirname(destination), `.${basename(destination)}.${randomUUID()}`);
|
|
121
|
+
const digest = createHash('sha256');
|
|
122
|
+
const hashed = new Transform({
|
|
123
|
+
transform(chunk, _encoding, done) {
|
|
124
|
+
digest.update(chunk);
|
|
125
|
+
done(null, chunk);
|
|
126
|
+
},
|
|
36
127
|
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
128
|
+
try {
|
|
129
|
+
await pipeline(answer.body, hashed, createWriteStream(partial, { flags: 'wx' }));
|
|
130
|
+
if (digest.digest('hex') !== sha256)
|
|
131
|
+
throw new Error(`${name} did not arrive whole; download it again`);
|
|
132
|
+
await rename(partial, destination);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
await rm(partial, { force: true });
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export async function fetched(url, signal) {
|
|
142
|
+
const answer = await fetch(relayed(url), { signal });
|
|
143
|
+
return { status: answer.status, body: answer.body ?? Readable.from([]) };
|
|
42
144
|
}
|
package/dist/git.js
CHANGED
|
@@ -2,16 +2,16 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { isUtf8 } from 'node:buffer';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import { withoutProxy } from './clis.js';
|
|
6
5
|
import { workspaceRoot } from './home.js';
|
|
6
|
+
import { childEnvironment } from './relay.js';
|
|
7
7
|
export function localCopies() {
|
|
8
8
|
return join(workspaceRoot(), 'local-copies');
|
|
9
9
|
}
|
|
10
10
|
export const HOUSE_REF = 'refs/house/received';
|
|
11
|
-
const COMMITTER = '
|
|
11
|
+
const COMMITTER = 'Kit <house-kit@localhost>';
|
|
12
12
|
export function run(cwd, args, input, env = {}) {
|
|
13
13
|
return new Promise((resolve, reject) => {
|
|
14
|
-
const child = spawn('git', args, { cwd, env: { ...
|
|
14
|
+
const child = spawn('git', args, { cwd, env: { ...childEnvironment(), ...env } });
|
|
15
15
|
const stdout = [];
|
|
16
16
|
let stderr = '';
|
|
17
17
|
child.stdout.on('data', (chunk) => stdout.push(chunk));
|
package/dist/home.js
CHANGED
|
@@ -8,7 +8,6 @@ const ENROLMENT = 'credential.json';
|
|
|
8
8
|
const DISCONNECTED = 'disconnected.json';
|
|
9
9
|
const CONFIGURATION = 'kit.json';
|
|
10
10
|
const OWN_AGENT = 'own-agent.json';
|
|
11
|
-
const RESTART = 'restart';
|
|
12
11
|
let defaulted;
|
|
13
12
|
function writable(directory) {
|
|
14
13
|
try {
|
|
@@ -132,12 +131,6 @@ export async function forgetRejected(rejected) {
|
|
|
132
131
|
export function enrolled() {
|
|
133
132
|
return existsSync(join(kitHome(), ENROLMENT));
|
|
134
133
|
}
|
|
135
|
-
export function restartAsked() {
|
|
136
|
-
return existsSync(join(kitHome(), RESTART));
|
|
137
|
-
}
|
|
138
|
-
export function forgetRestart() {
|
|
139
|
-
return rm(join(kitHome(), RESTART), { force: true });
|
|
140
|
-
}
|
|
141
134
|
export function readOwnAgent() {
|
|
142
135
|
return readHome(OWN_AGENT);
|
|
143
136
|
}
|