@agentshouse/kit 0.1.0-alpha.26 → 0.1.0-alpha.28
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 +154 -145
- package/bin/kit.js +48 -46
- package/bootstrap.json +3 -3
- package/package.json +1 -1
- package/src/configuration.js +9 -209
- package/src/connector-login.js +3 -84
- package/src/contract.js +8 -3
- package/src/delivery.js +4 -7
- package/src/google/calendar.js +39 -45
- package/src/google/drive.js +61 -186
- package/src/google/entries.js +346 -0
- package/src/google/gateway.js +3 -6
- package/src/google/gmail.js +33 -23
- package/src/google/oauth.js +106 -66
- package/src/google/source.js +21 -23
- package/src/house.js +43 -16
- package/src/kit-work-stream.js +49 -27
- package/src/setup.js +8 -14
- package/src/source.js +51 -26
- package/src/state.js +19 -1
- package/src/tasks.js +75 -0
- package/src/worker.js +126 -115
- package/src/working-copy.js +19 -24
- package/src/google/declared.js +0 -146
- package/src/google/id.js +0 -1
- package/src/instance/google.js +0 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ The Skill set Kit installs is Matt Pocock's
|
|
|
12
12
|
On Linux `amd64` or `arm64`, run the published, versioned bootstrap once:
|
|
13
13
|
|
|
14
14
|
```sh
|
|
15
|
-
bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.
|
|
15
|
+
bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.28/connect-linux.sh)
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
The bootstrap first establishes Docker Engine from what it observes on the
|
|
@@ -51,8 +51,10 @@ the set again.
|
|
|
51
51
|
Running a newer release's bootstrap the same way is the only update. When the
|
|
52
52
|
installed container runs another image, the bootstrap pulls this release's
|
|
53
53
|
image and asks the running resident Kit to drain with `kit resident drain`. The
|
|
54
|
-
resident stops acquiring Runs, resumptions, setup
|
|
55
|
-
|
|
54
|
+
resident stops acquiring Runs, resumptions, setup, agent CLI work, Google
|
|
55
|
+
sign-ins and scheduled Google captures, while the Sessions it supervises keep
|
|
56
|
+
their renewal, owner stops, answers and reports and a capture already running
|
|
57
|
+
finishes.
|
|
56
58
|
While any Agent Session is still running or unreported, including one whose Run
|
|
57
59
|
was being acquired when the drain began, the bootstrap names it and leaves the
|
|
58
60
|
container as it is; rerun it once they end. Once nothing remains,
|
|
@@ -141,21 +143,17 @@ anything reaches House.
|
|
|
141
143
|
|
|
142
144
|
## Building the package
|
|
143
145
|
|
|
144
|
-
The repository checkout is the development source. Build the package
|
|
145
|
-
|
|
146
|
-
Google OAuth client, its id and its client secret:
|
|
146
|
+
The repository checkout is the development source. Build the package that can
|
|
147
|
+
be published:
|
|
147
148
|
|
|
148
149
|
```sh
|
|
149
|
-
HOUSE_KIT_GOOGLE_OAUTH_CLIENT_ID=kit-client.apps.googleusercontent.com \
|
|
150
|
-
HOUSE_KIT_GOOGLE_OAUTH_CLIENT_SECRET=GOCSPX-kit-installed-app \
|
|
151
150
|
npm run build
|
|
152
151
|
```
|
|
153
152
|
|
|
154
|
-
The command writes the publishable package to `dist/package`.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
from its runtime environment and never requests them from House.
|
|
153
|
+
The command writes the publishable package to `dist/package`. It reads no
|
|
154
|
+
environment and ships no provider OAuth client: each Google account signs in
|
|
155
|
+
with the User's own client, as [the Google preset](#the-google-preset)
|
|
156
|
+
describes.
|
|
159
157
|
|
|
160
158
|
Runtime dependencies stay minimal and exactly pinned in `package.json` and
|
|
161
159
|
`package-lock.json`: House's own `@agentshouse/mdmodel`, `yaml`, which reads
|
|
@@ -254,7 +252,8 @@ mode: `"intermittent"` for a laptop or other normally offline placement,
|
|
|
254
252
|
any other value refuses as `kit_configuration_invalid`; `kit apply` sends the
|
|
255
253
|
declared mode with the rest of the desired state. An
|
|
256
254
|
instance declares one or more services, and under the `custom` preset each
|
|
257
|
-
service owns the command that produces it.
|
|
255
|
+
service owns the command that produces it. The Kit reads only `custom` instances
|
|
256
|
+
and passes over any other entry of `instances`.
|
|
258
257
|
|
|
259
258
|
`host` declares the Linux host this installation measures for
|
|
260
259
|
[host capacity reporting](#host-capacity). `null` declares that this Kit reports
|
|
@@ -360,120 +359,81 @@ refuses as `kit_hook_rejected` instead of leaving whatever was installed before.
|
|
|
360
359
|
|
|
361
360
|
## The Google preset
|
|
362
361
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
362
|
+
House holds this Environment's Google Sources, and its owner sets them in the
|
|
363
|
+
App. Each entry names an account name the owner chooses, one service -
|
|
364
|
+
`gmail`, `calendar` or `drive` - the Rooms it goes to, and what its first
|
|
365
|
+
capture takes: `{ "new": true }` for new activity only, `{ "days": n }`,
|
|
366
|
+
`{ "items": n }` or `{ "all": true }`. Neither the configuration file nor any
|
|
367
|
+
command on this computer declares one.
|
|
368
|
+
|
|
369
|
+
Each service captures a fixed corpus under its own read-only scope: Gmail every
|
|
370
|
+
message but Spam and Trash, Calendar the account's primary calendar, and Drive
|
|
371
|
+
the files the account owns.
|
|
372
|
+
|
|
373
|
+
The resident reads the list from `/kit/sources/desired` each time its Kit
|
|
374
|
+
control stream connects and each time House signals `sources` on it, then
|
|
375
|
+
reconciles:
|
|
376
|
+
|
|
377
|
+
- a new entry gets its local state and is captured once its account is signed
|
|
378
|
+
in for its service;
|
|
379
|
+
- a changed entry's Rooms take effect on its next capture;
|
|
380
|
+
- an entry House no longer lists stops, and its checkpoint, health, gaps,
|
|
381
|
+
dropped items and unsaved originals are deleted;
|
|
382
|
+
- an account left with no entry has each of its grants revoked at Google's
|
|
383
|
+
revocation endpoint and its credentials, its OAuth client included, deleted.
|
|
384
|
+
Google's revocation ends every grant the Google user gave the client's
|
|
385
|
+
project, so when another listed account is signed in to the same Google user,
|
|
386
|
+
the Kit deletes the local credentials and revokes nothing.
|
|
387
|
+
|
|
388
|
+
It then reports every entry to `/kit/sources/report`, and reports an entry
|
|
389
|
+
again after each sign-in and each capture: `signed_out` while its
|
|
390
|
+
account holds no grant for its service, `failed` with its cause code when its
|
|
391
|
+
last capture failed, and `ready` otherwise, each with the account's signed-in
|
|
392
|
+
email once the Kit knows it.
|
|
393
|
+
|
|
394
|
+
The Kit ships no Google OAuth client. Each account signs in with the User's
|
|
395
|
+
own: an OAuth client of type Desktop app in the User's Google Cloud project,
|
|
396
|
+
with the service's API enabled there. An entry whose account holds no grant for
|
|
397
|
+
its service opens a login renewal for integration `google`, instance the
|
|
398
|
+
account name and service the entry's service, and presents its ceremony on
|
|
399
|
+
House's owner-authenticated page, so the owner's browser never has to reach
|
|
400
|
+
this Environment. When the account has no client yet, the page first collects
|
|
401
|
+
the client id and secret. It then links to Google's consent for that service's
|
|
402
|
+
scope only; Google sends the browser to a `http://127.0.0.1:<port>/` address
|
|
403
|
+
that nothing answers, and the owner pastes that whole address into the page.
|
|
404
|
+
The Kit accepts it only when it carries the state its own consent issued,
|
|
405
|
+
exchanges the code with its PKCE verifier and the account's client, and stores
|
|
406
|
+
the grant beside the client in `state/google/credentials/<account>.json`. The
|
|
407
|
+
collected values pass through House's live relay only; neither they nor any
|
|
408
|
+
token enter a report, a log or anything House stores. Adding a
|
|
409
|
+
service to a signed-in account asks only for that service's consent, and each
|
|
410
|
+
service keeps its own grant. An address that answers another consent, or a code
|
|
411
|
+
Google refuses to exchange, stores nothing; a declined consent or a ceremony
|
|
412
|
+
that outlives its ten-minute bound cancels the sign-in, and the Kit tries again
|
|
413
|
+
at the next reconciliation.
|
|
414
|
+
|
|
415
|
+
The resident captures each signed-in entry every 15 minutes, with no command.
|
|
416
|
+
An occurrence never overlaps its entry's previous one, and one missed while the
|
|
417
|
+
resident was stopped is not replayed: the next resumes from the checkpoint. The
|
|
418
|
+
first capture fixes the initial choice's boundary, so `new` captures nothing
|
|
419
|
+
that existed then and everything that arrives after.
|
|
367
420
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
them all, and no instance can read another's provider credential.
|
|
373
|
-
|
|
374
|
-
```json
|
|
375
|
-
{
|
|
376
|
-
"availability": "intermittent",
|
|
377
|
-
"execution_authority": "refuse",
|
|
378
|
-
"migrators": [],
|
|
379
|
-
"instances": [
|
|
380
|
-
{
|
|
381
|
-
"id": "google-account",
|
|
382
|
-
"preset": "google",
|
|
383
|
-
"scopes": ["https://www.googleapis.com/auth/contacts.readonly"],
|
|
384
|
-
"services": [
|
|
385
|
-
{
|
|
386
|
-
"service": "gmail",
|
|
387
|
-
"source_ref": "src_google_mail",
|
|
388
|
-
"rooms": ["work-room"],
|
|
389
|
-
"backfill": { "days": 30 },
|
|
390
|
-
"selection": { "labels": ["INBOX"] }
|
|
391
|
-
},
|
|
392
|
-
{
|
|
393
|
-
"service": "calendar",
|
|
394
|
-
"source_ref": "src_google_calendar",
|
|
395
|
-
"rooms": ["work-room"],
|
|
396
|
-
"backfill": { "all": true },
|
|
397
|
-
"selection": { "calendars": ["primary"] }
|
|
398
|
-
},
|
|
399
|
-
{
|
|
400
|
-
"service": "drive",
|
|
401
|
-
"source_ref": "src_google_drive",
|
|
402
|
-
"rooms": ["work-room"],
|
|
403
|
-
"backfill": { "count": 500 },
|
|
404
|
-
"selection": { "files": ["file-id"], "folders": ["folder-id"] }
|
|
405
|
-
}
|
|
406
|
-
]
|
|
407
|
-
}
|
|
408
|
-
],
|
|
409
|
-
"clients": [],
|
|
410
|
-
"host": null,
|
|
411
|
-
"skills": false
|
|
412
|
-
}
|
|
421
|
+
```sh
|
|
422
|
+
kit source capture work # one occurrence of every entry of the account now
|
|
423
|
+
kit source capture work gmail # one occurrence of one entry now
|
|
424
|
+
kit source status work
|
|
413
425
|
```
|
|
414
426
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
Additional Google API scopes are declared once on the preset instance and
|
|
420
|
-
remain local; a declared service names only scopes that instance already
|
|
421
|
-
holds. Drive requires at least one explicit file or folder selection and never
|
|
422
|
-
defaults to the whole account.
|
|
427
|
+
`kit source capture` and `kit source status` take the account name where a
|
|
428
|
+
custom connector takes its instance; a name House lists as an account is read
|
|
429
|
+
as the account. While the resident runs, it performs the requested occurrence
|
|
430
|
+
itself, after any occurrence of that entry already running.
|
|
423
431
|
|
|
424
|
-
A
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
```json
|
|
430
|
-
{
|
|
431
|
-
"service": "people",
|
|
432
|
-
"source_ref": "src_google_people",
|
|
433
|
-
"rooms": ["work-room"],
|
|
434
|
-
"backfill": { "all": true },
|
|
435
|
-
"declaration": {
|
|
436
|
-
"host": "people.googleapis.com",
|
|
437
|
-
"path": "/v1/people/me/connections",
|
|
438
|
-
"query": { "personFields": "names,emailAddresses,metadata" },
|
|
439
|
-
"page_size": "pageSize",
|
|
440
|
-
"scopes": ["https://www.googleapis.com/auth/contacts.readonly"],
|
|
441
|
-
"page_token": {
|
|
442
|
-
"request": "pageToken",
|
|
443
|
-
"response": "nextPageToken",
|
|
444
|
-
"checkpoint_request": "syncToken",
|
|
445
|
-
"checkpoint_response": "nextSyncToken"
|
|
446
|
-
},
|
|
447
|
-
"items": "connections",
|
|
448
|
-
"fields": {
|
|
449
|
-
"native_id": "resourceName",
|
|
450
|
-
"native_version": "etag",
|
|
451
|
-
"provider_modified_at": "metadata.sources.0.updateTime",
|
|
452
|
-
"observed_at": "metadata.sources.0.updateTime",
|
|
453
|
-
"body": ["names.0.displayName", "emailAddresses.0.value"],
|
|
454
|
-
"container": "metadata.sources.0.type"
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
A Google instance signs in only through [Environment setup](#environment-setup),
|
|
461
|
-
on House's owner-authenticated page, so the owner's browser never has to reach
|
|
462
|
-
this Environment. For each enabled service whose scopes the instance does not
|
|
463
|
-
hold yet, the page links to Google's consent for exactly that service's scopes.
|
|
464
|
-
Google then sends the browser to a `http://127.0.0.1:<port>/` address that
|
|
465
|
-
nothing answers: the owner copies that whole address from the address bar and
|
|
466
|
-
pastes it into the page. The Kit accepts it only when it carries the state its
|
|
467
|
-
own consent issued, exchanges the code with its PKCE verifier and the built
|
|
468
|
-
client, and stores the grant locally. An address that answers another consent,
|
|
469
|
-
or a code Google refuses to exchange, fails the login and stores nothing; a
|
|
470
|
-
declined consent cancels it. Grants already completed for earlier services stay.
|
|
471
|
-
|
|
472
|
-
`kit source sign-in <instance>` signs in a custom connector instance through
|
|
473
|
-
its own command, described below. It skips an instance whose local
|
|
474
|
-
authorization is already valid and answers `ready`, `skipped`, or
|
|
475
|
-
`needs_attention`. A Google instance refuses it as `kit_connector_login_guided`
|
|
476
|
-
and names House setup.
|
|
432
|
+
A failed occurrence records its cause. A Google 403 for an API disabled in the
|
|
433
|
+
client's project fails as `api_disabled`, and `kit source status` names the API
|
|
434
|
+
to enable; a revoked scope (`google_scope_revoked`), an expired or revoked grant
|
|
435
|
+
(`google_authority_revoked`) and a provider failure
|
|
436
|
+
(`google_provider_unavailable`) keep their own codes.
|
|
477
437
|
|
|
478
438
|
## Environment setup
|
|
479
439
|
|
|
@@ -486,7 +446,9 @@ kit setup
|
|
|
486
446
|
```
|
|
487
447
|
|
|
488
448
|
`kit worker run` performs the same step at the start of a pass, before it
|
|
489
|
-
hands out any work, so a managed Environment needs no terminal
|
|
449
|
+
hands out any work, so a managed Environment needs no terminal; the resident
|
|
450
|
+
Kit performs it each time its control stream connects and on each `runs`
|
|
451
|
+
signal, which House also sends for every setup request. The walk first
|
|
490
452
|
prepares the absolute `/agents/house` base, then visits every declared agent
|
|
491
453
|
CLI and then every connector instance, in configuration order:
|
|
492
454
|
|
|
@@ -590,11 +552,46 @@ An object whose content changes without its native version changing keeps its
|
|
|
590
552
|
key and is refused by House as a delivery-key conflict rather than silently
|
|
591
553
|
overwriting the earlier Record.
|
|
592
554
|
|
|
593
|
-
The same command owns its module authorization. `kit source sign-in
|
|
594
|
-
|
|
555
|
+
The same command owns its module authorization. `kit source sign-in <instance>`
|
|
556
|
+
skips an instance whose local authorization is already valid and answers
|
|
557
|
+
`ready`, `skipped`, or `needs_attention`. It and the setup walk's status check
|
|
558
|
+
send one JSON line
|
|
595
559
|
with `action: "authorization"` and `mode: "status"` or `mode: "login"`; the
|
|
596
560
|
command returns exactly one JSON object such as `{ "authorized": true }`.
|
|
597
561
|
|
|
562
|
+
## The Kit control stream
|
|
563
|
+
|
|
564
|
+
The resident Kit holds one `/kit/stream` WebSocket for its Environment,
|
|
565
|
+
authenticated with its Environment control lease and the Kit contract header,
|
|
566
|
+
for as long as it holds that lease; `kit worker run` holds one for the length
|
|
567
|
+
of its pass to receive its owner's commands. The Kit sends nothing on it and
|
|
568
|
+
accepts every frame House sends.
|
|
569
|
+
|
|
570
|
+
Each time the socket connects, the resident acquires everything a signal could
|
|
571
|
+
have named, because a signal House sent while the socket was down is lost: its
|
|
572
|
+
Runs and resumptions with their setup and conversation files, the owner's
|
|
573
|
+
pending commands, the agent CLI list, the saved working copy choice and its
|
|
574
|
+
Google Sources. After that it acquires only what House names. A
|
|
575
|
+
`work_available` frame for `agents`, `working_copy` or `sources` acquires that
|
|
576
|
+
subject alone. `runs` takes a resumption or else a waiting Run, readying the
|
|
577
|
+
agent CLIs, a requested setup and conversation files first. `commands` hands
|
|
578
|
+
the supervised Session its stops, interrupts and answers and takes a
|
|
579
|
+
resumption the same way, without acquiring a Run. An `entries` frame, a change
|
|
580
|
+
in a Room the lease admits, delivers the working copies. A pass that took a Run
|
|
581
|
+
acquires again once that Run ends, and a pass that found nothing waits for the
|
|
582
|
+
next signal. A Run outcome House did not take is reported again every 30
|
|
583
|
+
seconds until House answers. The Kit has no idle acquisition cycle, no command
|
|
584
|
+
poll and no Session heartbeat: a Session is observed when its process starts
|
|
585
|
+
and when its supervisor sees it stop.
|
|
586
|
+
|
|
587
|
+
A socket that closes or never opens reconnects after a delay drawn between half
|
|
588
|
+
and all of one second, doubled for each consecutive failure up to 30 seconds.
|
|
589
|
+
An `authority_changed` close reconnects after a delay drawn between 0 and 2
|
|
590
|
+
seconds, with no error backoff. A `replaced` close, which means a newer
|
|
591
|
+
connection holds the Environment, ends the resident with `kit_stream_replaced`,
|
|
592
|
+
and an upgrade House refuses with `kit_upgrade_required` stops every House call
|
|
593
|
+
as [above](#connect-a-linux-computer) describes; neither is retried.
|
|
594
|
+
|
|
598
595
|
## The Worker runtime
|
|
599
596
|
|
|
600
597
|
```sh
|
|
@@ -1018,8 +1015,9 @@ A save that fails leaves one note in `state/conversation-originals/` naming the
|
|
|
1018
1015
|
attachment, its Run, the local file and the descriptor it declared. Each
|
|
1019
1016
|
later `kit worker run` retries it from that file without materializing or
|
|
1020
1017
|
acknowledging anything again, and reports `source_unavailable` when
|
|
1021
|
-
the file is gone or changed rather than saving different bytes.
|
|
1022
|
-
WorkerSession
|
|
1018
|
+
the file is gone or changed rather than saving different bytes. When a
|
|
1019
|
+
supervised WorkerSession ends, the same retry runs once more before its Run is
|
|
1020
|
+
reported. The note leaves once
|
|
1023
1021
|
House records the save, once House answers that the Run is over, or when House
|
|
1024
1022
|
authorizes that Run's cache cleanup; House accepts no save for a Run that has
|
|
1025
1023
|
settled, so an unsaved original keeps its failed outcome from then on. The owner
|
|
@@ -1155,7 +1153,10 @@ written into the durable binding. Reconciliation must obtain current House
|
|
|
1155
1153
|
authority before the live process can make further calls.
|
|
1156
1154
|
|
|
1157
1155
|
An owner stop requested through House reaches this Kit as a durable command,
|
|
1158
|
-
both at startup and while a Session is supervised
|
|
1156
|
+
both at startup and while a Session is supervised: House signals `commands` on
|
|
1157
|
+
the Kit control stream and the supervisor acquires the pending stops,
|
|
1158
|
+
interrupts and answers at once, so a stop reaches the process within a second.
|
|
1159
|
+
The Kit destroys the exact
|
|
1159
1160
|
tmux session, confirms that it is gone, and reports the stopped Session. It
|
|
1160
1161
|
reports no continuation for a Run stopped by its owner and never reopens the
|
|
1161
1162
|
local channel for that Session.
|
|
@@ -1209,8 +1210,8 @@ on the door's own deduplication. When House stops a batch early, its answer
|
|
|
1209
1210
|
settles only the prefix it reached, and the unsettled tail is reoffered with the
|
|
1210
1211
|
rest. The loop repeats while the connector still returns items and no Room
|
|
1211
1212
|
destination is blocked, and every run ends by writing the service's health into
|
|
1212
|
-
this installation's own state. House holds no
|
|
1213
|
-
or health
|
|
1213
|
+
this installation's own state. House holds no custom connector's service, route,
|
|
1214
|
+
checkpoint or health, and no Google entry's checkpoint or health.
|
|
1214
1215
|
|
|
1215
1216
|
A Capture item that declares a `file` attachment answers one Record per Room
|
|
1216
1217
|
destination: Rooms of one owner share that owner's Record, and a Room of another
|
|
@@ -1316,8 +1317,8 @@ The Environment owner chooses in House which Rooms this Environment keeps
|
|
|
1316
1317
|
locally, and whether it keeps `/private`. An Environment whose owner never chose
|
|
1317
1318
|
copies nothing, and a Room joined later stays uncopied until the owner selects
|
|
1318
1319
|
it. The resident Kit reads the saved choice through
|
|
1319
|
-
`POST /kit/working-copy/selection`
|
|
1320
|
-
|
|
1320
|
+
`POST /kit/working-copy/selection` whenever its control stream connects or
|
|
1321
|
+
reconnects and whenever House points it there after a save;
|
|
1321
1322
|
`kit worker run` reads it once before its pass.
|
|
1322
1323
|
|
|
1323
1324
|
Each selected Room becomes one ordinary Git repository at
|
|
@@ -1346,8 +1347,9 @@ state. The House revision of every file content Kit received, keyed by path,
|
|
|
1346
1347
|
Git blob and the House state that received it, stays in Kit home under
|
|
1347
1348
|
`state/working-copies/`, outside the repository. A local commit is a local tree, never a House revision.
|
|
1348
1349
|
|
|
1349
|
-
The resident Kit keeps every active copy current.
|
|
1350
|
-
it reads the saved choice, one
|
|
1350
|
+
The resident Kit keeps every active copy current. Whenever its control stream
|
|
1351
|
+
carries a Room change, and whenever it reads the saved choice, one
|
|
1352
|
+
`POST /kit/door/discover` tells it where each
|
|
1351
1353
|
copied Room stands, and for a Room that moved on it reads the changes after the
|
|
1352
1354
|
copy's recorded position through `POST /kit/door/enumerate`. A changed, new or
|
|
1353
1355
|
removed authored file becomes a commit on `refs/house/received`, its House
|
|
@@ -1366,7 +1368,8 @@ or protected paths House declares for the Room change, Kit reads the Room again
|
|
|
1366
1368
|
as a reselection does. A position older than House
|
|
1367
1369
|
retains, or from before House's database was restored, makes Kit read the Room
|
|
1368
1370
|
again as a reselection does; while House's replica has not reached the position
|
|
1369
|
-
the copy waits
|
|
1371
|
+
the copy waits, and the resident delivers again 30 seconds later. A Room House
|
|
1372
|
+
no longer answers under its
|
|
1370
1373
|
recorded handle makes Kit read the saved choice again, which follows a handle
|
|
1371
1374
|
change or marks the copy inactive, and a Room that lost sync is named on
|
|
1372
1375
|
standard error as `operation_denied` and keeps what it holds. `kit worker run`
|
|
@@ -1755,7 +1758,13 @@ $HOUSE_KIT_HOME/
|
|
|
1755
1758
|
state/skill-set.json
|
|
1756
1759
|
state/how-we-work
|
|
1757
1760
|
state/continuations/<run identity>.json
|
|
1758
|
-
state
|
|
1761
|
+
state/google/entries/<source>.json
|
|
1762
|
+
state/google/credentials/<account>.json
|
|
1763
|
+
state/google/checkpoints/<source>.json
|
|
1764
|
+
state/google/health/<source>.json
|
|
1765
|
+
state/google/gaps/<source>.json
|
|
1766
|
+
state/google/dropped/<source>.json
|
|
1767
|
+
state/google/originals/<source>.json
|
|
1759
1768
|
state/<instance>/checkpoints/<service>.json
|
|
1760
1769
|
state/<instance>/health/<service>.json
|
|
1761
1770
|
state/<instance>/gaps/<service>.json
|
|
@@ -1765,15 +1774,18 @@ $HOUSE_KIT_HOME/
|
|
|
1765
1774
|
|
|
1766
1775
|
Every file is written atomically and readable only by its owner. A file the Kit
|
|
1767
1776
|
cannot read is refused rather than treated as a service that never captured.
|
|
1768
|
-
|
|
1769
|
-
|
|
1777
|
+
A Google entry's state is keyed by its Source reference and lives only while
|
|
1778
|
+
House lists the entry. Disabling a custom service or instance leaves its state
|
|
1779
|
+
in place, so re-enabling resumes from the stored cursor instead of importing
|
|
1780
|
+
again.
|
|
1770
1781
|
|
|
1771
1782
|
```sh
|
|
1772
1783
|
kit source reset acme # every service
|
|
1773
1784
|
kit source reset acme notes # one service
|
|
1774
1785
|
```
|
|
1775
1786
|
|
|
1776
|
-
Reset is the only deletion of captured
|
|
1787
|
+
Reset serves custom connectors, and is the only deletion of their captured
|
|
1788
|
+
state. It removes the named checkpoints
|
|
1777
1789
|
and their outstanding retained references so the next capture restarts exactly at
|
|
1778
1790
|
the configured backfill boundary. It takes the same
|
|
1779
1791
|
Environment control lease a capture takes, so it cannot delete a checkpoint a
|
|
@@ -1993,10 +2005,7 @@ workspaces.
|
|
|
1993
2005
|
```sh
|
|
1994
2006
|
docker compose -f test/compose.yaml build kit
|
|
1995
2007
|
docker compose -f test/compose.yaml run --rm kit npm test
|
|
1996
|
-
docker compose -f test/compose.yaml run --rm
|
|
1997
|
-
-e HOUSE_KIT_GOOGLE_OAUTH_CLIENT_ID=kit-client.apps.googleusercontent.com \
|
|
1998
|
-
-e HOUSE_KIT_GOOGLE_OAUTH_CLIENT_SECRET=GOCSPX-kit-installed-app \
|
|
1999
|
-
kit npm run build
|
|
2008
|
+
docker compose -f test/compose.yaml run --rm kit npm run build
|
|
2000
2009
|
```
|
|
2001
2010
|
|
|
2002
2011
|
`test/pair/run.sh` boots real Core, Auth and PostgreSQL and runs the installed
|
package/bin/kit.js
CHANGED
|
@@ -5,12 +5,12 @@ import { join, resolve } from "node:path";
|
|
|
5
5
|
import { connectHouse, loginHouse, storedEnvironment, storedHouse } from "../src/login.js";
|
|
6
6
|
import { apply } from "../src/apply.js";
|
|
7
7
|
import { captureSource, resetSource, retrySourceOriginals, sourceStatus } from "../src/source.js";
|
|
8
|
+
import { accountEntries, accountStatus, captureAccount } from "../src/google/entries.js";
|
|
8
9
|
import { setupEnvironment } from "../src/setup.js";
|
|
9
10
|
import { signInConnector } from "../src/connector-login.js";
|
|
10
11
|
import { attachSession } from "../src/attach.js";
|
|
11
12
|
import { runResident, runWorkers } from "../src/worker.js";
|
|
12
13
|
import { openSession, releaseControl, requestDrain } from "../src/house.js";
|
|
13
|
-
import { GOOGLE_OAUTH_CLIENT } from "../src/instance/google.js";
|
|
14
14
|
import { reportHost } from "../src/host.js";
|
|
15
15
|
import { signInAgent, updateAgents } from "../src/agents.js";
|
|
16
16
|
import { serveWorkspaces } from "../src/workspace.js";
|
|
@@ -37,8 +37,8 @@ const AGENTS_USAGE =
|
|
|
37
37
|
"usage: kit agents update [--config <path>] | kit agents sign-in <kind> [--config <path>]";
|
|
38
38
|
const WORKSPACE_USAGE = "usage: kit workspace host [--config <path>] [--passes <count>]";
|
|
39
39
|
const SOURCE_USAGE =
|
|
40
|
-
"usage: kit source <capture|
|
|
41
|
-
" kit source
|
|
40
|
+
"usage: kit source <capture|status> <account|instance> [service] [--config <path>]\n" +
|
|
41
|
+
" kit source <reset|retry-files> <instance> [service] [--config <path>]\n" +
|
|
42
42
|
" kit source sign-in <instance> [--config <path>]";
|
|
43
43
|
const SETUP_USAGE = "usage: kit setup [--config <path>]";
|
|
44
44
|
const REPOSITORY_USAGE =
|
|
@@ -108,13 +108,6 @@ function write(line) {
|
|
|
108
108
|
process.stdout.write(`${line}\n`);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
function builtGoogleOAuthClient() {
|
|
112
|
-
if (GOOGLE_OAUTH_CLIENT === null) {
|
|
113
|
-
throw new KitRefusal("google_oauth_client_unconfigured", "run npm run build");
|
|
114
|
-
}
|
|
115
|
-
return GOOGLE_OAUTH_CLIENT;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
111
|
async function runLogin(args, directory) {
|
|
119
112
|
const { values } = declared(args, ["--house", "--manual", "--host-browser", "--environment"], 0, { flags: ["--host-browser"] });
|
|
120
113
|
if (values["--manual"] && values["--host-browser"]) throw new Error(LOGIN_USAGE);
|
|
@@ -188,15 +181,12 @@ async function runSetup(args, directory) {
|
|
|
188
181
|
process.once("SIGINT", cancel);
|
|
189
182
|
let setup;
|
|
190
183
|
try {
|
|
191
|
-
setup = await setupEnvironment(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
},
|
|
198
|
-
{ googleOAuthClient: builtGoogleOAuthClient },
|
|
199
|
-
);
|
|
184
|
+
setup = await setupEnvironment({
|
|
185
|
+
house,
|
|
186
|
+
directory,
|
|
187
|
+
configuration: values["--config"] ?? join(directory, "kit.json"),
|
|
188
|
+
signal: cancellation.signal,
|
|
189
|
+
});
|
|
200
190
|
} finally {
|
|
201
191
|
process.removeListener("SIGINT", cancel);
|
|
202
192
|
}
|
|
@@ -268,22 +258,27 @@ async function runSourceCapture(args, directory) {
|
|
|
268
258
|
const house = await storedHouse(directory);
|
|
269
259
|
const cancellation = new AbortController();
|
|
270
260
|
process.once("SIGINT", () => cancellation.abort());
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
261
|
+
const entries = await accountEntries(directory, rest[0]);
|
|
262
|
+
const options = {
|
|
263
|
+
house,
|
|
264
|
+
directory,
|
|
265
|
+
service: rest[1] ?? null,
|
|
266
|
+
signal: cancellation.signal,
|
|
267
|
+
};
|
|
268
|
+
const result =
|
|
269
|
+
entries.length > 0
|
|
270
|
+
? await captureAccount({ ...options, account: rest[0], entries })
|
|
271
|
+
: await captureSource({
|
|
272
|
+
...options,
|
|
273
|
+
configuration: values["--config"] ?? join(directory, "kit.json"),
|
|
274
|
+
instance: rest[0],
|
|
275
|
+
});
|
|
282
276
|
for (const delivered of result.services) {
|
|
283
277
|
const category =
|
|
284
278
|
delivered.health.category === null ? "" : ` (${delivered.health.category})`;
|
|
279
|
+
const cause = delivered.health.cause === undefined ? "" : `, cause ${delivered.health.cause}`;
|
|
285
280
|
write(
|
|
286
|
-
`Delivered ${delivered.delivered} items for ${result.instance}/${delivered.service} in ${delivered.batches} batches; health ${delivered.health.state}${category}.`,
|
|
281
|
+
`Delivered ${delivered.delivered} items for ${result.instance}/${delivered.service} in ${delivered.batches} batches; health ${delivered.health.state}${category}${cause}.`,
|
|
287
282
|
);
|
|
288
283
|
if (delivered.refused !== null) {
|
|
289
284
|
write(
|
|
@@ -332,22 +327,32 @@ async function runSourceRetryFiles(args, directory) {
|
|
|
332
327
|
async function runSourceStatus(args, directory) {
|
|
333
328
|
const { values, rest } = declared(args, ["--config"], 2);
|
|
334
329
|
if (rest.length === 0) throw new Error(SOURCE_USAGE);
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
330
|
+
const entries = await accountEntries(directory, rest[0]);
|
|
331
|
+
const result =
|
|
332
|
+
entries.length > 0
|
|
333
|
+
? await accountStatus({ directory, account: rest[0], entries, service: rest[1] ?? null })
|
|
334
|
+
: await sourceStatus({
|
|
335
|
+
directory,
|
|
336
|
+
configuration: values["--config"] ?? join(directory, "kit.json"),
|
|
337
|
+
instance: rest[0],
|
|
338
|
+
service: rest[1] ?? null,
|
|
339
|
+
});
|
|
341
340
|
for (const reported of result.services) {
|
|
342
341
|
const category = reported.health.category === null ? "" : ` (${reported.health.category})`;
|
|
342
|
+
const cause = reported.health.cause === undefined ? "" : `, cause ${reported.health.cause}`;
|
|
343
343
|
const observed =
|
|
344
344
|
reported.health.observed_at === null ? "never observed" : reported.health.observed_at;
|
|
345
|
+
const signedIn =
|
|
346
|
+
reported.signed_in === undefined ? "" : reported.signed_in ? "signed in; " : "signed out; ";
|
|
345
347
|
write(
|
|
346
348
|
`${result.instance}/${reported.service} -> ${reported.source_ref} in ${reported.rooms.join(", ")}: ` +
|
|
347
|
-
`${reported.health.state}${category} at ${observed}; ` +
|
|
349
|
+
`${signedIn}${reported.health.state}${category}${cause} at ${observed}; ` +
|
|
348
350
|
`cursor ${reported.checkpoint?.cursor ?? "none"}, ${reported.gaps.length} gaps, ` +
|
|
349
351
|
`${reported.dropped.length} dropped items, ${reported.originals.length} unsaved originals.`,
|
|
350
352
|
);
|
|
353
|
+
if (reported.enable !== null && reported.enable !== undefined) {
|
|
354
|
+
write(`Enable the ${reported.enable} in the Google Cloud project of ${result.instance}'s OAuth client.`);
|
|
355
|
+
}
|
|
351
356
|
}
|
|
352
357
|
if (result.services.some((reported) => reported.health.state !== "ok")) process.exitCode = 1;
|
|
353
358
|
}
|
|
@@ -425,14 +430,11 @@ function writeConversationFiles(supervised) {
|
|
|
425
430
|
async function runWorkerRun(args, directory) {
|
|
426
431
|
const { values } = declared(args, ["--config"], 0);
|
|
427
432
|
const house = await storedHouse(directory);
|
|
428
|
-
const supervised = await runWorkers(
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
},
|
|
434
|
-
{ googleOAuthClient: builtGoogleOAuthClient },
|
|
435
|
-
);
|
|
433
|
+
const supervised = await runWorkers({
|
|
434
|
+
house,
|
|
435
|
+
directory,
|
|
436
|
+
configuration: values["--config"] ?? join(directory, "kit.json"),
|
|
437
|
+
});
|
|
436
438
|
write(`Worker runtime ready on tmux ${supervised.tmux} in namespace ${supervised.namespace}.`);
|
|
437
439
|
if (supervised.epochReport !== null) {
|
|
438
440
|
write(`House started a new log epoch; reported ${supervised.epochReport.length} held Runs and House settled each.`);
|
package/bootstrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.28",
|
|
4
4
|
"hosts": {
|
|
5
5
|
"linux": {
|
|
6
|
-
"command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.
|
|
6
|
+
"command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.28/connect-linux.sh)",
|
|
7
7
|
"asset": {
|
|
8
8
|
"name": "connect-linux.sh",
|
|
9
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.
|
|
9
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.28/connect-linux.sh"
|
|
10
10
|
},
|
|
11
11
|
"workspace": {
|
|
12
12
|
"argument": "--workspace",
|