@agentshouse/kit 0.1.0-alpha.1 → 0.1.0-alpha.10
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 +86 -6
- package/bin/connect-linux.sh +58 -12
- package/bin/kit.js +1 -1
- package/bootstrap.json +17 -0
- package/package.json +6 -2
- package/src/agents.js +63 -8
- package/src/contract.js +3 -3
- package/src/house.js +197 -7
- package/src/kit-work-stream.js +84 -0
- package/src/mount.js +7 -7
- package/src/setup.js +7 -2
- package/src/source.js +10 -6
- package/src/sync-state.js +4 -0
- package/src/sync.js +11 -6
- package/src/telegram.js +24 -8
- package/src/worker.js +197 -25
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ On Linux `amd64` or `arm64` with a working Docker Engine, run the published,
|
|
|
10
10
|
versioned bootstrap once:
|
|
11
11
|
|
|
12
12
|
```sh
|
|
13
|
-
bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.
|
|
13
|
+
bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.10/connect-linux.sh)
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
The release asset pins one immutable multi-architecture image digest. It creates
|
|
@@ -23,9 +23,65 @@ another House and is retained in Kit home. A repeated invocation reuses the
|
|
|
23
23
|
same container, credential and Environment; a different installed image is
|
|
24
24
|
left for the explicit update journey.
|
|
25
25
|
|
|
26
|
+
The versioned `bootstrap.json` is the machine-readable command source for this
|
|
27
|
+
release. It is exported from the npm package as `@agentshouse/kit/bootstrap`
|
|
28
|
+
(JSON import) and published byte-for-byte beside `connect-linux.sh` in the
|
|
29
|
+
GitHub release. Its schema version is `1`, its `version` is the Kit package
|
|
30
|
+
version, and `hosts` contains only bootstrap scripts published in that release.
|
|
31
|
+
The package exports only the `./bootstrap` subpath; `./package.json` and source
|
|
32
|
+
subpaths are not exported. For this release, only `hosts.linux` exists. In this
|
|
33
|
+
schema example, `<version>` stands for the package's version:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"schemaVersion": 1,
|
|
38
|
+
"version": "<version>",
|
|
39
|
+
"hosts": {
|
|
40
|
+
"linux": {
|
|
41
|
+
"command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v<version>/connect-linux.sh)",
|
|
42
|
+
"asset": {
|
|
43
|
+
"name": "connect-linux.sh",
|
|
44
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v<version>/connect-linux.sh"
|
|
45
|
+
},
|
|
46
|
+
"workspace": { "argument": "--workspace", "quoting": "posix" }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`command` is complete for the default workspace. To show a selected absolute
|
|
53
|
+
workspace, append the declared `workspace.argument` and the path as one POSIX
|
|
54
|
+
shell-quoted argument. For example, `/home/user/My Agents' House` becomes
|
|
55
|
+
`--workspace '/home/user/My Agents'\'' House'`. Quote the entire path before
|
|
56
|
+
adding it to a copyable shell command; never interpolate it as shell syntax.
|
|
57
|
+
The bootstrap validates an absolute workspace root and encodes it as one Docker
|
|
58
|
+
`--mount` CSV source field, including commas and double quotes in the name.
|
|
59
|
+
The package manifest contains no image digest: the release workflow
|
|
60
|
+
fills the immutable image digest into the published script after the package
|
|
61
|
+
has been built.
|
|
62
|
+
|
|
26
63
|
The bootstrap does not install native Kit on an ordinary computer. The native
|
|
27
64
|
package remains for managed Nodes and an explicitly selected eligible Kit host.
|
|
28
65
|
|
|
66
|
+
The same bootstrap is the host's entry point for the existing `kit` commands:
|
|
67
|
+
append `kit` and the command to the connect command above, as in
|
|
68
|
+
`bash <(curl -fsSL …/connect-linux.sh) kit apply` or
|
|
69
|
+
`bash <(curl -fsSL …/connect-linux.sh) kit telegram connect`. It forwards the
|
|
70
|
+
command and its arguments unchanged into the installed container through the
|
|
71
|
+
host's Docker client and ends with the command's own output and exit status.
|
|
72
|
+
Standard input stays attached and a terminal stays a terminal, so a hidden
|
|
73
|
+
prompt such as the Telegram bot token is read inside the container and never
|
|
74
|
+
becomes an argument or printed output. With the output redirected, the
|
|
75
|
+
command's output and errors stay separate and the terminal stops echoing typed
|
|
76
|
+
input until the command ends. A running container runs the command beside its
|
|
77
|
+
resident Kit; a stopped one runs it in a one-off container over the same Kit
|
|
78
|
+
home and workspace and stays stopped. `kit login` takes the bootstrap's own
|
|
79
|
+
login route, a one-off container on the host network so the host browser can
|
|
80
|
+
return to its loopback listener, or `--manual` for the URL and one-time code.
|
|
81
|
+
Forwarding installs no host-native Kit and gives the container no Docker socket
|
|
82
|
+
or further mount; it refuses when no Kit is installed or the installed one
|
|
83
|
+
differs from this bootstrap's.
|
|
84
|
+
|
|
29
85
|
## Building the package
|
|
30
86
|
|
|
31
87
|
The repository checkout is the development source. Build the package
|
|
@@ -48,7 +104,8 @@ Runtime dependencies stay minimal and exactly pinned in `package.json` and
|
|
|
48
104
|
`package-lock.json`: House's own `@agentshouse/mdmodel`, `yaml`, which reads
|
|
49
105
|
House's text answers, and the Agent Client Protocol SDK
|
|
50
106
|
`@agentclientprotocol/sdk` with its peer `zod`, which is this Kit's one protocol
|
|
51
|
-
client for the compiled adapter contracts
|
|
107
|
+
client for the compiled adapter contracts, and `ws` for the authenticated Kit
|
|
108
|
+
control stream. The SDK's approval
|
|
52
109
|
receipt is [`docs/agents/acp-sdk-dependency-approval.json`](docs/agents/acp-sdk-dependency-approval.json),
|
|
53
110
|
which the User wrote once this adoption's proof had passed. The adapter
|
|
54
111
|
packages themselves are Environment prerequisites, not Kit dependencies, and
|
|
@@ -575,6 +632,17 @@ minimum's major - under
|
|
|
575
632
|
`$HOUSE_KIT_HOME/agents/<kind>/releases/<release>/`, never touching what the
|
|
576
633
|
owner installed globally, and an acquired route's command is that installed
|
|
577
634
|
executable rather than a path anyone writes into `kit.json`.
|
|
635
|
+
The resident Kit also listens for an `agents` availability pointer on its
|
|
636
|
+
control stream. It reads the whole wanted list from House over HTTPS, removes
|
|
637
|
+
Kit-owned CLI directories that are no longer wanted, installs newly wanted
|
|
638
|
+
kinds, and reports its held state immediately, including while another Agent
|
|
639
|
+
run remains active. A newly wanted kind that needs provider authorization uses
|
|
640
|
+
the existing guided House login ceremony; Kit reports readiness again when it
|
|
641
|
+
ends. A reconnect reads the list again, so a pointer missed during a stream
|
|
642
|
+
outage changes no authority.
|
|
643
|
+
Removal waits for an active worker pass or any unresolved live WorkerSession
|
|
644
|
+
binding to stop using a CLI directory; the Kit keeps reporting that kind until
|
|
645
|
+
it removes the directory.
|
|
578
646
|
|
|
579
647
|
Every candidate release is proved before it serves: the Kit installs it beside
|
|
580
648
|
the current release, runs the contract's own conformance check - the protocol's
|
|
@@ -1067,6 +1135,15 @@ has stopped, releases that exact lease so the next command acquires at once; a
|
|
|
1067
1135
|
refused command leaves it to expire. A `kit login` credential is presented only to take
|
|
1068
1136
|
that lease.
|
|
1069
1137
|
|
|
1138
|
+
While `kit resident` holds that lease, a command runs through it instead: the
|
|
1139
|
+
command presents no credential, the resident confirms its current lease with
|
|
1140
|
+
House and sends the command's House requests under it, an interrupted command
|
|
1141
|
+
cancels its request there, and the lease stays held when the command ends. The
|
|
1142
|
+
resident serves one such command at a time and refuses another with
|
|
1143
|
+
`environment_control_lease_active`, so two commands still never work at once.
|
|
1144
|
+
A resident whose lease House refuses answers the command with that refusal; the
|
|
1145
|
+
command never acquires a lease of its own while a resident serves it.
|
|
1146
|
+
|
|
1070
1147
|
Interrupting the command cancels the running connector without advancing the
|
|
1071
1148
|
checkpoint.
|
|
1072
1149
|
|
|
@@ -1111,8 +1188,11 @@ It selects prefixes over the User root, materializes them into a named local
|
|
|
1111
1188
|
directory, enumerates whole content and removals after the recorded positions,
|
|
1112
1189
|
and submits a resolved exact-base change set the caller prepared. Kit does no
|
|
1113
1190
|
local reconciliation. The mode shares no Working Copy manifest, never infers a
|
|
1114
|
-
removal from a locally absent file, and writes authored mutations only
|
|
1115
|
-
|
|
1191
|
+
removal from a locally absent file, and writes authored mutations only within
|
|
1192
|
+
House's declared write scopes: a selected Room's Library and, where granted,
|
|
1193
|
+
`ROOM.md`. `/private` selects the Private Room by its Room reference, records an independent Room
|
|
1194
|
+
position, and keeps its `agents/` cards and Room-specific private `AGENTS.md`
|
|
1195
|
+
beside `library/**`.
|
|
1116
1196
|
|
|
1117
1197
|
```sh
|
|
1118
1198
|
kit sync bootstrap ./replica \
|
|
@@ -1179,7 +1259,7 @@ for a batch that reaches it.
|
|
|
1179
1259
|
|
|
1180
1260
|
## Attachments
|
|
1181
1261
|
|
|
1182
|
-
Upload one local file as an attachment to a Room or to your
|
|
1262
|
+
Upload one local file as an attachment to a Room or to your Private Room. Name
|
|
1183
1263
|
the place explicitly. Kit sends the descriptor and the selected bytes; it does
|
|
1184
1264
|
not scan a directory, convert a URL, or attach sibling files, credentials, or
|
|
1185
1265
|
absolute paths.
|
|
@@ -1583,7 +1663,7 @@ prepared model definitions survive a later content-mount refusal or unmount.
|
|
|
1583
1663
|
|
|
1584
1664
|
The atomic text mount includes a report with pending transfer rows. Kit then
|
|
1585
1665
|
uploads each downloaded file once as an attachment to the mount's Room or
|
|
1586
|
-
|
|
1666
|
+
Private Room, independently retrieves and compares the saved bytes, and only
|
|
1587
1667
|
after that proof succeeds rewrites every link to it, in every referring
|
|
1588
1668
|
document, to its attachment reference, such as `at_1x.q`. The reference is an
|
|
1589
1669
|
identity House resolves, not an HTTP download URL. A link's query and fragment
|
package/bin/connect-linux.sh
CHANGED
|
@@ -8,12 +8,41 @@ WORKSPACE="$HOME/AgentsHouse"
|
|
|
8
8
|
MANUAL=0
|
|
9
9
|
WORKSPACE_SELECTED=0
|
|
10
10
|
HOUSE_SELECTED=0
|
|
11
|
+
FORWARDING=0
|
|
12
|
+
FORWARD=()
|
|
11
13
|
|
|
12
14
|
refuse() {
|
|
13
15
|
printf 'kit_bootstrap_refused: %s\n' "$1" >&2
|
|
14
16
|
exit 1
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
bind_mount() {
|
|
20
|
+
local source=${1//\"/\"\"}
|
|
21
|
+
printf 'type=bind,"src=%s",dst=%s' "$source" "$2"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
login_kit() {
|
|
25
|
+
if [[ " $* " == *' --manual '* ]]; then
|
|
26
|
+
"${DOCKER_RUN[@]:0:2}" -i "${DOCKER_RUN[@]:2}" login "$@"
|
|
27
|
+
return
|
|
28
|
+
fi
|
|
29
|
+
command -v xdg-open >/dev/null || refuse 'no host browser opener; rerun with --manual'
|
|
30
|
+
"${DOCKER_RUN[@]}" login "$@" --host-browser | while IFS= read -r line; do
|
|
31
|
+
if [[ "$line" == 'Open '* ]]; then
|
|
32
|
+
printf 'Opening House Login in the host browser.\n'
|
|
33
|
+
xdg-open "${line#Open }" >/dev/null 2>&1 || refuse 'host browser did not open; rerun with --manual'
|
|
34
|
+
else
|
|
35
|
+
printf '%s\n' "$line"
|
|
36
|
+
fi
|
|
37
|
+
done
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if [[ "${1:-}" == kit ]]; then
|
|
41
|
+
FORWARDING=1
|
|
42
|
+
FORWARD=("${@:2}")
|
|
43
|
+
set --
|
|
44
|
+
fi
|
|
45
|
+
|
|
17
46
|
while (($#)); do
|
|
18
47
|
case "$1" in
|
|
19
48
|
--workspace|--house)
|
|
@@ -53,6 +82,10 @@ case "$DOCKER_PLATFORM" in
|
|
|
53
82
|
*) refuse "unsupported Docker Engine platform $DOCKER_PLATFORM" ;;
|
|
54
83
|
esac
|
|
55
84
|
[[ "$DOCKER_ARCH" == "$ARCH" ]] || refuse "host linux/$ARCH does not match Docker Engine linux/$DOCKER_ARCH"
|
|
85
|
+
if ((FORWARDING)); then
|
|
86
|
+
docker container inspect "$NAME" >/dev/null 2>&1 || refuse 'House Kit is not installed; run this bootstrap without arguments first'
|
|
87
|
+
WORKSPACE=$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/agents/house"}}{{.Source}}{{end}}{{end}}' "$NAME")
|
|
88
|
+
fi
|
|
56
89
|
|
|
57
90
|
umask 077
|
|
58
91
|
KIT_HOME="$HOME/.house-kit"
|
|
@@ -65,6 +98,8 @@ for path in "$KIT_HOME" "$WORKSPACE"; do
|
|
|
65
98
|
done
|
|
66
99
|
KIT_HOME=$(cd "$KIT_HOME" && pwd -P)
|
|
67
100
|
WORKSPACE=$(cd "$WORKSPACE" && pwd -P)
|
|
101
|
+
KIT_HOME_MOUNT=$(bind_mount "$KIT_HOME" /kit-home)
|
|
102
|
+
WORKSPACE_MOUNT=$(bind_mount "$WORKSPACE" /agents/house)
|
|
68
103
|
if [[ -f "$KIT_HOME/credential" || -f "$KIT_HOME/environment" ]] && [[ ! -f "$KIT_HOME/house" ]]; then
|
|
69
104
|
refuse 'stored House origin is missing for an enrolled Environment'
|
|
70
105
|
fi
|
|
@@ -76,7 +111,7 @@ if [[ -f "$KIT_HOME/house" ]]; then
|
|
|
76
111
|
HOUSE="$BOUND_HOUSE"
|
|
77
112
|
fi
|
|
78
113
|
[[ "$HOUSE" == https://* || "$HOUSE" == http://127.0.0.1:* || "$HOUSE" == http://localhost:* ]] || refuse 'House origin must use HTTPS or local loopback HTTP'
|
|
79
|
-
DOCKER_RUN=(docker run --rm --network host --user "$(id -u):$(id -g)" --mount "
|
|
114
|
+
DOCKER_RUN=(docker run --rm --network host --user "$(id -u):$(id -g)" --mount "$KIT_HOME_MOUNT" --mount "$WORKSPACE_MOUNT" --env HOME=/kit-home --env HOUSE_KIT_HOME=/kit-home --env HOUSE_KIT_CONTAINER=1 --env "HOUSE_KIT_HOSTNAME=$(hostname)" "$IMAGE")
|
|
80
115
|
|
|
81
116
|
if docker container inspect "$NAME" >/dev/null 2>&1; then
|
|
82
117
|
[[ "$(docker inspect --format '{{.Config.Image}}' "$NAME")" == "$IMAGE" ]] || refuse 'an installed Kit uses a different image; update it explicitly'
|
|
@@ -88,6 +123,25 @@ if docker container inspect "$NAME" >/dev/null 2>&1; then
|
|
|
88
123
|
[[ "$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/agents/house"}}{{.Type}}/{{.RW}}{{end}}{{end}}' "$NAME")" == bind/true ]] || refuse 'the installed workspace is not a writable bind mount'
|
|
89
124
|
[[ "$(docker inspect --format '{{index .Config.Labels "agentshouse.house"}}' "$NAME")" == "$HOUSE" ]] || refuse 'the installed Kit uses another House origin'
|
|
90
125
|
[[ -f "$KIT_HOME/credential" && -f "$KIT_HOME/environment" && -f "$KIT_HOME/house" ]] || refuse 'the installed Kit has no enrolled authority'
|
|
126
|
+
if ((FORWARDING)); then
|
|
127
|
+
if [[ "${FORWARD[0]:-}" == login ]]; then
|
|
128
|
+
login_kit "${FORWARD[@]:1}"
|
|
129
|
+
exit 0
|
|
130
|
+
fi
|
|
131
|
+
TERMINAL=()
|
|
132
|
+
if [[ -t 0 && -t 1 ]]; then TERMINAL=(-t); fi
|
|
133
|
+
if [[ -t 0 && ! -t 1 ]]; then
|
|
134
|
+
ECHOED=$(stty -g)
|
|
135
|
+
trap 'stty "$ECHOED"' EXIT
|
|
136
|
+
stty -echo
|
|
137
|
+
fi
|
|
138
|
+
if [[ "$(docker inspect --format '{{.State.Running}}' "$NAME")" == true ]]; then
|
|
139
|
+
docker exec -i "${TERMINAL[@]}" "$NAME" kit "${FORWARD[@]}"
|
|
140
|
+
else
|
|
141
|
+
"${DOCKER_RUN[@]:0:2}" -i "${TERMINAL[@]}" "${DOCKER_RUN[@]:2}" "${FORWARD[@]}"
|
|
142
|
+
fi
|
|
143
|
+
exit
|
|
144
|
+
fi
|
|
91
145
|
"${DOCKER_RUN[@]}" credential check --house "$HOUSE"
|
|
92
146
|
if [[ "$(docker inspect --format '{{.State.Running}}' "$NAME")" == true ]]; then
|
|
93
147
|
printf 'House Kit is already running for Environment %s.\n' "$(cat "$KIT_HOME/environment")"
|
|
@@ -108,20 +162,12 @@ if [[ -f "$KIT_HOME/credential" ]]; then
|
|
|
108
162
|
"${DOCKER_RUN[@]}" credential check --house "$HOUSE"
|
|
109
163
|
else
|
|
110
164
|
if ((MANUAL)); then
|
|
111
|
-
|
|
165
|
+
login_kit --house "$HOUSE" --manual
|
|
112
166
|
else
|
|
113
|
-
|
|
114
|
-
"${DOCKER_RUN[@]}" login --house "$HOUSE" --host-browser | while IFS= read -r line; do
|
|
115
|
-
if [[ "$line" == 'Open '* ]]; then
|
|
116
|
-
printf 'Opening House Login in the host browser.\n'
|
|
117
|
-
xdg-open "${line#Open }" >/dev/null 2>&1 || refuse 'host browser did not open; rerun with --manual'
|
|
118
|
-
else
|
|
119
|
-
printf '%s\n' "$line"
|
|
120
|
-
fi
|
|
121
|
-
done
|
|
167
|
+
login_kit --house "$HOUSE"
|
|
122
168
|
fi
|
|
123
169
|
fi
|
|
124
170
|
|
|
125
171
|
"${DOCKER_RUN[@]}" apply --house "$HOUSE"
|
|
126
|
-
docker run -d --name "$NAME" --restart unless-stopped --user "$(id -u):$(id -g)" --mount "
|
|
172
|
+
docker run -d --name "$NAME" --restart unless-stopped --user "$(id -u):$(id -g)" --mount "$KIT_HOME_MOUNT" --mount "$WORKSPACE_MOUNT" --label "agentshouse.house=$HOUSE" --env HOME=/kit-home --env HOUSE_KIT_HOME=/kit-home --env HOUSE_KIT_CONTAINER=1 "$IMAGE" resident --house "$HOUSE" >/dev/null
|
|
127
173
|
printf 'House Kit connected for Environment %s.\n' "$(cat "$KIT_HOME/environment")"
|
package/bin/kit.js
CHANGED
|
@@ -140,7 +140,7 @@ async function runCredentialCheck(args, directory) {
|
|
|
140
140
|
const house = await selectedHouse(values, directory);
|
|
141
141
|
let session;
|
|
142
142
|
try {
|
|
143
|
-
session = await openSession({ house, directory });
|
|
143
|
+
session = await openSession({ house, directory, acquire: true });
|
|
144
144
|
} catch (error) {
|
|
145
145
|
if (error instanceof KitRefusal && error.code === "environment_control_lease_active") {
|
|
146
146
|
write("Stored Kit authority is accepted; its Environment already has an active control lease.");
|
package/bootstrap.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"version": "0.1.0-alpha.10",
|
|
4
|
+
"hosts": {
|
|
5
|
+
"linux": {
|
|
6
|
+
"command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.10/connect-linux.sh)",
|
|
7
|
+
"asset": {
|
|
8
|
+
"name": "connect-linux.sh",
|
|
9
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.10/connect-linux.sh"
|
|
10
|
+
},
|
|
11
|
+
"workspace": {
|
|
12
|
+
"argument": "--workspace",
|
|
13
|
+
"quoting": "posix"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentshouse/kit",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.10",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -11,9 +11,13 @@
|
|
|
11
11
|
"bin": {
|
|
12
12
|
"kit": "bin/kit.js"
|
|
13
13
|
},
|
|
14
|
+
"exports": {
|
|
15
|
+
"./bootstrap": "./bootstrap.json"
|
|
16
|
+
},
|
|
14
17
|
"dependencies": {
|
|
15
18
|
"@agentclientprotocol/sdk": "1.5.0",
|
|
16
|
-
"@agentshouse/mdmodel": "0.1.0-alpha.
|
|
19
|
+
"@agentshouse/mdmodel": "0.1.0-alpha.13",
|
|
20
|
+
"ws": "8.22.0",
|
|
17
21
|
"yaml": "2.9.1",
|
|
18
22
|
"zod": "4.6.5"
|
|
19
23
|
},
|
package/src/agents.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { readFile, rm } from "node:fs/promises";
|
|
2
|
+
import { readFile, readdir, rm } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import {
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "./adapter.js";
|
|
12
12
|
import { inheritedEnvironment } from "./command.js";
|
|
13
13
|
import {
|
|
14
|
+
ADAPTER_KINDS,
|
|
14
15
|
AGENT_HOME,
|
|
15
16
|
AGENT_INSTALL_TIMEOUT_MS,
|
|
16
17
|
AGENT_RELEASES_HOME,
|
|
@@ -261,6 +262,60 @@ export async function updateDeclaredAgents(options, configuration, dependencies
|
|
|
261
262
|
return updated;
|
|
262
263
|
}
|
|
263
264
|
|
|
265
|
+
export async function removeUndeclaredAgents(directory, wanted, keep = false) {
|
|
266
|
+
const home = join(directory, AGENT_HOME);
|
|
267
|
+
let entries;
|
|
268
|
+
try {
|
|
269
|
+
entries = await readdir(home, { withFileTypes: true });
|
|
270
|
+
} catch (error) {
|
|
271
|
+
if (error.code === "ENOENT") return [];
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
const retained = [];
|
|
275
|
+
for (const entry of entries) {
|
|
276
|
+
if (!wanted.includes(entry.name)) {
|
|
277
|
+
if (keep && ADAPTER_KINDS.includes(entry.name)) retained.push(entry.name);
|
|
278
|
+
else await rm(join(home, entry.name), { recursive: true, force: true });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return retained;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export async function reconcileAgentKinds(
|
|
285
|
+
options,
|
|
286
|
+
session,
|
|
287
|
+
configuration,
|
|
288
|
+
dependencies = {},
|
|
289
|
+
signal = session.signal,
|
|
290
|
+
state = {},
|
|
291
|
+
) {
|
|
292
|
+
const selected = { ...configuration, agents: await desiredAgents(session, signal) };
|
|
293
|
+
state.onDesired?.(selected.agents);
|
|
294
|
+
const newlyWanted = selected.agents.filter((kind) => !state.lastDesired?.includes(kind));
|
|
295
|
+
const retained = await removeUndeclaredAgents(options.directory, selected.agents, state.busy);
|
|
296
|
+
const pointerKinds = state.busy
|
|
297
|
+
? (
|
|
298
|
+
await Promise.all(
|
|
299
|
+
newlyWanted.map(async (kind) =>
|
|
300
|
+
(await readAgentState(options.directory, kind)).release === null ? kind : null,
|
|
301
|
+
),
|
|
302
|
+
)
|
|
303
|
+
).filter((kind) => kind !== null)
|
|
304
|
+
: newlyWanted;
|
|
305
|
+
const updated = await updateDeclaredAgents(
|
|
306
|
+
{ directory: options.directory, at: dependencies.now?.(), forced: state.forced },
|
|
307
|
+
{ ...selected, agents: state.pointer ? pointerKinds : selected.agents },
|
|
308
|
+
dependencies,
|
|
309
|
+
);
|
|
310
|
+
const readiness = await agentReadiness(
|
|
311
|
+
{ directory: options.directory },
|
|
312
|
+
{ ...selected, agents: [...selected.agents, ...retained] },
|
|
313
|
+
dependencies,
|
|
314
|
+
);
|
|
315
|
+
await reportAgentCapabilities(session, readiness.agents, signal);
|
|
316
|
+
return { selected, updated, reported: readiness.agents, refused: readiness.refused, retained };
|
|
317
|
+
}
|
|
318
|
+
|
|
264
319
|
export async function agentSignedIn(directory, kind, dependencies = {}) {
|
|
265
320
|
const contract = compiledContract(kind);
|
|
266
321
|
const state = await readAgentState(directory, kind);
|
|
@@ -341,15 +396,15 @@ export async function updateAgents(options, dependencies = {}) {
|
|
|
341
396
|
return maintainControl(
|
|
342
397
|
session,
|
|
343
398
|
async (signal) => {
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
399
|
+
const result = await reconcileAgentKinds(
|
|
400
|
+
options,
|
|
401
|
+
session,
|
|
402
|
+
configuration,
|
|
348
403
|
dependencies,
|
|
404
|
+
signal,
|
|
405
|
+
{ forced: true },
|
|
349
406
|
);
|
|
350
|
-
|
|
351
|
-
await reportAgentCapabilities(session, agents, signal);
|
|
352
|
-
return { updated, reported: agents };
|
|
407
|
+
return { updated: result.updated, reported: result.reported };
|
|
353
408
|
},
|
|
354
409
|
options.signal,
|
|
355
410
|
);
|
package/src/contract.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CLAUDE_PASTED_CODE, CODEX_DEVICE_CODE, GROK_DEVICE_CODE } from "./agent-logins.js";
|
|
2
2
|
|
|
3
|
-
export const KIT_CONTRACT_VERSION = "2026-09-
|
|
3
|
+
export const KIT_CONTRACT_VERSION = "2026-09-27-wanted-agent-clis";
|
|
4
4
|
export const KIT_CAPTURE_BATCH_MAX = 100;
|
|
5
5
|
export const COMMAND_INPUT_MAX_BYTES = 4 * 1024;
|
|
6
6
|
export const AUTHORIZATION_OUTPUT_MAX_BYTES = 4 * 1024;
|
|
@@ -195,7 +195,7 @@ export const ADAPTER_CONTRACTS = Object.freeze({
|
|
|
195
195
|
guidedLogin: CLAUDE_PASTED_CODE,
|
|
196
196
|
identity: Object.freeze({ agentInfo: "@agentclientprotocol/claude-agent-acp" }),
|
|
197
197
|
nativeQuestion: null,
|
|
198
|
-
sessionLoad:
|
|
198
|
+
sessionLoad: true,
|
|
199
199
|
sessionConfig: Object.freeze({ model: "model", effort: "effort" }),
|
|
200
200
|
capabilities: Object.freeze({
|
|
201
201
|
field_kinds: ELICITATION_FIELD_KINDS,
|
|
@@ -288,7 +288,7 @@ export const KIT_DOOR_DISCOVER_PATH = "/kit/door/discover";
|
|
|
288
288
|
export const KIT_DOOR_BOOTSTRAP_PATH = "/kit/door/bootstrap";
|
|
289
289
|
export const KIT_DOOR_ENUMERATE_PATH = "/kit/door/enumerate";
|
|
290
290
|
export const KIT_DOOR_BATCH_PATH = "/kit/door/batch";
|
|
291
|
-
export const EXPLICIT_SYNC_VERSION = "explicit-sync/
|
|
291
|
+
export const EXPLICIT_SYNC_VERSION = "explicit-sync/2";
|
|
292
292
|
export const KIT_DOOR_UNMOUNT_PATH = "/kit/door/unmount";
|
|
293
293
|
|
|
294
294
|
export const MIGRATOR_CONTRACT = "kit-migrator/1";
|