@agentshouse/kit 0.1.0-alpha.9 → 0.2.1-alpha.1

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.
Files changed (93) hide show
  1. package/README.md +14 -1740
  2. package/dist/acp.js +120 -0
  3. package/dist/agents.js +90 -0
  4. package/dist/api.js +45 -0
  5. package/dist/blocks.js +51 -0
  6. package/dist/bridge.js +124 -0
  7. package/dist/clis.js +110 -0
  8. package/dist/conversations.js +538 -0
  9. package/dist/files.js +21 -0
  10. package/dist/home.js +24 -0
  11. package/dist/house-main.js +92 -0
  12. package/dist/instructions.js +28 -0
  13. package/dist/kit-main.js +16 -0
  14. package/dist/login.js +112 -0
  15. package/dist/resident.js +66 -0
  16. package/dist/secret-input.js +16 -0
  17. package/dist/sign-in.js +83 -0
  18. package/dist/stream.js +47 -0
  19. package/package.json +34 -10
  20. package/bin/connect-linux.sh +0 -134
  21. package/bin/kit.js +0 -1023
  22. package/bootstrap.json +0 -17
  23. package/src/acp.js +0 -99
  24. package/src/adapter-mcp.js +0 -44
  25. package/src/adapter.js +0 -252
  26. package/src/agent-logins.js +0 -65
  27. package/src/agents.js +0 -504
  28. package/src/apply.js +0 -29
  29. package/src/attach.js +0 -52
  30. package/src/binding.js +0 -90
  31. package/src/blocks.js +0 -156
  32. package/src/bridge.js +0 -449
  33. package/src/bytes.js +0 -89
  34. package/src/channel.js +0 -37
  35. package/src/command.js +0 -109
  36. package/src/configuration.js +0 -465
  37. package/src/connector-login.js +0 -91
  38. package/src/connector.js +0 -107
  39. package/src/continuation.js +0 -61
  40. package/src/contract.js +0 -363
  41. package/src/conversation-originals.js +0 -143
  42. package/src/custom/authorization.js +0 -63
  43. package/src/declaration.js +0 -35
  44. package/src/delivery.js +0 -191
  45. package/src/directory.js +0 -102
  46. package/src/draft.js +0 -89
  47. package/src/google/calendar.js +0 -177
  48. package/src/google/declared.js +0 -146
  49. package/src/google/drive.js +0 -435
  50. package/src/google/gateway.js +0 -166
  51. package/src/google/gmail.js +0 -249
  52. package/src/google/id.js +0 -1
  53. package/src/google/oauth.js +0 -273
  54. package/src/google/source.js +0 -25
  55. package/src/hook.js +0 -376
  56. package/src/host.js +0 -121
  57. package/src/house-main.js +0 -79
  58. package/src/house.js +0 -1090
  59. package/src/inbound.js +0 -175
  60. package/src/instance/google.js +0 -1
  61. package/src/kit-work-stream.js +0 -84
  62. package/src/launcher-main.js +0 -19
  63. package/src/launcher.js +0 -276
  64. package/src/local-files.js +0 -113
  65. package/src/login.js +0 -274
  66. package/src/mcp.js +0 -71
  67. package/src/migrator.js +0 -248
  68. package/src/mount.js +0 -582
  69. package/src/operation.js +0 -20
  70. package/src/original.js +0 -631
  71. package/src/peer.js +0 -124
  72. package/src/plan.js +0 -57
  73. package/src/question.js +0 -316
  74. package/src/refusal.js +0 -79
  75. package/src/repository.js +0 -521
  76. package/src/session-bin/house +0 -2
  77. package/src/settlement.js +0 -57
  78. package/src/setup.js +0 -190
  79. package/src/source-originals.js +0 -116
  80. package/src/source.js +0 -286
  81. package/src/starter-set/grilling/LICENSE +0 -21
  82. package/src/starter-set/grilling/SKILL.md +0 -30
  83. package/src/starter-set/grilling/agents/openai.yaml +0 -3
  84. package/src/starter-set.js +0 -17
  85. package/src/state.js +0 -157
  86. package/src/sync-library.js +0 -293
  87. package/src/sync-state.js +0 -120
  88. package/src/sync.js +0 -482
  89. package/src/telegram.js +0 -1045
  90. package/src/terminal.js +0 -57
  91. package/src/tmux.js +0 -119
  92. package/src/worker.js +0 -925
  93. package/src/workspace.js +0 -500
package/src/sync.js DELETED
@@ -1,482 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import {
3
- EXPLICIT_SYNC_VERSION,
4
- KIT_DOOR_BATCH_PATH,
5
- KIT_DOOR_BOOTSTRAP_PATH,
6
- KIT_DOOR_DISCOVER_PATH,
7
- KIT_DOOR_ENUMERATE_PATH,
8
- } from "./contract.js";
9
- import { heldControl, houseGetJson, houseRequest, maintainControl, openSession } from "./house.js";
10
- import { operationId } from "./operation.js";
11
- import { houseFailed, KitRefusal } from "./refusal.js";
12
- import {
13
- assertWritableChanges,
14
- authorityFor,
15
- deriveChanges,
16
- OPERATIONS,
17
- relativeTo,
18
- resultingFiles,
19
- sidecarPath,
20
- unknownPathRefusal,
21
- VERSION,
22
- } from "./sync-library.js";
23
- import {
24
- canonicalReplica,
25
- clearPending,
26
- locate,
27
- listFiles,
28
- readLocalMap,
29
- readPending,
30
- readReplicaState,
31
- removeReplicaFile,
32
- writePending,
33
- writeReplicaFile,
34
- writeReplicaState,
35
- } from "./sync-state.js";
36
-
37
- function sessionDependencies(options, dependencies) {
38
- return {
39
- fetch: dependencies.fetch ?? options.fetch,
40
- now: dependencies.now ?? options.now,
41
- };
42
- }
43
-
44
- async function withSession(options, dependencies, operation) {
45
- const session = await openSession(options, sessionDependencies(options, dependencies));
46
- return maintainControl(session, (signal) => operation(session, signal), options.signal);
47
- }
48
-
49
- async function door(session, path, body, signal) {
50
- return houseRequest(
51
- session.house,
52
- await heldControl(session, signal),
53
- path,
54
- body,
55
- session.dependencies,
56
- signal,
57
- );
58
- }
59
-
60
- async function replicaDoor(session, options, state, path, body, signal) {
61
- try {
62
- return await door(session, path, body, signal);
63
- } catch (error) {
64
- if (error instanceof KitRefusal && error.code === "position_expired") {
65
- await writeReplicaState(options.directory, { ...state, expired: true }, signal);
66
- }
67
- throw error;
68
- }
69
- }
70
-
71
- function positionsOf(state) {
72
- return state.authorities.map((entry) => ({
73
- authority: entry.authority,
74
- position: entry.position,
75
- log_epoch: entry.log_epoch,
76
- }));
77
- }
78
-
79
- function changePosition(value, declaration) {
80
- if (typeof value?.position !== "string" || typeof value.log_epoch !== "string") {
81
- throw new KitRefusal("kit_sync_rejected", declaration);
82
- }
83
- return { position: value.position, log_epoch: value.log_epoch };
84
- }
85
-
86
- function discoveryOf(value) {
87
- if (typeof value?.md_model_version !== "string" || !Array.isArray(value.authorities)) {
88
- throw new KitRefusal("kit_sync_rejected", "discover");
89
- }
90
- if (value.md_model_version !== VERSION) {
91
- throw new KitRefusal("kit_md_model_version_mismatch", value.md_model_version);
92
- }
93
- return value;
94
- }
95
-
96
- async function bootstrapBundle(session, prefixes, signal) {
97
- const answer = await door(session, KIT_DOOR_BOOTSTRAP_PATH, { prefixes }, signal);
98
- if (typeof answer?.transfer?.url === "string") {
99
- const granted = await houseGetJson(answer.transfer.url, session.dependencies, signal);
100
- if (!Array.isArray(granted?.scopes)) throw new KitRefusal("kit_bootstrap_rejected");
101
- return granted;
102
- }
103
- if (!Array.isArray(answer?.scopes)) throw new KitRefusal("kit_bootstrap_rejected");
104
- return answer;
105
- }
106
-
107
- function matchedDiscovery(discovery, scope) {
108
- return discovery.authorities.find(
109
- (entry) => entry.kind === "room" && entry.room_ref === scope.manifest?.anchor,
110
- );
111
- }
112
-
113
- async function writeScope(replica, scope, signal) {
114
- if (
115
- typeof scope?.manifest?.path !== "string" ||
116
- typeof scope.manifest.position !== "string" ||
117
- typeof scope.manifest.log_epoch !== "string"
118
- ) {
119
- throw new KitRefusal("kit_bootstrap_rejected");
120
- }
121
- if (!Array.isArray(scope.contents) || !Array.isArray(scope.manifest.files)) {
122
- throw new KitRefusal("kit_bootstrap_rejected");
123
- }
124
- const files = [];
125
- for (const entry of scope.contents) {
126
- if (typeof entry?.path !== "string" || typeof entry.content !== "string") {
127
- throw new KitRefusal("kit_bootstrap_rejected");
128
- }
129
- const meta = scope.manifest.files.find((file) => file.path === entry.path);
130
- await writeReplicaFile(locate(replica, scope.manifest.path, entry.path), entry.content, signal);
131
- files.push({
132
- path: entry.path,
133
- revision: typeof meta?.revision === "string" ? meta.revision : "",
134
- sha256: typeof meta?.sha256 === "string" ? meta.sha256 : "",
135
- content: entry.content,
136
- });
137
- }
138
- return files;
139
- }
140
-
141
- async function replaceScope(replica, previous, scope, signal) {
142
- const next = await writeScope(replica, scope, signal);
143
- const kept = new Set(next.map((file) => file.path));
144
- const root = locate(replica, scope.manifest.path);
145
- for (const path of await listFiles(root)) {
146
- if (!kept.has(path)) await removeReplicaFile(locate(replica, scope.manifest.path, path));
147
- }
148
- if (previous !== undefined) {
149
- for (const file of previous.files) {
150
- if (!kept.has(file.path)) await removeReplicaFile(locate(replica, previous.path, file.path));
151
- }
152
- }
153
- return next;
154
- }
155
-
156
- function stringList(value) {
157
- return Array.isArray(value) && value.every((entry) => typeof entry === "string");
158
- }
159
-
160
- function operationsOf(value) {
161
- if (value === null || typeof value !== "object") return null;
162
- const operations = {};
163
- for (const operation of OPERATIONS) {
164
- if (!stringList(value[operation])) return null;
165
- operations[operation] = [...value[operation]];
166
- }
167
- return operations;
168
- }
169
-
170
- function authorityRecord(scope, discovery, files) {
171
- const found = matchedDiscovery(discovery, scope);
172
- const operations = found === undefined ? null : operationsOf(found.operations);
173
- if (
174
- scope.manifest?.scope !== "room" ||
175
- typeof scope.manifest.anchor !== "string" ||
176
- found === undefined ||
177
- !stringList(found.protected_paths) ||
178
- operations === null
179
- ) {
180
- throw new KitRefusal("kit_bootstrap_rejected");
181
- }
182
- return {
183
- kind: "room",
184
- authority: scope.manifest.anchor,
185
- room_handle: scope.manifest.room_handle,
186
- path: scope.manifest.path,
187
- position: scope.manifest.position,
188
- log_epoch: scope.manifest.log_epoch,
189
- protected_paths: found.protected_paths,
190
- operations,
191
- files,
192
- };
193
- }
194
-
195
- async function materialize(options, discovery, bundle, signal, previous = null) {
196
- const authorities = [];
197
- for (const scope of bundle.scopes) {
198
- const record = authorityRecord(scope, discovery, []);
199
- const held = previous?.authorities.find((entry) => entry.authority === scope.manifest?.anchor);
200
- const files = await replaceScope(options.replica, held, scope, signal);
201
- authorities.push({ ...record, files });
202
- }
203
- const state = {
204
- version: EXPLICIT_SYNC_VERSION,
205
- directory: options.canonical,
206
- prefixes: options.prefixes,
207
- md_model_version: discovery.md_model_version,
208
- bounds: discovery.bounds ?? null,
209
- authorities,
210
- };
211
- await writeReplicaState(options.directory, state, signal);
212
- return state;
213
- }
214
-
215
- async function loadState(options) {
216
- const canonical = await canonicalReplica(options.replica);
217
- const state = await readReplicaState(options.directory, canonical);
218
- if (state === null || state.version !== EXPLICIT_SYNC_VERSION) {
219
- throw new KitRefusal("kit_sync_not_bootstrapped", canonical);
220
- }
221
- return { ...state, directory: canonical };
222
- }
223
-
224
- function enumeratedRevision(change) {
225
- return typeof change.revision === "string" ? change.revision : "";
226
- }
227
-
228
- function enumeratedDigest(change) {
229
- return typeof change.sha256 === "string" ? change.sha256 : "";
230
- }
231
-
232
- function enumeratedRecord(path, content, change) {
233
- return {
234
- path,
235
- revision: enumeratedRevision(change),
236
- sha256: enumeratedDigest(change),
237
- content,
238
- };
239
- }
240
-
241
- async function applyEnumeration(options, state, enumeration, signal) {
242
- if (!Array.isArray(enumeration?.authorities)) throw new KitRefusal("kit_sync_rejected", "enumerate");
243
- for (const view of enumeration.authorities) {
244
- const authority = state.authorities.find((entry) => entry.authority === view.authority);
245
- if (authority === undefined) throw new KitRefusal("kit_sync_rejected", view.authority);
246
- const reached = changePosition(view, "enumerate");
247
- const recorded = new Map(authority.files.map((file) => [file.path, file]));
248
- for (const change of view.changes ?? []) {
249
- if (typeof change?.path !== "string") throw new KitRefusal("kit_sync_rejected", "enumerate");
250
- if (change.removed === true) {
251
- await removeReplicaFile(locate(options.replica, authority.path, change.path));
252
- recorded.delete(change.path);
253
- continue;
254
- }
255
- if (typeof change.content === "string") {
256
- await writeReplicaFile(
257
- locate(options.replica, authority.path, change.path),
258
- change.content,
259
- signal,
260
- );
261
- recorded.set(change.path, enumeratedRecord(change.path, change.content, change));
262
- }
263
- if (typeof change.provenance === "string") {
264
- const sidecar = sidecarPath(change.path);
265
- await writeReplicaFile(
266
- locate(options.replica, authority.path, sidecar),
267
- change.provenance,
268
- signal,
269
- );
270
- recorded.set(sidecar, enumeratedRecord(sidecar, change.provenance, change));
271
- }
272
- }
273
- authority.files = [...recorded.values()];
274
- Object.assign(authority, reached);
275
- }
276
- await writeReplicaState(options.directory, state, signal);
277
- return state;
278
- }
279
-
280
- async function replicaText(path) {
281
- try {
282
- return await readFile(path, "utf8");
283
- } catch (error) {
284
- if (error.code === "ENOENT") return null;
285
- throw error;
286
- }
287
- }
288
-
289
- function predictedReplica(authority, pending) {
290
- if (!Array.isArray(pending?.body?.changes)) return null;
291
- return resultingFiles(authority, pending.body.changes);
292
- }
293
-
294
- function uncertainTransport(error) {
295
- return (
296
- !(error instanceof KitRefusal) ||
297
- houseFailed(error) ||
298
- error.code === "kit_command_cancelled"
299
- );
300
- }
301
-
302
- async function acceptBatch(options, state, outcome, signal, pending = null) {
303
- const authority = state.authorities.find((entry) => entry.authority === outcome.authority);
304
- if (authority === undefined) throw new KitRefusal("kit_sync_rejected", outcome.authority);
305
- const reached = changePosition(outcome, "batch");
306
- const recorded = new Map(authority.files.map((file) => [file.path, file]));
307
- const predicted = predictedReplica(authority, pending);
308
- for (const file of outcome.contents ?? []) {
309
- const relative = relativeTo(authority.path, file.path);
310
- const destination = locate(options.replica, authority.path, relative);
311
- const local = await replicaText(destination);
312
- const held = recorded.get(relative)?.content ?? null;
313
- const accepted = file.content;
314
- const expected = predicted === null ? null : (predicted[relative] ?? null);
315
- const preserve =
316
- pending !== null &&
317
- local !== null &&
318
- local !== held &&
319
- local !== accepted &&
320
- local !== expected;
321
- if (accepted === null) {
322
- if (!preserve) await removeReplicaFile(destination);
323
- recorded.delete(relative);
324
- continue;
325
- }
326
- if (!preserve) await writeReplicaFile(destination, accepted, signal);
327
- recorded.set(relative, {
328
- path: relative,
329
- revision: typeof file.revision === "string" ? file.revision : "",
330
- sha256: "",
331
- content: accepted,
332
- });
333
- }
334
- authority.files = [...recorded.values()];
335
- Object.assign(authority, reached);
336
- await writeReplicaState(options.directory, state, signal);
337
- return { authority: outcome.authority, ...reached, contents: outcome.contents };
338
- }
339
-
340
- function sameBody(left, right) {
341
- return JSON.stringify(left) === JSON.stringify(right);
342
- }
343
-
344
- async function recoverPending(session, options, state, signal) {
345
- const pending = await readPending(options.directory, state.directory);
346
- if (pending === null) return null;
347
- try {
348
- const outcome = await replicaDoor(session, options, state, pending.path, pending.body, signal);
349
- const accepted = await acceptBatch(options, state, outcome, signal, pending);
350
- await clearPending(options.directory, state.directory);
351
- return { pending, accepted };
352
- } catch (error) {
353
- if (uncertainTransport(error)) throw error;
354
- await clearPending(options.directory, state.directory);
355
- throw error;
356
- }
357
- }
358
-
359
- function groupChanges(state, changes) {
360
- const groups = [];
361
- const index = new Map();
362
- for (const change of changes) {
363
- const authority = authorityFor(state, change.path);
364
- if (authority === undefined) throw unknownPathRefusal(change.path);
365
- const key = authority.authority;
366
- if (!index.has(key)) {
367
- index.set(key, groups.length);
368
- groups.push({ authority, changes: [] });
369
- }
370
- groups[index.get(key)].changes.push(change);
371
- }
372
- return groups;
373
- }
374
-
375
- async function sendBatch(session, options, state, changes, signal) {
376
- assertWritableChanges(state, changes);
377
- const groups = groupChanges(state, changes);
378
- let last = null;
379
- for (const group of groups) {
380
- const body = { operation_id: operationId(), changes: group.changes };
381
- await writePending(
382
- options.directory,
383
- state.directory,
384
- { operation_id: body.operation_id, path: KIT_DOOR_BATCH_PATH, body },
385
- signal,
386
- );
387
- try {
388
- const outcome = await replicaDoor(session, options, state, KIT_DOOR_BATCH_PATH, body, signal);
389
- last = await acceptBatch(options, state, outcome, signal);
390
- await clearPending(options.directory, state.directory);
391
- } catch (error) {
392
- if (uncertainTransport(error)) throw error;
393
- await clearPending(options.directory, state.directory);
394
- throw error;
395
- }
396
- }
397
- return last;
398
- }
399
-
400
- async function derivedChanges(options, state) {
401
- const changes = [];
402
- for (const authority of state.authorities) {
403
- const local = await readLocalMap(options.replica, authority.path);
404
- changes.push(...deriveChanges(state, authority, local));
405
- }
406
- return changes;
407
- }
408
-
409
- export async function bootstrap(options, dependencies = {}) {
410
- if (!Array.isArray(options.prefixes) || options.prefixes.length === 0) {
411
- throw new KitRefusal("kit_sync_prefixes_required");
412
- }
413
- const canonical = await canonicalReplica(options.replica);
414
- const existing = await readReplicaState(options.directory, canonical);
415
- if (existing !== null) throw new KitRefusal("kit_sync_already_bootstrapped", canonical);
416
- await clearPending(options.directory, canonical);
417
- return withSession(options, dependencies, async (session, signal) => {
418
- const discovery = discoveryOf(
419
- await door(session, KIT_DOOR_DISCOVER_PATH, { prefixes: options.prefixes }, signal),
420
- );
421
- const bundle = await bootstrapBundle(session, options.prefixes, signal);
422
- const state = await materialize(
423
- { ...options, canonical, prefixes: options.prefixes },
424
- discovery,
425
- bundle,
426
- signal,
427
- );
428
- return { positions: positionsOf(state) };
429
- });
430
- }
431
-
432
- export async function pull(options, dependencies = {}) {
433
- return withSession(options, dependencies, async (session, signal) => {
434
- const state = await loadState(options);
435
- const enumeration = await replicaDoor(
436
- session,
437
- options,
438
- state,
439
- KIT_DOOR_ENUMERATE_PATH,
440
- { prefixes: state.prefixes, positions: positionsOf(state) },
441
- signal,
442
- );
443
- const next = await applyEnumeration(options, state, enumeration, signal);
444
- return { positions: positionsOf(next) };
445
- });
446
- }
447
-
448
- export async function rebootstrap(options, dependencies = {}) {
449
- return withSession(options, dependencies, async (session, signal) => {
450
- const state = await loadState(options);
451
- const discovery = discoveryOf(
452
- await door(session, KIT_DOOR_DISCOVER_PATH, { prefixes: state.prefixes }, signal),
453
- );
454
- const bundle = await bootstrapBundle(session, state.prefixes, signal);
455
- const next = await materialize(
456
- { ...options, canonical: state.directory, prefixes: state.prefixes },
457
- discovery,
458
- bundle,
459
- signal,
460
- state,
461
- );
462
- await clearPending(options.directory, state.directory);
463
- return { positions: positionsOf(next) };
464
- });
465
- }
466
-
467
- export async function submit(options, dependencies = {}) {
468
- return withSession(options, dependencies, async (session, signal) => {
469
- const state = await loadState(options);
470
- if (state.expired === true) throw new KitRefusal("position_expired", state.directory);
471
- const recovered = await recoverPending(session, options, state, signal);
472
- const requested = Array.isArray(options.changes) ? options.changes : null;
473
- if (recovered !== null && requested !== null && sameBody(recovered.pending.body.changes, requested)) {
474
- return recovered.accepted;
475
- }
476
- const changes = requested ?? (await derivedChanges(options, state));
477
- if (changes.length === 0) {
478
- return recovered !== null ? recovered.accepted : { submitted: false };
479
- }
480
- return sendBatch(session, options, state, changes, signal);
481
- });
482
- }