@agentshouse/kit 0.1.0-alpha.36 → 0.1.0-alpha.38

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 CHANGED
@@ -1,2309 +1,70 @@
1
1
  # House Kit
2
2
 
3
- House Kit is a public package repository. Product development is planned and
4
- coordinated in the [Agents House repository](https://github.com/trof3039/agents.house);
5
- this repository contains only the package source and its operator documentation.
3
+ House Kit connects a computer you control to [agents.house](https://agents.house).
4
+ It runs your Agents' sessions there with the agent CLIs House selects, keeps
5
+ local working copies of the Rooms you choose, captures the sources you
6
+ configure, and can serve your own Telegram bot.
6
7
 
7
8
  The Skill set Kit installs is Matt Pocock's
8
9
  [`mattpocock/skills`](https://github.com/mattpocock/skills); thank you, Matt.
9
10
 
10
- ## Connect a Linux computer
11
+ ## Connect a computer
11
12
 
12
- On Linux `amd64` or `arm64`, run the published, versioned bootstrap once:
13
+ Linux (`amd64`, `arm64`) or macOS (Apple silicon, Intel):
13
14
 
14
15
  ```sh
15
- bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.36/connect-linux.sh)
16
+ bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.38/connect-linux.sh)
16
17
  ```
17
18
 
18
- The bootstrap first establishes Docker Engine from what it observes on the
19
- computer. A Docker Engine that already answers is used as it is, on any
20
- distribution. On Ubuntu 22.04, 24.04 or 26.04 LTS it follows Docker's
21
- [apt repository recipe](https://docs.docker.com/engine/install/ubuntu/) for
22
- whatever is missing: it installs `docker-ce`, `docker-ce-cli`,
23
- `containerd.io`, `docker-buildx-plugin` and `docker-compose-plugin` from
24
- `download.docker.com`, starts the `docker` service when it is stopped and adds
25
- the account to the `docker` group. Every such step runs through `sudo`, which
26
- asks for the password as it ordinarily does. A new group membership applies
27
- from the account's next sign-in, so the invocation that grants it reaches
28
- Docker Engine through `sudo` for the rest of its own run; a later invocation
29
- from a session older than the membership is refused until the account signs
30
- out and back in. The bootstrap refuses an Ubuntu computer whose installed
31
- `docker.io`, `docker-compose`, `docker-compose-v2`, `docker-doc`,
32
- `docker-buildx`, `podman-docker`, `containerd` or `runc` package conflicts with
33
- that recipe rather than removing it. Any other distribution needs a working
34
- Docker Engine supplied by its owner, and without one the bootstrap stops with
35
- that missing prerequisite. A step that fails names its cause and changes
36
- nothing further; running the same bootstrap again continues from the state it
37
- left, including after a restart.
38
-
39
- The release asset pins one immutable multi-architecture image digest. It creates
40
- owner-protected `~/.house-kit` and `~/AgentsHouse`, mounts those as the complete
41
- Kit home and `/agents/house`, enrolls through House Login in the host browser,
42
- and starts the container with Docker's `unless-stopped` lifecycle. Pass
43
- `--workspace /absolute/path` for one other workspace root or `--manual` for
44
- the existing URL and one-time-code login route. `--house <origin>` selects
45
- another House and is retained in Kit home. A local loopback House,
46
- `http://127.0.0.1:<port>` or `http://localhost:<port>`, runs the resident
47
- container on the host network, as the one-shot login, credential check and
48
- apply containers always run, so the resident reaches the House the host serves;
49
- an HTTPS House leaves it on Docker's own network. A repeated invocation reuses the
50
- same container, credential and Environment. Every invocation that is not a
51
- forwarded command also sets `skills` in Kit's configuration: `--no-skills`
52
- declines [the Skill set](#the-skill-set) and an invocation without it accepts
53
- the set again.
54
-
55
- Running a newer release's bootstrap the same way is the only update. When the
56
- installed container runs another image, the bootstrap pulls this release's
57
- image and asks the running resident Kit to drain with `kit resident drain`. The
58
- resident stops acquiring Runs, resumptions, setup, agent CLI work, Google
59
- sign-ins and scheduled Google captures, while the Sessions it supervises keep
60
- their renewal, owner stops, answers and reports and a capture already running
61
- finishes.
62
- While any Agent Session is still running or unreported, including one whose Run
63
- was being acquired when the drain began, the bootstrap names it and leaves the
64
- container as it is; rerun it once they end. Once nothing remains,
65
- the resident stops renewing, releases its exact lease and stays quiescent, and
66
- the bootstrap replaces the container with one on this release's image over the
67
- same Kit home and workspace. The credential, Environment, Kit-home state and
68
- everything under the workspace are kept; anything installed elsewhere in the old
69
- container is not. A stopped container is replaced without a drain.
70
-
71
- If House refuses the installed Kit with `kit_upgrade_required`, including in
72
- its answer to a command the resident serves, the resident calls House no more,
73
- neither to release nor to report. It keeps supervising
74
- every running Session locally and never stops one to make progress. A Session
75
- that ends keeps its exact binding, stop observation and outcome in Kit home,
76
- and the drain answers ready once no supervised process runs. The updated Kit
77
- waits for the old lease to expire, acquires the next one and reports each held
78
- Session before it acquires any work, restoring no process and inventing no
79
- outcome.
80
-
81
- The versioned `bootstrap.json` is the machine-readable command source for this
82
- release. It is exported from the npm package as `@agentshouse/kit/bootstrap`
83
- (JSON import) and published byte-for-byte beside `connect-linux.sh` and `connect-windows.ps1`
84
- in the GitHub release. Its schema version is `1`, its `version` is the Kit package
85
- version, and `hosts` contains only bootstrap scripts published in that release.
86
- The package exports only the `./bootstrap` subpath; `./package.json` and source
87
- subpaths are not exported. For this release, `hosts.linux` and `hosts.windows`
88
- exist. In this schema example, `<version>` stands for the package's version:
89
-
90
- ```json
91
- {
92
- "schemaVersion": 1,
93
- "version": "<version>",
94
- "hosts": {
95
- "linux": {
96
- "command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v<version>/connect-linux.sh)",
97
- "asset": {
98
- "name": "connect-linux.sh",
99
- "url": "https://github.com/agentshouse/kit/releases/download/v<version>/connect-linux.sh"
100
- },
101
- "workspace": { "argument": "--workspace", "quoting": "posix" }
102
- },
103
- "windows": {
104
- "command": "& ([scriptblock]::Create((irm https://github.com/agentshouse/kit/releases/download/v<version>/connect-windows.ps1)))",
105
- "asset": {
106
- "name": "connect-windows.ps1",
107
- "url": "https://github.com/agentshouse/kit/releases/download/v<version>/connect-windows.ps1"
108
- },
109
- "workspace": { "argument": "--workspace", "quoting": "powershell" }
110
- }
111
- }
112
- }
113
- ```
114
-
115
- `command` is complete for the default workspace. To show a selected absolute
116
- workspace, append the declared `workspace.argument` and the path as one POSIX
117
- shell-quoted argument. For example, `/home/user/My Agents' House` becomes
118
- `--workspace '/home/user/My Agents'\'' House'`. A `powershell` workspace is one
119
- single-quoted PowerShell string with each `'` doubled: `C:\Users\Alice\Alice's Room`
120
- becomes `--workspace 'C:\Users\Alice\Alice''s Room'`. Quote the entire path before
121
- adding it to a copyable shell command; never interpolate it as shell syntax.
122
- `connect-linux.sh` validates an absolute workspace root and encodes it as one Docker
123
- `--mount` CSV source field, including commas and double quotes in the name.
124
- `connect-windows.ps1` refuses a workspace root containing a comma.
125
- The package manifest contains no image digest: the release workflow
126
- fills the immutable image digest into the published script after the package
127
- has been built.
128
-
129
- The bootstrap does not install native Kit on an ordinary computer. The native
130
- package remains for managed Nodes and an explicitly selected eligible Kit host.
131
-
132
- The same bootstrap is the host's entry point for the existing `kit` commands:
133
- append `kit` and the command to the connect command above, as in
134
- `bash <(curl -fsSL …/connect-linux.sh) kit apply` or
135
- `bash <(curl -fsSL …/connect-linux.sh) kit telegram connect`. It forwards the
136
- command and its arguments unchanged into the installed container through the
137
- host's Docker client and ends with the command's own output and exit status.
138
- Standard input stays attached and a terminal stays a terminal, so a hidden
139
- prompt such as the Telegram bot token is read inside the container and never
140
- becomes an argument or printed output. With the output redirected, the
141
- command's output and errors stay separate and the terminal stops echoing typed
142
- input until the command ends. A running container runs the command beside its
143
- resident Kit; a stopped one runs it in a one-off container over the same Kit
144
- home and workspace and stays stopped. `kit login` takes the bootstrap's own
145
- login route, a one-off container on the host network so the host browser can
146
- return to its loopback listener, or `--manual` for the URL and one-time code.
147
- Forwarding installs no host-native Kit and gives the container no Docker socket
148
- or further mount; it refuses when no Kit is installed or the installed one
149
- differs from this bootstrap's.
150
-
151
- The owner's working copy push is forwarded the same way: run
152
- `bash <(curl -fsSL …/connect-linux.sh) house git push [commit] --owner` from a
153
- directory beneath the workspace root, such as a Room copy under
154
- `working-copies/rooms/`. The bootstrap runs the container's `house` in the
155
- matching directory beneath `/agents/house`, through the resident's lease when
156
- the container runs, and refuses a directory outside the workspace before
157
- anything reaches House.
158
-
159
- ## Connect a macOS computer
160
-
161
- On a Mac with Apple silicon or an Intel chip, run the same
162
- `bash <(curl -fsSL …/connect-linux.sh)` command as
163
- [a Linux computer](#connect-a-linux-computer). It runs in macOS's own
164
- `bash` and reads the chip from the hardware, so it selects `arm64` or `amd64`
165
- correctly even from a terminal running under Rosetta.
166
-
167
- The bootstrap first establishes Docker Desktop from what it observes. A Docker
168
- Desktop that already answers is used as it is. Another Docker engine that
169
- answers, such as Colima's or OrbStack's, is refused before Kit home exists,
170
- because `docker info` does not report it as Docker Desktop. Without
171
- `/Applications/Docker.app` it follows Docker's
172
- [command-line installation](https://docs.docker.com/desktop/setup/install/mac-install/):
173
- it downloads `Docker.dmg` for the chip from `desktop.docker.com`, attaches it
174
- through `sudo hdiutil`, runs its `install --user=<account>` through `sudo`, and
175
- detaches it. `sudo` asks for the password as it ordinarily does, and the
176
- installer does not accept Docker's Subscription Service Agreement for you. The
177
- bootstrap then opens Docker Desktop, including one a restart left stopped, and
178
- waits up to five minutes for its Linux containers to answer while you accept
179
- its terms or finish what its window asks. If Docker Desktop is still not ready,
180
- or the download or the installer fails, the bootstrap names the cause and changes
181
- nothing further; running it again continues from that state. Docker Desktop
182
- built for the other chip is refused. The bootstrap reaches `docker` in
183
- Docker Desktop's app bundle when no terminal has added it to `PATH` yet.
184
-
185
- Everything after that is the Linux journey above, with `open` as the host
186
- browser opener. With an HTTPS House, login's container publishes its one
187
- callback port on the Mac's `127.0.0.1`, so House Login returns from the Mac's
188
- browser, and no Kit container uses the host network. A loopback House needs
189
- Docker Desktop's host networking enabled. A `--workspace` root must be a
190
- directory Docker Desktop shares, such as one under `/Users`.
191
-
192
- ## Connect a Windows computer
193
-
194
- On Windows 11 `amd64`, run the published, versioned bootstrap once in
195
- PowerShell:
19
+ Windows 11 (`amd64`), in PowerShell:
196
20
 
197
21
  ```powershell
198
- & ([scriptblock]::Create((irm https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.36/connect-windows.ps1)))
199
- ```
200
-
201
- It runs in the Windows PowerShell 5.1 that Windows ships and needs no change to
202
- its execution policy. It reads the edition, build and processor from Windows
203
- itself and refuses Windows Server, Windows 10, a Windows 11 build older than
204
- 23H2 (build 22631), which [Docker Desktop requires](https://docs.docker.com/desktop/setup/install/windows-install/),
205
- and Windows on ARM, each with its cause. There is no native Windows Kit and no
206
- Windows container.
207
-
208
- The bootstrap then establishes Docker Desktop and its WSL 2 engine from what it
209
- observes. A Docker Desktop that already answers with Linux containers on its
210
- WSL 2 engine is used as it is. Otherwise, when `wsl --version` does not answer,
211
- it runs Microsoft's [`wsl --install --no-distribution`](https://learn.microsoft.com/windows/wsl/basic-commands#install),
212
- which asks you to approve administrator rights in the ordinary Windows prompt,
213
- and stops until you restart Windows; a WSL older than the 2.1.5 Docker Desktop
214
- requires is brought up to date with `wsl --update` the same way. Without
215
- Docker Desktop in `%ProgramFiles%\Docker\Docker` or
216
- `%LOCALAPPDATA%\Programs\DockerDesktop`, it downloads Docker Desktop's
217
- installer from `desktop.docker.com` and runs its
218
- [per-user installation](https://docs.docker.com/desktop/setup/install/windows-install/#install-from-the-command-line),
219
- `install --user --quiet`, which needs no administrator rights and does not
220
- accept Docker's Subscription Service Agreement for you. It starts Docker
221
- Desktop, including one a restart left stopped, and waits up to five minutes for
222
- it to answer while you accept its terms or finish what its window asks.
223
- Declined administrator rights, a pending restart, a failed download or
224
- installer, or a Docker Desktop that is still not ready each stop the bootstrap
225
- with their cause and change nothing further; running the same command again
226
- continues from that state. Docker Desktop's Windows containers, another Docker
227
- engine or Docker Desktop's Hyper-V engine are refused.
228
-
229
- Everything after that is [the Linux journey](#connect-a-linux-computer):
230
- the same immutable `linux/amd64` image, owner-only `%USERPROFILE%\.house-kit`
231
- and `%USERPROFILE%\AgentsHouse` or one `--workspace` root such as
232
- `--workspace 'D:\Agents'`, mounted as the Kit home and `/agents/house`,
233
- identity reuse, `unless-stopped`, and House Login in the host browser. As on a
234
- Mac, login's container publishes one callback port on `127.0.0.1` for an HTTPS
235
- House, and a loopback House needs Docker Desktop's host networking. Kit home and
236
- the workspace are restricted to your Windows account alone. A path with a comma
237
- is refused, because this bootstrap cannot pass one to a Docker mount from
238
- Windows PowerShell. The containers run as the image's own user, since Windows
239
- has no Linux user id to map. Run it from the 64-bit Windows PowerShell; from an
240
- administrator PowerShell it still makes your own account the owner of both
241
- directories. Append `kit` and a command, or run `house git push … --owner`
242
- from beneath the workspace, to forward it as on Linux, with each argument
243
- passed unchanged; with the output redirected, a hidden prompt still echoes
244
- what you type, so leave the output on the terminal when a command asks for a
245
- token. Each run and forwarded command leaves its exit status in
246
- `$LASTEXITCODE` and never closes the PowerShell window.
247
-
248
- This recipe is authored from Microsoft's and Docker's documentation and checked
249
- off Windows: the Windows PowerShell 5.1 syntax check and the release packaging
250
- tests in this repository. No Windows computer has run it yet;
251
- the one real Windows 11 installation is owed at the final MVP candidate.
252
-
253
- ## Building the package
254
-
255
- The repository checkout is the development source. Build the package that can
256
- be published:
257
-
258
- ```sh
259
- npm run build
260
- ```
261
-
262
- The command writes the publishable package to `dist/package`. It reads no
263
- environment and ships no provider OAuth client: each Google account signs in
264
- with the User's own client, as [the Google preset](#the-google-preset)
265
- describes.
266
-
267
- Runtime dependencies stay minimal and exactly pinned in `package.json` and
268
- `package-lock.json`: House's own `@agentshouse/mdmodel`, `yaml`, which reads
269
- House's text answers, and the Agent Client Protocol SDK
270
- `@agentclientprotocol/sdk` with its peer `zod`, which is this Kit's one protocol
271
- client for the compiled adapter contracts, and `ws` for the authenticated Kit
272
- control stream. The SDK's approval
273
- receipt is [`docs/agents/acp-sdk-dependency-approval.json`](docs/agents/acp-sdk-dependency-approval.json),
274
- which the User wrote once this adoption's proof had passed. The adapter
275
- packages themselves are Environment prerequisites, not Kit dependencies, and
276
- carry no record.
277
-
278
- The bootstrap invokes bare `kit login`, which connects to `https://agents.house`;
279
- `--house <origin>` names another House. Login records the House in Kit home
280
- beside the credential. Every House-bound command talks to that recorded House,
281
- and every command but login refuses `--house` as an unknown argument. The first login names its new
282
- Environment after the host computer and stores its Environment id in Kit home;
283
- the panel renames it. An explicit later login reconnects that retained id, and
284
- `--environment <uuid>` reconnects the Environment it names instead, whether or
285
- not Kit home records one, and Kit home then records that id. Reconnecting issues the next credential for the same Environment
286
- and ends the previous one, including one `envs/revoke` ended; House refuses an
287
- Environment it no longer holds with its cause and never creates a replacement.
288
- A malformed id or an answer naming a different Environment is refused before
289
- installation.
290
-
291
- Use `--manual` when the browser cannot reach the Kit's loopback listener. The
292
- command stores the issued Kit User credential in `$HOUSE_KIT_HOME/credential`
293
- or `~/.house-kit/credential` with owner-only permissions, replacing the previous
294
- file in one rename and leaving configuration, checkpoints, source state and
295
- Agent relationships untouched. Reconnecting installs authority only: it starts
296
- no Run and resumes no work, and the next `kit worker run` reconciles this
297
- installation's bound sessions under its ordinary control rules.
298
-
299
- If House never answers the exchange, the command refuses with
300
- `kit_credential_not_installed` and no credential is written; the remedy is a
301
- fresh owner-authorized `kit login` for the same Environment. The credential is
302
- never printed by Kit.
303
-
304
- ## Configuration
305
-
306
- `$HOUSE_KIT_HOME/kit.json` is the one declarative configuration file. Use
307
- `--config <path>` to name it elsewhere.
308
-
309
- ```json
310
- {
311
- "availability": "continuous",
312
- "execution_authority": "allow",
313
- "migrators": [],
314
- "instances": [
315
- {
316
- "id": "acme",
317
- "preset": "custom",
318
- "services": [
319
- {
320
- "service": "notes",
321
- "source_ref": "src_acme_notes",
322
- "rooms": ["work-room"],
323
- "backfill": { "days": 30 },
324
- "connector": {
325
- "command": ["/usr/local/bin/acme-export", "--ndjson"],
326
- "timeout_ms": 120000,
327
- "environment": { "ACME_REGION": "eu" },
328
- "secrets": {
329
- "ACME_TOKEN": { "environment": "ACME_TOKEN_SOURCE" },
330
- "ACME_KEY": { "file": "/run/secrets/acme-key" }
331
- }
332
- }
333
- }
334
- ]
335
- }
336
- ],
337
- "clients": [
338
- {
339
- "kind": "claude-code",
340
- "settings": "/home/ada/.claude/settings.json"
341
- }
342
- ],
343
- "host": null,
344
- "skills": true
345
- }
346
- ```
347
-
348
- This file is the whole desired state, so every section it declares is
349
- reconciled and every section it omits is refused rather than assumed. The
350
- agent CLI list is not local configuration: House owns it for this Environment,
351
- and every controlled pass reads, installs, updates, signs in and reports exactly
352
- the kinds House names. House also owns every Agent, its Profile and its route,
353
- so this file declares neither Agents nor agent CLIs. The Kit runs whatever Agent
354
- House assigns on one of House's selected kinds and refuses any other kind. The required
355
- top-level `execution_authority` is this Environment's one answer to every agent
356
- permission request, `"allow"` or `"refuse"`; the Agents sharing the Environment
357
- share it. The
358
- required top-level `availability` declares this Environment's availability
359
- mode: `"intermittent"` for a laptop or other normally offline placement,
360
- `"continuous"` for a server expected to stay online. Omitting it or declaring
361
- any other value refuses as `kit_configuration_invalid`; `kit apply` sends the
362
- declared mode with the rest of the desired state. An
363
- instance declares one or more services, and under the `custom` preset each
364
- service owns the command that produces it. The Kit reads only `custom` instances
365
- and passes over any other entry of `instances`.
366
-
367
- `host` declares the Linux host this installation measures for
368
- [host capacity reporting](#host-capacity). `null` declares that this Kit reports
369
- no capacity; the section is required like the others, so a host is measured only
370
- where the Environment owner says so.
371
-
372
- `skills` is this Environment's choice of [the Skill set](#the-skill-set):
373
- `true` accepts it and `false` declines it. A configuration that omits it
374
- accepts the set. The bootstrap writes it on every invocation that is not a
375
- forwarded command.
376
-
377
- `clients` is the local Agent client catalogue and never leaves the installation.
378
- Each entry names one client whose lifecycle hook configuration this Kit
379
- reconciles: `kind` is `claude-code`, the one client this Kit installs a hook
380
- into, and `settings` is the absolute path of that client's own settings
381
- document. The section is required like the others and declares `[]` when this
382
- Environment runs no such client. An unknown `kind`, a relative path, and two
383
- entries naming the same document each refuse as `kit_configuration_invalid`.
384
-
385
- A secret is never written into this file. `secrets` names either an
386
- environment variable the Kit reads at run time or a mounted file it reads from
387
- disk; the resolved value reaches the connector's environment and nothing else.
388
-
389
- `backfill` is chosen once and honored on the first capture: `{ "days": 30 }`
390
- never delivers anything observed earlier, `{ "count": 500 }` bounds the first
391
- capture to 500 items and then continues from its cursor unbounded, and
392
- `{ "all": true }` takes every history the connector offers. The boundary is
393
- fixed when the service first captures, not each time a capture is attempted, so
394
- a run that fails and is repeated days later still starts where it would have.
395
-
396
- `timeout_ms` may shorten the connector's 120000 ms bound but never raise it. The
397
- Kit holds this installation's Environment control lease across the connector run
398
- and the batch it produces, and the lease has to outlive both.
399
-
400
- Apply the declared source state to House:
401
-
402
- ```sh
403
- kit apply
22
+ & ([scriptblock]::Create((irm https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.38/connect-windows.ps1)))
404
23
  ```
405
24
 
406
- Only the state House declares - instances, services, `source_ref`, and Room
407
- routes - leaves the installation. The connector, its secret references, and the
408
- backfill boundary stay local.
409
-
410
- ## The SessionStart hook
411
-
412
- House answers `kit apply` with its current SessionStart hook: one
413
- `{id, version, event, instruction}` document whose `instruction` tells the Agent
414
- which Pulse and continuation reads to perform itself. The document is House's
415
- own text under a stable `id`, versioned with the Kit contract version, and it
416
- carries no Room content, open item, count, checkpoint, or identity.
417
-
418
- `kit apply` writes that instruction to
419
- `$HOUSE_KIT_HOME/state/hooks/<hook id>.txt` and installs one `SessionStart`
420
- command hook printing it in every declared client's settings document. The
421
- installed hook is that read and nothing else: it calls no House operation, marks
422
- nothing seen, embeds no Room material, and infers no chat Session, so the Agent
423
- decides what to read from the guidance it receives.
424
-
425
- `state/hooks.json` records exactly which settings documents this installation
426
- has written and the exact command it installed in each, so ownership is that
427
- record and the command it names rather than any resemblance to one. An entry
428
- whose command merely mentions the instruction is another owner's and is left
429
- alone. `clients` is the desired state of that record: applying the same served
430
- document again leaves each settings document byte-identical, dropping a client
431
- from the catalogue removes this installation's entry from the document it named,
432
- and an empty catalogue removes every entry, the record, and `state/hooks/`
433
- without touching a hook, group, or setting the installation does not own.
25
+ The command sets up Docker if it is missing, creates `~/.house-kit` (Kit home)
26
+ and `~/AgentsHouse` (your workspace), signs this computer in to House in your
27
+ browser, and starts Kit in a container that comes back after a restart. Running
28
+ it again is safe: it continues where it stopped. Running a newer release's
29
+ command is the update.
434
30
 
435
- One apply reaches every client or restores the ones it already wrote. Every
436
- declared and previously recorded document is canonicalized, checked, and parsed
437
- before House is contacted at all - including the `hooks` object and the
438
- `SessionStart` array this Kit edits, so a document shaped differently refuses as
439
- `kit_client_settings_unreadable` without changing anything remotely. The counters
440
- House answers with are validated before the first local change. Each write then
441
- re-resolves the whole declared path and re-checks the document it prepared,
442
- refusing as `kit_client_settings_refused` when any part of the path, or the
443
- document itself, was replaced meanwhile - a parent directory turned into a link
444
- after preparation receives no write at all. A failed write or a cancellation
445
- part-way restores every document already written to its previous bytes, removes
446
- one this installation created, and names as `kit_client_settings_unrestored` any
447
- it could not restore.
448
- The previous instruction stays readable until the last document is written and
449
- only then is it pruned, so a client left holding an older command still reads an
450
- instruction that is there.
31
+ Options:
451
32
 
452
- A path is canonicalized through `..` and symlinks before it is used: the Kit
453
- writes through a symlinked settings document instead of replacing the link, and
454
- refuses as `kit_client_settings_refused` a path that is not a regular document,
455
- that two entries share, that resolves into this installation's own directory,
456
- or - for a document already in the record - that no longer resolves to itself,
457
- rather than writing through a link that replaced it. An existing document keeps
458
- its file mode, one the client does not have yet is created owner-only, and one
459
- the Kit cannot parse refuses as `kit_client_settings_unreadable` rather than
460
- being replaced. Preparation reads before the Environment control lease is taken;
461
- every write happens under that renewed lease with the apply it belongs to.
33
+ - `--workspace /absolute/path` uses another workspace directory.
34
+ - `--manual` signs in with a URL and a one-time code instead of the browser.
35
+ - `--no-skills` declines the Skill set; running without it accepts it again.
36
+ - `--house <origin>` connects to another House.
462
37
 
463
- Continuity does not depend on the hook. An Agent working through any client
464
- receives the same guidance in the server instructions and the tool descriptions,
465
- and an installation whose catalogue is empty neither installs a hook nor needs
466
- one served. A declared client is the opposite: an absent or malformed document
467
- refuses as `kit_hook_rejected` instead of leaving whatever was installed before.
38
+ ## Run a Kit command
468
39
 
469
- ## The Google preset
470
-
471
- House holds this Environment's Google Sources, and its owner sets them in the
472
- App. Each entry names an account name the owner chooses, one service -
473
- `gmail`, `calendar` or `drive` - the Rooms it goes to, and what its first
474
- capture takes: `{ "new": true }` for new activity only, `{ "days": n }`,
475
- `{ "items": n }` or `{ "all": true }`. Neither the configuration file nor any
476
- command on this computer declares one.
477
-
478
- Each service captures a fixed corpus under its own read-only scope: Gmail every
479
- message but Spam and Trash, Calendar the account's primary calendar, and Drive
480
- the files the account owns.
481
-
482
- The resident reads the list from `/kit/sources/desired` each time its Kit
483
- control stream connects and each time House signals `sources` on it, then
484
- reconciles:
485
-
486
- - a new entry gets its local state and is captured once its account is signed
487
- in for its service;
488
- - a changed entry's Rooms take effect on its next capture;
489
- - an entry House no longer lists stops, and its checkpoint, health, gaps,
490
- dropped items and unsaved originals are deleted;
491
- - an account left with no entry has each of its grants revoked at Google's
492
- revocation endpoint and its credentials, its OAuth client included, deleted.
493
- Google's revocation ends every grant the Google user gave the client's
494
- project, so when another listed account is signed in to the same Google user,
495
- the Kit deletes the local credentials and revokes nothing.
496
-
497
- It then reports every entry to `/kit/sources/report`, and reports an entry
498
- again after each sign-in and each capture: `signed_out` while its
499
- account holds no grant for its service, `failed` with its cause code when its
500
- last capture failed, and `ready` otherwise, each with the account's signed-in
501
- email once the Kit knows it.
502
-
503
- The Kit ships no Google OAuth client. Each account signs in with the User's
504
- own: an OAuth client of type Desktop app in the User's Google Cloud project,
505
- with the service's API enabled there. An entry whose account holds no grant for
506
- its service opens a login renewal for integration `google`, instance the
507
- account name and service the entry's service, and presents its ceremony on
508
- House's owner-authenticated page, so the owner's browser never has to reach
509
- this Environment. When the account has no client yet, the page first collects
510
- the client id and secret. It then links to Google's consent for that service's
511
- scope only; Google sends the browser to a `http://127.0.0.1:<port>/` address
512
- that nothing answers, and the owner pastes that whole address into the page.
513
- The Kit accepts it only when it carries the state its own consent issued,
514
- exchanges the code with its PKCE verifier and the account's client, and stores
515
- the grant beside the client in `state/google/credentials/<account>.json`. The
516
- collected values pass through House's live relay only; neither they nor any
517
- token enter a report, a log or anything House stores. Adding a
518
- service to a signed-in account asks only for that service's consent, and each
519
- service keeps its own grant. An address that answers another consent, or a code
520
- Google refuses to exchange, stores nothing; a declined consent or a ceremony
521
- that outlives its ten-minute bound cancels the sign-in, and the Kit tries again
522
- at the next reconciliation. A capture that finds the grant revoked removes it,
523
- reports the entry `signed_out` and opens a new sign-in at once.
524
-
525
- The resident captures each signed-in entry every 15 minutes, with no command.
526
- An occurrence never overlaps its entry's previous one, and one missed while the
527
- resident was stopped is not replayed: the next resumes from the checkpoint. The
528
- first capture fixes the initial choice's boundary, so `new` captures nothing
529
- that existed then and everything that arrives after.
40
+ Append `kit` and the command to the connect command above:
530
41
 
531
42
  ```sh
532
- kit source capture work # one occurrence of every entry of the account now
533
- kit source capture work gmail # one occurrence of one entry now
534
- kit source status work
43
+ bash <(curl -fsSL …/connect-linux.sh) kit apply
535
44
  ```
536
45
 
537
- `kit source capture` and `kit source status` take the account name where a
538
- custom connector takes its instance; a name House lists as an account is read
539
- as the account. While the resident runs, it performs the requested occurrence
540
- itself, after any occurrence of that entry already running.
541
-
542
- A failed occurrence records its cause. A Google 403 for an API disabled in the
543
- client's project fails as `api_disabled`, and `kit source status` names the API
544
- to enable; a revoked scope (`google_scope_revoked`), an expired or revoked grant
545
- (`google_authority_revoked`) and a provider failure
546
- (`google_provider_unavailable`) keep their own codes.
46
+ The command runs inside the installed container and ends with its own output
47
+ and exit status.
547
48
 
548
- ## Environment setup
549
-
550
- House requests one setup when it creates a managed Environment and again when
551
- its owner asks it to renew what the Environment holds. The Kit acts on the
552
- request it acquires, never on its own:
49
+ ## Sign in again
553
50
 
554
51
  ```sh
555
- kit setup
556
- ```
557
-
558
- `kit worker run` performs the same step at the start of a pass, before it
559
- hands out any work, so a managed Environment needs no terminal; the resident
560
- Kit performs it each time its control stream connects and on each `runs`
561
- signal, which House also sends for every setup request. The walk first
562
- prepares the absolute `/agents/house` base, then visits every declared agent
563
- CLI and then every connector instance, in configuration order:
564
-
565
- - an agent CLI is installed or kept at its served release as
566
- [the declared agent CLIs](#the-declared-agent-clis) describe; a kind no
567
- release could be installed for needs attention as `install_failed`;
568
- - a module whose own status check already answers signed in reports
569
- `authenticated` to `/kit/logins/report`, which closes any renewal episode an
570
- earlier attempt left open, and is `skipped`;
571
- - otherwise the Kit runs the module's guided login. It reports the login to
572
- House as `renewal_required` for `/kit/logins/report`, which opens or returns
573
- one renewal episode, and presents the provider's steps on House's
574
- owner-authenticated page by holding `/kit/secret-input/<episode>` until the
575
- owner submits. Collected values go only to the waiting sign-in process; the
576
- Kit sends none of them, and none of the sign-in's output, to any other act. A
577
- sign-in whose status check then answers signed in reports `authenticated`
578
- and is `ready`;
579
- - a module without a guided route in this Kit, or one House does not advertise
580
- (`kit_login_renewal_unsupported`), needs attention as `login_unavailable`
581
- and opens no ceremony; a sign-in that exits without authenticating needs
582
- attention as `login_failed`, and one that outlives its ten-minute bound as
583
- `login_cancelled`. Either leaves its renewal episode open.
584
-
585
- One module's failure never stops the walk. The Kit reports what it now holds
586
- for every declared agent CLI and then the whole summary, one entry per module
587
- with its `category`, `name`, `status` and `reason`, to `/kit/setup/report`
588
- under the request it served, and releases its control lease. A cancelled
589
- command reports no partial summary and releases its lease once the walk has
590
- stopped.
591
-
592
- Each compiled adapter contract carries the guided route its provider's own
593
- headless sign-in offers, read from that command's output:
594
-
595
- - `codex-acp` runs Codex's device sign-in. The page links to the provider's
596
- device page and shows the one-time code the command printed; the owner signs
597
- in with ChatGPT there and enters the code, and the command completes on its
598
- own. Nothing is written back to it.
599
- - `claude-agent-acp` runs Claude Code's sign-in. The page links to the authorize
600
- page the command printed and collects the authentication code Claude shows
601
- afterwards, which the Kit writes to that command alone. Claude Code answers a
602
- code it refuses by asking for another, so the Kit ends that sign-in as soon as
603
- the command prints `Invalid code.` and it needs attention as `login_failed`
604
- rather than waiting out its ten-minute bound.
605
- - `grok-build` runs Grok Build's device sign-in. The page links to the Grok
606
- device URL and shows the anti-phishing one-time code printed by the command;
607
- authorization completes in the browser and no secret is written back to the
608
- process.
609
-
610
- House offers only a route its provider evidence qualifies; one it does not
611
- advertise stays `login_unavailable`.
612
-
613
- ## The custom connector
614
-
615
- Each custom service names one User-owned command. The Kit invokes it through an
616
- argument vector, never a shell, with an environment holding only `PATH`, the
617
- declared `environment` entries, and the resolved secrets. No House credential
618
- and no Kit User credential is ever placed there. The request carries no service
619
- name because the command is the service.
620
-
621
- Stdin carries one JSON request line:
622
-
623
- ```json
624
- { "contract": "2026-08-22", "instance": "acme", "cursor": null, "limit": 100 }
625
- ```
626
-
627
- `cursor` is the opaque cursor the connector last returned, or `null` on the
628
- first capture and after a reset. Stdout carries NDJSON: zero or more `item`
629
- lines and at most one terminal `cursor` line.
630
-
631
- ```
632
- {"type":"item","namespace":"acme/notes","native_object_id":"note-91","native_version":"7","observed_at":"2026-08-20T09:15:00.000Z","body":"the note","author":"ada","url":"https://acme.example/notes/91","attachments":[{"url":"https://acme.example/files/1","name":"plan.pdf"}]}
633
- {"type":"cursor","cursor":"page-2"}
634
- ```
635
-
636
- `namespace`, `native_object_id`, `observed_at`, and `body` are required;
637
- `native_version`, `author`, `url`, and `attachments` are optional. There is no
638
- metadata bag, no executable field, and no embedded credential. A malformed line
639
- anywhere rejects the whole run: nothing is delivered and no checkpoint moves. A
640
- non-zero exit status, a timeout, or output beyond the bound rejects the run the
641
- same way.
642
-
643
- An attachment names exactly one target. `url` is a verbatim reference House never
644
- fetches. `file` names one work file the connector already acquired into this
645
- Environment and declares its exact version:
646
-
647
- ```
648
- {"file":{"path":"/srv/acme/files/plan.pdf","bytes":48213,"sha256":"<hex>","media_type":"application/pdf"},"name":"plan.pdf"}
52
+ bash <(curl -fsSL …/connect-linux.sh) kit login
649
53
  ```
650
54
 
651
- `path` is absolute and never leaves the installation; `bytes` and `sha256`
652
- describe the accepted version even when the connector could not download it,
653
- and `media_type` is optional. The Kit publishes that descriptor as a retained
654
- original on the Capture item, under an accepted version derived from the
655
- delivery key, the attachment position and the descriptor, so reoffering the same
656
- item always names the same version and a different descriptor under the same
657
- delivery key is House's delivery-key conflict.
55
+ `kit login` reconnects this computer's Environment with a fresh credential.
56
+ Add `--manual` when the browser cannot reach this computer, or
57
+ `--environment <uuid>` to reconnect a different Environment of yours.
658
58
 
659
- The Kit derives each item's delivery key from the instance, the service, the
660
- declared namespace, the native object id, and the native version when present.
661
- An object whose content changes without its native version changing keeps its
662
- key and is refused by House as a delivery-key conflict rather than silently
663
- overwriting the earlier Record.
59
+ ## Guides
664
60
 
665
- The same command owns its module authorization. `kit source sign-in <instance>`
666
- skips an instance whose local authorization is already valid and answers
667
- `ready`, `skipped`, or `needs_attention`. It and the setup walk's status check
668
- send one JSON line
669
- with `action: "authorization"` and `mode: "status"` or `mode: "login"`; the
670
- command returns exactly one JSON object such as `{ "authorized": true }`.
671
-
672
- ## The Kit control stream
673
-
674
- The resident Kit holds one `/kit/stream` WebSocket for its Environment,
675
- authenticated with its Environment control lease and the Kit contract header,
676
- for as long as it holds that lease; `kit worker run` holds one for the length
677
- of its pass to receive its owner's commands, and `kit telegram serve` holds one
678
- while it serves the gateway under a lease of its own. The Kit sends no
679
- subscribe frame and accepts every frame House sends. What it writes there is
680
- the live view of the turns it runs, as draft and plan frames the
681
- [Worker runtime](#the-worker-runtime) describes, and a `gap` frame when the
682
- [Telegram gateway](#the-telegram-gateway) needs a turn's whole view again.
683
-
684
- Each time the socket connects, the resident acquires everything a signal could
685
- have named, because a signal House sent while the socket was down is lost: its
686
- Runs and resumptions with their setup and conversation files, the owner's
687
- pending commands, the agent CLI list, the saved working copy choice and its
688
- Google Sources. After that it acquires only what House names. A
689
- `work_available` frame for `agents`, `working_copy` or `sources` acquires that
690
- subject alone. `runs` takes a resumption or else a waiting Run, readying the
691
- agent CLIs, a requested setup and conversation files first. `commands` hands
692
- the supervised Session its stops, interrupts and answers and takes a
693
- resumption the same way, without acquiring a Run. An `entries` frame carries
694
- the Change entries of a Room the lease admits, which the resident applies to
695
- that Room's [working copy](#working-copies) in the order the frames arrive. A
696
- pass that took a Run acquires again once that Run ends, and a pass that found
697
- nothing waits for the next signal. A Run outcome House neither took nor finally
698
- refused is reported again every 30 seconds. The Kit has no idle acquisition
699
- cycle, no command poll and no Session heartbeat: a Session is observed when its
700
- process starts and when its supervisor sees it stop.
701
-
702
- A socket that closes or never opens reconnects after a delay drawn between half
703
- and all of one second, doubled for each consecutive failure up to 30 seconds.
704
- An `authority_changed` close reconnects after a delay drawn between 0 and 2
705
- seconds, with no error backoff. A `replaced` close, which means a newer
706
- connection holds the Environment, ends the resident with `kit_stream_replaced`,
707
- and an upgrade House refuses with `kit_upgrade_required` stops every House call
708
- as [above](#connect-a-linux-computer) describes; neither is retried.
709
-
710
- ## The Worker runtime
711
-
712
- ```sh
713
- kit worker run
714
- ```
715
-
716
- Worker-runtime readiness validates the tmux this Kit declares support for,
717
- reopens any already-bound live WorkerSession, brings every declared agent CLI
718
- to its current release, starts each installed declared kind's command adapter
719
- long enough to verify its compiled contract and read the options its own
720
- session offers, reports what it holds to House, and only then acquires another
721
- WorkerRun. This Kit compiles three MVP adapter kinds: `codex-acp` contract `2` selects
722
- `@agentclientprotocol/codex-acp` `1.12.0` or a later `1.x` release, and
723
- `claude-agent-acp` contract `1` selects
724
- `@agentclientprotocol/claude-agent-acp` `0.79.0` or a later `0.x` release, while
725
- `grok-build` contract `1` selects `@xai-official/grok` `1.0.41` or a later
726
- `1.x` release and launches its native supervised ACP stdio mode. Each
727
- contract names the adapter package, the minimum release the `initialize` answer
728
- must reach, the executable it installs, the install recipe's own settings, the
729
- CLI's headless sign-in command and the command that answers whether it is
730
- signed in, the Agent Client Protocol version it speaks, its capability
731
- descriptor - the admitted `elicitation/create` field kinds, whether the adapter
732
- admits a declined form, and the stop-reason table of that adapter - whether the
733
- adapter loads a session, and the identifiers of the session configuration
734
- options the route's model and effort reach, or `null` for a field whose
735
- adapter offers no such option. An acquired route carrying any other descriptor for
736
- that kind refuses as `kit_adapter_contract_mismatch` before any spawn
737
- commitment.
738
- The `codex-acp` and `grok-build` contracts pin session loading and the
739
- `claude-agent-acp` contract does not. The same readiness asserts that the initialize exchange Kit
740
- wrote carried its elicitation declaration, because without it Claude Code
741
- disables its question tool and Codex answers nothing.
742
- An adapter whose running release is older than the contract's minimum, of
743
- another major, mismatched, or unresponsive refuses `kit worker run` within 10
744
- seconds per installed kind and names what it found, after the pass has reported
745
- what it holds, and every other Kit command keeps working.
746
-
747
- Once `initialize` passes, the Kit opens one Agent Client Protocol session with
748
- `session/new` in its own home directory and no MCP server, reads that session's
749
- configuration options, and stops the adapter; no prompt is sent. For each field
750
- the contract names, it takes the `select` option carrying that identifier and
751
- reports every value it offers with the display name the adapter gave it,
752
- flattening option groups and keeping nothing else. A field whose identifier the
753
- contract declares `null`, or whose option the session does not expose as a
754
- `select`, reports none. An adapter that refuses the session with the Agent
755
- Client Protocol's `auth_required` error, as `codex-acp` does before it is signed
756
- in, reports no options at all and its kind stays served. A session that fails
757
- for any other reason, does not open within 30 seconds, answers without a session
758
- id, or offers options the Kit cannot read as values and names reports no options
759
- either and refuses `kit worker run` as `kit_adapter_contract_mismatch`, like any
760
- other contract failure. The Kit keeps no catalogue of these values and no copy
761
- of them between passes: every report reads them from the adapter again.
762
-
763
- ## Agent working directories
764
-
765
- Connecting an installation creates `/agents/house`, the absolute base every
766
- default Agent working directory lives under. Connecting again keeps whatever is
767
- already under it. An installation that cannot create that directory refuses
768
- with `kit_launch_base_unprepared` and names it, rather than substituting a
769
- home-relative location: the Environment owner creates it once and gives the
770
- installation write access to it.
771
-
772
- Every acquired WorkerRun carries its Agent's absolute working directory and its
773
- Agent id in its execution plan, and Kit launches the supervised session and its
774
- adapter in exactly that directory. Kit recognises the Agent's own default
775
- directory by exact equality with `/agents/house/<agent id>`, never by a
776
- directory's mere position beneath the base: a sibling directory one level
777
- inside `/agents/house` is somebody else's default or an owner's own workspace,
778
- never this Agent's. Every path that is not this exact equality, one deeper
779
- inside `/agents/house` included, must
780
- already exist in this Environment as a directory the installation can enter;
781
- Kit never creates it.
782
-
783
- When the Agent's own default directory is absent, Kit creates it empty; two
784
- concurrent first launches create the same directory. A present default
785
- directory launches exactly as it is, and a deleted one is created empty again
786
- on its next launch. Kit writes no file into any working directory.
787
-
788
- A relative, missing, inaccessible, or non-directory working directory refuses
789
- with `kit_launch_directory_unavailable` before any provider process starts. Kit falls back to no other location - not the
790
- base, not `$HOUSE_KIT_HOME`, and not its own current directory - and neither
791
- connecting an installation nor launching a session moves, copies, overwrites,
792
- or removes anything a working directory holds.
793
-
794
- ## The Skill set
795
-
796
- The Skill set is every skill the `mattpocock/skills` plugin manifest names at
797
- one pinned commit but `setup-matt-pocock-skills`, `triage` and `wizard`. Each
798
- is vendored under `src/skill-set/<name>/` with every file it ships, the
799
- upstream MIT `LICENSE` and a provenance header in `SKILL.md` naming the commit
800
- and upstream path. Nothing else is edited but two lines: a
801
- `disable-model-invocation: true` frontmatter line in `SKILL.md` and an
802
- `allow_implicit_invocation: false` line in `agents/openai.yaml` are removed, so
803
- an agent reaches every skill without being told its name.
804
-
805
- Kit installs the Skill set once for the Environment, for every Agent whatever
806
- its working directory, under the home it launches agent CLIs with: each skill
807
- as the directory `$HOME/.agents/skills/<name>`, which Codex and Grok Build
808
- read, and a relative symbolic link `$HOME/.claude/skills/<name>` to it, which
809
- Claude Code reads; when `$HOME/.claude/skills` already resolves to
810
- `$HOME/.agents/skills`, Claude Code reads the directory there and Kit makes no
811
- link. In a container that home is Kit home, so the set survives
812
- container recreation; on a native host it is the host user's home.
813
-
814
- `$HOUSE_KIT_HOME/state/skill-set.json` records the names Kit installed and the
815
- Kit version that installed them, and Kit owns exactly those names: the
816
- directory and the link of each. At start and on every resident pass, when
817
- the set is accepted and the record names another version or none, Kit deletes
818
- every recorded name, installs the current Skill set and rewrites the record. A
819
- name whose directory or link exists without being recorded is the owner's: Kit
820
- leaves it untouched and installs nothing under it. Kit never compares, merges
821
- or restores a file, so an edit to an installed skill is lost at the next
822
- release and a copy under another name is kept. When `skills` is `false`, Kit
823
- deletes every recorded name and the record and installs nothing.
824
-
825
- After Kit first installs the Skill set, the first Session it launches whose
826
- Profile ceiling lets it write the owner's How-we-work document
827
- `/private/library/how-we-work.md` creates it, when it is absent, with the text
828
- Kit ships in `src/how-we-work.md`, over the Session's own bridge before its
829
- first prompt is built. Kit then notes in `$HOUSE_KIT_HOME/state/how-we-work`,
830
- apart from the record, that the document exists, and a present document is
831
- noted the same way without being touched. Nothing ever creates it again: a
832
- release, a declined Skill set and a later acceptance leave the note, so an
833
- owner's edit stands and a deleted document stays deleted. A Session whose
834
- ceiling cannot write the document launches without it and leaves it for the
835
- next one; any other refused creation refuses the attempt as
836
- `kit_how_we_work_unwritable` with House's answer before a prompt is sent. A
837
- Skill set Kit cannot install or remove refuses the pass as
838
- `kit_skill_set_unavailable` and names the path.
839
-
840
- ## The declared agent CLIs
841
-
842
- The Environment owner installs no adapter binary by hand. `kit agents update`
843
- and every `kit worker run` bring each declared kind to the latest release its
844
- contract serves - no older than the contract's minimum and inside that
845
- minimum's major - under
846
- `$HOUSE_KIT_HOME/agents/<kind>/releases/<release>/`, never touching what the
847
- owner installed globally, and an acquired route's command is that installed
848
- executable rather than a path anyone writes into `kit.json`.
849
- The resident Kit also listens for an `agents` availability pointer on its
850
- control stream. It reads the whole wanted list from House over HTTPS, removes
851
- Kit-owned CLI directories that are no longer wanted, installs newly wanted
852
- kinds, and reports its held state immediately, including while another Agent
853
- run remains active. A newly wanted kind that needs provider authorization uses
854
- the existing guided House login ceremony; Kit reports readiness again when it
855
- ends. A reconnect reads the list again, so a pointer missed during a stream
856
- outage changes no authority.
857
- Removal waits for an active worker pass or any unresolved live WorkerSession
858
- binding to stop using a CLI directory; the Kit keeps reporting that kind until
859
- it removes the directory.
860
-
861
- Every candidate release is proved before it serves: the Kit installs it beside
862
- the current release, runs the contract's own conformance check - the protocol's
863
- `initialize` against the minimum release, the protocol version and the
864
- capability descriptor the contract requires, and no provider-billed turn - and
865
- switches to it only on green. On red it keeps the release it already has,
866
- removes the candidate, and holds that kind with the refused release named in
867
- its report until a newer candidate appears. Non-major releases advance this way
868
- on their own; a major release waits for a contract revision. The candidate
869
- check runs at most every six hours per kind, and `kit agents update` runs it at
870
- once. A kind whose release lookup or installation fails keeps the release it
871
- already has, or none, and is named with its failure; the other kinds go on,
872
- `kit agents update` then exits non-zero, and a worker pass verifies and serves only the kinds with an installed release, so
873
- one CLI the Kit could not install leaves only that kind's work unavailable.
874
-
875
- The install recipe carries what the CLI needs beside its binary: for
876
- `codex-acp` the Kit enables user-input questions in Codex's own configuration
877
- document, because a Codex that disables them raises no `elicitation/create` and
878
- answers nothing, and the Kit adds that setting only where the owner declared
879
- none.
880
-
881
- `kit agents sign-in <kind>` runs that CLI's own headless sign-in command in the
882
- terminal it was started from, so the owner only completes it; House opens that
883
- terminal and runs nothing itself. Both commands hold one control lease, release
884
- it once they are done, and report to House afterwards what
885
- this Environment now holds: per declared kind its compiled contract version,
886
- the installed release or none, whether the contract's native readiness evidence
887
- shows a signed-in provider, the candidate release a red conformance check refused, and the model and
888
- effort options the installed adapter's own session offered, or `null` when no
889
- release is installed or its session did not open. Every report, the worker pass's
890
- and `kit setup`'s included, reads those options the same way and goes to
891
- `POST /kit/agents/report` with one entry per declared kind:
892
-
893
- ```json
894
- {"agents": [{"kind": "claude-agent-acp", "contract_version": "1", "release": "0.81.2",
895
- "signed_in": true, "hold": null,
896
- "options": {"model": [{"value": "haiku", "name": "Haiku 4.5"}], "effort": []}}]}
897
- ```
898
-
899
- House knows about an agent CLI only
900
- what this Kit reported: it probes no binary, version or login of its own, and
901
- it refuses an Agent, an Agent Session and Agent-runtime acquisition for a kind
902
- this Environment does not declare or this Kit has not reported installed and
903
- signed in.
904
-
905
- This installation owns one private tmux server namespace derived from
906
- `$HOUSE_KIT_HOME`, so House never joins, lists, or destroys an ordinary tmux
907
- session. Each acquired WorkerRun becomes one WorkerSession in one opaque exact
908
- tmux session of that namespace, created from a Kit-owned configuration that
909
- disables terminal retention. tmux owns the terminal lifecycle and nothing else:
910
- the pane, its scrollback, and anything the adapter writes to its own stderr are
911
- never read, stored, parsed, or answered, and no key is ever injected into a
912
- pane.
913
-
914
- Inside that session the Kit starts the launcher for the acquired route's
915
- declared command adapter and hands it, over a Kit-private local channel, the
916
- canonical request House gave the Kit and the adapter argument vector. Routine assignments carry their Routine Request.
917
- Direct conversation assignments carry the sealed turn and its ordered messages,
918
- and the Kit seals the reply itself on its own ingress.
919
- An Agent resolves exactly one Execution route, so the acquisition envelope's
920
- execution plan carries one `route` object and this Kit selects, orders, and
921
- hands off nothing. The Kit launches that route as House admitted it and keeps
922
- no local copy of it: an acquired route naming a kind `agents` does not declare
923
- refuses as `kit_agent_undeclared`, and one whose kind has no installed release
924
- as `kit_agent_not_installed`. The route names `adapter.kind` and
925
- `adapter.contract_version`. The installed release of that kind plus the route's
926
- `arguments` are the exact argument vector, so no route names a binary path.
927
- The route's `model` and its `effort` or `null` are House-visible route facts in
928
- the CLI's own vocabulary, which House stores as given and never validates; the
929
- Kit hands them to the adapter at session start as its contract names, and a
930
- value the CLI refuses fails the attempt with the CLI's own cause as
931
- `kit_adapter_failed`. A route whose `effort` is `null` sets no effort, so the
932
- CLI's own default effort applies and a model that offers no effort option
933
- starts.
934
- The adapter inherits only `PATH` and `HOME`, so the provider's own local
935
- authorization stays in the Environment where its CLI already keeps it. The Kit
936
- adds the Session's `house` CLI in front of that `PATH` and names the Session's
937
- bridge socket and nonce in `HOUSE_KIT_BRIDGE` and `HOUSE_KIT_BRIDGE_NONCE`.
938
-
939
- The launcher speaks the Agent Client Protocol through the protocol's official
940
- TypeScript SDK, `@agentclientprotocol/sdk`, as this Kit's one protocol client;
941
- the Kit frames no protocol JSON-RPC of its own. It opens the session with `session/new`
942
- in the Environment's working directory with no MCP server, or reloads the
943
- retained one with `session/load`, then sets the
944
- route's model, and its effort when the route names one, through the protocol's
945
- `session/set_config_option` with the option identifiers the adapter contract
946
- names: `model` and
947
- `reasoning_effort` for `codex-acp`, `model` and `effort` for
948
- `claude-agent-acp`. Neither pinned adapter reads a model or effort flag of its
949
- own. It carries the turn with one `session/prompt`, and maps that
950
- prompt's stop reason through the contract's own table to one of four turn
951
- outcomes: completed, interrupted, refused by the provider, or exhausted. A stop
952
- reason the contract does not name, including a vendor-specific one a different
953
- contract names, fails the turn as `kit_adapter_contract_mismatch` rather than
954
- being guessed. After committing the Run, and again on every resumption, the
955
- Kit reads what the Session was started for from House's
956
- `/kit/runs/<run>/request` with the Session credential: the Routine Request with
957
- its input, references, requester and the pinned revision's instruction, or the
958
- sealed conversation turn. It sends that as the first prompt block, followed by
959
- current House guidance fetched through the Run connection, so the Agent calls
960
- nothing to learn it. An unreadable request or guidance refuses the turn.
961
- A session opened with `session/new` gets one more text block ahead of those,
962
- the same for every adapter kind: the line
963
- `The agents.house house CLI is available: run house --help.`, the base
964
- instructions of the Execution attempt plan House acquired, and the current text
965
- of the owner's How-we-work document `/private/library/how-we-work.md`, which the
966
- launcher reads with the `shell` tool over the Session's own bridge, running
967
- the continuation each cut reply's `output_cut` line prints until a reply comes
968
- back uncut. A document that is absent, or that the Session's ceiling cannot
969
- read, at the first read adds nothing, and so do empty base instructions. Any other failed read
970
- refuses the attempt as `kit_how_we_work_unreadable` with the bridge's cause
971
- before a prompt is sent. A session reloaded with `session/load` already holds
972
- the block and gets none. It forwards only adapter-classified User-visible assistant
973
- text back over the local channel, and leaves reasoning, tool traces, and
974
- terminal output where they are. The supervised process's own House calls travel
975
- that same channel through the `house` CLI. The Kit holds the WorkerRunToken, replaces it in place when
976
- it nears expiry without the child noticing, attaches a bridge-generated UUIDv7
977
- to every Session mutation it forwards, and never places a House
978
- credential in any child environment.
979
-
980
- The Kit is the single source of assistant text: it observes the adapter, streams
981
- what it has seen so far, and seals the turn itself. No Agent is asked to report
982
- text a deterministic observer already sees, and no agent-facing tool admits final
983
- text.
984
-
985
- While the adapter generates, the supervisor writes the whole User-visible
986
- assistant text of the turn so far as `draft` frames on the Environment's
987
- [control stream](#the-kit-control-stream); it calls no draft route. Only
988
- adapter-classified assistant message text enters that stream; reasoning, tool
989
- calls, and terminal output never do. The Kit maps the structure it can read in
990
- that text - paragraphs, headings, fenced code, ordered and unordered lists, pipe
991
- tables, and `$$` formulas - into House's blocks, and leaves anything it did not
992
- parse as the paragraph it observed. It invents no structure, renders nothing,
993
- and holds back or refuses no draft for its length: a frame carries every block
994
- of the text however long it is.
995
-
996
- A frame names the turn it belongs to, as House answered the Session's `started`
997
- observation with the turn's `conversation_id` and `turn_id`, together with the
998
- Execution attempt as `attempt_id`. It carries a `sequence` that grows by one
999
- with each paced view of the turn, and replaces the turn's view from the block
1000
- index `from` onward with the `blocks` it carries, so a frame for text that only
1001
- grew carries the blocks from the first one that changed, and a view that
1002
- changed no block is a frame that replaces nothing. The launcher numbers the
1003
- views, so a Kit that restarts mid-turn continues the turn's sequence. The Kit
1004
- coalesces and paces that stream rather than mirroring every chunk: it writes
1005
- the first observed view at once and then at most one view in each
1006
- 500-millisecond pacing window, the latest one, even when the adapter completes
1007
- a message inside a window. A frame written while the socket is down is lost,
1008
- so the first frame after each reconnect, and after any frame the socket could not carry, replaces the view
1009
- from block 0. A Session House names no conversation turn for gets no frames. A
1010
- draft persists nowhere - not in this Kit's local state, not as House history -
1011
- is never retried, and can neither delay nor prove turn completion.
1012
-
1013
- A `plan` session update is one snapshot of the agent's steps. The Kit writes
1014
- each snapshot as a `plan` frame paced by the same rule, carrying every step's
1015
- `label`, `priority` and `status` exactly as the adapter gave them, its own
1016
- `sequence`, and the step index `from` it replaces the plan onward. The act that
1017
- ends the turn carries the last snapshot the Kit saw as its final `plan`: the
1018
- seal, the observed stop, the Run outcome and an `interrupted` interrupt outcome
1019
- each name the provider turn and every step's `label` and `status`, however many
1020
- steps and however long their text, and a turn that never planned carries none.
1021
- There is no per-snapshot plan route.
1022
-
1023
- When the adapter completes the turn, the Kit seals it with one
1024
- `sessions/<session>/assistant-messages` call carrying the same blocks, the final
1025
- plan, and a UUIDv7 `operation_id`. The exact request - that identity, those
1026
- blocks and that plan - is written into the WorkerSession binding before the
1027
- call leaves, so a recovered call returns the outcome House recorded and never
1028
- seals a turn twice, and House's answer names the turn it closed and the
1029
- delivery it queued. An answer the Kit never received leaves the Run unsettled
1030
- with the request retained: the next `kit worker run` replays exactly that
1031
- operation, those blocks and that plan before settling anything, rather than
1032
- reporting a failure it cannot prove. A refusal House did decide is the Run's
1033
- reported failure.
1034
-
1035
- That seal is the one durable record of the turn, and only a seal House accepted
1036
- completes a turn: the Kit reports whole-turn completion to House after the seal
1037
- succeeds and never otherwise. A turn whose adapter produced no User-visible text
1038
- seals nothing and claims no completed turn; the Run's own terminal outcome still
1039
- follows what the adapter reported.
1040
-
1041
- The Agent reaches House only through the `house` CLI. Its verbs are the Tools
1042
- House lists to the Session, and its help is House's own: `house --help` prints
1043
- each listed Tool's name and description, and `house <verb> --help` prints that
1044
- Tool's description and input schema, all read from House's `tools/list` at the
1045
- call, so a catalogue change needs no Kit release. `house <verb>
1046
- '<arguments as one JSON object>'` calls that Tool, or `house <verb> -` with the
1047
- object on standard input for arguments longer than one command-line argument
1048
- holds. It prints House's text answer, or a refusal on standard error with a
1049
- nonzero exit status. The CLI holds
1050
- no credential: it calls over its Session's bridge, which attaches the Session
1051
- credential and refuses a caller without that Session's nonce as
1052
- `kit_bridge_unauthorized`. House network calls use only the selected
1053
- `2026-07-28` profile. `house git push` is the one local verb House does not list;
1054
- [working copies](#working-copies) describe it, and `house --help` and
1055
- `house git push --help` explain it.
1056
-
1057
- Two verbs name files on the Session's own computer: `upload_attachment` takes
1058
- one `path`, and `append_record` takes `attachments` as local paths. The CLI
1059
- resolves each path against its working directory. The bridge reads each regular
1060
- file, computes its size and SHA-256 and the media type an image's leading bytes
1061
- show, declares them beside the call, and uploads the exact bytes through the
1062
- one-use grant House answers before the call completes, so the answer carries the
1063
- attachment's `at_` reference. `append_record` uploads every file into its Room
1064
- first and names their references in the Record; a file that cannot be read or
1065
- saved appends nothing. The bridge reads House's YAML answers to
1066
- `upload_attachment`. A Room or `/private` upload ends at the byte upload rather
1067
- than at a House answer, so the bridge prints that saved answer in the same YAML;
1068
- every other answer passes through as House wrote it. With `conversation: true`
1069
- the file becomes the owner's conversation original, and the bridge calls again
1070
- and waits for House's delivery outcome. House derives the presentation from the
1071
- declared media type: an image is uploaded inline beside a link to the unmodified
1072
- original, and every other file is offered as that link alone.
1073
-
1074
- The bridge retains the exact pending House request in memory before transfer.
1075
- Within the same live Kit and supervised WorkerSession, a lost response or
1076
- infrastructure failure resends that request with the same identifier. A local
1077
- socket reconnect delivers the retained outcome to the process.
1078
- A second deliberate call gets a new identifier; read-only calls carry none.
1079
- House refusals pass through to the CLI without automatic resend. [R193](https://github.com/agentshouse/core/blob/main/docs/concept/implementation/14-agents-profiles-sessions-and-runs.md#r193)
1080
- owns recovery authority, lifetime, and refusal semantics.
1081
-
1082
- The launcher speaks only what the adapter contract fixes for this build. Every
1083
- session initializes with the same fixed client capabilities: no file-system
1084
- methods, no terminal methods, and elicitation in form mode and not in URL mode.
1085
- It offers the adapter no filesystem or terminal surface of its own, so an adapter
1086
- that needs a decision the Kit cannot carry fails its turn instead of receiving a
1087
- guessed answer. Execution permission is the one decision the Environment already
1088
- settled: `kit.json` declares the Environment's `execution_authority` as `allow`
1089
- or `refuse`, and the launcher answers every adapter permission request with that
1090
- declared outcome and nothing else. The authority is a local Environment fact - it never reaches
1091
- House, and no permission request is ever projected to a human.
1092
-
1093
- A task question is the decision the Environment cannot settle, so the launcher
1094
- carries it to the owner instead of answering it. The Interaction is the
1095
- protocol's own form end to end: an adapter's `elicitation/create` request in
1096
- form mode reaches House's interaction act as its `message` and its
1097
- `requested_schema`, unchanged in meaning, with the exact correlation of the
1098
- provider turn and of the adapter request that raised it. House alone judges
1099
- whether the schema is inside the flat subset it can ask; a form it admits as
1100
- unsupported, and a URL-mode request the Kit never forwards, cancel the
1101
- adapter's request with the protocol's `cancel` and ask the owner nothing.
1102
-
1103
- Nothing of an adapter's native wire crosses: the Kit strips every native
1104
- `_meta` from the request, the schema, its properties and their options, and
1105
- gives a property the adapter left untitled its own name as title. Two facts
1106
- cross as House-namespaced marks instead. The batch carries
1107
- `_meta["agents.house/secret"]` when the adapter classifies the request as
1108
- secret in its own meta, as `codex-acp` does with `isSecret`; House records it
1109
- as unsupported and raises an interrupt rather than a card, so no secret prompt
1110
- is projected to a human and no secret answer travels back through House. A
1111
- choice carries `_meta["agents.house/free-text"]` when the adapter's own
1112
- convention admits the owner's words beside the listed options:
1113
- `claude-agent-acp` pairs a question with a `_askUserQuestionCustomAnswer`
1114
- companion field, and `codex-acp` pairs it with a `"None of the above"` option
1115
- and a `<id>_note` field tagged `user_note`. The companion field and the
1116
- synthetic option never reach House.
1117
-
1118
- The owner's answer arrives as the `answer` command carrying the protocol's own
1119
- response, `accept` with content keyed by property name or `decline` for the
1120
- whole form, and is applied to that exact pending request; an answer naming
1121
- another turn or another correlation has no effect, so a stale or cross-Session
1122
- answer cannot reach a live adapter. A typed answer to a free-text choice goes
1123
- back through the adapter's own convention: into the companion field for
1124
- `claude-agent-acp`, and as `"None of the above"` with the typed text in the note
1125
- field for `codex-acp`. The turn waits on the adapter's own request while the
1126
- owner decides, and the Kit reports what became of each Interaction, so a
1127
- Session that ends with one outstanding expires it instead of leaving it live.
1128
-
1129
- Before new execution, `kit worker run` reconciles House-authorized inbound cache
1130
- cleanup and materializes every pending conversation file House offers, ten at a
1131
- time, under `$HOUSE_KIT_HOME/conversation-inbound/<run>/<attachment>/<name>`.
1132
- House seals due input itself. The Kit uses the existing Environment lease and
1133
- byte endpoint, limits each file to 32 MiB, and reports materialization after
1134
- receiving a complete byte stream, validating any reported size, and writing the
1135
- file. A lost acknowledgement preserves the completed local file for House's
1136
- idempotent admission and later cleanup. Failed files remain pending; House's
1137
- all-files-ready gate keeps their turn unprocessed.
1138
- The provider receives absolute local paths beside the owning messages in its
1139
- first prompt. The cache persists across
1140
- turns; only House-authorized terminal Run cleanup removes its Run directory.
1141
-
1142
- The accepted attachment's retained original is separate from that cache. The Kit
1143
- records a local note for the attachment before it acknowledges materialization,
1144
- then saves the exact bytes it received through House's conversation-original
1145
- door, naming only the attachment; a download that fails reports
1146
- `source_unavailable` for the same version.
1147
-
1148
- A save that fails leaves one note in `state/conversation-originals/` naming the
1149
- attachment, its Run, the local file and the descriptor it declared. Each
1150
- later `kit worker run` retries it from that file without materializing or
1151
- acknowledging anything again, and reports `source_unavailable` when
1152
- the file is gone or changed rather than saving different bytes. When a
1153
- supervised WorkerSession ends, the same retry runs once more before its Run is
1154
- reported. The note leaves once
1155
- House records the save, once House answers that the Run is over, or when House
1156
- authorizes that Run's cache cleanup; House accepts no save for a Run that has
1157
- settled, so an unsaved original keeps its failed outcome from then on. The owner
1158
- reads every saved original through House afterwards, whether or not this
1159
- Environment still exists. `kit worker run` prints every conversation original
1160
- outcome it handled, and exits non-zero while any of them is unsaved.
1161
-
1162
- One `kit worker run` then takes one House-authorized resumption, or otherwise
1163
- acquires one waiting Run, and supervises exactly that WorkerSession. A resumed
1164
- conversation loads its retained native provider session through ACP
1165
- `session/load`, receives the current input House answers for the Run in its
1166
- first prompt, and executes one turn. Historical output replayed during
1167
- loading is not emitted as new assistant output. Missing or rejected provider
1168
- continuation never starts a replacement provider conversation in the same Run.
1169
-
1170
- Continuation is per adapter. A contract that pins session loading retains the
1171
- adapter's opaque session handle between turns and resumes through it, as
1172
- `codex-acp` and `grok-build` do. A contract without it, as `claude-agent-acp`, retains nothing:
1173
- its turn reports no provider continuation, the Agent Session ends with the
1174
- reported cause `kit_provider_continuation_unavailable`, and the owner starts the
1175
- next one.
1176
-
1177
- Between turns, Kit retains the opaque provider session identifier and the Run's
1178
- Worker, working directory, and route references in an owner-only local record.
1179
- The record carries no transcript or Session credential and grants no authority:
1180
- House must authorize each successor WorkerSession. A reported terminal state and
1181
- owner stops remove the record. [R199](https://github.com/agentshouse/core/blob/main/docs/concept/implementation/14-agents-profiles-sessions-and-runs.md#r199)
1182
- owns conversation continuity.
1183
-
1184
- Launcher exit is the terminal process observation. The Kit closes the local
1185
- channel, grants the launcher a fixed local exit grace, destroys the exact tmux
1186
- session if it is still there, and then reports one stopped Session at
1187
- `POST /kit/sessions/:session/stopped` and the adapter's own continuation facts at
1188
- `POST /kit/sessions/:session/continuation`, whose body is exactly
1189
- `{"provider_continuation": <bool>, "provider_capacity_exhausted": <bool>}`. No
1190
- adapter-supplied reset instant and no handoff availability is sent, and nothing
1191
- schedules a wake.
1192
-
1193
- Kit, not the Agent, then reports the terminal state the execution reached at
1194
- `POST /kit/runs/:run/outcome`. No agent-facing tool posts it, and settling writes
1195
- no Record, Posting or Capture by itself: whatever the instruction wanted written
1196
- is written through the ordinary authorized Run mutations first. The body is
1197
- `{"operation_id": <uuidv7>, "outcome": "completed"}` or
1198
- `{"operation_id": <uuidv7>, "outcome": "failed", "cause": "<1-200 chars>"}`, and
1199
- the reported cause is exactly what the adapter gave. A finished turn that needed
1200
- no further Provider continuation is `completed`. A known execution failure, a
1201
- provider capacity exhaustion, a positively lost Provider continuation, and a
1202
- rejected native resume command are each `failed` with their own cause; House
1203
- notifies the owner and nothing is retried. A confirmed-stopped Session whose
1204
- Provider continuation remains available reports no terminal state at all and
1205
- leaves the Run active for an owner-originated resumption.
1206
-
1207
- The Kit decides that terminal state once, records it beside its stable operation
1208
- identity before it reports it, and reports it from that record. It decides and
1209
- records it as soon as the stop is recorded and the final text sealed, before it
1210
- reports the stopped Session and its continuation facts: a refusal of either of
1211
- those does not change the terminal state, so the Kit reports that recorded state
1212
- whatever House answered to them. A Run with a
1213
- supervised session records it in that session's durable local binding; a Run
1214
- whose terminal state was decided without one — a resume command this Kit rejects
1215
- because it retains no Provider continuation for the Run, or an acquired Run it
1216
- refuses before any provider process starts, which it commits and fails with the
1217
- refusal as the cause so no Run stays acquired behind it — records it in a
1218
- durable local settlement note of its own, named by the Run. A lost response is recovered by reporting that
1219
- identical body under the same operation identity on the next `kit worker run`;
1220
- House answers the committed settlement verbatim and the Agent is never executed
1221
- again. A Run House has already settled answers `worker_run_settled`, which the
1222
- Kit accepts as settled and drops the record for.
1223
-
1224
- The Kit keeps that record until House answers it. The record stays exactly as it
1225
- is while House is unavailable, never answers or answers in a form the Kit cannot
1226
- read, declares its refusal retryable, answers 429, 502 or 503 without declaring
1227
- otherwise, refuses the Kit contract or the Kit's lease, including a lease
1228
- renewal the report waited on, refuses the operation identity as issued ahead of
1229
- its clock, or answers `conversation_file_send_pending` because a conversation
1230
- file the Run sent is still being delivered. A report whose operation identity
1231
- House no longer holds, answered `operation_expired` or `position_expired`, keeps
1232
- the same body under a new operation identity. Either way the Run stays bound to
1233
- this installation, `kit worker run` names the refusal House answered with, if
1234
- any, and exits non-zero, and the same decided body is reported again before
1235
- anything is acquired. Every other refusal is House's final answer for that
1236
- report, such as `worker_run_not_found` or `worker_run_not_acquired`: the Kit
1237
- writes `kit: run <run> outcome refused: <code>` to standard error once, drops
1238
- the record, and acquires as if the report had been taken. For the Run it
1239
- supervised in that pass, `kit worker run` also names that refusal and exits
1240
- non-zero. A refused report is never presented to the owner as a delivered one.
1241
-
1242
- While a WorkerSession is supervised the Kit keeps one durable local binding
1243
- naming this installation, the WorkerRun, the WorkerSession, the exact tmux
1244
- session, and the local channel that reaches it. The binding carries no terminal
1245
- content, no prompt, and no resolved secret, and Session authority is always
1246
- reissued by House rather than recovered from disk. It is evidence for
1247
- reconciliation, never permission to resume or recreate work, and nothing keeps
1248
- it once House has answered for the Run: a settled terminal state, a confirmed
1249
- owner stop, and a retained Provider continuation each drop it. When the
1250
- supervised process ends, the binding first records what the Kit observed: the
1251
- sealed final text, whether the turn completed, the capacity or provider refusal,
1252
- the execution failure and whether a Provider continuation is retained. The Kit
1253
- then reports that stop, and a restarted Kit reports the same recorded stop
1254
- instead of treating the session as lost.
1255
-
1256
- A restarted `kit worker run` reconciles the records it kept before it acquires
1257
- anything new. It reports every retained settlement note first, because those
1258
- Runs have no session left to reconcile, and drops each note House accepts,
1259
- already settled or finally refused. It next consumes the durable commands House
1260
- holds for this Environment, so a requested owner stop ends the exact tmux
1261
- session before any local channel reopens. It then considers only sessions of its
1262
- own namespace whose binding is complete, and never lists unknown sessions, scans
1263
- host processes, searches another namespace, or adopts an arbitrary process. A
1264
- session that is already gone is confirmed gone, reported as one stopped Session
1265
- with no Provider continuation, and settled as a terminal execution failure. A
1266
- session that is still running is restored only after House positively replaces
1267
- the Session authority for the same Run and Session; the Kit then reconnects the
1268
- typed adapter channel, refreshes the started observation, and supervises that
1269
- session to settlement instead of acquiring another Run. An explicit House
1270
- refusal destroys the stale tmux session and exposes no channel. House that
1271
- cannot be reached leaves the binding and the session exactly as they are,
1272
- invents no stopped, active, or authorized state, and acquires nothing.
1273
-
1274
- A restore of House's database starts a new log epoch. Until this Environment
1275
- has reported in it, House refuses every act that touches a Run, acquisition and
1276
- lease release included, as `epoch_report_required` naming the epoch. The first
1277
- such refusal ends whatever the pass was doing. One raised inside a supervised
1278
- Run's bridge, while it admits or settles a question, reports an interrupt, or
1279
- replaces Session authority for a forwarded call, never becomes an answer to the
1280
- Agent: it ends the bridge and the pass at once. The Kit then reports, under the
1281
- lease it holds and at `POST /kit/sessions/report`, every Run it holds a spawn
1282
- commitment or a terminal outcome for: each retained binding, settlement note,
1283
- and Provider continuation, as `{"session": "<run>", "outcome": null}` while its
1284
- process runs, while it holds only its continuation, or while its outcome is
1285
- unknown, or with the held outcome it would have reported at
1286
- `POST /kit/runs/:run/outcome`, `completed` or `failed` with its cause. One
1287
- report carries at most 256 Runs, so the Kit sends as many as it needs, all
1288
- before it acquires anything. House settles every reported Run and never offers
1289
- it again, so once it has answered every report the Kit destroys each reported
1290
- tmux session still running, drops each reported Run's binding, settlement note,
1291
- and retained Provider continuation, and runs the pass again from its start. A
1292
- refused act is never a Run failure, nothing is acquired in the new epoch before
1293
- the report, and a refusal that follows the report ends the command naming it.
1294
-
1295
- The launcher inside the supervised session outlives the Kit process that started
1296
- it. Its local channel reconnects to the same socket with a bounded backoff and
1297
- announces that its adapter session already exists, so a restored bridge
1298
- acknowledges the resumption instead of starting a second turn and the supervised
1299
- process never sees a second request. A call already transferred when the Kit
1300
- process ends is never automatically replayed by its successor, even when the
1301
- same local channel reopens. Pending mutation requests and identifiers are not
1302
- written into the durable binding. Reconciliation must obtain current House
1303
- authority before the live process can make further calls.
1304
-
1305
- An owner stop requested through House reaches this Kit as a durable command,
1306
- both at startup and while a Session is supervised: House signals `commands` on
1307
- the Kit control stream and the supervisor acquires the pending stops,
1308
- interrupts and answers at once, so a stop reaches the process within a second.
1309
- The Kit destroys the exact
1310
- tmux session, confirms that it is gone, and reports the stopped Session. It
1311
- reports no continuation for a Run stopped by its owner and never reopens the
1312
- local channel for that Session.
1313
-
1314
- ```sh
1315
- kit worker attach # the one supervised session
1316
- kit worker attach <session> # the exact session
1317
- ```
1318
-
1319
- Attachment is a read-only diagnostic for the Environment owner. It reaches only
1320
- the exact tmux session of a live binding in this installation's namespace, gives
1321
- the Worker no input, makes no House call, causes no lifecycle transition, and
1322
- leaves the namespace configuration and its disabled terminal retention as they
1323
- are. Once the Session has settled or its tmux session is gone, attaching is
1324
- refused and names why.
1325
-
1326
- ## Paired proof
1327
-
1328
- Paired installed-CLI proof against the merged House producer is a labelled
1329
- validation build, not an npm release. From Linux Compose:
1330
-
1331
- ```sh
1332
- docker build -f test/pair/producer.dockerfile -t kit-original-pair:latest .
1333
- HOUSE_CORE_ROOT=<agents.house checkout> \
1334
- docker compose -f test/pair/compose.yaml up --abort-on-container-exit --force-recreate producer
1335
- ```
1336
-
1337
- It boots the real producer against a real PostgreSQL, installs the exact
1338
- candidate package, and runs `test/pair/*.vitest.ts`: the attachment doors,
1339
- a Source adapter retaining a declared file into Rooms of two owners, and an
1340
- installed Worker retaining an incoming attachment through failure, retry, Run
1341
- settlement and cache disposal. `KIT_PAIR_SPECS`
1342
- names the specs to run, and `HOUSE_CORE_REVISION` and `KIT_REVISION` are copied
1343
- into the evidence each spec writes under `tmp/delivery-evidence/`.
1344
-
1345
- ## Capturing
1346
-
1347
- ```sh
1348
- kit source capture acme # every declared service
1349
- kit source capture acme notes # one service
1350
- ```
1351
-
1352
- Each run reads the checkpoint, acquires one bounded page, submits one batch,
1353
- and advances the checkpoint only once every item in that batch is accepted or
1354
- an idempotent duplicate at the House door. Every batch names the service's exact
1355
- Source reference and its complete Room destination set, so House admits all of
1356
- them or none: one destination the delivering User cannot currently write refuses
1357
- the whole publication before any content changes. A refused item leaves the
1358
- checkpoint where it was, so the next run reoffers the whole batch and converges
1359
- on the door's own deduplication. When House stops a batch early, its answer
1360
- settles only the prefix it reached, and the unsettled tail is reoffered with the
1361
- rest. The loop repeats while the connector still returns items and no Room
1362
- destination is blocked, and every run ends by writing the service's health into
1363
- this installation's own state. House holds no custom connector's service, route,
1364
- checkpoint or health, and no Google entry's checkpoint or health.
1365
-
1366
- A Capture item that declares a `file` attachment answers one Record per Room
1367
- destination: Rooms of one owner share that owner's Record, and a Room of another
1368
- owner holds its own Record of the same delivery. The Kit records one retained
1369
- reference for every destination Room and attachment in
1370
- `state/<instance>/originals/<service>.json` before it saves anything, then
1371
- saves that batch's references through House's captured-original door before it
1372
- asks the connector for the next page: it reads the declared path, and uploads
1373
- the bytes only when they still match the declared descriptor. References still
1374
- outstanding from earlier runs are retried once the run's pages are delivered.
1375
- A missing or changed file reports `source_unavailable` instead, so House never
1376
- receives a substitute version. House, not the Kit, chooses where each Room
1377
- owner's copy is kept. Every run prints each reference's save outcome beside its
1378
- Room and Record and exits non-zero while any stays unsaved; acquisition health
1379
- stays about acquisition, so a failed save never changes it.
1380
-
1381
- ```sh
1382
- kit source retry-files acme # every declared service
1383
- kit source retry-files acme notes # one service
1384
- ```
1385
-
1386
- `retry-files` saves only the references still outstanding. It runs no connector,
1387
- submits no Capture, moves no checkpoint, and keeps each reference's Record and
1388
- accepted version; with nothing outstanding it contacts no House at all. A saved
1389
- reference, or one whose Posting House no longer holds, leaves the local state. A
1390
- Room whose write authority is gone answers `room_not_found` and stays outstanding
1391
- until that authority returns.
1392
-
1393
- ```sh
1394
- kit source status acme # every declared service
1395
- kit source status acme notes # one service
1396
- ```
1397
-
1398
- `kit source status` reads only local state: the current health and its exact
1399
- category, the stored cursor, every open coverage gap with the recovery reference
1400
- House returned for it, the items this installation dropped permanently, and the
1401
- retained references still waiting to be saved.
1402
-
1403
- Every command that changes or reads installation state takes this
1404
- installation's exclusive Environment control lease for the length of its run
1405
- and renews it before it expires, so two Kit commands never work against the
1406
- same installation at once. A command that ends, is refused, or is interrupted
1407
- once its work has stopped releases that exact lease so the next command acquires
1408
- at once. A `kit login` credential is presented only to take that lease.
1409
-
1410
- While `kit resident` holds that lease, a command runs through it instead: the
1411
- command presents no credential, the resident confirms its current lease with
1412
- House and sends the command's House requests under it, an interrupted command
1413
- cancels its request there, and the lease stays held when the command ends. The
1414
- resident serves one such command at a time and refuses another with
1415
- `environment_control_lease_active`, so two commands still never work at once.
1416
- A resident whose lease House refuses answers the command with that refusal; the
1417
- command never acquires a lease of its own while a resident serves it. While an
1418
- update drains, `state/update-drain` refuses every such command, `kit login` and
1419
- `kit worker run` with `kit_update_draining` before it enrolls, acquires or
1420
- delegates anything. A command already served keeps the resident's lease until
1421
- it ends, while the resident refuses its acquisition of new work and its
1422
- Telegram updates with `kit_update_draining`, which ends a served
1423
- `kit telegram serve` once each of its activities reports the deliveries and
1424
- files it already acquired; until it ends, the drain answers that it waits for
1425
- that command and the bootstrap leaves the container as it is. The bootstrap
1426
- removes the file when it replaces the container.
1427
-
1428
- Interrupting the command cancels the running connector without advancing the
1429
- checkpoint.
1430
-
1431
- ## Repository capture
1432
-
1433
- Publish one local Git checkout as a Repository Source without giving House any
1434
- Git credential:
1435
-
1436
- ```sh
1437
- kit repository capture /srv/acme \
1438
- --room work-room \
1439
- --source-handle acme-repository
1440
- ```
1441
-
1442
- The command runs ordinary `git pull` in the checkout with the local User's Git
1443
- configuration, credential helper, and SSH agent. After pull succeeds, Kit asks
1444
- Git for tracked files plus untracked non-ignored files, hashes each selected
1445
- file, and opens a Publication by posting that manifest - every selected path
1446
- with its content fingerprint and byte length - to the authenticated Kit door.
1447
- Git remains the only ignore owner.
1448
-
1449
- House answers the Publication reference, the paths it needs, and the chunk
1450
- bound it accepts. Kit delivers only those paths, in House's order, in chunks
1451
- under that bound, streaming each file from disk at delivery time; it carries no
1452
- bound, no delta, no counter and no retained archive of its own. House owns
1453
- every tombstone, orders the Publication, and completes it in the request that
1454
- leaves no needed path outstanding.
1455
-
1456
- A file whose bytes disagree with the manifest House holds is refused as that
1457
- item and the rest of the chunk stands. When a delivery round ends with refused
1458
- items and the Publication is still active, Kit opens a new manifest of the
1459
- current checkout and delivers again, at most three manifests per run. An
1460
- interrupted run is never resumed: the next run opens a new manifest and House
1461
- answers with what is still needed. The command reports the Publication
1462
- reference, the paths delivered, the items refused and the ending.
1463
-
1464
- ## Working copies
1465
-
1466
- The Environment owner chooses in House which Rooms this Environment keeps
1467
- locally, and whether it keeps `/private`. An Environment whose owner never chose
1468
- copies nothing, and a Room joined later stays uncopied until the owner selects
1469
- it. The resident Kit reads the saved choice through
1470
- `POST /kit/working-copy/selection` whenever its control stream connects or
1471
- reconnects and whenever House points it there after a save;
1472
- `kit worker run` reads it once before its pass.
1473
-
1474
- Each selected Room becomes one ordinary Git repository at
1475
- `/agents/house/working-copies/rooms/<room_ref>`, and `/private` one at
1476
- `/agents/house/working-copies/private`. The Room reference names the location,
1477
- so a Room handle change moves nothing. Every Agent launched afterwards receives
1478
- the House path and local repository of each active copy in its context, one
1479
- line each, such as `/rooms/work: /agents/house/working-copies/rooms/r_1x.q`.
1480
-
1481
- A Room repository holds the Room's complete readable content: `ROOM.md`,
1482
- `library/`, `capture/` with its provenance sidecars, and `collaboration/`. The
1483
- `/private` repository holds its Library, collaboration, each Room's private
1484
- `AGENTS.md`, the `agents/` cards and `house/`. Attached settings and attachment
1485
- bytes are not copied. The branch `main` starts at the commit House served, which
1486
- tracks exactly the files House declares writable under the Environment's
1487
- current grant. Every other file is read-only House content: the generated
1488
- `.gitignore` names and explains it, so Git never tracks it, and the matching
1489
- `.ignore` keeps it visible to ripgrep and other native search. Neither file
1490
- grants anything, and House refuses a change to a read-only path however it is
1491
- committed.
1492
-
1493
- Local edits, staging and commits change nothing in House until a commit is
1494
- pushed. `refs/house/received` records the House content last received; each
1495
- commit Kit wrote there, and each commit it acknowledged for a push, is a House
1496
- state. The House revision of every file content Kit received, keyed by path,
1497
- Git blob and the House state that received it, stays in Kit home under
1498
- `state/working-copies/`, outside the repository. A local commit is a local tree, never a House revision.
1499
-
1500
- The resident Kit keeps every active copy current. An `entries` frame on its
1501
- control stream names a copied Room, the Room's position and log epoch, and the
1502
- Change entries that brought it there: each changed path with its revision and
1503
- text, or its removal. The resident applies the entries under the copy's own
1504
- homes directly, without reading House, when the frame follows on from the copy:
1505
- the same socket already carried an earlier frame for that Room, the copy's
1506
- recorded position has reached that earlier frame's position, the log epoch is
1507
- the copy's, and every changed path carries its revision and text. A frame the
1508
- copy already holds changes nothing. Any other frame, and every reading of the
1509
- saved choice, reads House instead, with the frames that arrive during one
1510
- reading served by the next: one `POST /kit/door/discover` tells it where each
1511
- copied Room stands, and for a Room that moved on it reads the changes after the
1512
- copy's recorded position through `POST /kit/door/enumerate`. A changed, new or
1513
- removed authored file becomes a commit on `refs/house/received`, its House
1514
- revision kept in Kit home; changed, new or removed read-only content, such as
1515
- Capture with its provenance sidecar or a House projection, is written into or
1516
- removed from the ignored local tree, with each directory a removal leaves empty,
1517
- and `.gitignore` and `.ignore` follow it.
1518
- Delivery never touches the branch, the index or a tracked file, and a push
1519
- waiting for its outcome or its local integration keeps its record. A push takes
1520
- each file's revision from the newest House state in its own base's history that
1521
- received that content, so a local commit keeps the base it was made on even when
1522
- House returns to the same text under a newer revision. A file new to the copy is tracked when a write scope
1523
- House declares for the Room covers it more closely than a declared protected
1524
- path, and a local file already sitting where new read-only content arrives
1525
- stays yours: Kit neither overwrites nor later removes it. When the write scopes
1526
- or protected paths House declares for the Room change, Kit reads the Room again
1527
- as a reselection does. A position older than House
1528
- retains, or from before House's database was restored, makes Kit read the Room
1529
- again as a reselection does; while House's replica has not reached the position
1530
- the copy waits, and the resident delivers again 30 seconds later. A Room House
1531
- no longer answers under its
1532
- recorded handle makes Kit read the saved choice again, which follows a handle
1533
- change or marks the copy inactive, and a Room that lost sync is named on
1534
- standard error as `operation_denied` and keeps what it holds. `kit worker run`
1535
- delivers once, after reading the saved choice.
1536
-
1537
- `house git push [commit]`, run anywhere inside a copy or one of its linked
1538
- worktrees, submits one commit, `HEAD` by default, resolved once. Kit compares
1539
- that commit's tree with the newest House state in its history, so every linked
1540
- worktree keeps its own base, and sends the difference as one ordinary House edit:
1541
- a changed field, section or preamble against its own prior value, any other
1542
- change as the whole file against its exact revision, a new file as a create and
1543
- a committed deletion as a removal. Staged and unstaged work, and files absent
1544
- only from the working tree, are never sent. Native `git push` reaches no House
1545
- remote. Inside an Agent Session the push goes through that Session's bridge,
1546
- under its own authority and provenance; outside one it needs
1547
- `house git push [commit] --owner`, which the resident Kit carries out under its
1548
- control lease when a resident runs, and which otherwise acquires and releases
1549
- its own. A Session refuses `--owner`, and a missing bridge is never replaced by
1550
- owner authority.
1551
-
1552
- A Session's edit too large for one MCP request of 4 MiB goes through House's
1553
- `prepare_edit_upload` Command on that Session's bridge, naming every changed
1554
- path, the byte count and the SHA-256 of the bytes to upload under the push's
1555
- operation. Those bytes are the serialized changes, padded with trailing spaces
1556
- to one byte over 4 MiB when only the request around them passed the bound,
1557
- because House takes no smaller upload. Kit sends them to the upload address
1558
- House returns and then calls `edit` with the paths and the returned upload
1559
- instead of the changes, so House applies them as the same one edit. The upload
1560
- is recorded in Kit home before the edit is sent, so a push whose answer was lost
1561
- resends that same edit; when House no longer holds the uploaded bytes and has
1562
- not applied the edit, the push uploads them again once under the same operation.
1563
- A change set beyond House's batch bounds is refused whole and never split.
1564
-
1565
- Inside an Agent Session `git` is Kit's shim in front of the installed Git, and
1566
- every command runs exactly as that Git runs it. In a copy or one of its linked
1567
- worktrees the shim compares that worktree's index before and after each
1568
- command. When `git mv` succeeds, Git's own dry run of the same command names
1569
- which path went where, and the shim hands each move to the resident Kit, which
1570
- keeps every moved document's successive paths in Kit home. A staged move
1571
- belongs to its worktree's index: a command there that takes its path out of the
1572
- index or brings it in anew, such as `git reset`, `git restore --staged`,
1573
- `git rm`, `git stash` or `git switch`, takes it back, and so does removing the
1574
- worktree. A `git commit` whose commit carries the move ties it to that commit.
1575
- A tied move counts for every pushed commit whose history holds one of its
1576
- commits, whatever the index does meanwhile. `git commit --amend` and
1577
- `git rebase`, Kit's own rebase onto House's result included, also tie it to
1578
- their result when the rewritten branch still carries it, so aborting the rebase
1579
- or resetting back to the original commit keeps it as well. A `git reset` that
1580
- leaves every commit of the move behind without touching its latest path in the
1581
- index returns it to that worktree's index, where the next commit ties it again.
1582
- Any other rewrite, such as a cherry-pick, leaves the move tied to its earlier
1583
- commits. A push sends House's rename of the same document from the path its
1584
- House base holds to the latest of its counted paths the pushed commit holds,
1585
- when the commit no longer has the first and the base did not have the second;
1586
- a moved file whose content also changed follows as a
1587
- whole-file replacement at its destination. House keeps the document's identity
1588
- and rewrites the links and references that name it; Kit reads those rewrites
1589
- through `POST /kit/door/enumerate` and records them in the acknowledgment.
1590
- Anything else records no move and the push sends an ordinary removal and
1591
- creation, which House refuses while another document still refers to the
1592
- removed one: a failed `git mv`, one undone or never committed through the
1593
- shim, and a move made with Git outside the shim such as in the owner's own
1594
- shell. An accepted push drops the moves its commit's history holds.
1595
-
1596
- House decides before any local Git step. A refusal changes nothing in House;
1597
- after `edit_conflict` Kit records the current content House names for the
1598
- refused paths in `refs/house/received`, reading by revision any content the
1599
- refusal does not carry, so the agent merges or rebases onto it with native Git,
1600
- commits, and pushes again. After acceptance Kit records House's result for the
1601
- pushed paths, the contents an owner push receives or each accepted revision
1602
- read by name, in a commit that covers the pushed one and in
1603
- `refs/house/received`; a read-only path House regenerates, such as the Wiki
1604
- index, stays out of Git. The copy's recorded position goes back to where it
1605
- stood when the push was sent, so the next delivery brings again anything House
1606
- changed after the push's result, including what delivery received while the
1607
- push awaited its answer. Kit then
1608
- fast-forwards a clean branch still at the pushed commit, or rebases only its
1609
- later commits. A result not yet readable, uncommitted tracked changes,
1610
- a moved branch or a rebase conflict leave the branch as it was, with no stash or
1611
- reset, and the push answers that House accepted it while local integration is
1612
- pending; the next `house git push` finishes that integration without another
1613
- House edit. A push whose answer was lost is recorded in Kit home before it is
1614
- sent, and the next push by the same Session, or by the owner for an owner push,
1615
- resends it under its original operation. Another caller cannot
1616
- recover it: while the pushing Session lives it is refused as
1617
- `kit_working_copy_push_pending`, and once that Session has ended Kit reads House
1618
- again and drops it. When a retained push's operation has expired, Kit reads the
1619
- Room again: if House holds the pushed content the push finishes as accepted,
1620
- otherwise it is dropped and the next push submits it as a new operation.
1621
-
1622
- Deselecting a Room, or losing read access to it, marks its copy inactive:
1623
- launches omit it, delivery stops, a new push is refused as
1624
- `kit_working_copy_inactive`, and nothing local is deleted; a push already sent still settles and integrates. Selecting it again reads the Room
1625
- again, advances `refs/house/received`, and replaces only the read-only content
1626
- Kit wrote, never the branch, the index or a tracked file. A Room the owner may
1627
- read without sync is refused as `operation_denied` and stays uncopied, while its
1628
- ordinary remote reads are unaffected. Kit names each refused copy on its
1629
- standard error.
1630
-
1631
- ## Explicit synchronization
1632
-
1633
- Beside [working copies](#working-copies), Kit has one explicit synchronization mode.
1634
- It selects prefixes over the User root, materializes them into a named local
1635
- directory, enumerates whole content and removals after the recorded positions,
1636
- and submits a resolved exact-base change set the caller prepared. Kit does no
1637
- local reconciliation. The mode shares no Working Copy manifest, never infers a
1638
- removal from a locally absent file, and writes authored mutations only within
1639
- House's declared write scopes: a selected Room's Library and, where granted,
1640
- `ROOM.md`. `/private` selects the Private Room by its Room reference, records an independent Room
1641
- position, and keeps its `agents/` cards and Room-specific private `AGENTS.md`
1642
- beside `library/**`.
1643
-
1644
- ```sh
1645
- kit sync bootstrap ./replica \
1646
- --prefix /rooms/work/library \
1647
- --prefix /rooms/work/capture \
1648
- --prefix /private/library
1649
- kit sync pull ./replica
1650
- kit sync submit ./replica --changes ./changes.json
1651
- kit sync rebootstrap ./replica
1652
- ```
1653
-
1654
- `bootstrap` writes `ROOM.md`, Library text, House-derived projections, and
1655
- Capture text, with the provenance House serves on a file's manifest entry as
1656
- its `_provenance` sidecar, and records one position per selected authority from
1657
- the bundle, with the log epoch House issued it in, together with
1658
- the positive operation scopes House declared for it: `read`, `sync`, `write`,
1659
- and `delete`, each a list of paths relative to the authority under which that
1660
- operation is admitted. Managed replication needs `read` plus `sync` on a Room;
1661
- a Room the User can only read answers `bootstrap`, `pull`, and `rebootstrap`
1662
- with `operation_denied`. Generated `_provenance` files delivered beside authored
1663
- Library paths are House metadata, not authored Library documents. `pull` presents
1664
- each recorded position with its log epoch, applies each enumerated path's whole
1665
- content or removal, and advances those positions. A removal, pulled or accepted
1666
- from `submit`, also removes the file's `_provenance` sidecar and each directory
1667
- it leaves empty up to the authority, so a later file can take that path; a
1668
- directory still holding a file, such as a local draft, stays. A position older
1669
- than House's declared retention, or one of a log epoch before House's database
1670
- was restored, is reported as `position_expired`; only `rebootstrap` rebuilds the directory.
1671
-
1672
- `submit` sends field, section, and preamble operations with that value's prior
1673
- content as base, and whole-file, rename, and removal operations with the file
1674
- revision. A rename of a populated directory instead carries a `base` map from
1675
- every relative member path to its exact revision, such as
1676
- `{"plan.md": "<revision>", "deep/log.md": "<revision>"}`, and House refuses it
1677
- unless the map names exactly the directory's current members at their current
1678
- revisions. Kit admits the rename at its source and destination and each named
1679
- member at both, as House does.
1680
- House alone validates the resulting Library: a batch it refuses answers with
1681
- House's own file path, field, rule, and version. A refusal is reported with the
1682
- current material House returned and the local directory is left for the caller.
1683
- An acceptance updates the recorded position, its log epoch, and the affected
1684
- files from the returned final content. It also reads House's own provenance
1685
- for those files by enumerating from the position the batch started at, and
1686
- writes each served sidecar whose revision is the accepted one, so a submit,
1687
- answered or recovered after a lost answer, leaves each file's `_provenance`
1688
- sidecar at the revision House recorded. Kit never composes that metadata
1689
- itself.
1690
-
1691
- Pending submissions are written before transfer. A lost response, reconnect, or
1692
- lease renewal recovers that exact request under the same identifier. A domain
1693
- refusal is reported without automatic retry and without minting a replacement
1694
- identifier. A second deliberate submit is a new identifier. After a restore of
1695
- House's database, a request House holds no receipt for and whose identifier
1696
- dates earlier than five minutes after the new log epoch began is answered
1697
- `position_expired`: it wrote nothing and never will under that identifier, so
1698
- the Kit drops it like any other refusal, `rebootstrap` rebuilds the directory,
1699
- and a change still wanted is a new deliberate submit against the rebuilt bases.
1700
- Once House has answered any request for a replica `position_expired`, the
1701
- replica refuses `submit` as `position_expired` until `rebootstrap` rebuilds it.
1702
- A pending submission House answers `operation_expired` is uncertain, so the
1703
- Kit first pulls confirmed state. If House already holds the pending change,
1704
- the Kit drops the pending copy and sends nothing more; only if it does not
1705
- does the Kit send that change again under a new identifier.
1706
- `rebootstrap` drops a submission still pending against the directory it
1707
- replaces.
1708
-
1709
- `--changes` names a JSON array of exact-base operations, or an object with a
1710
- `changes` array. Omitting it derives field, section, preamble, create, and whole-file
1711
- operations from local writable files against the last confirmed snapshot and
1712
- still never infers a removal. A local edit of a selected protected path is
1713
- refused before House is contacted as `protected_path`; a change outside the
1714
- authority's `write` scope, or a removal outside its `delete` scope, is refused
1715
- before House is contacted as `operation_denied`, the same code House answers
1716
- for a batch that reaches it.
1717
-
1718
- ## Attachments
1719
-
1720
- Upload one local file as an attachment to a Room or to your Private Room. Name
1721
- the place explicitly. Kit sends the descriptor and the selected bytes; it does
1722
- not scan a directory, convert a URL, or attach sibling files, credentials, or
1723
- absolute paths.
1724
-
1725
- ```sh
1726
- kit attachment upload ./notes.bin \
1727
- --room room:work \
1728
- --media-type application/pdf
1729
-
1730
- kit attachment upload ./notes.bin \
1731
- --private
1732
-
1733
- kit attachment get \
1734
- --attachment at_1x.q \
1735
- --room room:work \
1736
- --output ./notes.bin
1737
- ```
1738
-
1739
- `--room` and `--private` are mutually exclusive and one is required; `--name`
1740
- defaults to the file's base name. The upload prints the attachment reference
1741
- (`at_…`) House answered. That reference is what text links to: an attachment is
1742
- meaningful only because a document links it, and House records those links from
1743
- the accepted text. Retrying the same file after a lost response or failed upload
1744
- reuses the stored version under the identical descriptor, so it completes the
1745
- same attachment instead of creating a second one; uploading the same file to the
1746
- same place again answers that same attachment. A changed file is a new
1747
- attachment. HTTP 200 is not retention: only `save.status: saved` plus an
1748
- independent download whose length and SHA-256 match counts. A failed local read
1749
- or failed House save is not reported as saved. Transfer uses the current control
1750
- lease and the one reusable byte client; the durable credential is presented only
1751
- to `POST /kit/lease`. Sync commands do not fetch attachment bytes; Source and
1752
- Worker retention save through the same byte client.
1753
-
1754
- The same paired installed-CLI proof covers these doors; see
1755
- [Paired proof](#paired-proof) for the command that runs it.
1756
-
1757
- ## Persistent container Environments
1758
-
1759
- An Always-on Linux host can run the persistent container Environments its owner
1760
- creates in House. House owns the target: which host, the workspace name, and the
1761
- CPU, memory and persistent-disk ceilings. This Kit owns the local lifecycle and
1762
- reports only what it proved.
1763
-
1764
- The supported configuration is one Linux host with
1765
-
1766
- - cgroup v2 delegating the `cpu` and `memory` controllers, read from
1767
- `/sys/fs/cgroup/cgroup.controllers`;
1768
- - a reachable Docker Engine daemon;
1769
- - `/dev/loop-control` present, so a workspace disk can carry an enforced size;
1770
- - `host.workspace` pointing at a directory this Kit may create workspaces under;
1771
- - `host.image` naming an image that carries Node, this Kit, `mkfs.ext4` and
1772
- `nsenter`, and whose own entrypoint starts `kit worker run` from
1773
- `HOUSE_KIT_HOME`.
1774
-
1775
- Any missing prerequisite refuses the create before House ever presents the
1776
- workspace as constrained, naming the exact missing piece.
1777
-
1778
- ```sh
1779
- kit workspace host
1780
- ```
1781
-
1782
- The command acquires this host's workspace work under its Environment control
1783
- lease and settles each operation with what it observed:
1784
-
1785
- - **create** formats one ext4 image of exactly the configured disk ceiling,
1786
- mounts it as the workspace's only writable filesystem, writes that
1787
- Environment's own Kit credential and a `kit.json` declaring the selected agent
1788
- CLIs with `execution_authority` `allow` inside it, since nobody holds a
1789
- terminal there to answer otherwise, and creates the container with
1790
- `--cpus`, `--memory` and `--memory-swap` at the configured ceilings. The
1791
- workspace is reported `stopped` with the runtime and the enforcement it
1792
- proved. This host's own credential never enters the workspace: the only
1793
- credential written there belongs to the Environment running inside it.
1794
- - **start** mounts the workspace disk if the host rebooted, refuses when
1795
- starting would leave less than the host reserve, starts the container, and
1796
- reports `ready` only after the exact workspace proved it accepts execution.
1797
- - **stop** stops the container and keeps the disk mounted, so ordinary files,
1798
- installed tools and login state survive to the next start.
1799
- - **delete** removes that exact container, unmounts that exact workspace disk and
1800
- removes that exact directory. No other workspace, and nothing else on this
1801
- host, is touched.
1802
-
1803
- Every operation names one workspace this Kit created. A workspace the runtime no
1804
- longer has is reported `absent` rather than recreated, so House ends the
1805
- Environment visibly instead of inventing a process observation. On every pass
1806
- this Kit reconciles its own labelled containers against the exact inventory
1807
- House authorizes: a managed workspace House no longer authorizes to run is
1808
- stopped, and a container this Kit did not create is never adopted, started or
1809
- removed.
1810
-
1811
- A pass House does not answer, such as one during a House restart, ends that
1812
- pass only, and the host tries again on the next one; a pass House refuses ends
1813
- the host. Without `--passes` the host serves until SIGINT or SIGTERM. It
1814
- asks House to release its control lease when it ends, after its passes, a
1815
- refusal, SIGINT or SIGTERM; once House has released it, a restarted host is
1816
- admitted at once.
1817
-
1818
- Ceilings are shared ceilings. The ceilings configured across one host may exceed
1819
- that host, nothing is reserved for any workspace, and no workspace is promised
1820
- its maximum while another one runs.
1821
-
1822
- ## The Telegram gateway
1823
-
1824
- House can project one owner's conversations through that owner's own Telegram
1825
- bot. In Kit mode the BotFather token stays in one installed Environment, and
1826
- this Kit is the only thing that calls the Bot API. House never receives the
1827
- token: it appears in no request body, no refusal and no printed summary.
1828
-
1829
- Exactly one Environment may be the gateway for one bot. House records that
1830
- binding when the gateway registers, and refuses a second Environment claiming
1831
- the same bot. Worker Environments stay unchanged: they still acquire Runs and
1832
- materialize inbound conversation files.
1833
- The gateway never runs a Worker's provider and a Worker never holds the token;
1834
- the two exchange bytes only through House, which relays an inbound attachment
1835
- from the gateway to the Worker that needs it and serves the gateway an outbound
1836
- image from the original the Session stored.
1837
-
1838
- Bind the bot to this Environment:
1839
-
1840
- ```sh
1841
- kit telegram connect
1842
- ```
1843
-
1844
- The command asks for the token on a hidden terminal prompt, or reads one line
1845
- from standard input when it is not attached to a terminal. It asks Telegram `getMe`, and
1846
- refuses a bot whose Threaded Mode is off, because House projects each
1847
- conversation into its own forum topic. It then asks House whether this
1848
- Environment may become the gateway, before it writes anything locally. Once
1849
- House admits the preflight it deletes any webhook, so long polling is the only
1850
- receiver, and proves the owner by reading one ordinary private message outside
1851
- any topic through `getUpdates`, advancing the stored offset past it. Only then
1852
- is the token written, and only then is the gateway registered with House. A
1853
- setup House refuses removes the written token again, so a refused connect leaves
1854
- no custody behind. The command prints the bot username, the bot user id and the
1855
- proven owner's Telegram user id.
1856
-
1857
- Run the gateway:
1858
-
1859
- ```sh
1860
- kit telegram serve
1861
- ```
1862
-
1863
- The command holds one Environment control lease for the whole run and performs
1864
- three activities inside it, each paced on its own so none of them blocks
1865
- another, beside the live drafts described below:
1866
-
1867
- - **ingress** reads updates from the stored offset, normalizes each one into the
1868
- update House accepts, submits it, and advances the stored offset only after
1869
- House has taken it. A submission that fails leaves the offset where it was, so
1870
- the same update is offered again rather than lost.
1871
- - **inbound files** acquires the attachments House still needs, refuses anything
1872
- reported above the 20 MiB download ceiling before calling `getFile`, downloads
1873
- the file with the local token, and pushes the bytes to House for the Worker
1874
- Environment that will materialize them. That push waits for the Worker to pull
1875
- and a single failed fetch never ends the pass.
1876
- - **outbound queue** acquires the per-bot queue in its exact order, marks each
1877
- delivery attempted before it sends, and reports what Telegram answered:
1878
- `delivered` with the exact message or topic Telegram returned, `failed` when
1879
- Telegram refuses outright, and `delivery_unknown` when
1880
- the answer is absent or unreadable. File items are pulled back from House,
1881
- refused as `file_too_large` above the 10 MiB image ceiling, uploaded as
1882
- `sendPhoto` with the caption House composed around the link to the retained
1883
- original, and reported the same way. A caption past Telegram's
1884
- 1,024-character caption limit keeps its head under the image, cut at its last
1885
- line break that fits or else at the limit, and the rest follows in the topic
1886
- as ordinary messages; the upload is reported with the image's answer when
1887
- every part was delivered and otherwise with the answer of the first part that
1888
- was not. A pull that fails reports
1889
- `transfer_failed` and an ambiguous upload answer reports `delivery_unknown`.
1890
- Only an image ever becomes a file item, because House offers every other file
1891
- as a `conversation_file_link` message this queue delivers like any other. The
1892
- queue carries no draft: an answer holding a draft item is refused as
1893
- `kit_telegram_response_invalid`. House caps no message, so the gateway alone
1894
- fits one to Telegram's rich message limits, 32,768 characters and 500 blocks:
1895
- a rich message past them is sent as consecutive messages in order, split
1896
- between blocks and inside a block only when that block alone exceeds them,
1897
- and reported once, `delivered` with the first message's id when every part
1898
- was delivered and otherwise with the answer of the first part that was not.
1899
- A new plain-text message past Telegram's 4,096-character message limit, such
1900
- as a `conversation_file_link` text, is sent and reported the same way, cut
1901
- at the last line break that fits or else at the limit, so the first message
1902
- keeps its reply and keyboard and a link on its own line stays whole.
1903
- Each answer also names the `threads` the bot's topics carry, a
1904
- `conversation_id` with its `chat_id` and `topic_id`, which is where live
1905
- drafts go.
1906
-
1907
- Live drafts come from the control stream, not from House's queue. Run on its
1908
- own, the command holds the Environment's [control stream](#the-kit-control-stream)
1909
- for its lease; served by `kit resident`, it receives the draft and plan frames
1910
- the resident's socket receives, through the resident, and never opens a second
1911
- socket. The gateway Environment's socket carries every conversation bound to a
1912
- topic of the bot, with no subscribe frame. The gateway keeps the latest view of
1913
- each such conversation's turn: a frame that starts at block 0 replaces a view,
1914
- the next frame in sequence applies from its `from`, and a frame after a gap in
1915
- the sequence, or one for a turn it holds no view of, is applied nowhere and
1916
- answered with one `gap` frame naming the conversation and turn, so House sends
1917
- the whole view again. It renders each view as one Telegram draft per turn with
1918
- `sendRichMessageDraft` on the bound topic, the live plan above the assistant
1919
- text, under one draft id derived from the turn, because Telegram shows one bot
1920
- draft per topic and a second id would replace the first. A view past the rich
1921
- message limits is shortened inside that one draft, never split into a second.
1922
- A live plan is part of the draft and never a sent message; the frozen plan
1923
- House queues at the turn's end is the turn's one plan message. Drafts coalesce
1924
- to the latest view, one Bot API call at a time, and are never reported back:
1925
- the same bot-wide `retry_after` gate drops a draft while it is closed, and a
1926
- draft answered with `retry_after` closes it. A closed gate drops a preview,
1927
- never a durable delivery.
1928
-
1929
- Each serve run first asks House to settle the attempts an earlier run abandoned,
1930
- so a restarted gateway never leaves a delivery attempted forever. A Telegram
1931
- `retry_after` opens one bot-wide gate: nothing is sent, fetched or polled until
1932
- the delay Telegram supplied has passed. `--passes <count>` bounds each activity
1933
- to that many passes and is what tests and one-shot runs use; without it the
1934
- command is resident and stops on SIGINT or SIGTERM, printing what it settled.
1935
- Each refused pass is printed as it happens, naming its activity, item and
1936
- refusal code, so a gateway refused on every pass says so while it runs; the
1937
- summary printed when it stops still lists each of them.
1938
-
1939
- Release the bot:
1940
-
1941
- ```sh
1942
- kit telegram disconnect
1943
- ```
1944
-
1945
- The command writes a local disconnect marker first, deletes the webhook, removes
1946
- the token and the stored offset, tells House this Environment is no longer the
1947
- gateway, and removes the marker last. Local custody is therefore always gone
1948
- before House admits the change. A disconnect interrupted after the marker was
1949
- written finishes on a later run even though the token is already gone, and a
1950
- `kit telegram serve` that finds the marker refuses to act.
1951
-
1952
- ## Local state
1953
-
1954
- ```
1955
- $HOUSE_KIT_HOME/
1956
- credential
1957
- kit.json
1958
- conversation-inbound/<run>/<attachment>/<name>
1959
- secrets/personal-telegram-bot.token
1960
- state/telegram/get-updates-offset.json
1961
- state/telegram/disconnecting.json
1962
- state/hooks.json
1963
- state/hooks/<hook id>.txt
1964
- state/repositories/<repository identity>.json
1965
- state/sync/<replica identity>.json
1966
- state/sync/<replica identity>.pending.json
1967
- state/working-copies/rooms/<room ref>.json
1968
- state/working-copies/private.json
1969
- state/attachments/<upload identity>.json
1970
- state/conversation-originals/<attachment identity>.json
1971
- state/sessions/<tmux session>.json
1972
- state/update-drain
1973
- state/skill-set.json
1974
- state/how-we-work
1975
- state/continuations/<run identity>.json
1976
- state/google/entries/<source>.json
1977
- state/google/credentials/<account>.json
1978
- state/google/checkpoints/<source>.json
1979
- state/google/health/<source>.json
1980
- state/google/gaps/<source>.json
1981
- state/google/dropped/<source>.json
1982
- state/google/originals/<source>.json
1983
- state/<instance>/checkpoints/<service>.json
1984
- state/<instance>/health/<service>.json
1985
- state/<instance>/gaps/<service>.json
1986
- state/<instance>/dropped/<service>.json
1987
- state/<instance>/originals/<service>.json
1988
- ```
1989
-
1990
- Every file is written atomically and readable only by its owner. A file the Kit
1991
- cannot read is refused rather than treated as a service that never captured.
1992
- A Google entry's state is keyed by its Source reference and lives only while
1993
- House lists the entry. Disabling a custom service or instance leaves its state
1994
- in place, so re-enabling resumes from the stored cursor instead of importing
1995
- again.
1996
-
1997
- ```sh
1998
- kit source reset acme # every service
1999
- kit source reset acme notes # one service
2000
- ```
2001
-
2002
- Reset serves custom connectors, and is the only deletion of their captured
2003
- state. It removes the named checkpoints
2004
- and their outstanding retained references so the next capture restarts exactly at
2005
- the configured backfill boundary. It takes the same
2006
- Environment control lease a capture takes, so it cannot delete a checkpoint a
2007
- running capture is about to write back.
2008
-
2009
- ## Host capacity
2010
-
2011
- An owned Linux host can report what it actually has, so its owner reads real
2012
- capacity instead of guessing from Run counts. Declare the host section:
2013
-
2014
- ```json
2015
- "host": {
2016
- "workspace": "/var/lib/house-kit/workspaces",
2017
- "maxima": {
2018
- "cpu_millicores": 2000,
2019
- "memory_bytes": 4294967296,
2020
- "disk_bytes": 53687091200
2021
- },
2022
- "image": null
2023
- }
2024
- ```
2025
-
2026
- `image` is the workspace image this host runs persistent container Environments
2027
- from, and is only read by `kit workspace host` below; leave it `null` on a host
2028
- that reports capacity and hosts no container. `workspace` is the absolute path of the persistent workspace directory; the
2029
- report measures the filesystem holding it, not the root filesystem by
2030
- assumption. `maxima` are the ceilings this host is configured to apply to
2031
- Kit-managed workloads, and House keeps them apart from what it measured. Declare
2032
- `"maxima": null` when this host configures no ceiling; an empty object is
2033
- refused rather than read as no limit. A ceiling is a shared ceiling, never a
2034
- reservation, and House infers no supported Agent count from any of these values.
2035
-
2036
- ```sh
2037
- kit host report
2038
- ```
2039
-
2040
- The command reads `/proc/stat` twice a second apart, reads `/proc/meminfo`, and
2041
- asks the kernel for the workspace filesystem's block counts. It sends the host
2042
- totals, what is in use, and what is available for each of processors, memory and
2043
- that filesystem, with the instant it measured them. Nothing else is collected:
2044
- no process list, no command line, no environment value, and no credential. A
2045
- native source this Kit cannot read or parse refuses as
2046
- `kit_host_unmeasurable` and sends nothing, because a host that cannot be
2047
- measured is unknown to its owner rather than empty.
2048
-
2049
- The report is one observation, not a stream. Run it on a timer the host owner
2050
- owns:
2051
-
2052
- ```
2053
- [Unit]
2054
- Description=Report measured host capacity to House
2055
-
2056
- [Service]
2057
- Type=oneshot
2058
- User=house-kit
2059
- ExecStart=/usr/bin/kit host report
2060
- ```
2061
-
2062
- ```
2063
- [Timer]
2064
- OnBootSec=1min
2065
- OnUnitActiveSec=5min
2066
- ```
2067
-
2068
- House reads an observation as fresh only while three instants - the one this
2069
- Kit says it measured, the one House received the report, and now - all fall
2070
- inside one fifteen-minute window. A host that stops reporting reads as aged from
2071
- then on, a report that was already stale when it arrived reads as aged
2072
- immediately, and a host that never reported reads as unknown; none of them ever
2073
- reads as a measured zero. Keep this host's clock in step with real time - a
2074
- clock more than fifteen minutes out of step in either direction makes every
2075
- report it sends read as aged, which is the honest answer House can give about a
2076
- reading it cannot date, and dating a reading into the future buys no freshness
2077
- because it widens the same window. A smaller skew is not free either: it is
2078
- spent from the same fifteen minutes, so a clock a few minutes out of step leaves
2079
- the timer above correspondingly less room to miss a run before House reads this
2080
- host as aged.
2081
-
2082
- ## Migrating authored text and linked originals
2083
-
2084
- `kit migrate` runs under the Environment User's control lease. Its module
2085
- category is `migrator`, declared in the required `migrators` array in `kit.json`
2086
- (use `"migrators": []` when none are installed):
2087
-
2088
- ```json
2089
- {
2090
- "id": "my-import",
2091
- "category": "migrator",
2092
- "command": ["/home/ada/tools/my-import"],
2093
- "timeout_ms": 600000,
2094
- "environment": { "EXPORT_REGION": "eu" },
2095
- "secrets": { "EXPORT_TOKEN": { "file": "/run/secrets/export-token" } }
2096
- }
2097
- ```
2098
-
2099
- The example is a user-installed module. Kit ships no concrete external-system
2100
- migrator. Secret references resolve only in the Environment and are passed only
2101
- to that module's process environment. The module receives neither House's
2102
- credential nor its lease. Module output and stderr are not published or echoed;
2103
- a module must never write an external credential into its emitted content,
2104
- manifest, URLs, or attachments.
2105
-
2106
- ```sh
2107
- kit migrate emit my-import --package /home/ada/exports/project
2108
- kit migrate mount /home/ada/exports/project --subtree /rooms/work/library/project
2109
- # Or emit and mount in one maintained mount session:
2110
- kit migrate run my-import --package /home/ada/exports/another-project \
2111
- --subtree /private/library/another-project
2112
- ```
2113
-
2114
- Emission refuses an existing output directory. The module reads one JSON line
2115
- on stdin: `{"contract":"kit-migrator/1","migrator":"my-import","package":"/absolute/output"}`.
2116
- It must finish writing this tree before exiting successfully:
2117
-
2118
- ```text
2119
- project/
2120
- migrator.json
2121
- model/note.md # optional independently prepared type definition
2122
- content/page.md # UTF-8 authored Markdown
2123
- attachments/image.png # exact downloaded original bytes
2124
- attachments/notes.txt
2125
- ```
2126
-
2127
- The manifest is closed and versioned:
2128
-
2129
- ```json
2130
- {
2131
- "contract": "kit-migrator/1",
2132
- "category": "migrator",
2133
- "migrator": "my-import",
2134
- "attachments": [
2135
- {
2136
- "path": "image.png",
2137
- "source": "https://export.example/image.png",
2138
- "media_type": "image/png",
2139
- "download": "downloaded",
2140
- "referrers": ["page.md"]
2141
- },
2142
- {
2143
- "source": "https://export.example/unavailable",
2144
- "download": "unavailable",
2145
- "failure": "not_found",
2146
- "referrers": ["page.md"]
2147
- }
2148
- ]
2149
- }
2150
- ```
2151
-
2152
- `path` is relative to `attachments/`; `referrers` are relative to `content/`.
2153
- `name` optionally supplies the attachment's file name. Downloaded entries require a
2154
- local path and forbid `failure`. Unavailable or skipped entries require
2155
- `failure` and forbid a local path. Every downloaded attachment must be linked
2156
- from a declared referring document, for example
2157
- `![Image](../attachments/image.png)` in `content/page.md`. Unavailable ordinary
2158
- Markdown URLs remain visible; they do not satisfy required typed references.
2159
- Only Markdown enters the Library. Symlinks, invalid UTF-8, binary content, type
2160
- aliases under `content/types/`, and generated `_provenance` are refused.
2161
- `content/MIGRATION.md` is reserved for Kit's report.
2162
-
2163
- Before creating the text subtree, Kit checks the House-declared MD model version
2164
- against its exact `@agentshouse/mdmodel` dependency, which rewrites the
2165
- package's links. House alone validates the resulting Library: a mount it
2166
- refuses answers with House's own file path, field, rule and version, and no
2167
- content lands. Definitions in `model/` require their own write
2168
- grant at `/rooms/<handle>/settings/model/<type>.md` or
2169
- `/private/settings/model/<type>.md`; a content write grant cannot prepare them.
2170
- Kit adopts an identical existing definition and refuses a different one.
2171
- Preparation uses the existing authored batch producer before the empty-subtree
2172
- mount, so a missing model grant leaves no partial content tree. Independently
2173
- prepared model definitions survive a later content-mount refusal or unmount.
2174
-
2175
- The atomic text mount includes a report with pending transfer rows. Kit then
2176
- uploads each downloaded file once as an attachment to the mount's Room or
2177
- Private Room, independently retrieves and compares the saved bytes, and only
2178
- after that proof succeeds rewrites every link to it, in every referring
2179
- document, to its attachment reference, such as `at_1x.q`. The reference is an
2180
- identity House resolves, not an HTTP download URL. A link's query and fragment
2181
- are kept after the reference, as in `at_1x.q#page=3`. Failed or unverified
2182
- uploads remain source links in the authored text; their failure details appear
2183
- only in the report. Use `kit attachment get --attachment <at_ref> --room
2184
- <room-ref> --output <file>` (or `--private`) to retrieve the exact bytes. Binary
2185
- bytes never enter the text replica. Migrators create no Capture, Source
2186
- instance, or provenance.
2187
-
2188
- The mounted `MIGRATION.md` separates local downloads from saved, pending,
2189
- failed, and skipped attachment uploads, with source URLs, referring documents
2190
- and the attachment reference of each saved upload.
2191
- `MIGRATION.json` beside the emitted package preserves local pending and final
2192
- outcomes. A failed upload leaves usable mounted text and returns a nonzero
2193
- exit status. A failure to publish the final report also returns nonzero and
2194
- leaves the pending mounted report plus the local outcomes. Re-migration is
2195
- explicit unmount followed by mount.
2196
-
2197
- ```sh
2198
- kit migrate unmount /rooms/work/library/project
2199
- kit migrate unmount /rooms/work/library/project \
2200
- --confirm /rooms/work/library/project
2201
- ```
2202
-
2203
- The first command shows the subtree destruction summary and removes nothing.
2204
- Confirmation must repeat the exact subtree. Kit checks discovered write/delete
2205
- authority and protected paths for every selected target, then makes House's one
2206
- unmount call. House rechecks authority and subtree state at commit. Each
2207
- command requires the ordinary available Kit control lease; an already held lease
2208
- is refused by the existing Kit rail.
2209
-
2210
- ### Linux Compose validation
2211
-
2212
- The test service uses the host Docker daemon, loop device availability and a
2213
- shared `/tmp` mount because `npm test` includes Kit's real workspace-volume
2214
- proof. It runs with an init process, because the supervised-path proofs kill a
2215
- tmux pane whose adapter is then orphaned and has to be reaped rather than left
2216
- as a zombie. Set `KIT_TEST_UID`, `KIT_TEST_GID`, and `KIT_TEST_DOCKER_GID` for
2217
- the host when they differ from 1001, 1001, and 112. It starts only test-owned
2218
- workspaces.
2219
-
2220
- ```sh
2221
- docker compose -f test/compose.yaml build kit
2222
- docker compose -f test/compose.yaml run --rm kit npm test
2223
- docker compose -f test/compose.yaml run --rm kit npm run build
2224
- ```
2225
-
2226
- `test/pair/run.sh` boots real Core, Auth and PostgreSQL and runs the installed
2227
- package against them. `HOUSE_CORE_ROOT` names the Core checkout under proof and
2228
- `HOUSE_AUTH_ROOT` names an Auth checkout; the run exports the exact Auth revision
2229
- Core's own `scripts/auth-pair-source.mjs` pins into `tmp/auth-pinned` and pairs
2230
- that revision rather than whatever the checkout's working tree carries. Set
2231
- `HOUSE_AUTH_REVISION` to name another revision when Core's pin trails the Auth
2232
- contract that Core revision already requires. Both producer databases are
2233
- dropped and made again before either migrates, so a run never inherits the
2234
- schema another Core revision left behind. Every run prints the three revisions
2235
- it paired and writes them into each evidence document.
2236
-
2237
- The installed fixture in `test/fixtures/migrator.mjs` uses only local material
2238
- and is excluded from the published package. Its paired proof runs from Kit:
2239
-
2240
- ```sh
2241
- HOUSE_CORE_ROOT=<committed-core-snapshot> HOUSE_AUTH_ROOT=<committed-auth-snapshot> \
2242
- KIT_PAIR_SPECS=/kit/test/pair/migrator.vitest.ts bash test/pair/run.sh
2243
- ```
2244
-
2245
- The pair boots real Core, Auth, and PostgreSQL. It covers Room and private
2246
- mounts, separately authorized models, House's typed refusal, exact
2247
- attachment retrieval, failed storage, mounted reports, occupied mounts and the
2248
- confirmed unmount. As in the other installed-CLI pair tests, the fixture expires
2249
- the previous control lease before starting the next isolated command. Evidence
2250
- is written to `tmp/delivery-evidence/migrator-pair.json` with producer revisions
2251
- and the installed package digest.
2252
-
2253
- Explicit sync has its own paired proof:
2254
-
2255
- ```sh
2256
- HOUSE_CORE_ROOT=<core checkout> HOUSE_AUTH_ROOT=<auth checkout> \
2257
- KIT_PAIR_SPECS=/kit/test/pair/sync.vitest.ts bash test/pair/run.sh
2258
- ```
2259
-
2260
- It proves that an accepted `submit`, answered or recovered after a lost answer,
2261
- leaves each file's `_provenance` sidecar equal to the provenance House serves,
2262
- and that a pending submission House answers `operation_expired` is pulled first,
2263
- then sent again under a new identifier only if it never landed. Evidence is
2264
- written to `tmp/delivery-evidence/sync-pair.json`.
2265
-
2266
- The How-we-work reader has its own paired proof:
2267
-
2268
- ```sh
2269
- HOUSE_CORE_ROOT=<core checkout> HOUSE_AUTH_ROOT=<auth checkout> \
2270
- KIT_PAIR_SPECS=/kit/test/pair/how-we-work.vitest.ts bash test/pair/run.sh
2271
- ```
2272
-
2273
- It writes a document longer than two of House's cut shell replies and proves
2274
- the installed reader returns its whole text by running each printed
2275
- continuation. Evidence is written to `tmp/delivery-evidence/how-we-work-pair.json`.
2276
-
2277
- The retained-original consumers have their own paired proofs:
2278
-
2279
- ```sh
2280
- HOUSE_CORE_ROOT=<core checkout> HOUSE_AUTH_ROOT=<auth checkout> \
2281
- KIT_PAIR_SPECS="/kit/test/pair/source-originals.vitest.ts \
2282
- /kit/test/pair/conversation-originals.vitest.ts \
2283
- /kit/test/pair/conversation-house-storage.vitest.ts" bash test/pair/run.sh
2284
- ```
61
+ - [Linux](docs/linux.md)
62
+ - [macOS](docs/macos.md)
63
+ - [Windows](docs/windows.md)
64
+ - [Configuration](docs/configuration.md)
65
+ - [Google sources](docs/google.md)
66
+ - [Telegram bot](docs/telegram.md)
2285
67
 
2286
- `source-originals` drives a controlled local Source adapter through one
2287
- installed Kit: it proves that the connector receives its own authorization,
2288
- selection and cursor and never a House credential, that an unreachable House
2289
- leaves the checkpoint where it was and converges on reconnect, that two Room
2290
- owners hold independently owned retained copies of one delivery while one
2291
- owner's two Rooms share a Record, that a failed save is truthful and a later
2292
- byte retry publishes no second Capture and no substitute version, that an
2293
- unauthorized Room destination stays outstanding while acquisition health stays
2294
- its own, that an explicit backfill bound still holds in the installed package,
2295
- and that interrupting the Environment stops the receiver without advancing the
2296
- checkpoint or being woken again. After the Environment's authority is revoked
2297
- its next capture is refused.
68
+ ## License
2298
69
 
2299
- `conversation-originals` and `conversation-house-storage` drive the same
2300
- installed personal-bot gateway and Worker over House storage. Both prove an
2301
- incoming accepted attachment retained through the shared byte client
2302
- independently of transport, a failed save and its retry leaving every accepted
2303
- message identity unchanged, the Run settling and its temporary media cache being
2304
- disposed, the owner downloading the exact original through House afterwards, and
2305
- the Environment's next protected act being refused once its authority is gone.
2306
- A file a Session sends to its owner's conversation through `house
2307
- upload_attachment` is proven by Core's pair lane. Evidence is written
2308
- to `tmp/delivery-evidence/source-originals-pair.json`,
2309
- `conversation-originals-pair.json` and `conversation-house-storage-pair.json`.
70
+ [MIT](LICENSE)