@staix/agent-hub 0.21.0 → 0.23.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 +19 -0
- package/README.md +2 -2
- package/docs/agent-notes/adapters.md +2 -1
- package/docs/agent-notes/budget.md +1 -1
- package/docs/agent-notes/bus.md +5 -2
- package/docs/agent-notes/daemon.md +5 -4
- package/docs/agent-notes/local-worker.md +1 -1
- package/docs/agent-notes/models.md +2 -1
- package/docs/agent-notes/tasks.md +2 -1
- package/docs/agent-notes/tests.md +2 -2
- package/docs/dogfood.md +10 -2
- package/docs/operations.md +192 -6
- package/docs/profiles.md +29 -4
- package/docs/quickstart.md +9 -1
- package/docs/security.md +7 -1
- package/docs/smoke.md +6 -0
- package/docs/specs/2026-09-19-agent-hub-design.md +94 -1
- package/docs/verification/2026-10-30-0.22.0.md +49 -0
- package/docs/verification/2026-10-30-0.23.0.md +70 -0
- package/docs/verification/2026-10-30-445-capability-probes.md +172 -0
- package/docs/verification/2026-10-30-adapter-operational-settings.md +261 -0
- package/docs/verification/2026-10-30-bus-delivery-operational-settings.md +77 -0
- package/docs/verification/2026-10-30-jcode-ac4.md +24 -0
- package/docs/verification/2026-10-30-jcode-ac6.md +52 -0
- package/docs/verification/2026-10-30-model-operational-settings.md +211 -0
- package/docs/verification/2026-10-30-operational-settings-closure.md +1217 -0
- package/docs/verification/2026-10-30-recovery-wait-settings.md +141 -0
- package/docs/verification/2026-10-30-ui-console-operational-settings.md +1845 -0
- package/docs/verified.json +15 -15
- package/package.json +1 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/ROLE-NOTICE.md +1 -1
- package/plugins/agent-hub/server.js +559 -115
- package/src/adapters/acp.ts +48 -2
- package/src/adapters/claude-channel.ts +6 -4
- package/src/adapters/codex-appserver.ts +19 -0
- package/src/adapters/jcode.ts +1 -1
- package/src/adapters/local-worker.ts +3 -3
- package/src/adapters/native-capabilities.ts +50 -0
- package/src/adapters/registry-types.ts +3 -10
- package/src/cli/capabilities-output.ts +30 -0
- package/src/cli/console-state.ts +8 -5
- package/src/cli/console.ts +27 -4
- package/src/cli/help.ts +3 -1
- package/src/cli/identity.ts +4 -1
- package/src/cli/learn-output.ts +38 -0
- package/src/cli/main.ts +28 -2
- package/src/cli/recovery-waits.ts +26 -0
- package/src/cli/smoke-pty-bridge.ts +67 -18
- package/src/cli/smoke-pty.ts +10 -2
- package/src/cli/upgrade-runtime.ts +16 -14
- package/src/cli/upgrade.ts +2 -1
- package/src/hub/board.ts +8 -2
- package/src/hub/bus.ts +117 -17
- package/src/hub/capabilities.ts +215 -0
- package/src/hub/config-apply.ts +8 -4
- package/src/hub/config-trust.ts +1 -0
- package/src/hub/control-client.ts +41 -8
- package/src/hub/daemon.ts +436 -33
- package/src/hub/deferred-hub.ts +2 -2
- package/src/hub/delivery-hints.ts +2 -2
- package/src/hub/delivery-journal.ts +80 -61
- package/src/hub/envelope.ts +12 -2
- package/src/hub/events.ts +2 -0
- package/src/hub/hub-tools.ts +16 -1
- package/src/hub/inference.ts +10 -5
- package/src/hub/invocations.ts +316 -0
- package/src/hub/learning.ts +33 -1
- package/src/hub/operational-settings.ts +180 -1
- package/src/hub/peers.ts +3 -0
- package/src/hub/restart.ts +2 -2
- package/src/hub/room-invocations.ts +145 -0
- package/src/hub/settings.ts +22 -2
- package/src/hub/shipped-packs.ts +5 -2
- package/src/hub/shipped-roles.ts +13 -0
- package/src/hub/tasks.ts +92 -1
- package/src/memory/capture.ts +2 -2
- package/src/models/relay.ts +20 -9
- package/src/rooms/application.ts +67 -2
- package/src/rooms/boundary.ts +43 -11
- package/src/rooms/service.ts +40 -2
- package/templates/checks/analysis.mjs +59 -0
- package/templates/checks/fixtures/analysis-expected.json +1 -0
- package/templates/checks/fixtures/analysis-input.json +1 -0
- package/templates/checks/fixtures/analysis.mjs +7 -0
- package/templates/checks/fixtures/ops.json +4 -0
- package/templates/checks/fixtures/writing-rules.json +4 -0
- package/templates/checks/fixtures/writing.json +5 -0
- package/templates/checks/ops.mjs +40 -0
- package/templates/checks/writing.mjs +40 -0
- package/templates/packs/analysis.pack.json +14 -0
- package/templates/packs/ops.pack.json +14 -0
- package/templates/packs/writing-pack.pack.json +14 -0
- package/templates/roles/NOTICE.md +1 -1
- package/templates/roles/ops/meeting-actions-owner.md +26 -0
- package/templates/roles/ops/project-manager.md +26 -0
- package/templates/roles/writing/editor.md +26 -0
- package/templates/roles/writing/style-checker.md +26 -0
- package/templates/roles/writing/terminology-checker.md +26 -0
- package/templates/roles/writing/translator.md +26 -0
|
@@ -9,7 +9,7 @@ export function stateDirFor(cwd: string): string {
|
|
|
9
9
|
|
|
10
10
|
/** Control WS wire version. 2 = `deliver` carries `envs` (digests); 3 = `tools` role and task messages; 4 = budget messages and `hub_checkpoint`; 5 = `ask`; 6 = console-only `ui` session bootstrap; 8 = controlled recovery; 9 = Pi bridge metadata; 10 = durable delivery receipts; 11 = queue hold diagnostics; 12 = generation-bound channel settlement and execution budgets; 13 = turn-free facts (`facts` with tool, session and transcript binding, `silenced`) and per-recipient send results (issue #108). 14 = native context-window status and session-bound context checkpoint request ids (issue #185). 15 = conductor tools and permission deadlines/closed events (issues #190, #194). 16 = a Claude session without channel evidence: hello `channel: false` and `inbox` (issue #205). The plugin is installed apart from the daemon, so they can drift. */
|
|
11
11
|
export const PROTOCOL = 16;
|
|
12
|
-
export const DAEMON_FEATURES = ["conductor_recovery", "encrypted_rooms", "external_adapter_instances", "external_adapter_modules", "configured_acp", "native_hook_launch", "task_cancel", "recovery_deferred_hub", "peer_instances", "role_stages", "task_planners", "release_trains", "reviewer_pool", "native_tui_launch", "terminal_owner", "cli_audit_delegated"] as const;
|
|
12
|
+
export const DAEMON_FEATURES = ["conductor_recovery", "encrypted_rooms", "external_adapter_instances", "external_adapter_modules", "configured_acp", "native_hook_launch", "task_cancel", "invocation", "recovery_deferred_hub", "peer_instances", "role_stages", "task_planners", "release_trains", "reviewer_pool", "native_tui_launch", "terminal_owner", "cli_audit_delegated"] as const;
|
|
13
13
|
/** Protocols a current coordinator may authenticate while upgrading a running source. */
|
|
14
14
|
export const RECOVERY_SOURCE_PROTOCOLS = [9, 10, 11, 12, 13, 14, 15, PROTOCOL] as const;
|
|
15
15
|
|
|
@@ -86,15 +86,25 @@ export function readControl(stateDir: string): ControlDescriptor | undefined {
|
|
|
86
86
|
|
|
87
87
|
const instancePins = new Map<string, { instanceId: string }>();
|
|
88
88
|
|
|
89
|
+
/** Maximum supported 3600 s approval plus admission/transport margin; applies to every hub_invoke client path. */
|
|
90
|
+
export const INVOCATION_CONTROL_TIMEOUT_MS = 3_665_000;
|
|
91
|
+
export function controlRequestTimeout(msg: Record<string, unknown>, requested = 30_000): number {
|
|
92
|
+
return msg.t === "task" && msg.op === "hub_invoke" && requested !== 0
|
|
93
|
+
? Math.max(Number.isFinite(requested) ? requested : 0, INVOCATION_CONTROL_TIMEOUT_MS) : requested;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
89
97
|
/** One authenticated control connection. `request` correlates replies by `rid`; everything else goes to `onPush`. */
|
|
90
98
|
export class ControlClient {
|
|
91
99
|
features: readonly string[] = [];
|
|
92
100
|
private welcomeInstanceId?: string;
|
|
93
101
|
/** Identity from the authenticated welcome, never the unauthenticated descriptor alone. */
|
|
94
102
|
get instanceId(): string | undefined { return this.welcomeInstanceId; }
|
|
95
|
-
get isOpen(): boolean { return this.ws.readyState === WebSocket.OPEN; }
|
|
103
|
+
get isOpen(): boolean { return !this.closing && this.ws.readyState === WebSocket.OPEN; }
|
|
96
104
|
private nextRid = 1;
|
|
97
105
|
private readonly pending = new Map<number, (msg: any) => void>();
|
|
106
|
+
private readonly invocations = new Set<number>();
|
|
107
|
+
private closing = false;
|
|
98
108
|
onPush: (msg: any) => void = () => {};
|
|
99
109
|
/** `code`/`reason`: what the hub closed with, e.g. 4000 when another client attached as the same peer. */
|
|
100
110
|
onClose: (code: number, reason: string) => void = () => {};
|
|
@@ -179,13 +189,32 @@ export class ControlClient {
|
|
|
179
189
|
}
|
|
180
190
|
|
|
181
191
|
/** `timeoutMs`: for calls a person waits on. The hub has its own budget; this one only makes sure the CLI never hangs. */
|
|
182
|
-
request(msg: Record<string, unknown>, timeoutMs = 30_000): Promise<any> {
|
|
183
|
-
if (this.ws.readyState !== WebSocket.OPEN) return Promise.resolve({ ok: false, error: "hub connection is not open" });
|
|
192
|
+
request(msg: Record<string, unknown>, timeoutMs = 30_000, signal?: AbortSignal): Promise<any> {
|
|
193
|
+
if (this.ws.readyState !== WebSocket.OPEN || this.closing && msg.t !== "request_cancel") return Promise.resolve({ ok: false, error: "hub connection is not open" });
|
|
194
|
+
const invocation = msg.t === "task" && msg.op === "hub_invoke";
|
|
195
|
+
timeoutMs = controlRequestTimeout(msg, timeoutMs);
|
|
196
|
+
if (signal?.aborted) return Promise.resolve({ ok: false, error: "hub request cancelled before sending" });
|
|
184
197
|
const rid = this.nextRid++;
|
|
185
198
|
return new Promise((resolve) => {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
199
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
200
|
+
const done = (reply: any) => {
|
|
201
|
+
this.pending.delete(rid); this.invocations.delete(rid); clearTimeout(timer); signal?.removeEventListener("abort", aborted); resolve(reply);
|
|
202
|
+
};
|
|
203
|
+
const abandon = (error: string) => {
|
|
204
|
+
if (!this.pending.has(rid)) return;
|
|
205
|
+
if (invocation && this.ws.readyState === WebSocket.OPEN) {
|
|
206
|
+
try { this.ws.send(JSON.stringify({ t: "request_cancel", request: rid })); } catch { /* Socket close also withdraws admission. */ }
|
|
207
|
+
}
|
|
208
|
+
done({ ok: false, error: invocation ? `${error}; invocation admission unconfirmed, inspect before retrying` : error });
|
|
209
|
+
};
|
|
210
|
+
const aborted = () => abandon("hub request cancelled");
|
|
211
|
+
this.pending.set(rid, done);
|
|
212
|
+
if (invocation) this.invocations.add(rid);
|
|
213
|
+
signal?.addEventListener("abort", aborted, { once: true });
|
|
214
|
+
if (signal?.aborted) { aborted(); return; }
|
|
215
|
+
timer = timeoutMs ? setTimeout(() => abandon(`no answer from the hub within ${Math.round(timeoutMs / 1000)} s`), timeoutMs) : undefined;
|
|
216
|
+
try { this.ws.send(JSON.stringify({ ...msg, rid })); }
|
|
217
|
+
catch { abandon("hub request could not be sent"); }
|
|
189
218
|
});
|
|
190
219
|
}
|
|
191
220
|
|
|
@@ -194,6 +223,10 @@ export class ControlClient {
|
|
|
194
223
|
}
|
|
195
224
|
|
|
196
225
|
close(): void {
|
|
197
|
-
this.
|
|
226
|
+
if (this.closing) return;
|
|
227
|
+
this.closing = true;
|
|
228
|
+
if (!this.invocations.size || this.ws.readyState !== WebSocket.OPEN) { this.ws.close(); return; }
|
|
229
|
+
// Withdraw admissions before reporting a graceful close to callers. An abrupt transport loss is fenced by the daemon close handler.
|
|
230
|
+
void Promise.allSettled([...this.invocations].map(request => this.request({ t: "request_cancel", request }, 3000))).finally(() => this.ws.close());
|
|
198
231
|
}
|
|
199
232
|
}
|