@agentshouse/kit 0.2.1-cd9cbd8b6428c → 0.2.1-cfe80fd914176
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 +8 -9
- package/dist/acp.js +26 -13
- package/dist/agents.js +79 -38
- package/dist/api.js +30 -2
- package/dist/app-git.js +64 -0
- package/dist/bootstrap.json +4 -4
- package/dist/bridge.js +34 -40
- package/dist/clis.js +81 -65
- package/dist/conversations.js +282 -231
- package/dist/copies.js +27 -35
- package/dist/end.js +8 -0
- package/dist/events.js +587 -0
- package/dist/files.js +14 -0
- package/dist/house-main.js +28 -10
- package/dist/instructions.js +2 -2
- package/dist/kit-main.js +3 -0
- package/dist/login.js +4 -0
- package/dist/logout.js +3 -0
- package/dist/parts.js +64 -0
- package/dist/refusals.js +12 -0
- package/dist/resident.js +3 -3
- package/dist/secret-input.js +4 -1
- package/dist/shell.js +3 -0
- package/dist/sign-in.js +33 -8
- package/dist/skills.js +1 -1
- package/dist/stream.js +29 -4
- package/dist/transcription.js +54 -6
- package/dist/update.js +2 -0
- package/package.json +1 -1
- package/dist/blocks.js +0 -51
package/dist/copies.js
CHANGED
|
@@ -2,7 +2,7 @@ import { parse } from '@agentshouse/mdmodel';
|
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { mkdir, readFile, readdir, realpath, rename, rm, rmdir, stat, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
|
-
import { HouseRefusal } from './api.js';
|
|
5
|
+
import { finalRefusal, HouseRefusal, retrying } from './api.js';
|
|
6
6
|
import { blobId, blobText, git, HOUSE_REF, importCommits, localCopies, revision, run, succeeded, tree, } from './git.js';
|
|
7
7
|
import { kitHome } from './home.js';
|
|
8
8
|
import { operationId } from './operation.js';
|
|
@@ -11,7 +11,6 @@ import { changed } from './translate.js';
|
|
|
11
11
|
import { relayed } from './relay.js';
|
|
12
12
|
const BRANCH = 'main';
|
|
13
13
|
const PROVENANCE = '_provenance';
|
|
14
|
-
const REPLICA_RETRY_MS = 30_000;
|
|
15
14
|
const IDENTITY = { GIT_COMMITTER_NAME: 'House Kit', GIT_COMMITTER_EMAIL: 'house-kit@localhost' };
|
|
16
15
|
function causeOf(error) {
|
|
17
16
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -41,11 +40,11 @@ function sidecar(path) {
|
|
|
41
40
|
segments.splice(-1, 0, PROVENANCE);
|
|
42
41
|
return segments.join('/');
|
|
43
42
|
}
|
|
44
|
-
function rootOf(
|
|
45
|
-
return
|
|
43
|
+
function rootOf(handle) {
|
|
44
|
+
return `/rooms/${handle}`;
|
|
46
45
|
}
|
|
47
46
|
function relative(path) {
|
|
48
|
-
return path.replace(/^\/
|
|
47
|
+
return path.replace(/^\/rooms\/[^/]+\//, '');
|
|
49
48
|
}
|
|
50
49
|
function declaredWritable(path, copy) {
|
|
51
50
|
const deepest = (scopes) => Math.max(-1, ...scopes.filter((scope) => covers(path, scope)).map((scope) => scope.length));
|
|
@@ -161,11 +160,11 @@ export class LocalCopies {
|
|
|
161
160
|
key: 'owner',
|
|
162
161
|
submit: async (operation, changes) => {
|
|
163
162
|
try {
|
|
164
|
-
const outcome = await house.post('/kit/door/batch', { operation_id: operation, changes });
|
|
163
|
+
const outcome = await retrying(() => house.post('/kit/door/batch', { operation_id: operation, changes }));
|
|
165
164
|
return { accepted: outcome.contents };
|
|
166
165
|
}
|
|
167
166
|
catch (error) {
|
|
168
|
-
if (!(error
|
|
167
|
+
if (!finalRefusal(error))
|
|
169
168
|
throw error;
|
|
170
169
|
return {
|
|
171
170
|
refused: refusalOf(error.text) ?? { code: '', text: `House refused it with ${error.status}`, conflicts: [] },
|
|
@@ -177,7 +176,7 @@ export class LocalCopies {
|
|
|
177
176
|
async load() {
|
|
178
177
|
const home = join(kitHome(), 'local-copies');
|
|
179
178
|
const names = (await readdir(join(home, 'rooms')).catch(() => [])).filter((name) => name.endsWith('.json'));
|
|
180
|
-
for (const path of
|
|
179
|
+
for (const path of names.map((name) => join(home, 'rooms', name))) {
|
|
181
180
|
const text = await readFile(path, 'utf8').catch(() => null);
|
|
182
181
|
if (text === null)
|
|
183
182
|
continue;
|
|
@@ -189,7 +188,7 @@ export class LocalCopies {
|
|
|
189
188
|
await this.selecting;
|
|
190
189
|
return [...this.copies.values()]
|
|
191
190
|
.filter((copy) => copy.active)
|
|
192
|
-
.map((copy) => `${rootOf(copy)}: ${copy.repository}`)
|
|
191
|
+
.map((copy) => `${rootOf(copy.room_handle)}: ${copy.repository}`)
|
|
193
192
|
.sort();
|
|
194
193
|
}
|
|
195
194
|
select() {
|
|
@@ -199,12 +198,7 @@ export class LocalCopies {
|
|
|
199
198
|
}
|
|
200
199
|
async reselect() {
|
|
201
200
|
const { local_copy: chosen } = await this.house.post('/kit/local-copy/selection', {});
|
|
202
|
-
const selected = [
|
|
203
|
-
...(chosen?.rooms ?? [])
|
|
204
|
-
.filter((room) => room.room_handle !== null)
|
|
205
|
-
.map((room) => ({ key: `rooms/${room.room_ref}`, private: false, handle: room.room_handle })),
|
|
206
|
-
...(chosen?.private === true ? [{ key: 'private', private: true, handle: null }] : []),
|
|
207
|
-
];
|
|
201
|
+
const selected = (chosen?.rooms ?? []).flatMap((room) => room.room_handle === null ? [] : [{ key: `rooms/${room.room_ref}`, handle: room.room_handle }]);
|
|
208
202
|
const keys = new Set(selected.map((scope) => scope.key));
|
|
209
203
|
const work = [];
|
|
210
204
|
for (const key of this.copies.keys()) {
|
|
@@ -288,7 +282,7 @@ export class LocalCopies {
|
|
|
288
282
|
}).catch(logged);
|
|
289
283
|
}
|
|
290
284
|
async save(copy) {
|
|
291
|
-
const path = join(kitHome(), 'local-copies',
|
|
285
|
+
const path = join(kitHome(), 'local-copies', `${copy.key}.json`);
|
|
292
286
|
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
293
287
|
const temporary = `${path}.${process.pid}`;
|
|
294
288
|
await writeFile(temporary, JSON.stringify(copy), { mode: 0o600 });
|
|
@@ -296,8 +290,8 @@ export class LocalCopies {
|
|
|
296
290
|
this.copies.set(copy.key, copy);
|
|
297
291
|
return copy;
|
|
298
292
|
}
|
|
299
|
-
async read(
|
|
300
|
-
const prefixes = [rootOf(
|
|
293
|
+
async read(handle) {
|
|
294
|
+
const prefixes = [rootOf(handle)];
|
|
301
295
|
const discovery = await this.house.post('/kit/door/discover', { prefixes });
|
|
302
296
|
let bundle = await this.house.post('/kit/door/bootstrap', { prefixes });
|
|
303
297
|
if ('transfer' in bundle)
|
|
@@ -344,13 +338,13 @@ export class LocalCopies {
|
|
|
344
338
|
return;
|
|
345
339
|
}
|
|
346
340
|
const discovery = await this.house.post('/kit/door/discover', {
|
|
347
|
-
prefixes: [rootOf(copy)],
|
|
341
|
+
prefixes: [rootOf(copy.room_handle)],
|
|
348
342
|
});
|
|
349
343
|
const authority = discovery.authorities.find((entry) => entry.room_ref === copy.room_ref);
|
|
350
344
|
if (authority === undefined)
|
|
351
345
|
return;
|
|
352
346
|
if (authority.managed_replication !== 'admitted') {
|
|
353
|
-
logged(`operation_denied: ${rootOf(copy)} is not synchronized to this Environment`);
|
|
347
|
+
logged(`operation_denied: ${rootOf(copy.room_handle)} is not synchronized to this Environment`);
|
|
354
348
|
return;
|
|
355
349
|
}
|
|
356
350
|
if (JSON.stringify([authority.operations.write, authority.protected_paths]) !==
|
|
@@ -361,12 +355,12 @@ export class LocalCopies {
|
|
|
361
355
|
await this.save(await this.caughtUp(copy));
|
|
362
356
|
}
|
|
363
357
|
async materialize(scope) {
|
|
364
|
-
const read = await this.read(
|
|
358
|
+
const read = await this.read(scope.handle);
|
|
365
359
|
if (read === null) {
|
|
366
|
-
logged(`House serves nothing of ${rootOf(
|
|
360
|
+
logged(`House serves nothing of ${rootOf(scope.handle)} to copy`);
|
|
367
361
|
return;
|
|
368
362
|
}
|
|
369
|
-
const repository =
|
|
363
|
+
const repository = join(localCopies(), 'rooms', read.anchor);
|
|
370
364
|
if (await exists(repository)) {
|
|
371
365
|
logged(`${repository} already exists and House Kit leaves it as it is`);
|
|
372
366
|
return;
|
|
@@ -378,7 +372,7 @@ export class LocalCopies {
|
|
|
378
372
|
try {
|
|
379
373
|
await git(staging, ['init', '-q', '-b', BRANCH]);
|
|
380
374
|
house = await importCommits(staging, [
|
|
381
|
-
{ message: `House ${rootOf(
|
|
375
|
+
{ message: `House ${rootOf(scope.handle)}`, from: null, files: read.writable },
|
|
382
376
|
]);
|
|
383
377
|
await git(staging, ['update-ref', `refs/heads/${BRANCH}`, house]);
|
|
384
378
|
await git(staging, ['reset', '-q', '--hard']);
|
|
@@ -392,7 +386,6 @@ export class LocalCopies {
|
|
|
392
386
|
}
|
|
393
387
|
await this.save({
|
|
394
388
|
key: scope.key,
|
|
395
|
-
private: scope.private,
|
|
396
389
|
room_ref: read.anchor,
|
|
397
390
|
room_handle: scope.handle,
|
|
398
391
|
repository,
|
|
@@ -408,11 +401,11 @@ export class LocalCopies {
|
|
|
408
401
|
});
|
|
409
402
|
}
|
|
410
403
|
async received(copy) {
|
|
411
|
-
const read = await this.read(copy);
|
|
404
|
+
const read = await this.read(copy.room_handle);
|
|
412
405
|
if (read === null)
|
|
413
406
|
return copy;
|
|
414
407
|
const house = await importCommits(copy.repository, [
|
|
415
|
-
{ message: `House ${rootOf(copy)} at position ${read.position}`, from: copy.house, replaces: true, files: read.writable },
|
|
408
|
+
{ message: `House ${rootOf(copy.room_handle)} at position ${read.position}`, from: copy.house, replaces: true, files: read.writable },
|
|
416
409
|
]);
|
|
417
410
|
const removed = copy.read_only.filter((path) => !read.readOnly.has(path));
|
|
418
411
|
const readOnly = await readOnlyPlaced(copy.repository, copy.read_only, read.readOnly, removed);
|
|
@@ -435,7 +428,7 @@ export class LocalCopies {
|
|
|
435
428
|
let answer;
|
|
436
429
|
try {
|
|
437
430
|
answer = await this.house.post('/kit/door/enumerate', {
|
|
438
|
-
prefixes: [rootOf(copy)],
|
|
431
|
+
prefixes: [rootOf(copy.room_handle)],
|
|
439
432
|
positions: [{ authority: copy.room_ref, position: copy.position, log_epoch: copy.log_epoch }],
|
|
440
433
|
});
|
|
441
434
|
}
|
|
@@ -445,7 +438,6 @@ export class LocalCopies {
|
|
|
445
438
|
return this.received(copy);
|
|
446
439
|
if (code !== 'replica_behind')
|
|
447
440
|
throw error;
|
|
448
|
-
setTimeout(() => this.deliver(copy.key), REPLICA_RETRY_MS).unref();
|
|
449
441
|
return copy;
|
|
450
442
|
}
|
|
451
443
|
const view = answer.authorities.find((entry) => entry.authority === copy.room_ref);
|
|
@@ -485,7 +477,7 @@ export class LocalCopies {
|
|
|
485
477
|
let { house, manifests } = copy;
|
|
486
478
|
if (files.size > 0) {
|
|
487
479
|
house = await importCommits(copy.repository, [
|
|
488
|
-
{ message: `House ${rootOf(copy)} at position ${position}`, from: copy.house, files },
|
|
480
|
+
{ message: `House ${rootOf(copy.room_handle)} at position ${position}`, from: copy.house, files },
|
|
489
481
|
]);
|
|
490
482
|
manifests = { ...manifests, [house]: { parent: copy.house, files: revisions } };
|
|
491
483
|
}
|
|
@@ -516,7 +508,7 @@ export class LocalCopies {
|
|
|
516
508
|
copy = await this.save(without(copy));
|
|
517
509
|
}
|
|
518
510
|
if (!copy.active) {
|
|
519
|
-
return { refused: true, text: `${rootOf(copy)} is no longer synced to this Environment, so House takes no push from it` };
|
|
511
|
+
return { refused: true, text: `${rootOf(copy.room_handle)} is no longer synced to this Environment, so House takes no push from it` };
|
|
520
512
|
}
|
|
521
513
|
const listed = (await git(copy.repository, ['rev-list', '--topo-order', commit])).split('\n');
|
|
522
514
|
const base = listed.find((ancestor) => ancestor in copy.manifests);
|
|
@@ -550,7 +542,7 @@ export class LocalCopies {
|
|
|
550
542
|
return this.settle(submitted, caller);
|
|
551
543
|
}
|
|
552
544
|
async derived(copy, base, commit) {
|
|
553
|
-
const root = rootOf(copy);
|
|
545
|
+
const root = rootOf(copy.room_handle);
|
|
554
546
|
const before = await tree(copy.repository, base);
|
|
555
547
|
const after = await tree(copy.repository, commit);
|
|
556
548
|
const moved = movedPaths(copy.moves, before, after);
|
|
@@ -632,7 +624,7 @@ export class LocalCopies {
|
|
|
632
624
|
const push = copy.push;
|
|
633
625
|
const current = await this.save(await this.caughtUp(copy));
|
|
634
626
|
const house = await tree(copy.repository, current.house);
|
|
635
|
-
const root = rootOf(current);
|
|
627
|
+
const root = rootOf(current.room_handle);
|
|
636
628
|
if (!(await this.landed(copy.repository, push.changes, house, await tree(copy.repository, push.commit)))) {
|
|
637
629
|
await this.save(without(current));
|
|
638
630
|
return {
|
|
@@ -688,7 +680,7 @@ export class LocalCopies {
|
|
|
688
680
|
written = [
|
|
689
681
|
...written,
|
|
690
682
|
{
|
|
691
|
-
path: `${rootOf(copy)}/${path}`,
|
|
683
|
+
path: `${rootOf(copy.room_handle)}/${path}`,
|
|
692
684
|
revision: entry === undefined ? null : (revisionAt(copy, copy.house, path) ?? null),
|
|
693
685
|
content: entry === undefined ? null : await blobText(copy.repository, entry, path),
|
|
694
686
|
},
|
|
@@ -708,7 +700,7 @@ export class LocalCopies {
|
|
|
708
700
|
}
|
|
709
701
|
const edits = new Map([...files].filter(([path, content]) => (content === null ? selected.has(path) : selected.get(path) !== blobId(content))));
|
|
710
702
|
const received = [...files.keys()].filter((path) => revisionAt(copy, copy.house, path) === revisionAt(copy, push.house, path));
|
|
711
|
-
const accepted = `House ${rootOf(copy)} with ${push.commit} accepted`;
|
|
703
|
+
const accepted = `House ${rootOf(copy.room_handle)} with ${push.commit} accepted`;
|
|
712
704
|
const house = await importCommits(copy.repository, [
|
|
713
705
|
...(edits.size === 0 ? [] : [{ message: accepted, from: push.commit, files: edits }]),
|
|
714
706
|
{
|
package/dist/end.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { killDescendants } from './acp.js';
|
|
2
|
+
export const REPLACED = 'another Kit started for this Environment';
|
|
3
|
+
export const SIGN_IN_AGAIN = 'sign in again';
|
|
4
|
+
export function end(reason) {
|
|
5
|
+
process.stderr.write(`kit: ${reason}\n`);
|
|
6
|
+
killDescendants();
|
|
7
|
+
process.exit(0);
|
|
8
|
+
}
|