@staix/agent-hub 0.12.33 → 0.12.34
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 +9 -0
- package/README.md +3 -3
- package/docs/agent-notes/adapters.md +7 -3
- package/docs/agent-notes/budget.md +5 -3
- package/docs/agent-notes/daemon.md +3 -3
- package/docs/agent-notes/tasks.md +2 -1
- package/docs/agent-notes/tests.md +2 -1
- package/docs/operations.md +20 -10
- package/docs/quickstart.md +4 -2
- package/docs/security.md +4 -1
- package/docs/smoke.md +1 -1
- package/docs/specs/2026-09-19-agent-hub-design.md +246 -4
- package/docs/verification/2026-10-18-0.12.34.md +48 -0
- package/docs/verified.json +29 -18
- package/package.json +1 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +22882 -16496
- package/src/adapters/codex-appserver.ts +81 -11
- package/src/adapters/definitions/builtin-metadata.ts +101 -7
- package/src/adapters/definitions/native-cli-claude-launch.ts +90 -0
- package/src/adapters/definitions/native-cli-claude-setup.ts +80 -0
- package/src/adapters/definitions/native-cli-codex-launch.ts +14 -0
- package/src/adapters/definitions/native-cli-doctor.ts +46 -0
- package/src/adapters/definitions/native-cli-entry-claude.ts +52 -0
- package/src/adapters/definitions/native-cli-entry-codex.ts +26 -0
- package/src/adapters/definitions/native-cli-entry-jcode.ts +24 -0
- package/src/adapters/definitions/native-cli-entry-kimi.ts +52 -0
- package/src/adapters/definitions/native-cli-entry-local.ts +14 -0
- package/src/adapters/definitions/native-cli-entry-pi.ts +51 -0
- package/src/adapters/definitions/native-cli-entry-qwen.ts +20 -0
- package/src/adapters/definitions/native-cli-entry-types.ts +40 -0
- package/src/adapters/definitions/native-cli-entry.ts +19 -0
- package/src/adapters/definitions/native-cli-jcode-mcp.ts +58 -0
- package/src/adapters/definitions/native-cli-launch-common.ts +17 -0
- package/src/adapters/definitions/native-cli-pi.ts +33 -0
- package/src/adapters/definitions/native-cli-preview-claude.ts +37 -0
- package/src/adapters/definitions/native-cli-preview-codex.ts +20 -0
- package/src/adapters/definitions/native-cli-preview-common.ts +57 -0
- package/src/adapters/definitions/native-cli-preview-jcode.ts +20 -0
- package/src/adapters/definitions/native-cli-preview-kimi.ts +28 -0
- package/src/adapters/definitions/native-cli-preview-pi.ts +36 -0
- package/src/adapters/definitions/native-cli-preview-qwen.ts +22 -0
- package/src/adapters/definitions/native-cli-preview.ts +16 -0
- package/src/adapters/definitions/native-config-defaults.ts +19 -0
- package/src/adapters/definitions/native-config-policy.ts +35 -0
- package/src/adapters/definitions/native-context.ts +35 -0
- package/src/adapters/definitions/native-daemon-kimi.ts +5 -0
- package/src/adapters/definitions/native-daemon-policy.ts +18 -0
- package/src/adapters/definitions/native-display.ts +18 -0
- package/src/adapters/definitions/native-factories.ts +48 -0
- package/src/adapters/definitions/native-policy-aux.ts +130 -0
- package/src/adapters/definitions/native-quota.ts +29 -0
- package/src/adapters/definitions/native-recovery-continuity.ts +12 -0
- package/src/adapters/definitions/native-recovery.ts +88 -0
- package/src/adapters/definitions/qwen.ts +8 -4
- package/src/adapters/registry-types.ts +97 -10
- package/src/adapters/registry.ts +103 -3
- package/src/cli/console-state.ts +5 -5
- package/src/cli/identity.ts +6 -5
- package/src/cli/jcode-mcp.ts +2 -58
- package/src/cli/launch.ts +14 -100
- package/src/cli/main.ts +35 -272
- package/src/cli/output.ts +3 -2
- package/src/cli/preview.ts +14 -125
- package/src/cli/recovery-guidance.ts +17 -13
- package/src/cli/setup.ts +2 -80
- package/src/cli/shell-quote.ts +3 -0
- package/src/cli/terminal-recovery.ts +17 -10
- package/src/cli/upgrade-interactive.ts +4 -2
- package/src/cli/upgrade-restore-state.ts +2 -1
- package/src/cli/upgrade-runtime.ts +92 -141
- package/src/cli/upgrade.ts +8 -7
- package/src/hub/budget.ts +6 -30
- package/src/hub/conductor.ts +8 -4
- package/src/hub/config-apply.ts +9 -13
- package/src/hub/config-reload.ts +7 -2
- package/src/hub/context-window.ts +3 -27
- package/src/hub/crash.ts +2 -35
- package/src/hub/daemon.ts +226 -232
- package/src/hub/execution-budget.ts +2 -1
- package/src/hub/hub-tools.ts +3 -3
- package/src/hub/peer-catalog.ts +4 -13
- package/src/hub/permission-mode.ts +9 -12
- package/src/hub/recovery-continuity.ts +6 -5
- package/src/hub/report.ts +9 -8
- package/src/hub/roster.ts +6 -7
- package/src/hub/routing.ts +21 -10
- package/src/hub/settings.ts +6 -5
- package/src/hub/settled-roles.ts +1 -1
- package/src/hub/start-mode.ts +8 -6
- package/src/hub/tasks.ts +13 -10
- package/src/hub/tooling.ts +4 -60
- package/src/hub/ui.ts +3 -1
- package/src/memory/recall.ts +4 -5
- package/src/models/route/signals.ts +3 -1
- package/src/pi/launch.ts +2 -1
- package/src/ui/index.html +3 -2
- package/src/ui/office-model.ts +3 -1
- package/src/ui/peer-palette.ts +16 -6
- package/templates/routing.toml +2 -2
package/src/hub/ui.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { PEER_PALETTE, peerPaletteCss } from "../ui/peer-palette.ts";
|
|
|
5
5
|
import { PEER_START_RETRY_MS } from "./start-mode.ts";
|
|
6
6
|
import { taskProgress } from "../ui/task-progress.ts";
|
|
7
7
|
import { officeModel } from "../ui/office-model.ts";
|
|
8
|
+
import { OFFICE_PEER_ORDER, ADAPTER_DISPLAY } from "../adapters/registry.ts";
|
|
8
9
|
import { needs } from "../ui/needs.ts";
|
|
9
10
|
import { createHash, randomBytes } from "node:crypto";
|
|
10
11
|
import { readFileSync } from "node:fs";
|
|
@@ -35,10 +36,11 @@ export function startDashboard(options: DashboardOptions) {
|
|
|
35
36
|
// Inject the same pure public-board model used by the console before computing CSP hashes.
|
|
36
37
|
const html = readFileSync(new URL("../ui/index.html", import.meta.url), "utf8")
|
|
37
38
|
.replace("/* BOARD_MODEL */", () => `const boardColumns = ${boardColumns.toString()}; const peerPalette = ${JSON.stringify(PEER_PALETTE)};`)
|
|
39
|
+
.replace("/* ADAPTER_DISPLAY */", () => `const adapterDisplay = ${JSON.stringify(ADAPTER_DISPLAY)};`)
|
|
38
40
|
.replace("/* PEER_PALETTE_CSS */", () => peerPaletteCss())
|
|
39
41
|
.replace("/* TASK_PROGRESS_MODEL */", () => `const taskProgress = ${taskProgress.toString()};`)
|
|
40
42
|
.replace("/* PEER_START_RETRY_MS */", () => `const PEER_START_RETRY_MS = ${PEER_START_RETRY_MS};`)
|
|
41
|
-
.replace("/* OFFICE_MODEL */", () => `const officeModel = ${officeModel.toString()};`)
|
|
43
|
+
.replace("/* OFFICE_MODEL */", () => `const officeModel = ((OFFICE_PEER_ORDER) => ${officeModel.toString()})(${JSON.stringify(OFFICE_PEER_ORDER)});`)
|
|
42
44
|
.replace("/* NEEDS_MODEL */", () => `const needs = ${needs.toString()};`);
|
|
43
45
|
const hashes = (tag: string) => [...html.matchAll(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`, "g"))]
|
|
44
46
|
.map((m) => `'sha256-${createHash("sha256").update(m[1]!).digest("base64")}'`).join(" ");
|
package/src/memory/recall.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
import { ADAPTERS, definitionOf } from "../adapters/registry.ts";
|
|
1
2
|
import type { PeerId } from "../hub/envelope.ts";
|
|
2
3
|
import type { MemoryClient } from "./client.ts";
|
|
3
4
|
import type { View as MemoryView } from "@staix/agent-mem";
|
|
4
5
|
|
|
5
|
-
const PLATFORMS = ["claude", "codex", "kimi", "agent-hub"];
|
|
6
|
-
/** Peers whose own hooks already inject their own platform's context at session start. */
|
|
7
|
-
const OWN_PLATFORM: Record<PeerId, string> = { claude: "claude", codex: "codex" };
|
|
8
6
|
|
|
9
7
|
const HEADER =
|
|
10
8
|
"Shared project memory (claude-mem): recent work by the agents in this project. Reference only, not a request. " +
|
|
@@ -26,11 +24,12 @@ const hasContext = (text: string | undefined): text is string => !!text && text.
|
|
|
26
24
|
const entries = (text: string) => text.slice(Math.max(text.indexOf("\n### "), 0)).trim();
|
|
27
25
|
|
|
28
26
|
export async function recallFor(peer: PeerId, client: MemoryClient, chain: string[], tokens: number): Promise<string | undefined> {
|
|
29
|
-
const
|
|
27
|
+
const hooks = definitionOf(peer)?.hooks;
|
|
28
|
+
const own = hooks?.recallInjected ? hooks.recallPlatform : undefined;
|
|
30
29
|
// Everyone else gets all platforms in one call; claude and codex get the other platforms only.
|
|
31
30
|
const blocks = own
|
|
32
31
|
? await Promise.all(
|
|
33
|
-
|
|
32
|
+
[...ADAPTERS.flatMap(adapter => adapter.hooks?.recallSource ? [adapter.hooks.recallSource] : []), "agent-hub"].filter((p) => p !== own).map(async (p) => {
|
|
34
33
|
const text = await client.contextInject(chain, p);
|
|
35
34
|
return hasContext(text) ? `## from ${p} sessions\n${entries(text)}` : undefined;
|
|
36
35
|
}),
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// Ported to TypeScript from NVIDIA NeMo Switchyard crates/libsy/src/algorithms/util/tool_signals.rs at c8848511, modified.
|
|
4
4
|
|
|
5
5
|
import type { Conversation, NormalizedToolCall } from './normalize.ts';
|
|
6
|
+
import { definitionOf } from '../../adapters/registry.ts';
|
|
6
7
|
|
|
7
8
|
export const DEFAULT_RECENT_WINDOW = 3;
|
|
8
9
|
export const SOFT = 0.3;
|
|
@@ -133,7 +134,8 @@ function shellClassification(command: string): Semantic {
|
|
|
133
134
|
function semantic(name: string, command?: string, source?: string): Semantic {
|
|
134
135
|
if (source?.toLowerCase() === 'filechange') return 'edit';
|
|
135
136
|
const lower = name.toLowerCase();
|
|
136
|
-
|
|
137
|
+
const native = source === undefined ? undefined : definitionOf(source)?.hooks?.classifyTool?.(name, command);
|
|
138
|
+
if (native !== undefined) return native;
|
|
137
139
|
if (WRITE.has(lower)) return 'write';
|
|
138
140
|
if (EDITOR.has(lower) && command === 'view') return 'read';
|
|
139
141
|
if (EDIT.has(lower)) return 'edit';
|
package/src/pi/launch.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ADAPTERS } from "../adapters/registry.ts";
|
|
1
2
|
import { join } from "node:path";
|
|
2
3
|
import { peerChildEnv } from "../hub/child-process.ts";
|
|
3
4
|
import type { PiOptions, PiModelDescriptor, PiTuiLaunch } from "../adapters/pi.ts";
|
|
@@ -16,6 +17,6 @@ export function buildPiLaunch(opts: Pick<PiOptions, "stateDir" | "cmd" | "mode"
|
|
|
16
17
|
const env: NodeJS.ProcessEnv = { ...peerChildEnv("pi", inherited), PI_CODING_AGENT_DIR: join(opts.stateDir, "pi"), AGENTHUB_PI_BRIDGE_URL: `http://127.0.0.1:${bridgePort}`, AGENTHUB_PI_BRIDGE_TOKEN: bridgeToken, AGENTHUB_PI_OWNER_TOKEN: bridgeToken, AGENTHUB_PI_RELAY_URL: opts.relay.url, AGENTHUB_PI_RELAY_TOKEN: opts.relay.token, AGENTHUB_PI_MODELS: JSON.stringify(opts.relay.models), AGENTHUB_PI_TOOLS: JSON.stringify(opts.tools), AGENTHUB_PI_MAX_STEPS: String(opts.maxSteps ?? 30) };
|
|
17
18
|
const args = [ ...(opts.mode === "headless" ? ["--mode", "rpc"] : []), "--provider", "agent-hub-local", "--model", opts.model ?? modelFor(opts.backend, opts.relay.models), "--models", opts.relay.models.map((model) => `agent-hub-local/${model.id}`).join(","), "--no-builtin-tools", "--no-skills", "--no-prompt-templates", "--no-extensions", "--extension", extension, ...(opts.preamble ? ["--append-system-prompt", opts.preamble] : []), "--session-dir", join(opts.stateDir, "pi-sessions")];
|
|
18
19
|
if (opts.sessionFile) args.push("--session", opts.sessionFile); else if (opts.sessionId) args.push("--session-id", opts.sessionId);
|
|
19
|
-
const command = opts.cmd ?? ["pi"];
|
|
20
|
+
const command = opts.cmd ?? [ADAPTERS.find(adapter => adapter.start.daemonPort === "pi-rpc")!.cli.command];
|
|
20
21
|
return { cmd: command[0]!, args: [...command.slice(1), ...args], env };
|
|
21
22
|
}
|
package/src/ui/index.html
CHANGED
|
@@ -72,6 +72,7 @@ const dashboardTheme = (() => {
|
|
|
72
72
|
/* TASK_PROGRESS_MODEL */
|
|
73
73
|
/* PEER_START_RETRY_MS */
|
|
74
74
|
/* BOARD_MODEL */
|
|
75
|
+
/* ADAPTER_DISPLAY */
|
|
75
76
|
/* NEEDS_MODEL */
|
|
76
77
|
/* OFFICE_MODEL */
|
|
77
78
|
const officeView = (() => {
|
|
@@ -284,7 +285,7 @@ function renderTaskStage(stage) {
|
|
|
284
285
|
row.append(el('span', ` / Stage ${stage.stage}/4: ${stage.label}`)); return row;
|
|
285
286
|
}
|
|
286
287
|
function renderApprovals(permissions) {
|
|
287
|
-
const terminalWaiting = permissions.filter((permission) => permission.peer
|
|
288
|
+
const terminalWaiting = permissions.filter((permission) => adapterDisplay[permission.peer]?.terminalApproval).length;
|
|
288
289
|
$('permission-count').textContent = permissions.length + ' waiting' + (terminalWaiting ? ', ' + terminalWaiting + ' Pi/local: answer in the console or ahub tail / ahub permit' : '');
|
|
289
290
|
update('permissions', permissions, (target) => {
|
|
290
291
|
if (!permissions.length) return empty(target, 'No approvals waiting.');
|
|
@@ -783,7 +784,7 @@ function render(snapshot) {
|
|
|
783
784
|
if (peer.attached === false) row.append(el('p', 'Disconnected recipient; work is retained.', 'meta'));
|
|
784
785
|
if (peer.toolsOnly) row.append(el('p', peer.toolsOnly, 'meta'));
|
|
785
786
|
if (peer.permissionNote) row.append(el('p', peer.permissionNote, 'meta'));
|
|
786
|
-
if (peer.pullOnly && id
|
|
787
|
+
if (peer.pullOnly && adapterDisplay[id]?.pullOnlyRead) row.append(el('p', 'Last hub_inbox read: ' + (typeof peer.lastInboxAt === 'number' ? Math.max(0, Math.floor((Date.now() - peer.lastInboxAt) / 1000)) + 's ago' : 'never'), 'meta'));
|
|
787
788
|
if (peer.paused) row.append(el('p', peer.paused, 'meta'));
|
|
788
789
|
if (peer.requestedModel) row.append(el('p', 'Requested: ' + peer.requestedModel, 'meta'));
|
|
789
790
|
for (const backend of peer.backends || []) {
|
package/src/ui/office-model.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { OFFICE_PEER_ORDER } from "../adapters/registry.ts";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Pure office scene model for the dashboard (#264, phase 1: the static Flat view).
|
|
3
5
|
* Self-contained so `src/hub/ui.ts` injects it into the page by toString(), exactly like
|
|
@@ -178,7 +180,7 @@ export interface OfficeScene {
|
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
export function officeModel(snapshot: OfficeSnapshotInput, previous: OfficeScene | null, options: { cols?: number; now?: number }): OfficeScene {
|
|
181
|
-
const FIXED =
|
|
183
|
+
const FIXED = OFFICE_PEER_ORDER;
|
|
182
184
|
const KNOWN = ["proposed", "in_progress", "in_review", "changes_requested", "approved", "cancelled"];
|
|
183
185
|
const GLYPHS: Record<string, string> = { idle: "○", busy: "●", offline: "×", paused: "‖", waiting: "⚑", unknown: "?" };
|
|
184
186
|
const LABELS: Record<string, string> = { idle: "idle", busy: "busy", offline: "offline", paused: "paused", waiting: "waiting on approval" };
|
package/src/ui/peer-palette.ts
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
|
+
import { definitionOf, OFFICE_PEER_ORDER } from "../adapters/registry.ts";
|
|
2
|
+
import type { NATIVE_DISPLAYS } from "../adapters/definitions/native-display.ts";
|
|
3
|
+
|
|
4
|
+
type NativePalette = { [Kind in keyof typeof NATIVE_DISPLAYS]: {
|
|
5
|
+
tone: (typeof NATIVE_DISPLAYS)[Kind]["palette"]["tone"];
|
|
6
|
+
ansi: (typeof NATIVE_DISPLAYS)[Kind]["palette"]["ansi"];
|
|
7
|
+
css: (typeof NATIVE_DISPLAYS)[Kind]["palette"]["fg"];
|
|
8
|
+
} };
|
|
9
|
+
const other = { tone: "muted", ansi: "\x1b[90m", css: "#8a8f99" } as const;
|
|
10
|
+
const nativePalette = Object.fromEntries(OFFICE_PEER_ORDER.map(kind => {
|
|
11
|
+
const palette = definitionOf(kind)!.display.palette;
|
|
12
|
+
return [kind, { tone: palette.tone ?? other.tone, ansi: palette.ansi ?? other.ansi, css: palette.fg }];
|
|
13
|
+
})) as NativePalette;
|
|
14
|
+
|
|
1
15
|
/** Stable peer identity colors shared by terminal board/status and the dashboard (#263). */
|
|
2
16
|
export const PEER_PALETTE = Object.freeze({
|
|
3
|
-
|
|
4
|
-
codex: { tone: "peerCodex", ansi: "\x1b[96m", css: "#22b8cf" },
|
|
5
|
-
kimi: { tone: "taskKeyword", ansi: "\x1b[35m", css: "#bc73ca" },
|
|
6
|
-
pi: { tone: "attention", ansi: "\x1b[33m", css: "#c79936" },
|
|
7
|
-
local: { tone: "success", ansi: "\x1b[32m", css: "#4caa77" },
|
|
17
|
+
...nativePalette,
|
|
8
18
|
hub: { tone: "info", ansi: "\x1b[36m", css: "#489da8" },
|
|
9
|
-
other
|
|
19
|
+
other,
|
|
10
20
|
} as const);
|
|
11
21
|
export function peerStyle(peer: string): (typeof PEER_PALETTE)[keyof typeof PEER_PALETTE] {
|
|
12
22
|
return PEER_PALETTE[Object.hasOwn(PEER_PALETTE, peer) ? peer as keyof typeof PEER_PALETTE : "other"];
|
package/templates/routing.toml
CHANGED
|
@@ -46,8 +46,8 @@ target = "dsv4f"
|
|
|
46
46
|
# confirmations = 2
|
|
47
47
|
|
|
48
48
|
# L1: task assignment. `peers` is the preference order among attached, unpaused peers (idle before busy).
|
|
49
|
-
# `route` / `fixed_model` apply when the task lands on the local worker.
|
|
50
|
-
# task to the next attached peer in `escalate_to`.
|
|
49
|
+
# `route` / `fixed_model` apply when the task lands on the local worker. `tasks.escalate_after` consecutive
|
|
50
|
+
# changes_requested (default 3, in .agenthub/config.json) move the task to the next attached peer in `escalate_to`.
|
|
51
51
|
|
|
52
52
|
[signals]
|
|
53
53
|
pii_patterns = ["\\b\\d{6}-\\d{7}\\b"] # Korean resident registration numbers; add your own, e.g. "@your-org\\.example" for staff mail addresses
|