@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/workspace.js DELETED
@@ -1,500 +0,0 @@
1
- import { access, mkdir, readFile, rm } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import { readConfiguration } from "./configuration.js";
4
- import {
5
- CGROUP_CONTROLLERS,
6
- HOST_MOUNT_INFO,
7
- LOOP_CONTROL,
8
- WORKSPACE_ACQUIRE_MAX,
9
- WORKSPACE_COMMAND_TIMEOUT_MS,
10
- WORKSPACE_DISK_IMAGE,
11
- WORKSPACE_DISK_MOUNT,
12
- WORKSPACE_KIT_HOME,
13
- WORKSPACE_MOUNT_OPTIONS,
14
- WORKSPACE_LABEL,
15
- WORKSPACE_MOUNT,
16
- WORKSPACE_OPERATION_WITHDRAWN,
17
- WORKSPACE_POLL_MS,
18
- WORKSPACE_READY_ATTEMPTS,
19
- WORKSPACE_READY_POLL_MS,
20
- WORKSPACE_STOP_TIMEOUT_SECONDS,
21
- WORKSPACE_SYSTEM_RESERVE_BYTES,
22
- } from "./contract.js";
23
- import { runCommand } from "./command.js";
24
- import {
25
- acquireWorkspaces,
26
- maintainControl,
27
- openSession,
28
- releaseControl,
29
- reportWorkspaceOutcome,
30
- } from "./house.js";
31
- import { KitRefusal } from "./refusal.js";
32
-
33
- const REQUIRED_CONTROLLERS = ["cpu", "memory"];
34
- const MEMINFO = "/proc/meminfo";
35
- const MEM_AVAILABLE = /^MemAvailable:\s+(\d+)\s+kB$/m;
36
-
37
- export function workspaceIdentity(operation) {
38
- return `house-kit-${operation.name}-${operation.environment.slice(0, 8)}`;
39
- }
40
-
41
- export function workspaceEnforcement(maxima) {
42
- return [
43
- `cgroup-v2 cpu.max at ${maxima.cpu_millicores} millicores`,
44
- `memory.max and swap at ${maxima.memory_bytes} bytes`,
45
- `dedicated ext4 image of ${maxima.disk_bytes} bytes mounted as the only writable workspace`,
46
- ].join(", ");
47
- }
48
-
49
- export function workspaceDirectory(root, identity) {
50
- return join(root, identity);
51
- }
52
-
53
- export function workspaceImagePath(root, identity) {
54
- return join(root, identity, WORKSPACE_DISK_IMAGE);
55
- }
56
-
57
- export function workspaceMountPath(root, identity) {
58
- return join(root, identity, WORKSPACE_DISK_MOUNT);
59
- }
60
-
61
- export function workspaceMountArguments(root, identity, image) {
62
- return [
63
- "run",
64
- "--rm",
65
- "--privileged",
66
- "--pid=host",
67
- "--entrypoint",
68
- "nsenter",
69
- image,
70
- "-t",
71
- "1",
72
- "-m",
73
- "--",
74
- "mount",
75
- "-o",
76
- WORKSPACE_MOUNT_OPTIONS,
77
- workspaceImagePath(root, identity),
78
- workspaceMountPath(root, identity),
79
- ];
80
- }
81
-
82
- export function workspaceUnmountArguments(root, identity, image) {
83
- return [
84
- "run",
85
- "--rm",
86
- "--privileged",
87
- "--pid=host",
88
- "--entrypoint",
89
- "nsenter",
90
- image,
91
- "-t",
92
- "1",
93
- "-m",
94
- "--",
95
- "umount",
96
- workspaceMountPath(root, identity),
97
- ];
98
- }
99
-
100
- export function mountedPaths(document) {
101
- return new Set(
102
- document
103
- .split("\n")
104
- .map((line) => line.split(" ")[4])
105
- .filter((target) => target !== undefined && target !== ""),
106
- );
107
- }
108
-
109
- export function workspaceCreateArguments(identity, operation, image, house, root) {
110
- return [
111
- "create",
112
- "--name",
113
- identity,
114
- "--label",
115
- `${WORKSPACE_LABEL}=${operation.environment}`,
116
- "--cpus",
117
- (operation.maxima.cpu_millicores / 1000).toFixed(3),
118
- "--memory",
119
- `${operation.maxima.memory_bytes}b`,
120
- "--memory-swap",
121
- `${operation.maxima.memory_bytes}b`,
122
- "--restart",
123
- "no",
124
- "--mount",
125
- `type=bind,source=${workspaceMountPath(root, identity)},target=${WORKSPACE_MOUNT}`,
126
- "--env",
127
- `HOUSE_KIT_HOME=${WORKSPACE_KIT_HOME}`,
128
- "--env",
129
- `HOUSE_KIT_HOUSE=${house}`,
130
- "--workdir",
131
- WORKSPACE_MOUNT,
132
- image,
133
- ];
134
- }
135
-
136
- export function memoryAvailable(document) {
137
- const found = MEM_AVAILABLE.exec(document);
138
- if (found === null) throw new KitRefusal("kit_host_unmeasurable", MEMINFO);
139
- return Number(found[1]) * 1024;
140
- }
141
-
142
- function adapter(dependencies) {
143
- return {
144
- run: dependencies.run ?? runCommand,
145
- read: dependencies.readFile ?? ((path) => readFile(path, "utf8")),
146
- reachable: dependencies.access ?? access,
147
- make: dependencies.mkdir ?? mkdir,
148
- remove: dependencies.rm ?? rm,
149
- wait: dependencies.wait ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
150
- };
151
- }
152
-
153
- async function docker(host, args, stdin = "") {
154
- return host.run({
155
- command: ["docker", ...args],
156
- environment: {},
157
- stdin,
158
- timeoutMs: WORKSPACE_COMMAND_TIMEOUT_MS,
159
- });
160
- }
161
-
162
- export async function workspacePrerequisites(host, image) {
163
- let controllers;
164
- try {
165
- controllers = await host.read(CGROUP_CONTROLLERS);
166
- } catch {
167
- return { supported: false, refusal: `${CGROUP_CONTROLLERS} is absent, so no cgroup v2 ceiling can be applied` };
168
- }
169
- const present = controllers.trim().split(/\s+/);
170
- const missing = REQUIRED_CONTROLLERS.filter((controller) => !present.includes(controller));
171
- if (missing.length > 0) {
172
- return {
173
- supported: false,
174
- refusal: `the cgroup v2 controllers ${missing.join(" and ")} are not delegated to this host`,
175
- };
176
- }
177
- try {
178
- await host.reachable(LOOP_CONTROL);
179
- } catch {
180
- return {
181
- supported: false,
182
- refusal: `${LOOP_CONTROL} is absent, so no workspace volume can carry an enforced size`,
183
- };
184
- }
185
- const version = await docker(host, ["version", "--format", "{{.Server.Version}}"]);
186
- if (version.status !== 0 || version.stdout.trim() === "") {
187
- return { supported: false, refusal: "the container runtime daemon did not answer a version" };
188
- }
189
- const formatter = await docker(host, [
190
- "run",
191
- "--rm",
192
- "--entrypoint",
193
- "sh",
194
- image,
195
- "-c",
196
- "command -v mkfs.ext4",
197
- ]);
198
- if (formatter.status !== 0) {
199
- return {
200
- supported: false,
201
- refusal: `the declared workspace image ${image} carries no mkfs.ext4, so no volume ceiling can be created`,
202
- };
203
- }
204
- return { supported: true, runtime: `docker ${version.stdout.trim()}` };
205
- }
206
-
207
- function bootstrapConfiguration() {
208
- return JSON.stringify({
209
- availability: "continuous",
210
- execution_authority: "allow",
211
- migrators: [],
212
- instances: [],
213
- clients: [],
214
- host: null,
215
- });
216
- }
217
-
218
- function provisionArguments(root, identity, image, target, mode) {
219
- return [
220
- "run",
221
- "--rm",
222
- "--interactive",
223
- "--mount",
224
- `type=bind,source=${workspaceMountPath(root, identity)},target=${WORKSPACE_MOUNT}`,
225
- "--entrypoint",
226
- "sh",
227
- image,
228
- "-c",
229
- `set -e; mkdir -p ${WORKSPACE_KIT_HOME}; chmod 700 ${WORKSPACE_KIT_HOME}; cat > ${target}; chmod ${mode} ${target}`,
230
- ];
231
- }
232
-
233
- async function provisionWorkspaceState(host, root, identity, image, authority) {
234
- for (const [target, mode, content] of [
235
- [`${WORKSPACE_KIT_HOME}/credential`, "600", `${authority.credential}\n`],
236
- [`${WORKSPACE_KIT_HOME}/kit.json`, "600", `${bootstrapConfiguration()}\n`],
237
- ]) {
238
- const written = await docker(
239
- host,
240
- provisionArguments(root, identity, image, target, mode),
241
- content,
242
- );
243
- if (written.status !== 0) {
244
- throw new KitRefusal("kit_workspace_unprovisioned", written.stderr.trim() || identity);
245
- }
246
- }
247
- }
248
-
249
- export async function workspaceMounted(host, root, identity) {
250
- const target = workspaceMountPath(root, identity);
251
- return mountedPaths(await host.read(HOST_MOUNT_INFO)).has(target);
252
- }
253
-
254
- async function mountWorkspace(host, settings, identity) {
255
- if (await workspaceMounted(host, settings.root, identity)) return null;
256
- const mounted = await docker(
257
- host,
258
- workspaceMountArguments(settings.root, identity, settings.image),
259
- );
260
- if (mounted.status !== 0) {
261
- return `the workspace disk could not be mounted at its ceiling: ${mounted.stderr.trim()}`;
262
- }
263
- return null;
264
- }
265
-
266
- export async function createWorkspace(host, settings, operation) {
267
- const prerequisites = await workspacePrerequisites(host, settings.image);
268
- if (!prerequisites.supported) {
269
- return { outcome: "unsupported", refusal: prerequisites.refusal };
270
- }
271
- const identity = workspaceIdentity(operation);
272
- await host.make(workspaceMountPath(settings.root, identity), { recursive: true });
273
- const formatted = await docker(host, [
274
- "run",
275
- "--rm",
276
- "--mount",
277
- `type=bind,source=${workspaceDirectory(settings.root, identity)},target=/image`,
278
- "--entrypoint",
279
- "sh",
280
- settings.image,
281
- "-c",
282
- `set -e; rm -f /image/${WORKSPACE_DISK_IMAGE}; truncate -s ${operation.maxima.disk_bytes} /image/${WORKSPACE_DISK_IMAGE}; mkfs.ext4 -q -F /image/${WORKSPACE_DISK_IMAGE}`,
283
- ]);
284
- if (formatted.status !== 0) {
285
- return {
286
- outcome: "unsupported",
287
- refusal: `the workspace disk image could not be created at its declared ceiling: ${formatted.stderr.trim()}`,
288
- };
289
- }
290
- const mountRefusal = await mountWorkspace(host, settings, identity);
291
- if (mountRefusal !== null) return { outcome: "unsupported", refusal: mountRefusal };
292
- if (operation.authority !== undefined) {
293
- await provisionWorkspaceState(host, settings.root, identity, settings.image, operation.authority);
294
- }
295
- const created = await docker(
296
- host,
297
- workspaceCreateArguments(identity, operation, settings.image, settings.house, settings.root),
298
- );
299
- if (created.status !== 0) {
300
- return { outcome: "failed", refusal: `the workspace was refused: ${created.stderr.trim()}` };
301
- }
302
- return {
303
- outcome: "stopped",
304
- workspace: identity,
305
- runtime: prerequisites.runtime,
306
- enforcement: workspaceEnforcement(operation.maxima),
307
- };
308
- }
309
-
310
- async function workspacePresent(host, identity) {
311
- const inspected = await docker(host, ["container", "inspect", "--format", "{{.State.Status}}", identity]);
312
- return inspected.status === 0 ? inspected.stdout.trim() : null;
313
- }
314
-
315
- export async function startWorkspace(host, settings, operation) {
316
- const identity = operation.workspace ?? workspaceIdentity(operation);
317
- if ((await workspacePresent(host, identity)) === null) return { outcome: "absent" };
318
- const available = memoryAvailable(await host.read(MEMINFO));
319
- if (available - operation.maxima.memory_bytes < WORKSPACE_SYSTEM_RESERVE_BYTES) {
320
- return {
321
- outcome: "failed",
322
- refusal: `starting this workspace would leave less than the ${WORKSPACE_SYSTEM_RESERVE_BYTES} byte host reserve`,
323
- };
324
- }
325
- const mountRefusal = await mountWorkspace(host, settings, identity);
326
- if (mountRefusal !== null) return { outcome: "unsupported", refusal: mountRefusal };
327
- const started = await docker(host, ["start", identity]);
328
- if (started.status !== 0) {
329
- return { outcome: "failed", refusal: `the workspace did not start: ${started.stderr.trim()}` };
330
- }
331
- const prerequisites = await workspacePrerequisites(host, settings.image);
332
- if (!prerequisites.supported) {
333
- return { outcome: "unsupported", refusal: prerequisites.refusal };
334
- }
335
- for (let attempt = 0; attempt < WORKSPACE_READY_ATTEMPTS; attempt += 1) {
336
- const ready = await docker(host, [
337
- "exec",
338
- identity,
339
- "sh",
340
- "-c",
341
- `test -d ${WORKSPACE_KIT_HOME}`,
342
- ]);
343
- if (ready.status === 0) {
344
- return {
345
- outcome: "ready",
346
- workspace: identity,
347
- runtime: prerequisites.runtime,
348
- enforcement: workspaceEnforcement(operation.maxima),
349
- };
350
- }
351
- if ((await workspacePresent(host, identity)) === null) return { outcome: "absent" };
352
- await host.wait(WORKSPACE_READY_POLL_MS);
353
- }
354
- return { outcome: "failed", refusal: "the workspace never proved it accepts execution" };
355
- }
356
-
357
- export async function stopWorkspace(host, operation) {
358
- const identity = operation.workspace ?? workspaceIdentity(operation);
359
- if ((await workspacePresent(host, identity)) === null) return { outcome: "absent" };
360
- const stopped = await docker(host, [
361
- "stop",
362
- "--timeout",
363
- String(WORKSPACE_STOP_TIMEOUT_SECONDS),
364
- identity,
365
- ]);
366
- if (stopped.status !== 0) {
367
- return { outcome: "failed", refusal: `the workspace did not stop: ${stopped.stderr.trim()}` };
368
- }
369
- return {
370
- outcome: "stopped",
371
- workspace: identity,
372
- runtime: operation.runtime ?? "docker",
373
- enforcement: workspaceEnforcement(operation.maxima),
374
- };
375
- }
376
-
377
- export async function deleteWorkspace(host, settings, operation) {
378
- const identity = operation.workspace ?? workspaceIdentity(operation);
379
- const present = await workspacePresent(host, identity);
380
- const removed = present === null ? { status: 0 } : await docker(host, ["rm", "--force", identity]);
381
- if (removed.status !== 0) {
382
- return { outcome: "failed", refusal: `the workspace was not removed: ${removed.stderr.trim()}` };
383
- }
384
- if (await workspaceMounted(host, settings.root, identity)) {
385
- const unmounted = await docker(
386
- host,
387
- workspaceUnmountArguments(settings.root, identity, settings.image),
388
- );
389
- if (unmounted.status !== 0) {
390
- return {
391
- outcome: "failed",
392
- refusal: `the workspace disk was not unmounted: ${unmounted.stderr.trim()}`,
393
- };
394
- }
395
- }
396
- await host.remove(workspaceDirectory(settings.root, identity), { recursive: true, force: true });
397
- return { outcome: "removed" };
398
- }
399
-
400
- export async function managedWorkspaces(host) {
401
- const listed = await docker(host, [
402
- "ps",
403
- "--all",
404
- "--filter",
405
- `label=${WORKSPACE_LABEL}`,
406
- "--format",
407
- `{{.Names}} {{.Label "${WORKSPACE_LABEL}"}} {{.State}}`,
408
- ]);
409
- if (listed.status !== 0) {
410
- throw new KitRefusal("kit_workspace_inventory_unavailable", listed.stderr.trim() || null);
411
- }
412
- return listed.stdout
413
- .split("\n")
414
- .map((line) => line.trim())
415
- .filter((line) => line !== "")
416
- .map((line) => {
417
- const [name, environment, state] = line.split(/\s+/);
418
- return { name, environment, state };
419
- });
420
- }
421
-
422
- export async function reconcileWorkspaces(host, authorized) {
423
- const expected = new Map(
424
- authorized.map((workspace) => [workspace.environment, workspace.expected]),
425
- );
426
- const stopped = [];
427
- for (const managed of await managedWorkspaces(host)) {
428
- if (managed.state !== "running") continue;
429
- if (expected.get(managed.environment) === "running") continue;
430
- const settled = await docker(host, [
431
- "stop",
432
- "--timeout",
433
- String(WORKSPACE_STOP_TIMEOUT_SECONDS),
434
- managed.name,
435
- ]);
436
- if (settled.status === 0) stopped.push(managed.name);
437
- }
438
- return stopped;
439
- }
440
-
441
- export async function performOperation(host, settings, operation) {
442
- if (operation.kind === "create") return createWorkspace(host, settings, operation);
443
- if (operation.kind === "start") return startWorkspace(host, settings, operation);
444
- if (operation.kind === "stop") return stopWorkspace(host, operation);
445
- return deleteWorkspace(host, settings, operation);
446
- }
447
-
448
- export async function serveWorkspaces(options, dependencies = {}) {
449
- const configuration = await readConfiguration(options.configuration);
450
- if (configuration.host === null || configuration.host.image === null) {
451
- throw new KitRefusal("kit_workspace_host_unconfigured", options.configuration);
452
- }
453
- const host = adapter(dependencies);
454
- const settings = {
455
- root: configuration.host.workspace,
456
- image: configuration.host.image,
457
- house: options.house,
458
- };
459
- const session = await openSession(options, dependencies);
460
- const settled = [];
461
- const withdrawn = [];
462
- let reconciled = [];
463
- const passes = options.passes ?? Number.POSITIVE_INFINITY;
464
- for (let pass = 0; pass < passes; pass += 1) {
465
- const work = await maintainControl(
466
- session,
467
- async (signal) => {
468
- const acquired = await acquireWorkspaces(session, WORKSPACE_ACQUIRE_MAX, signal);
469
- reconciled = await reconcileWorkspaces(host, acquired.workspaces);
470
- for (const operation of acquired.operations) {
471
- let report;
472
- try {
473
- report = await performOperation(host, settings, operation);
474
- } catch (error) {
475
- if (!(error instanceof KitRefusal)) throw error;
476
- report = { outcome: "failed", refusal: error.message };
477
- }
478
- try {
479
- await reportWorkspaceOutcome(session, operation.operation, report, signal);
480
- } catch (error) {
481
- if (!(error instanceof KitRefusal) || error.code !== WORKSPACE_OPERATION_WITHDRAWN) {
482
- throw error;
483
- }
484
- withdrawn.push({ operation: operation.operation, kind: operation.kind });
485
- continue;
486
- }
487
- settled.push({ operation: operation.operation, kind: operation.kind, ...report });
488
- }
489
- return acquired;
490
- },
491
- options.signal,
492
- { release: false },
493
- );
494
- if (pass + 1 < passes && work.operations.length === 0) {
495
- await host.wait(WORKSPACE_POLL_MS);
496
- }
497
- }
498
- await releaseControl(session);
499
- return { settled, reconciled, withdrawn };
500
- }