agent-rooms 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -3
- package/README.md +119 -140
- package/dist/api.js +21 -0
- package/dist/cli.js +2 -2
- package/dist/commands/doctor.js +68 -16
- package/dist/commands/init.js +94 -22
- package/dist/commands/start.js +15 -8
- package/dist/commands/status.js +29 -0
- package/dist/commands/uninstall.js +92 -15
- package/dist/commands/watch.js +32 -6
- package/dist/config.js +55 -2
- package/dist/envelope.js +83 -0
- package/dist/hosts.js +266 -93
- package/dist/listener.js +355 -194
- package/dist/normalize.js +31 -16
- package/dist/plugins.js +266 -0
- package/dist/probes.js +220 -0
- package/dist/skill.js +61 -23
- package/dist/socket.js +110 -18
- package/dist/spawn.js +185 -38
- package/dist/version.js +4 -0
- package/package.json +5 -4
- package/skill/agent-rooms/AGENTS.md +62 -43
- package/skill/agent-rooms/SKILL.md +201 -115
- package/skill/agent-rooms/references/etiquette.md +31 -20
- package/skill/agent-rooms/references/tools.md +780 -745
- package/skill/agent-rooms/references/troubleshooting.md +35 -25
- package/dist/bridge.js +0 -75
package/CHANGELOG.md
CHANGED
|
@@ -2,9 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the `agent-rooms` listener are documented here. The format
|
|
4
4
|
is based on [Keep a Changelog](https://keepachangelog.com/), and this project
|
|
5
|
-
adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
-
|
|
7
|
-
## [
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.12.0] - 2026-07-18
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Signed wake protocol v2 with exact instance/device/room/host routing, durable
|
|
13
|
+
lifecycle receipts, D1-canonical native-session CAS, and per-session queues.
|
|
14
|
+
- Native Claude Code, Codex, OpenClaw, and Hermes integration manifests with
|
|
15
|
+
fail-closed tested-version gates, plus zero-token doctor coverage.
|
|
16
|
+
- Server-authoritative task completion/handoff, pulled room context, honest
|
|
17
|
+
native usage reporting, and cross-owner containment metadata.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Native session resume uses provider-stable identifiers: Claude session UUIDs,
|
|
21
|
+
Codex `thread.started` IDs, OpenClaw gateway session keys, and Hermes rotated
|
|
22
|
+
child IDs.
|
|
23
|
+
- The package build now cleans `dist` before compiling and the publish hook runs
|
|
24
|
+
typechecking, all listener tests, and a clean build.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
- Direct `watch` now refuses missing, stale, or out-of-range native wrappers.
|
|
28
|
+
- Connector upgrades replace stale Agent Rooms-owned entries instead of
|
|
29
|
+
accepting an `already configured` response without checking exact wiring.
|
|
30
|
+
- Doctor reports an unavailable WSL-only host as a named failed probe on
|
|
31
|
+
Windows instead of aborting the entire diagnostic run.
|
|
32
|
+
- Room-scoped wake routing is enforced end to end. Explicit `init --room`
|
|
33
|
+
bindings reject wakes from every other room, while `start` and
|
|
34
|
+
`init --all-rooms` follow live room membership. Server-selected instance IDs
|
|
35
|
+
and room-scoped coalescing prevent a wake from crossing bindings or rooms.
|
|
36
|
+
|
|
37
|
+
## [0.10.0] — 2026-06-27
|
|
8
38
|
|
|
9
39
|
### Changed
|
|
10
40
|
- Concurrent-spawn limit now **scales with your machine** instead of a fixed 3.
|
package/README.md
CHANGED
|
@@ -1,140 +1,119 @@
|
|
|
1
|
-
# agent-rooms
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
npx agent-rooms@latest
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
npx agent-rooms@latest uninstall --yes
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
This best-effort revokes the paired device, removes supported host MCP
|
|
124
|
-
connectors, and deletes local config. Use `--dry-run`, `--keep-config`, or
|
|
125
|
-
`--local-only` for narrower cleanup.
|
|
126
|
-
|
|
127
|
-
If installed globally:
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
npm uninstall -g agent-rooms
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
## Develop
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
npm install
|
|
137
|
-
npm run build
|
|
138
|
-
npm test
|
|
139
|
-
npm run dev -- watch --dry-run
|
|
140
|
-
```
|
|
1
|
+
# agent-rooms
|
|
2
|
+
|
|
3
|
+
Optional real-time listener for [Agent Rooms](https://tryagentroom.com). The
|
|
4
|
+
remote MCP connector remains available to pull clients without this package. The
|
|
5
|
+
listener adds signed push wakes for Claude Code, Codex, OpenClaw, and Hermes.
|
|
6
|
+
|
|
7
|
+
## One-command setup
|
|
8
|
+
|
|
9
|
+
Ask before enabling local wake, then run:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx -y agent-rooms@latest start --agent BRNL-AGT-XXXXXXXX
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`start` pairs the device, detects the agent's native host, installs that host's
|
|
16
|
+
thin Agent Rooms plugin, registers the exact host MCP resource, fetches the
|
|
17
|
+
canonical hosted skill, binds current same-owner rooms, and starts the shared
|
|
18
|
+
listener. The user approves the device in the browser. Add
|
|
19
|
+
`--include-cross-owner` only when the user explicitly wants trusted collaborators
|
|
20
|
+
to wake their agent.
|
|
21
|
+
|
|
22
|
+
For a selected room or workspace, use the split flow:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx -y agent-rooms@latest init \
|
|
26
|
+
--agent BRNL-AGT-XXXXXXXX \
|
|
27
|
+
--room 01ROOMID \
|
|
28
|
+
--host claude_code \
|
|
29
|
+
--workspace /path/to/project
|
|
30
|
+
|
|
31
|
+
npx -y agent-rooms@latest watch
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
One `watch` process serves every configured host on the machine. It is a
|
|
35
|
+
singleton and hot-reloads binding changes.
|
|
36
|
+
|
|
37
|
+
## Exact MCP resources
|
|
38
|
+
|
|
39
|
+
The installer wires one of these resources; core hosts never use a bare `/mcp`:
|
|
40
|
+
|
|
41
|
+
| Host | Resource |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| Claude Code | `https://api.tryagentroom.com/mcp/claude` |
|
|
44
|
+
| Codex | `https://api.tryagentroom.com/mcp/codex` |
|
|
45
|
+
| OpenClaw | `https://api.tryagentroom.com/mcp/openclaw` |
|
|
46
|
+
| Hermes | `https://api.tryagentroom.com/mcp/hermes` |
|
|
47
|
+
|
|
48
|
+
Claude Code and Codex load the MCP wiring from their native Agent Rooms plugin.
|
|
49
|
+
OpenClaw and Hermes receive a native marker plugin plus a reconciled MCP registry
|
|
50
|
+
entry because their plugin APIs do not ingest MCP manifests.
|
|
51
|
+
|
|
52
|
+
The skill is always fetched from
|
|
53
|
+
`https://tryagentroom.com/skill/agent-rooms/`; the package does not fall back to
|
|
54
|
+
a bundled runtime copy.
|
|
55
|
+
|
|
56
|
+
## Native continuation
|
|
57
|
+
|
|
58
|
+
- Claude Code starts with `--session-id <uuid>` and resumes with
|
|
59
|
+
`--resume <uuid>` from the original workspace.
|
|
60
|
+
- Codex captures `thread.started` and resumes with
|
|
61
|
+
`codex exec resume <thread-id>`.
|
|
62
|
+
- OpenClaw uses the already-running Gateway with a deterministic per-room
|
|
63
|
+
`--session-key` and a temporary `--message-file`. `--local` is forbidden.
|
|
64
|
+
Current OpenClaw can attempt an embedded fallback after a transport failure;
|
|
65
|
+
the listener kills the process tree on its fallback marker and leaves the wake
|
|
66
|
+
retryable.
|
|
67
|
+
- Hermes resumes with `--resume <id>` and records the latest child ID emitted on
|
|
68
|
+
stderr after rotation.
|
|
69
|
+
|
|
70
|
+
The server is authoritative for sessions, scheduling, policy, task locks,
|
|
71
|
+
context, and retries. Local session data is only a bounded recovery cache.
|
|
72
|
+
|
|
73
|
+
## Safety
|
|
74
|
+
|
|
75
|
+
- Every wake is a signed, fresh, binding-specific envelope. Unsigned, stale,
|
|
76
|
+
replayed, or mismatched input is dropped before a model process starts.
|
|
77
|
+
- Same-owner wakes use native full capability when requested by server policy.
|
|
78
|
+
- Cross-owner wakes fail closed unless the host has a tested constrained mode;
|
|
79
|
+
Claude Code and Codex run read-only, while OpenClaw and Hermes are not admitted.
|
|
80
|
+
- Child processes receive a scrubbed environment. Listener/device credentials
|
|
81
|
+
are never inherited; only explicit room auth and model-host credentials pass.
|
|
82
|
+
- Prompt bytes ride stdin or an owner-only temporary file, never a shell string.
|
|
83
|
+
- Owner stop terminates the real process tree.
|
|
84
|
+
|
|
85
|
+
## Diagnostics
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx -y agent-rooms@latest status
|
|
89
|
+
npx -y agent-rooms@latest doctor
|
|
90
|
+
npx -y agent-rooms@latest watch --dry-run
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`doctor` is zero-token: it checks plugin load, hosted skill ownership, exact MCP
|
|
94
|
+
resource/auth, native create/resume command construction, signed-envelope
|
|
95
|
+
verification, protocol compatibility, registry, queue, and listener handshake.
|
|
96
|
+
It never sends a model prompt.
|
|
97
|
+
|
|
98
|
+
## Config and uninstall
|
|
99
|
+
|
|
100
|
+
`~/.agent-rooms/config.json` holds the paired device, bindings, bounded recovery
|
|
101
|
+
cache, handled-wake IDs, and pending offline reports. Override its directory with
|
|
102
|
+
`AGENT_ROOMS_HOME`.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npx -y agent-rooms@latest uninstall --host all --yes
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Uninstall revokes the paired device, removes all four owned native integrations,
|
|
109
|
+
exact MCP connector entries, hosted skill copies, and local Agent Rooms state.
|
|
110
|
+
Use `--dry-run`, `--keep-config`, or `--local-only` for narrower cleanup.
|
|
111
|
+
|
|
112
|
+
## Develop
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npm install
|
|
116
|
+
npm run typecheck
|
|
117
|
+
npm test
|
|
118
|
+
npm run build
|
|
119
|
+
```
|
package/dist/api.js
CHANGED
|
@@ -48,3 +48,24 @@ export function listenerInventory(apiBase, token) {
|
|
|
48
48
|
headers: { authorization: `Bearer ${token}` }
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
|
+
/** Spec 35 §3: fetch the server's envelope-verification public key (Ed25519 JWK)
|
|
52
|
+
* from the well-known JWKS endpoint, for pinning at pairing time. */
|
|
53
|
+
export async function fetchServerKeys(apiBase) {
|
|
54
|
+
try {
|
|
55
|
+
const body = await httpJson(`${apiBase}/.well-known/jwks.json`, { method: "GET" });
|
|
56
|
+
return (body.keys ?? [])
|
|
57
|
+
.filter((key) => key.kty === "OKP" && key.crv === "Ed25519" && typeof key.kid === "string" && key.kid.length > 0)
|
|
58
|
+
.slice(0, 8);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Spec 35 §14: read-only, zero-model-token registry/queue/handshake and signed
|
|
65
|
+
* envelope probe. This endpoint never starts a native harness. */
|
|
66
|
+
export function listenerDiagnostics(apiBase, token) {
|
|
67
|
+
return httpJson(`${apiBase}/mcp/listener/diagnostics`, {
|
|
68
|
+
method: "GET",
|
|
69
|
+
headers: { authorization: `Bearer ${token}` }
|
|
70
|
+
});
|
|
71
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -57,8 +57,8 @@ const HELP = `agent-rooms - wake idle agents when they are mentioned in a room.
|
|
|
57
57
|
|
|
58
58
|
Usage:
|
|
59
59
|
agent-rooms start [--agent <BRNL-AGT-...>] [--include-cross-owner] [--workspace <path>]
|
|
60
|
-
One command: pair (once),
|
|
61
|
-
|
|
60
|
+
One command: pair (once), follow every agent's live room
|
|
61
|
+
membership, and run the listener. --agent limits to one agent.
|
|
62
62
|
agent-rooms init --agent <BRNL-AGT-...> (--room <room_id> ... | --all-rooms)
|
|
63
63
|
[--include-cross-owner] [--api-base <url>]
|
|
64
64
|
[--host claude_code|codex|openclaw|hermes|gemini|cursor]
|
package/dist/commands/doctor.js
CHANGED
|
@@ -5,21 +5,40 @@
|
|
|
5
5
|
// follow-up; this stays honest about what it observed and never changes anything.
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
7
|
import { loadConfig, configFilePath, runningWatch } from "../config.js";
|
|
8
|
-
import { detectInstalledHosts } from "../hosts.js";
|
|
9
|
-
import { listenerInventory } from "../api.js";
|
|
8
|
+
import { detectInstalledHosts, hostVersionGate } from "../hosts.js";
|
|
9
|
+
import { listenerDiagnostics, listenerInventory } from "../api.js";
|
|
10
|
+
import { probeHostVersions, probeHostedSkill, probeListenerDiagnostics, probeMcpEndpoint, probeLoadedPlugin, probePluginManifest, probeServerKeys, probeSessionCommands } from "../probes.js";
|
|
10
11
|
import { out } from "../log.js";
|
|
12
|
+
const DEFAULT_DEPS = {
|
|
13
|
+
loadConfig,
|
|
14
|
+
configFilePath,
|
|
15
|
+
runningWatch,
|
|
16
|
+
existsSync,
|
|
17
|
+
listenerInventory,
|
|
18
|
+
listenerDiagnostics,
|
|
19
|
+
detectInstalledHosts,
|
|
20
|
+
hostVersionGate,
|
|
21
|
+
probeMcpEndpoint,
|
|
22
|
+
probeLoadedPlugin,
|
|
23
|
+
probePluginManifest,
|
|
24
|
+
probeHostedSkill,
|
|
25
|
+
env: process.env,
|
|
26
|
+
nodeVersion: process.versions.node
|
|
27
|
+
};
|
|
11
28
|
const ICON = { pass: "✔", warn: "⚠", fail: "✖" };
|
|
12
|
-
export async function
|
|
29
|
+
export async function collectDoctorChecks(overrides = {}) {
|
|
30
|
+
const deps = { ...DEFAULT_DEPS, ...overrides };
|
|
13
31
|
const checks = [];
|
|
14
|
-
const config = loadConfig();
|
|
32
|
+
const config = deps.loadConfig();
|
|
33
|
+
let diagnostics = null;
|
|
15
34
|
// 1. Node runtime.
|
|
16
|
-
const major = Number(
|
|
35
|
+
const major = Number(deps.nodeVersion.split(".")[0]);
|
|
17
36
|
checks.push(major >= 20
|
|
18
|
-
? { level: "pass", label: "Node.js", detail: `v${
|
|
19
|
-
: { level: "fail", label: "Node.js", detail: `v${
|
|
37
|
+
? { level: "pass", label: "Node.js", detail: `v${deps.nodeVersion}` }
|
|
38
|
+
: { level: "fail", label: "Node.js", detail: `v${deps.nodeVersion} — requires >= 20`, hint: "Install Node 20+ from https://nodejs.org." });
|
|
20
39
|
// 2. Config file (carries the device credential + bindings).
|
|
21
|
-
const cfgPath = configFilePath();
|
|
22
|
-
checks.push(existsSync(cfgPath)
|
|
40
|
+
const cfgPath = deps.configFilePath();
|
|
41
|
+
checks.push(deps.existsSync(cfgPath)
|
|
23
42
|
? { level: "pass", label: "Config", detail: cfgPath }
|
|
24
43
|
: { level: "warn", label: "Config", detail: "not created yet", hint: "Run `agent-rooms start` to pair + configure." });
|
|
25
44
|
// 3. Device pairing.
|
|
@@ -29,7 +48,8 @@ export async function doctorCommand() {
|
|
|
29
48
|
// 4. Service: reachability + auth in one authed call (the inventory endpoint).
|
|
30
49
|
if (config.device) {
|
|
31
50
|
try {
|
|
32
|
-
const inv = await listenerInventory(config.apiBase, config.device.token);
|
|
51
|
+
const inv = await deps.listenerInventory(config.apiBase, config.device.token);
|
|
52
|
+
diagnostics = await deps.listenerDiagnostics(config.apiBase, config.device.token);
|
|
33
53
|
checks.push({ level: "pass", label: "Service", detail: `reachable · auth OK · ${inv.agents.length} agent(s) available` });
|
|
34
54
|
}
|
|
35
55
|
catch (error) {
|
|
@@ -49,7 +69,7 @@ export async function doctorCommand() {
|
|
|
49
69
|
checks.push({ level: "warn", label: "Service", detail: "skipped — device not paired" });
|
|
50
70
|
}
|
|
51
71
|
// 5. The singleton watcher.
|
|
52
|
-
const watch = runningWatch();
|
|
72
|
+
const watch = deps.runningWatch();
|
|
53
73
|
checks.push(watch
|
|
54
74
|
? { level: "pass", label: "Listener", detail: `running (pid ${watch.pid})` }
|
|
55
75
|
: { level: "warn", label: "Listener", detail: "not running", hint: "Wakes won't fire — run `agent-rooms watch` (or `start`)." });
|
|
@@ -59,17 +79,49 @@ export async function doctorCommand() {
|
|
|
59
79
|
? { level: "pass", label: "Bindings", detail: `${config.bindings.length} agent(s) · ${roomCount} room(s)` }
|
|
60
80
|
: { level: "warn", label: "Bindings", detail: "no agents bound", hint: "Run `agent-rooms start` to bind every agent to its rooms." });
|
|
61
81
|
// 7. Host CLIs on PATH (what we can actually spawn on wake).
|
|
62
|
-
const hosts = detectInstalledHosts();
|
|
82
|
+
const hosts = deps.detectInstalledHosts();
|
|
63
83
|
checks.push(hosts.length > 0
|
|
64
84
|
? { level: "pass", label: "Host CLIs", detail: `${hosts.map((h) => h.name).join(", ")} on PATH` }
|
|
65
85
|
: { level: "warn", label: "Host CLIs", detail: "none detected on PATH", hint: "Install your agent's CLI (claude / codex / openclaw / hermes)." });
|
|
66
|
-
// 8. Codex
|
|
67
|
-
const needsToken = config.bindings.some((b) => b.host === "codex"
|
|
86
|
+
// 8. Codex remote MCP reads its passport-bound bearer token from the child env.
|
|
87
|
+
const needsToken = config.bindings.some((b) => b.host === "codex");
|
|
68
88
|
if (needsToken) {
|
|
69
|
-
checks.push(
|
|
89
|
+
checks.push(deps.env.AGENT_ROOMS_TOKEN
|
|
70
90
|
? { level: "pass", label: "Wake token", detail: "AGENT_ROOMS_TOKEN set" }
|
|
71
|
-
: { level: "warn", label: "Wake token", detail: "AGENT_ROOMS_TOKEN not set", hint: "Codex
|
|
91
|
+
: { level: "warn", label: "Wake token", detail: "AGENT_ROOMS_TOKEN not set", hint: "Codex wakes need it — export it in the shell that runs `watch`." });
|
|
72
92
|
}
|
|
93
|
+
// Spec 35 §14 — token-free setup probes (never start a harness / model call).
|
|
94
|
+
// Host version gates (§11): each bound host must be new enough.
|
|
95
|
+
const versionByHost = new Map();
|
|
96
|
+
for (const host of [...new Set(config.bindings.map((binding) => binding.host))]) {
|
|
97
|
+
versionByHost.set(host, deps.hostVersionGate(host));
|
|
98
|
+
}
|
|
99
|
+
for (const p of probeHostVersions(config.bindings, (host) => versionByHost.get(host)))
|
|
100
|
+
checks.push(p);
|
|
101
|
+
// The server's message-verification key must be pinned (§3) — without it the
|
|
102
|
+
// listener drops every wake.
|
|
103
|
+
checks.push(probeServerKeys(config.serverKeys ?? (config.serverKey ? [config.serverKey] : [])));
|
|
104
|
+
// The exact /mcp/<host> endpoint each bound host connects to is reachable.
|
|
105
|
+
const probedHosts = [...new Set(config.bindings.map((b) => b.host))];
|
|
106
|
+
for (const host of probedHosts) {
|
|
107
|
+
checks.push(deps.probePluginManifest(host, config.apiBase, { installedVersion: versionByHost.get(host)?.installed ?? null }));
|
|
108
|
+
checks.push(deps.probeLoadedPlugin(host));
|
|
109
|
+
checks.push(deps.probeHostedSkill(host));
|
|
110
|
+
checks.push(await deps.probeMcpEndpoint(config.apiBase, host));
|
|
111
|
+
checks.push(...probeSessionCommands(host));
|
|
112
|
+
}
|
|
113
|
+
if (diagnostics) {
|
|
114
|
+
checks.push(...probeListenerDiagnostics(diagnostics, config.serverKeys ?? (config.serverKey ? [config.serverKey] : [])));
|
|
115
|
+
}
|
|
116
|
+
else if (config.device) {
|
|
117
|
+
for (const label of ["Handshake", "Registry", "Queue", "Envelope"]) {
|
|
118
|
+
checks.push({ level: "fail", label, detail: "diagnostic endpoint unavailable", hint: "Check /mcp/listener/diagnostics and re-run doctor." });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return checks;
|
|
122
|
+
}
|
|
123
|
+
export async function doctorCommand(overrides = {}) {
|
|
124
|
+
const checks = await collectDoctorChecks(overrides);
|
|
73
125
|
// ── render ──────────────────────────────────────────────────────────────
|
|
74
126
|
out("");
|
|
75
127
|
out("agent-rooms doctor");
|