@agentshouse/kit 0.1.0-alpha.9 → 0.2.0

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 (83) hide show
  1. package/README.md +14 -1740
  2. package/dist/api.js +11 -0
  3. package/dist/home.js +24 -0
  4. package/dist/house-main.js +4 -0
  5. package/dist/kit-main.js +16 -0
  6. package/dist/login.js +112 -0
  7. package/dist/resident.js +10 -0
  8. package/dist/stream.js +31 -0
  9. package/package.json +16 -11
  10. package/bin/connect-linux.sh +0 -134
  11. package/bin/kit.js +0 -1023
  12. package/bootstrap.json +0 -17
  13. package/src/acp.js +0 -99
  14. package/src/adapter-mcp.js +0 -44
  15. package/src/adapter.js +0 -252
  16. package/src/agent-logins.js +0 -65
  17. package/src/agents.js +0 -504
  18. package/src/apply.js +0 -29
  19. package/src/attach.js +0 -52
  20. package/src/binding.js +0 -90
  21. package/src/blocks.js +0 -156
  22. package/src/bridge.js +0 -449
  23. package/src/bytes.js +0 -89
  24. package/src/channel.js +0 -37
  25. package/src/command.js +0 -109
  26. package/src/configuration.js +0 -465
  27. package/src/connector-login.js +0 -91
  28. package/src/connector.js +0 -107
  29. package/src/continuation.js +0 -61
  30. package/src/contract.js +0 -363
  31. package/src/conversation-originals.js +0 -143
  32. package/src/custom/authorization.js +0 -63
  33. package/src/declaration.js +0 -35
  34. package/src/delivery.js +0 -191
  35. package/src/directory.js +0 -102
  36. package/src/draft.js +0 -89
  37. package/src/google/calendar.js +0 -177
  38. package/src/google/declared.js +0 -146
  39. package/src/google/drive.js +0 -435
  40. package/src/google/gateway.js +0 -166
  41. package/src/google/gmail.js +0 -249
  42. package/src/google/id.js +0 -1
  43. package/src/google/oauth.js +0 -273
  44. package/src/google/source.js +0 -25
  45. package/src/hook.js +0 -376
  46. package/src/host.js +0 -121
  47. package/src/house-main.js +0 -79
  48. package/src/house.js +0 -1090
  49. package/src/inbound.js +0 -175
  50. package/src/instance/google.js +0 -1
  51. package/src/kit-work-stream.js +0 -84
  52. package/src/launcher-main.js +0 -19
  53. package/src/launcher.js +0 -276
  54. package/src/local-files.js +0 -113
  55. package/src/login.js +0 -274
  56. package/src/mcp.js +0 -71
  57. package/src/migrator.js +0 -248
  58. package/src/mount.js +0 -582
  59. package/src/operation.js +0 -20
  60. package/src/original.js +0 -631
  61. package/src/peer.js +0 -124
  62. package/src/plan.js +0 -57
  63. package/src/question.js +0 -316
  64. package/src/refusal.js +0 -79
  65. package/src/repository.js +0 -521
  66. package/src/session-bin/house +0 -2
  67. package/src/settlement.js +0 -57
  68. package/src/setup.js +0 -190
  69. package/src/source-originals.js +0 -116
  70. package/src/source.js +0 -286
  71. package/src/starter-set/grilling/LICENSE +0 -21
  72. package/src/starter-set/grilling/SKILL.md +0 -30
  73. package/src/starter-set/grilling/agents/openai.yaml +0 -3
  74. package/src/starter-set.js +0 -17
  75. package/src/state.js +0 -157
  76. package/src/sync-library.js +0 -293
  77. package/src/sync-state.js +0 -120
  78. package/src/sync.js +0 -482
  79. package/src/telegram.js +0 -1045
  80. package/src/terminal.js +0 -57
  81. package/src/tmux.js +0 -119
  82. package/src/worker.js +0 -925
  83. 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
- }