@staix/agent-hub 0.18.0 → 0.19.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 +4 -4
- package/docs/agent-notes/README.md +6 -0
- package/docs/agent-notes/adapters.md +10 -8
- package/docs/agent-notes/daemon.md +5 -2
- package/docs/agent-notes/local-worker.md +2 -1
- package/docs/agent-notes/tasks.md +4 -1
- package/docs/agent-notes/tests.md +6 -3
- package/docs/dogfood.md +9 -3
- package/docs/memory.md +1 -1
- package/docs/operations.md +66 -28
- package/docs/quickstart.md +9 -5
- package/docs/security.md +22 -13
- package/docs/smoke.md +3 -3
- package/docs/specs/2026-09-19-agent-hub-design.md +16 -5
- package/docs/verification/2026-10-25-remote-docker-transport.md +10 -0
- package/docs/verification/2026-10-26-agy-1.4.7.md +69 -0
- package/docs/verification/2026-10-26-claude-implementer-2.1.310.md +69 -0
- package/docs/verification/2026-10-26-headless-claude-shell-context.md +62 -0
- package/docs/verification/2026-10-26-jcode-0.99.1.md +49 -0
- package/docs/verification/2026-10-26-kimi-232-reviewer-compat.md +53 -0
- package/docs/verification/2026-10-26-pi-140-compat.md +41 -0
- package/docs/verification/2026-10-26-qwen-0261-acp.md +60 -0
- package/docs/verification/2026-10-27-0.19.0.md +86 -0
- package/docs/verification/claude-permission-transport-native.py +187 -0
- package/docs/verification/claude-shell-wrapper-host.ts +20 -0
- package/docs/verification/claude-shell-wrapper-native.py +145 -0
- package/docs/verification/pi-140-model-free.ts +57 -0
- package/docs/verification/pi-140-native.mjs +21 -0
- package/docs/verified.json +14 -14
- package/package.json +2 -2
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +273 -68
- package/src/adapters/agy/entry.ts +1 -1
- package/src/adapters/claude-channel.ts +7 -3
- package/src/adapters/claude-implementer.ts +5 -3
- package/src/adapters/compat.ts +32 -18
- package/src/adapters/conformance/fakes/pi-rpc.ts +1 -0
- package/src/adapters/definitions/builtin-metadata.ts +2 -2
- package/src/adapters/definitions/native-factories.ts +7 -2
- package/src/adapters/definitions/reviewer-variants.ts +2 -2
- package/src/adapters/pi.ts +39 -3
- package/src/adapters/qwen-dual.ts +25 -0
- package/src/adapters/registry-types.ts +6 -2
- package/src/adapters/registry.ts +1 -0
- package/src/cli/agents.ts +69 -7
- package/src/cli/claude-shell-wrapper.ts +54 -0
- package/src/cli/main.ts +30 -24
- package/src/cli/output.ts +4 -0
- package/src/hub/agent-drift.ts +61 -0
- package/src/hub/board.ts +14 -2
- package/src/hub/check-runner.ts +148 -18
- package/src/hub/claude-shell-wrapper.ts +349 -0
- package/src/hub/config-edit.ts +2 -2
- package/src/hub/config-tier.ts +2 -2
- package/src/hub/daemon.ts +197 -35
- package/src/hub/host-executable.ts +20 -1
- package/src/hub/instances.ts +18 -2
- package/src/hub/lifecycle.ts +1 -1
- package/src/hub/operational-authority.ts +7 -0
- package/src/hub/operational-view.ts +6 -10
- package/src/hub/read-only-shell-proof.ts +7 -6
- package/src/hub/review-scope.ts +13 -0
- package/src/hub/sdlc-metrics.ts +6 -4
- package/src/hub/task-shell-command.ts +85 -0
- package/src/hub/task-shell-default.ts +78 -0
- package/src/hub/task-shell-proof.ts +171 -0
- package/src/hub/task-shell-sandbox.ts +61 -0
- package/src/hub/tasks.ts +139 -13
- package/src/local/deny.ts +91 -11
- package/src/local/sandbox.ts +67 -28
- package/src/local/shell-environment.ts +10 -0
- package/src/ui/index.html +1 -0
|
@@ -68,7 +68,10 @@ function realPath(path) {
|
|
|
68
68
|
}
|
|
69
69
|
function isDenied(path, extra = []) {
|
|
70
70
|
const norm = path.split(sep).join("/");
|
|
71
|
-
|
|
71
|
+
if (DENY_SEGMENTS.some((s) => hasSegment(norm, s)))
|
|
72
|
+
return true;
|
|
73
|
+
const name = basename(norm);
|
|
74
|
+
return DENY_NAMES.some((re) => (denyNamePatterns.get(re) ?? new RegExp(re)).test(name)) || extra.some((e) => e && norm.includes(e));
|
|
72
75
|
}
|
|
73
76
|
function memoryGitEnv(env = process.env, options = {}) {
|
|
74
77
|
const result = { ...env, GIT_TERMINAL_PROMPT: "0" };
|
|
@@ -118,11 +121,12 @@ var require2, processes, childProcess = () => processes ??= require2("node:child
|
|
|
118
121
|
const parent = dirname(absolute);
|
|
119
122
|
return parent === absolute ? absolute : join(canonical(parent), basename(absolute));
|
|
120
123
|
}
|
|
121
|
-
}, DENY_SEGMENTS, DENY_NAMES, hasSegment = (rel, seg) => `/${rel}/`.includes(`/${seg}/`), SAFE_CONFIG, CREDENTIAL_KEYS, memoryGitArgv = (args, opts = {}) => ["git", "--no-pager", ...SAFE_CONFIG.filter((c) => !opts.credentials || !CREDENTIAL_KEYS.has(c)).flatMap((c) => ["-c", c]), ...args], ambientGitTimeout;
|
|
124
|
+
}, DENY_SEGMENTS, DENY_NAMES, denyNamePatterns, hasSegment = (rel, seg) => `/${rel}/`.includes(`/${seg}/`), SAFE_CONFIG, CREDENTIAL_KEYS, memoryGitArgv = (args, opts = {}) => ["git", "--no-pager", ...SAFE_CONFIG.filter((c) => !opts.credentials || !CREDENTIAL_KEYS.has(c)).flatMap((c) => ["-c", c]), ...args], ambientGitTimeout;
|
|
122
125
|
var init_platform = __esm(() => {
|
|
123
126
|
require2 = createRequire(import.meta.url);
|
|
124
127
|
DENY_SEGMENTS = [".maru/secrets", ".agenthub/state", ".agenthub/archive"];
|
|
125
128
|
DENY_NAMES = ["^\\.env", "\\.pem$", "\\.key$", "^id_rsa", "^id_ed25519", "^credentials(\\.|$)", "^auth\\.json$", "^\\.netrc$", "^\\.npmrc$", "^\\.pypirc$"];
|
|
129
|
+
denyNamePatterns = new Map(DENY_NAMES.map((source) => [source, new RegExp(source)]));
|
|
126
130
|
SAFE_CONFIG = ["core.fsmonitor=false", "core.hooksPath=/dev/null", "diff.external=", "core.pager=cat", "credential.helper=", "core.askpass="];
|
|
127
131
|
CREDENTIAL_KEYS = new Set(["credential.helper=", "core.askpass="]);
|
|
128
132
|
});
|
|
@@ -291,13 +295,24 @@ var plain = (text) => text.length <= 64 && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/
|
|
|
291
295
|
var init_compat = __esm(() => {
|
|
292
296
|
init_project();
|
|
293
297
|
COMPAT = {
|
|
298
|
+
pi: {
|
|
299
|
+
label: "Pi",
|
|
300
|
+
verified: ["1.4.0"],
|
|
301
|
+
gate: "contract",
|
|
302
|
+
probe: ["pi", "--version"],
|
|
303
|
+
config: "pi.cmd",
|
|
304
|
+
parse: (out) => plain(out.trim()),
|
|
305
|
+
trustNote: "The model-free extension load, RPC session identity and managed tool receipt path were checked on 1.4.0. Permissions remain host-owned; account-backed inference and another version's extension contract are unverified.",
|
|
306
|
+
receipt: "docs/verification/2026-10-26-pi-140-compat.md"
|
|
307
|
+
},
|
|
294
308
|
"claude-implementer": {
|
|
295
309
|
label: "Claude headless implementer",
|
|
296
|
-
verified: ["2.1.309"],
|
|
310
|
+
verified: ["2.1.309", "2.1.310"],
|
|
297
311
|
gate: "contract",
|
|
298
312
|
probe: ["claude", "--version"],
|
|
299
313
|
parse: (out) => plain(out.trim().replace(/ \(Claude Code\)$/, "")),
|
|
300
|
-
trustNote: "The native prompt-tool
|
|
314
|
+
trustNote: "The native prompt-tool transport and host-wrapper rewrite, environment isolation and replay contract have isolated receipts. No dontAsk/bypass fallback exists; another version's permission or rewrite behavior is unverified.",
|
|
315
|
+
receipt: "docs/verification/2026-10-26-headless-claude-shell-context.md"
|
|
301
316
|
},
|
|
302
317
|
"codex-review": {
|
|
303
318
|
label: "Codex reviewer (codex-cli)",
|
|
@@ -306,19 +321,21 @@ var init_compat = __esm(() => {
|
|
|
306
321
|
probe: ["codex", "--version"],
|
|
307
322
|
config: "codex_bin",
|
|
308
323
|
parse: (out) => plain(out.trim().replace(/^codex-cli /, "")),
|
|
309
|
-
trustNote: "Every launch still proves the MCP inventory, Apps/plugins/hooks disabled, sandbox read/write denial and the binary fence; a failed leg still refuses. Trust covers only the source-level claims those probes cannot see (docs/verification/2026-10-19-reviewer-isolation.md)."
|
|
324
|
+
trustNote: "Every launch still proves the MCP inventory, Apps/plugins/hooks disabled, sandbox read/write denial and the binary fence; a failed leg still refuses. Trust covers only the source-level claims those probes cannot see (docs/verification/2026-10-19-reviewer-isolation.md).",
|
|
325
|
+
receipt: "docs/verification/2026-10-24-codex-0164-reviewer-isolation.md"
|
|
310
326
|
},
|
|
311
327
|
"kimi-review": {
|
|
312
328
|
label: "Kimi reviewer (kimi-code)",
|
|
313
|
-
verified: ["2.2.0", "2.3.0", "2.3.1"],
|
|
329
|
+
verified: ["2.2.0", "2.3.0", "2.3.1", "2.3.2"],
|
|
314
330
|
gate: "isolation",
|
|
315
331
|
probe: ["kimi", "--version"],
|
|
316
332
|
parse: (out) => plain(out.trim()),
|
|
317
|
-
trustNote: "Every launch still builds the private config, exact two-tool agent file and task-bound MCP; the native tool allowlist behaviour of an untested version is not re-proven."
|
|
333
|
+
trustNote: "Every launch still builds the private config, exact two-tool agent file and task-bound MCP; the native tool allowlist behaviour of an untested version is not re-proven.",
|
|
334
|
+
receipt: "docs/verification/2026-10-26-kimi-232-reviewer-compat.md"
|
|
318
335
|
},
|
|
319
336
|
jcode: {
|
|
320
337
|
label: "Jcode",
|
|
321
|
-
verified: ["0.98.1", "0.99.0"],
|
|
338
|
+
verified: ["0.98.1", "0.99.0", "0.99.1"],
|
|
322
339
|
line: { min: "0.98.0", below: "1.0.0" },
|
|
323
340
|
gate: "contract",
|
|
324
341
|
probe: ["jcode", "--quiet", "version", "--json"],
|
|
@@ -331,25 +348,28 @@ var init_compat = __esm(() => {
|
|
|
331
348
|
return;
|
|
332
349
|
}
|
|
333
350
|
},
|
|
334
|
-
trustNote: "The ACP, serve and MCP contract was verified on 0.98.1 and steering on 0.99.0; an untested version may change them."
|
|
351
|
+
trustNote: "The ACP, serve and MCP contract was verified on 0.98.1 and 0.99.1 and steering on 0.99.0 and 0.99.1; an untested version may change them.",
|
|
352
|
+
receipt: "docs/verification/2026-10-26-jcode-0.99.1.md"
|
|
335
353
|
},
|
|
336
354
|
agy: {
|
|
337
355
|
label: "agy",
|
|
338
|
-
verified: ["1.4.2"],
|
|
356
|
+
verified: ["1.4.2", "1.4.7"],
|
|
339
357
|
line: { min: "1.4.0", below: "1.5.0" },
|
|
340
358
|
gate: "contract",
|
|
341
359
|
probe: ["agy", "--version"],
|
|
342
360
|
parse: (out) => plain(out.trim()),
|
|
343
|
-
trustNote: "Only 1.4.2
|
|
361
|
+
trustNote: "Only 1.4.2 and 1.4.7 are T0-verified; the plugin MCP and hook contract of another version is unverified.",
|
|
362
|
+
receipt: "docs/verification/2026-10-26-agy-1.4.7.md"
|
|
344
363
|
},
|
|
345
364
|
qwen: {
|
|
346
365
|
label: "Qwen Code",
|
|
347
|
-
verified: ["0.25.0", "0.26.0"],
|
|
366
|
+
verified: ["0.25.0", "0.26.0", "0.26.1"],
|
|
348
367
|
gate: "contract",
|
|
349
368
|
probe: ["qwen", "--version"],
|
|
350
369
|
config: "qwen_cmd",
|
|
351
370
|
parse: (out) => plain(out.trim()),
|
|
352
|
-
trustNote: "Native ACP identity
|
|
371
|
+
trustNote: "Native ACP identity, mode ids and permission callbacks were re-checked without inference on 0.26.1; its two ACP fixes were judged not to change the hub contract. Another version may change those contracts.",
|
|
372
|
+
receipt: "docs/verification/2026-10-26-qwen-0261-acp.md"
|
|
353
373
|
},
|
|
354
374
|
"qwen-dual": {
|
|
355
375
|
label: "Qwen Code dual-output TUI",
|
|
@@ -358,7 +378,8 @@ var init_compat = __esm(() => {
|
|
|
358
378
|
probe: ["qwen", "--version"],
|
|
359
379
|
config: "qwen_cmd",
|
|
360
380
|
parse: (out) => plain(out.trim()),
|
|
361
|
-
trustNote: "The dual-output file contract (submit and confirmation_response input, user/assistant/stream_event/control events, session_start identity) was verified on 0.26.1 only. A hub deny ends the turn without any Qwen event, and no external stop of a running turn exists; another version may change either."
|
|
381
|
+
trustNote: "The dual-output file contract (submit and confirmation_response input, user/assistant/stream_event/control events, session_start identity) was verified on 0.26.1 only. A hub deny ends the turn without any Qwen event, and no external stop of a running turn exists; another version may change either.",
|
|
382
|
+
receipt: "docs/verification/2026-10-25-qwen-dual-output-t0.md"
|
|
362
383
|
}
|
|
363
384
|
};
|
|
364
385
|
MAX_BYTES = 64 * 1024;
|
|
@@ -4819,7 +4840,10 @@ function qwenDualNamesStateDir(input, stateDir) {
|
|
|
4819
4840
|
};
|
|
4820
4841
|
return seen(input, 0);
|
|
4821
4842
|
}
|
|
4822
|
-
|
|
4843
|
+
function isQwenTaskNotification(text) {
|
|
4844
|
+
return text.length <= NOTIFICATION_CAP && QWEN_TASK_NOTIFICATIONS.test(text);
|
|
4845
|
+
}
|
|
4846
|
+
var SUBMIT_CAP, READ_CAP, ANSWERED_CAP = 256, QWEN_STALLED_NOTE = "stalled: Qwen has said nothing for a while; the hub cannot stop a Qwen turn, press Esc in the Qwen terminal", QWEN_DETACHED_NOTE = "detached: the hub cannot read Qwen's event file; Qwen keeps working in its terminal and the hub reattaches when the file is readable again", DIR = "qwen-dual", NOTIFICATION, QWEN_TASK_NOTIFICATIONS, NOTIFICATION_CAP, QwenDualPeer;
|
|
4823
4847
|
var init_qwen_dual = __esm(() => {
|
|
4824
4848
|
init_envelope();
|
|
4825
4849
|
init_child_process();
|
|
@@ -4828,6 +4852,9 @@ var init_qwen_dual = __esm(() => {
|
|
|
4828
4852
|
init_qwen_native();
|
|
4829
4853
|
SUBMIT_CAP = 256 * 1024;
|
|
4830
4854
|
READ_CAP = 1024 * 1024;
|
|
4855
|
+
NOTIFICATION = String.raw`<task-notification>\n<task-id>bg_[0-9a-f]{4,32}<\/task-id>\n<kind>[a-z_-]{1,32}<\/kind>\n<status>[a-z_-]{1,32}<\/status>\n(?:(?!<task-notification>|<\/task-notification>)[\s\S])*<\/task-notification>`;
|
|
4856
|
+
QWEN_TASK_NOTIFICATIONS = new RegExp(`^${NOTIFICATION}(?:\\n\\n${NOTIFICATION})*$`);
|
|
4857
|
+
NOTIFICATION_CAP = 64 * 1024;
|
|
4831
4858
|
QwenDualPeer = class QwenDualPeer extends BasePeer {
|
|
4832
4859
|
opts;
|
|
4833
4860
|
permissionModeState = "unmanaged";
|
|
@@ -5276,6 +5303,13 @@ ${text}`;
|
|
|
5276
5303
|
if (!this.stopping && !this.detached && this.state === "offline")
|
|
5277
5304
|
this.setState("idle");
|
|
5278
5305
|
}
|
|
5306
|
+
isNotificationEvent(event) {
|
|
5307
|
+
const content = event.message?.content;
|
|
5308
|
+
if (!Array.isArray(content) || content.length !== 1)
|
|
5309
|
+
return false;
|
|
5310
|
+
const block = content[0];
|
|
5311
|
+
return !!block && typeof block === "object" && block.type === "text" && typeof block.text === "string" && isQwenTaskNotification(block.text);
|
|
5312
|
+
}
|
|
5279
5313
|
promptText(event) {
|
|
5280
5314
|
const content = event.message?.content;
|
|
5281
5315
|
if (typeof content === "string")
|
|
@@ -5286,6 +5320,8 @@ ${text}`;
|
|
|
5286
5320
|
return blocks.length ? blocks.map((block) => block.text).join("") : undefined;
|
|
5287
5321
|
}
|
|
5288
5322
|
onUser(event) {
|
|
5323
|
+
if (this.isNotificationEvent(event))
|
|
5324
|
+
return;
|
|
5289
5325
|
const text = this.promptText(event);
|
|
5290
5326
|
if (text === undefined)
|
|
5291
5327
|
return;
|
|
@@ -5808,26 +5844,61 @@ var init_path_policy = __esm(() => {
|
|
|
5808
5844
|
|
|
5809
5845
|
// src/local/deny.ts
|
|
5810
5846
|
import { basename as basename4, dirname as dirname8 } from "path";
|
|
5811
|
-
function hubWriteRegexes(root, gitDirs = [], moduleGit) {
|
|
5847
|
+
function hubWriteRegexes(root, gitDirs = [], moduleGit = [], worktreesRoot) {
|
|
5812
5848
|
const git = hfsFoldedName(".git");
|
|
5813
5849
|
const re = (s) => `(regex ${sbplString(s)})`;
|
|
5814
5850
|
const external = gitDirs.flatMap((d) => [
|
|
5815
5851
|
`(require-all ${re(`^${sbplEscape(d)}/[^/]+$`)} ${re(`^.*/(${hfsFoldedName("config")}|${hfsFoldedName("hooks")})$`)})`,
|
|
5852
|
+
`(require-all ${re(`^${sbplEscape(d)}/[^/]+$`)} ${CONFIG_WORKTREE_NAME.map(re).join(" ")})`,
|
|
5816
5853
|
`(subpath ${sbplString(`${d}/hooks`)})`,
|
|
5817
5854
|
`(require-all (subpath ${sbplString(d)}) ${re(`^.*/${hfsFoldedName("commondir")}$`)})`
|
|
5818
5855
|
]);
|
|
5819
|
-
const
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5856
|
+
const allDirs = moduleGit.flatMap((tree) => tree.dirs ?? []);
|
|
5857
|
+
const modules = moduleGit.flatMap((tree) => {
|
|
5858
|
+
const root = sbplEscape(tree.root);
|
|
5859
|
+
const found = tree.dirs ?? [];
|
|
5860
|
+
const inside = found.map((d) => `(require-not ${re(`^${sbplEscape(d)}(/|$)`)})`).join(" ");
|
|
5861
|
+
const anchored = found.flatMap((d) => {
|
|
5862
|
+
const dir = sbplEscape(d);
|
|
5863
|
+
return [
|
|
5864
|
+
`(require-all ${re(`^${dir}/[^/]+$`)} ${re(`^.*/${hfsFoldedName("config")}$`)})`,
|
|
5865
|
+
`(require-all ${re(`^${dir}/[^/]+$`)} ${CONFIG_WORKTREE_NAME.map(re).join(" ")})`,
|
|
5866
|
+
`(require-all ${re(`^${dir}/`)} ${re(hooksUnder(d))})`,
|
|
5867
|
+
`(require-all ${re(`^${dir}/worktrees/[^/]+/[^/]+$`)} ${CONFIG_WORKTREE_NAME.map(re).join(" ")})`,
|
|
5868
|
+
`(require-all ${re(`^${dir}/[^/]+$`)} ${re(`^.*/${hfsFoldedName("worktrees")}$`)})`,
|
|
5869
|
+
re(`^${dir}/worktrees/[^/]+$`),
|
|
5870
|
+
`(require-all ${re(`^${dir}/[^/]+$`)} ${re(`^.*/${hfsFoldedName("hooks")}$`)})`,
|
|
5871
|
+
`(require-all ${re(`^${dir}/worktrees/[^/]+/modules/.`)} ${allDirs.filter((f) => f.startsWith(`${d}/worktrees/`)).map((f) => `(require-not ${re(`^${sbplEscape(f)}(/|$)`)})`).join(" ")})`,
|
|
5872
|
+
`(require-all ${re(`^${dir}/worktrees/[^/]+/[^/]+$`)} ${re(`^.*/${hfsFoldedName("modules")}$`)})`
|
|
5873
|
+
];
|
|
5874
|
+
});
|
|
5875
|
+
return [
|
|
5876
|
+
`(require-all ${re(`^${root}/.`)} ${inside})`,
|
|
5877
|
+
...anchored,
|
|
5878
|
+
...tree.entries.map((d) => `(require-all ${re(`^${sbplEscape(dirname8(d))}/[^/]+$`)} ${re(`^.*/${hfsFoldedName(basename4(d))}$`)})`)
|
|
5879
|
+
];
|
|
5880
|
+
});
|
|
5881
|
+
const siblings = worktreesRoot ? (() => {
|
|
5882
|
+
const w = sbplEscape(worktreesRoot);
|
|
5883
|
+
return [
|
|
5884
|
+
`(require-all ${re(`^${sbplEscape(dirname8(worktreesRoot))}/[^/]+$`)} ${re(`^.*/${hfsFoldedName(basename4(worktreesRoot))}$`)})`,
|
|
5885
|
+
re(`^${w}/[^/]+$`),
|
|
5886
|
+
`(require-all ${re(`^${w}/[^/]+/[^/]+$`)} ${re(`^.*/${hfsFoldedName("config")}$`)})`,
|
|
5887
|
+
`(require-all ${re(`^${w}/[^/]+/[^/]+$`)} ${CONFIG_WORKTREE_NAME.map(re).join(" ")})`,
|
|
5888
|
+
`(require-all ${re(`^${w}/[^/]+/`)} ${re(hooksUnder(`${worktreesRoot}/x`))})`,
|
|
5889
|
+
`(require-all ${re(`^${w}/[^/]+/modules/.`)} ${moduleGit.flatMap((tree) => tree.dirs ?? []).filter((d) => d.startsWith(`${worktreesRoot}/`)).map((d) => `(require-not ${re(`^${sbplEscape(d)}(/|$)`)})`).join(" ")})`,
|
|
5890
|
+
`(require-all ${re(`^${w}/[^/]+/[^/]+$`)} ${re(`^.*/${hfsFoldedName("modules")}$`)})`
|
|
5891
|
+
];
|
|
5892
|
+
})() : [];
|
|
5824
5893
|
return [
|
|
5825
5894
|
`(require-all (subpath ${sbplString(root)}) ${re(`^.*/${hfsFoldedName(".agenthub")}(/|$)`)})`,
|
|
5826
5895
|
re(`^.*/${git}$`),
|
|
5827
5896
|
re(`^.*/${git}/${hfsFoldedName("config")}$`),
|
|
5897
|
+
`(require-all ${re(`^.*/${git}/[^/]+$`)} ${CONFIG_WORKTREE_NAME.map(re).join(" ")})`,
|
|
5828
5898
|
re(`^.*/${git}/${hfsFoldedName("hooks")}(/|$)`),
|
|
5829
5899
|
`(require-all ${re(`^.*/${git}/.*$`)} ${re(`^.*/${hfsFoldedName("commondir")}$`)})`,
|
|
5830
5900
|
...modules,
|
|
5901
|
+
...siblings,
|
|
5831
5902
|
...external
|
|
5832
5903
|
];
|
|
5833
5904
|
}
|
|
@@ -5842,12 +5913,16 @@ function denyRegexes(root, extra = [], withNames = true) {
|
|
|
5842
5913
|
...extra.filter(Boolean).map((e) => `^${sbplEscape(root)}/.*${sbplEscape(e)}`)
|
|
5843
5914
|
].map((re) => `(regex ${sbplString(re)})`);
|
|
5844
5915
|
}
|
|
5845
|
-
var sbplEscape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), sbplString = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`, HFS_IGNORABLE_POINTS, HFS_IGNORABLE, hfsFoldedName = (name) => `${HFS_IGNORABLE}*${[...name].map((c) => sbplEscape(c)).join(`${HFS_IGNORABLE}*`)}${HFS_IGNORABLE}
|
|
5916
|
+
var sbplEscape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), sbplString = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`, HFS_IGNORABLE_POINTS, HFS_IGNORABLE, hfsFoldedName = (name) => `${HFS_IGNORABLE}*${[...name].map((c) => sbplEscape(c)).join(`${HFS_IGNORABLE}*`)}${HFS_IGNORABLE}*`, CONFIG_WORKTREE_NAME, MAX_ANCHOR_DEPTH = 80, hooksUnder = (parent) => {
|
|
5917
|
+
const depth = parent.split("/").filter(Boolean).length;
|
|
5918
|
+
return depth > MAX_ANCHOR_DEPTH ? `^.*/${hfsFoldedName("hooks")}(/|$)` : `^${"/[^/]+".repeat(depth)}/${hfsFoldedName("hooks")}(/|$)`;
|
|
5919
|
+
};
|
|
5846
5920
|
var init_deny = __esm(() => {
|
|
5847
5921
|
init_platform();
|
|
5848
5922
|
init_platform();
|
|
5849
5923
|
HFS_IGNORABLE_POINTS = ["\u200C", "\u200D", "\u200E", "\u200F", "\u202A", "\u202B", "\u202C", "\u202D", "\u202E", "\u206A", "\u206B", "\u206C", "\u206D", "\u206E", "\u206F", "\uFEFF"];
|
|
5850
5924
|
HFS_IGNORABLE = `(${HFS_IGNORABLE_POINTS.join("|")})`;
|
|
5925
|
+
CONFIG_WORKTREE_NAME = [`^.*/${hfsFoldedName("config")}\\.[^/]*$`, `^.*/[^/]*${hfsFoldedName("worktree")}$`];
|
|
5851
5926
|
});
|
|
5852
5927
|
|
|
5853
5928
|
// src/local/sandbox.ts
|
|
@@ -5871,39 +5946,70 @@ function externalGitDirs(root) {
|
|
|
5871
5946
|
`).map((d) => resolve9(root, d));
|
|
5872
5947
|
return [...new Set(dirs)].filter((d) => !d.startsWith(`${root}/`));
|
|
5873
5948
|
}
|
|
5874
|
-
function
|
|
5949
|
+
function worktreesRoot(root) {
|
|
5875
5950
|
const out = memoryGitSync(["-C", root, "rev-parse", "--git-common-dir"], { encoding: "utf8" });
|
|
5951
|
+
return out.status === 0 && out.stdout.trim() ? join12(resolve9(root, out.stdout.trim()), "worktrees") : undefined;
|
|
5952
|
+
}
|
|
5953
|
+
function moduleGitDirs(root) {
|
|
5954
|
+
const out = memoryGitSync(["-C", root, "rev-parse", "--git-common-dir", "--absolute-git-dir"], { encoding: "utf8" });
|
|
5876
5955
|
if (out.status !== 0)
|
|
5877
|
-
return;
|
|
5878
|
-
const
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5956
|
+
return [];
|
|
5957
|
+
const [commonRaw, ownRaw] = out.stdout.trim().split(`
|
|
5958
|
+
`);
|
|
5959
|
+
if (!commonRaw || !ownRaw)
|
|
5960
|
+
return [];
|
|
5961
|
+
const common = resolve9(root, commonRaw), own = resolve9(root, ownRaw);
|
|
5962
|
+
const roots = new Set([join12(common, "modules"), join12(own, "modules")]);
|
|
5963
|
+
try {
|
|
5964
|
+
for (const e of readdirSync(join12(common, "worktrees"), { withFileTypes: true })) {
|
|
5965
|
+
if (!e.isDirectory())
|
|
5966
|
+
continue;
|
|
5967
|
+
const modules = join12(common, "worktrees", e.name, "modules");
|
|
5968
|
+
if (existsSync6(modules))
|
|
5969
|
+
roots.add(modules);
|
|
5888
5970
|
}
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5971
|
+
} catch {}
|
|
5972
|
+
const trees = [];
|
|
5973
|
+
const tree = (modulesRoot, nesting) => {
|
|
5974
|
+
const dirs = [];
|
|
5975
|
+
const walk = (dir, depth) => {
|
|
5976
|
+
if (depth > 8)
|
|
5977
|
+
return;
|
|
5978
|
+
let entries;
|
|
5979
|
+
try {
|
|
5980
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
5981
|
+
} catch {
|
|
5982
|
+
return;
|
|
5983
|
+
}
|
|
5984
|
+
const names = new Set(entries.map((e) => e.name));
|
|
5985
|
+
if (names.has("HEAD") && names.has("config")) {
|
|
5986
|
+
dirs.push(dir);
|
|
5987
|
+
return;
|
|
5988
|
+
}
|
|
5989
|
+
for (const e of entries)
|
|
5990
|
+
if (e.isDirectory())
|
|
5991
|
+
walk(join12(dir, e.name), depth + 1);
|
|
5992
|
+
};
|
|
5993
|
+
walk(modulesRoot, 0);
|
|
5994
|
+
const found = nesting >= MODULE_DEPTH ? [] : dirs;
|
|
5995
|
+
const entries = new Set([modulesRoot]);
|
|
5996
|
+
for (const d of found)
|
|
5997
|
+
for (let p = d;p.length > modulesRoot.length; p = dirname9(p))
|
|
5998
|
+
entries.add(p);
|
|
5999
|
+
trees.push({ root: modulesRoot, entries: [...entries], dirs: found });
|
|
6000
|
+
for (const d of found) {
|
|
6001
|
+
tree(join12(d, "modules"), nesting + 1);
|
|
6002
|
+
try {
|
|
6003
|
+
for (const e of readdirSync(join12(d, "worktrees"), { withFileTypes: true })) {
|
|
6004
|
+
if (e.isDirectory() && existsSync6(join12(d, "worktrees", e.name, "modules")))
|
|
6005
|
+
tree(join12(d, "worktrees", e.name, "modules"), nesting + 1);
|
|
6006
|
+
}
|
|
6007
|
+
} catch {}
|
|
5896
6008
|
}
|
|
5897
|
-
for (const e of entries)
|
|
5898
|
-
if (e.isDirectory())
|
|
5899
|
-
walk(join12(dir, e.name), depth + 1);
|
|
5900
6009
|
};
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
for (let p = d;p.length > modulesRoot.length; p = dirname9(p))
|
|
5905
|
-
entries.add(p);
|
|
5906
|
-
return { root: modulesRoot, entries: [...entries] };
|
|
6010
|
+
for (const modulesRoot of roots)
|
|
6011
|
+
tree(modulesRoot, 0);
|
|
6012
|
+
return trees;
|
|
5907
6013
|
}
|
|
5908
6014
|
function profile(cwd, network, readAllow = [], deny = [], readOnly = []) {
|
|
5909
6015
|
const home = homedir4();
|
|
@@ -5911,6 +6017,7 @@ function profile(cwd, network, readAllow = [], deny = [], readOnly = []) {
|
|
|
5911
6017
|
const root = realPath(cwd);
|
|
5912
6018
|
const gitDirs = externalGitDirs(root);
|
|
5913
6019
|
const moduleGit = moduleGitDirs(root);
|
|
6020
|
+
const worktrees = worktreesRoot(root);
|
|
5914
6021
|
const creds = [".ssh", ".aws", ".gnupg", ".config/gh", ".config/gcloud", ".kube", ".docker", ".netrc", ".npmrc", ".omniroute", ".claude", ".codex", ".kimi-code", "Library/Keychains"];
|
|
5915
6022
|
const dev = developerDir();
|
|
5916
6023
|
const readable = [root, ...HOME_READABLE.map((p) => join12(home, p)), ...readAllow.map(inHome), ...gitDirs, ...dev ? [dev] : []];
|
|
@@ -5937,7 +6044,7 @@ function profile(cwd, network, readAllow = [], deny = [], readOnly = []) {
|
|
|
5937
6044
|
...start,
|
|
5938
6045
|
"(deny file-write*)",
|
|
5939
6046
|
...readOnly.length ? ['(allow file-write* (literal "/dev/null"))'] : [`(allow file-write* (subpath ${q(root)}) (regex #"^/dev/") ${gitDirs.map((d) => `(subpath ${q(d)})`).join(" ")})`],
|
|
5940
|
-
`(deny file-write* ${hubWriteRegexes(root, gitDirs, moduleGit).join(" ")})`,
|
|
6047
|
+
`(deny file-write* ${hubWriteRegexes(root, gitDirs, moduleGit, worktrees).join(" ")})`,
|
|
5941
6048
|
`(deny file-read* file-write* ${creds.map((c) => `(subpath ${q(join12(home, c))})`).join(" ")})`,
|
|
5942
6049
|
`(deny file-read* file-write* ${denyRegexes(root, deny).join(" ")})`,
|
|
5943
6050
|
...network ? [`(allow file-read* ${CA_BUNDLES.map((p) => `(literal ${q(p)})`).join(" ")} (regex ${q("/certifi/cacert\\.pem$")}))`, `(deny file-read* file-write* ${places})`] : []
|
|
@@ -6012,7 +6119,7 @@ function proxyEnv(network) {
|
|
|
6012
6119
|
const url = `http://127.0.0.1:${network.proxyPort}`;
|
|
6013
6120
|
return { HTTPS_PROXY: url, https_proxy: url, HTTP_PROXY: url, http_proxy: url, ALL_PROXY: url, all_proxy: url, NO_PROXY: "", no_proxy: "" };
|
|
6014
6121
|
}
|
|
6015
|
-
var SANDBOX_EXEC = "/usr/bin/sandbox-exec", OUTPUT_CAP = 20000, sandboxAvailable = () => process.platform === "darwin" && existsSync6(SANDBOX_EXEC), q, HOME_READABLE, CA_BUNDLES, SYSTEM_READABLE, MACH_SERVICES, NETWORK_MACH_SERVICES, PROXY_MACH_SERVICES;
|
|
6122
|
+
var SANDBOX_EXEC = "/usr/bin/sandbox-exec", OUTPUT_CAP = 20000, sandboxAvailable = () => process.platform === "darwin" && existsSync6(SANDBOX_EXEC), q, HOME_READABLE, CA_BUNDLES, MODULE_DEPTH = 8, SYSTEM_READABLE, MACH_SERVICES, NETWORK_MACH_SERVICES, PROXY_MACH_SERVICES;
|
|
6016
6123
|
var init_sandbox = __esm(() => {
|
|
6017
6124
|
init_project();
|
|
6018
6125
|
init_git();
|
|
@@ -6269,6 +6376,54 @@ import { randomUUID as randomUUID4 } from "crypto";
|
|
|
6269
6376
|
import { existsSync as existsSync7, mkdirSync as mkdirSync9 } from "fs";
|
|
6270
6377
|
import { join as join14, resolve as resolve11 } from "path";
|
|
6271
6378
|
import { spawn as spawn9 } from "child_process";
|
|
6379
|
+
function piVersionAsync(command, cwd, timeoutMs = 1e4) {
|
|
6380
|
+
const [bin, ...prefix] = command;
|
|
6381
|
+
if (!bin)
|
|
6382
|
+
return Promise.resolve(assessVersion("pi", undefined));
|
|
6383
|
+
return new Promise((resolve) => {
|
|
6384
|
+
let proc;
|
|
6385
|
+
try {
|
|
6386
|
+
proc = spawn9(bin, [...prefix, "--version"], { cwd, env: peerChildEnv("pi"), detached: true, stdio: ["ignore", "pipe", "ignore"] });
|
|
6387
|
+
} catch {
|
|
6388
|
+
resolve(assessVersion("pi", undefined));
|
|
6389
|
+
return;
|
|
6390
|
+
}
|
|
6391
|
+
let output = "", bytes = 0, done = false;
|
|
6392
|
+
const finish = async (code) => {
|
|
6393
|
+
if (done)
|
|
6394
|
+
return;
|
|
6395
|
+
done = true;
|
|
6396
|
+
clearTimeout(timer);
|
|
6397
|
+
try {
|
|
6398
|
+
await stopOwnedProcess(proc, { group: true });
|
|
6399
|
+
} catch {
|
|
6400
|
+
resolve(assessVersion("pi", undefined));
|
|
6401
|
+
return;
|
|
6402
|
+
}
|
|
6403
|
+
resolve(assessVersion("pi", code === 0 && bytes <= 64 * 1024 ? COMPAT.pi.parse(output) : undefined));
|
|
6404
|
+
};
|
|
6405
|
+
const timer = setTimeout(() => {
|
|
6406
|
+
finish(null);
|
|
6407
|
+
}, timeoutMs);
|
|
6408
|
+
proc.once("spawn", () => trackGroup(proc));
|
|
6409
|
+
proc.stdout.on("data", (chunk) => {
|
|
6410
|
+
if (done)
|
|
6411
|
+
return;
|
|
6412
|
+
bytes += chunk.length;
|
|
6413
|
+
if (bytes > 64 * 1024) {
|
|
6414
|
+
finish(null);
|
|
6415
|
+
return;
|
|
6416
|
+
}
|
|
6417
|
+
output += chunk.toString("utf8");
|
|
6418
|
+
});
|
|
6419
|
+
proc.once("error", () => {
|
|
6420
|
+
finish(null);
|
|
6421
|
+
});
|
|
6422
|
+
proc.once("close", (code) => {
|
|
6423
|
+
finish(code);
|
|
6424
|
+
});
|
|
6425
|
+
});
|
|
6426
|
+
}
|
|
6272
6427
|
function ownerStillAlive(pid, signature) {
|
|
6273
6428
|
const current = processSignature(pid);
|
|
6274
6429
|
if (current !== undefined)
|
|
@@ -6280,11 +6435,12 @@ function ownerStillAlive(pid, signature) {
|
|
|
6280
6435
|
return false;
|
|
6281
6436
|
}
|
|
6282
6437
|
}
|
|
6283
|
-
var PiToolSettlementFailure, PiPromptNotStarted, PiPromptUncertain, TERMINAL_OWNER_MAX_AGE_MS = 1000, PiPeer;
|
|
6438
|
+
var PiToolSettlementFailure, PiPromptNotStarted, PiPromptUncertain, PiVersionRefusal, TERMINAL_OWNER_MAX_AGE_MS = 1000, PiPeer;
|
|
6284
6439
|
var init_pi = __esm(() => {
|
|
6285
6440
|
init_launch();
|
|
6286
6441
|
init_envelope();
|
|
6287
6442
|
init_child_process();
|
|
6443
|
+
init_compat();
|
|
6288
6444
|
init_process_signature();
|
|
6289
6445
|
init_session();
|
|
6290
6446
|
init_tools();
|
|
@@ -6299,8 +6455,14 @@ var init_pi = __esm(() => {
|
|
|
6299
6455
|
};
|
|
6300
6456
|
PiPromptUncertain = class PiPromptUncertain extends Error {
|
|
6301
6457
|
};
|
|
6458
|
+
PiVersionRefusal = class PiVersionRefusal extends Error {
|
|
6459
|
+
constructor(verdict) {
|
|
6460
|
+
super(verdict.reason);
|
|
6461
|
+
}
|
|
6462
|
+
};
|
|
6302
6463
|
PiPeer = class PiPeer extends BasePeer {
|
|
6303
6464
|
opts;
|
|
6465
|
+
preflight;
|
|
6304
6466
|
hubNative = true;
|
|
6305
6467
|
proc;
|
|
6306
6468
|
server;
|
|
@@ -6367,9 +6529,10 @@ var init_pi = __esm(() => {
|
|
|
6367
6529
|
tuiCommands = new Map;
|
|
6368
6530
|
tuiQueue = [];
|
|
6369
6531
|
tuiWaiters = [];
|
|
6370
|
-
constructor(id, opts) {
|
|
6532
|
+
constructor(id, opts, preflight) {
|
|
6371
6533
|
super(id, opts.watchdogMs);
|
|
6372
6534
|
this.opts = opts;
|
|
6535
|
+
this.preflight = preflight;
|
|
6373
6536
|
}
|
|
6374
6537
|
get tuiLaunch() {
|
|
6375
6538
|
return this._tuiLaunch;
|
|
@@ -6615,6 +6778,8 @@ var init_pi = __esm(() => {
|
|
|
6615
6778
|
return Response.json({ decisions, ...reservation ? { reservation } : {} });
|
|
6616
6779
|
}
|
|
6617
6780
|
async startImpl() {
|
|
6781
|
+
if (this.preflight)
|
|
6782
|
+
await this.preflight();
|
|
6618
6783
|
this.terminalOwnerObservation = undefined;
|
|
6619
6784
|
this.stopping = false;
|
|
6620
6785
|
this.rosterDetached = false;
|
|
@@ -10756,7 +10921,7 @@ import { randomUUID as randomUUID8 } from "crypto";
|
|
|
10756
10921
|
import { lstatSync as lstatSync11, mkdtempSync as mkdtempSync5, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
|
|
10757
10922
|
import { tmpdir as tmpdir5 } from "os";
|
|
10758
10923
|
import { join as join17 } from "path";
|
|
10759
|
-
function claudeImplementerArgs(turn, mcp, settings, prompt) {
|
|
10924
|
+
function claudeImplementerArgs(turn, mcp, settings, prompt, model) {
|
|
10760
10925
|
return [
|
|
10761
10926
|
"-p",
|
|
10762
10927
|
"--output-format",
|
|
@@ -10772,6 +10937,7 @@ function claudeImplementerArgs(turn, mcp, settings, prompt) {
|
|
|
10772
10937
|
"",
|
|
10773
10938
|
"--settings",
|
|
10774
10939
|
settings,
|
|
10940
|
+
...model ? ["--model", model] : [],
|
|
10775
10941
|
"--permission-mode",
|
|
10776
10942
|
"default",
|
|
10777
10943
|
"--permission-prompts",
|
|
@@ -11034,7 +11200,7 @@ var init_claude_implementer = __esm(() => {
|
|
|
11034
11200
|
const child = new OwnedPrintChild({
|
|
11035
11201
|
peer: this.id,
|
|
11036
11202
|
command: this.command,
|
|
11037
|
-
args: claudeImplementerArgs(turn, mcp, settings, this.options.systemPrompt),
|
|
11203
|
+
args: claudeImplementerArgs(turn, mcp, settings, this.options.systemPrompt, this.options.model),
|
|
11038
11204
|
cwd: this.options.cwd,
|
|
11039
11205
|
input: group.envs.map(frame).join(`
|
|
11040
11206
|
|
|
@@ -11166,7 +11332,14 @@ function createPi(ctx) {
|
|
|
11166
11332
|
if (ctx.permission === "read-only")
|
|
11167
11333
|
throw new Error("Pi has no verified read-only permission mapping");
|
|
11168
11334
|
init_pi();
|
|
11169
|
-
|
|
11335
|
+
const options = ctx.daemon.pi;
|
|
11336
|
+
return new PiPeer(ctx.instance, options, async () => {
|
|
11337
|
+
const verdict = await piVersionAsync(options.cmd ?? ["pi"], options.cwd);
|
|
11338
|
+
if (!verdict.ok)
|
|
11339
|
+
throw new PiVersionRefusal(verdict);
|
|
11340
|
+
if (verdict.note)
|
|
11341
|
+
options.log?.(verdict.note);
|
|
11342
|
+
});
|
|
11170
11343
|
}
|
|
11171
11344
|
function createLocal(ctx) {
|
|
11172
11345
|
if (!("local" in ctx.daemon))
|
|
@@ -11273,9 +11446,9 @@ var init_reviewer_variants = __esm(() => {
|
|
|
11273
11446
|
init_kimi_review();
|
|
11274
11447
|
init_native_factories();
|
|
11275
11448
|
init_native_display();
|
|
11276
|
-
codexVariant = reviewerVariant("codex", "Codex reviewer", { backend: codexReviewerBackend, defaultPool: true, declaration: "explicit" }, CODEX_DISPLAY);
|
|
11449
|
+
codexVariant = reviewerVariant("codex", "Codex reviewer", { backend: codexReviewerBackend, defaultPool: true, declaration: "explicit", scope: "code" }, CODEX_DISPLAY);
|
|
11277
11450
|
CODEX_REVIEWER_VARIANT = { ...codexVariant, start: { ...codexVariant.start, create: createCodexReviewer } };
|
|
11278
|
-
KIMI_REVIEWER_VARIANT = reviewerVariant("kimi", "Kimi reviewer", { backend: kimiReviewerBackend, defaultPool: true, declaration: "explicit" }, KIMI_DISPLAY);
|
|
11451
|
+
KIMI_REVIEWER_VARIANT = reviewerVariant("kimi", "Kimi reviewer", { backend: kimiReviewerBackend, defaultPool: true, declaration: "explicit", scope: "code" }, KIMI_DISPLAY);
|
|
11279
11452
|
AGY_REVIEWER_VARIANT = {
|
|
11280
11453
|
...reviewerVariant("agy", "agy reviewer", { defaultPool: false, declaration: "automatic", unavailable: "agy is an implementer peer (#485)" }, { palette: { fg: "#6558a6", bg: "#f1effa" } }),
|
|
11281
11454
|
optional: true
|
|
@@ -12760,8 +12933,8 @@ var init_builtin_metadata = __esm(() => {
|
|
|
12760
12933
|
trust: { onPrem: false, hubSandboxed: false },
|
|
12761
12934
|
start: { create: createClaudeImplementer, daemonPort: "claude-implementer", forms: headlessForms, defaultForm: "headless", conductorStartable: true, hubSwitches: [], ownedFlags: [] },
|
|
12762
12935
|
identity: { mcp: "env", currentChild: true },
|
|
12763
|
-
instances: { mode: "headless", roles: ["implementer", "verifier"] },
|
|
12764
|
-
permission: { grants: { ask: "every write, edit and shell command asks", "ask-when-needed": "file tools inside the owned worktree may run automatically;
|
|
12936
|
+
instances: { mode: "headless", roles: ["implementer", "verifier"], modelSelect: true },
|
|
12937
|
+
permission: { grants: { ask: "every write, edit and shell command asks", "ask-when-needed": "file tools, proven read-only shell commands and bound task commands inside the owned worktree may run automatically; protected paths and other commands ask" } },
|
|
12765
12938
|
session: { idField: "sessionId", currentId: sessionId("sessionId"), freshOnRestore: false, launchKind: "claude-implementer", crashPlan: () => ({ resume: {}, how: "Claude implementer resumes its recorded per-task sessions" }) },
|
|
12766
12939
|
turnEnd: "adapter",
|
|
12767
12940
|
budget: { account: "claude" },
|
|
@@ -13681,7 +13854,8 @@ function assertNativeCapabilityScope(definition) {
|
|
|
13681
13854
|
["hooks.factsHook", declared(definition.hooks, "factsHook")],
|
|
13682
13855
|
["cli.entry", declared(definition.cli, "entry")],
|
|
13683
13856
|
["cli.preview", declared(definition.cli, "preview")],
|
|
13684
|
-
["cli.usage", declared(definition.cli, "usage")]
|
|
13857
|
+
["cli.usage", declared(definition.cli, "usage")],
|
|
13858
|
+
["instances.modelSelect", declared(definition.instances, "modelSelect")]
|
|
13685
13859
|
];
|
|
13686
13860
|
const field = restricted.find(([, present]) => present)?.[0];
|
|
13687
13861
|
if (field)
|
|
@@ -21491,6 +21665,11 @@ init_platform();
|
|
|
21491
21665
|
init_store();
|
|
21492
21666
|
init_shard();
|
|
21493
21667
|
init_work_lists();
|
|
21668
|
+
init_capture();
|
|
21669
|
+
init_capture_input();
|
|
21670
|
+
init_config2();
|
|
21671
|
+
init_policy();
|
|
21672
|
+
init_private_spans();
|
|
21494
21673
|
|
|
21495
21674
|
// node_modules/@staix/agent-mem/src/index.ts
|
|
21496
21675
|
init_project2();
|
|
@@ -21502,7 +21681,7 @@ init_store();
|
|
|
21502
21681
|
init_sync();
|
|
21503
21682
|
init_project2();
|
|
21504
21683
|
var AMEM_NAMES = { memory: "amem", goal: "amem goal" };
|
|
21505
|
-
var memoryUsage = (name = AMEM_NAMES.memory) => `usage: ${name} [list] [--tier short|long] [--kind <kind>] [--peer <p>] [--machine <id>] [--since <iso>] [--limit N] | search <words> [--history] [--as-of <UTC>] | show <id> [--history] [--as-of <UTC>] | graph <id|node> [--hops 1|2] [--history] [--as-of <UTC>] | promote|demote|forget <id> | supersede <old> <new> | rebuild | sync [--remote <url>] | machine [--label <name>] | import claude-mem [--db <path>] [--limit N] [--restart] | dedupe [--apply] | suggestions | confirm|reject <suggestion id> [--json]`;
|
|
21684
|
+
var memoryUsage = (name = AMEM_NAMES.memory) => `usage: ${name} [list] [--tier short|long] [--kind <kind>] [--peer <p>] [--machine <id>] [--since <iso>] [--limit N] | search <words> [--history] [--as-of <UTC>] | show <id> [--history] [--as-of <UTC>] | graph <id|node> [--hops 1|2] [--history] [--as-of <UTC>] | promote|demote|forget <id> | supersede <old> <new> | rebuild | sync [--remote <url>] | machine [--label <name>] | import claude-mem [--db <path>] [--limit N] [--restart] [--capture-days N] | dedupe [--apply] | suggestions | confirm|reject <suggestion id> [--json]`;
|
|
21506
21685
|
var MEMORY_USAGE = memoryUsage();
|
|
21507
21686
|
var goalUsage = (name = AMEM_NAMES.goal) => `usage: ${name} [list] [--all] | show <id> | new <title> [--milestone <text>]... [--depends <id>]... [--shared] | link <id> <task id> | state <id> open|active|blocked|done|dropped | done <id> | take <id> [--force] [--json]`;
|
|
21508
21687
|
var GOAL_USAGE = goalUsage();
|
|
@@ -21695,7 +21874,7 @@ init_sdlc_metrics();
|
|
|
21695
21874
|
// package.json
|
|
21696
21875
|
var package_default = {
|
|
21697
21876
|
name: "@staix/agent-hub",
|
|
21698
|
-
version: "0.
|
|
21877
|
+
version: "0.19.0",
|
|
21699
21878
|
description: "Native multi-agent hub: Claude Code, Codex, Kimi Code, Pi and local inference as peers in one project",
|
|
21700
21879
|
license: "MIT",
|
|
21701
21880
|
type: "module",
|
|
@@ -21712,7 +21891,7 @@ var package_default = {
|
|
|
21712
21891
|
bun: ">=1.4.2"
|
|
21713
21892
|
},
|
|
21714
21893
|
dependencies: {
|
|
21715
|
-
"@staix/agent-mem": "0.
|
|
21894
|
+
"@staix/agent-mem": "0.10.0"
|
|
21716
21895
|
},
|
|
21717
21896
|
devDependencies: {
|
|
21718
21897
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
@@ -21774,6 +21953,9 @@ init_sdlc();
|
|
|
21774
21953
|
init_envelope();
|
|
21775
21954
|
var inFlight = new Set;
|
|
21776
21955
|
|
|
21956
|
+
// src/hub/review-scope.ts
|
|
21957
|
+
var isReviewScope = (value) => value === "full" || value === "code";
|
|
21958
|
+
|
|
21777
21959
|
// src/hub/tasks.ts
|
|
21778
21960
|
init_review_findings();
|
|
21779
21961
|
init_roles();
|
|
@@ -21915,7 +22097,8 @@ function validateInstances(raw, catalog) {
|
|
|
21915
22097
|
const path = `instances.${id}`, entry = fields2(descriptor.value, path);
|
|
21916
22098
|
const declaredKind = entry.kind?.value;
|
|
21917
22099
|
const jcodeFields = typeof declaredKind === "string" && resolve(declaredKind)?.instances?.nativeHome === true;
|
|
21918
|
-
|
|
22100
|
+
const modelSelect = typeof declaredKind === "string" && resolve(declaredKind)?.headlessVariant?.instances?.modelSelect === true;
|
|
22101
|
+
if (Object.keys(entry).some((key) => !["kind", "roles", "start_mode", "permission_mode", "worktree", "review_mode", "readOnly", "review_scope", ...jcodeFields ? JCODE_FIELDS : [], ...modelSelect ? ["model"] : []].includes(key)))
|
|
21919
22102
|
return fail4(path, "unsupported field");
|
|
21920
22103
|
const kind = entry.kind?.value;
|
|
21921
22104
|
if (typeof kind !== "string")
|
|
@@ -21990,6 +22173,21 @@ function validateInstances(raw, catalog) {
|
|
|
21990
22173
|
return fail4("instances", `at most ${MAX_JCODE_INSTANCES} extra Jcode instances are supported per project`);
|
|
21991
22174
|
Object.assign(value, jcodeInstanceFields(entry, path));
|
|
21992
22175
|
}
|
|
22176
|
+
if (entry.model && modelSelect) {
|
|
22177
|
+
if (!headlessWriter)
|
|
22178
|
+
return fail4(`${path}.model`, "only a headless implementer takes a model");
|
|
22179
|
+
const word = entry.model.value;
|
|
22180
|
+
if (typeof word !== "string" || !NATIVE_WORD.test(word))
|
|
22181
|
+
return fail4(`${path}.model`, "must be one bounded printable word without spaces");
|
|
22182
|
+
value.model = word;
|
|
22183
|
+
}
|
|
22184
|
+
if (entry.review_scope) {
|
|
22185
|
+
if (!freshReviewer)
|
|
22186
|
+
return fail4(`${path}.review_scope`, "only a fresh reviewer instance has a review scope");
|
|
22187
|
+
if (!isReviewScope(entry.review_scope.value))
|
|
22188
|
+
return fail4(`${path}.review_scope`, 'must be "code" or "full"');
|
|
22189
|
+
value.review_scope = entry.review_scope.value;
|
|
22190
|
+
}
|
|
21993
22191
|
if (entry.readOnly)
|
|
21994
22192
|
value.readOnly = entry.readOnly.value;
|
|
21995
22193
|
if (entry.roles) {
|
|
@@ -30784,8 +30982,15 @@ async function callTool(req) {
|
|
|
30784
30982
|
if (typeof args?.tool_name !== "string" || !/^[A-Za-z][A-Za-z0-9_.:/-]{0,127}$/.test(args.tool_name) || !args.input || typeof args.input !== "object" || Array.isArray(args.input) || typeof args.tool_use_id !== "string" || !args.tool_use_id.length || args.tool_use_id.length > 256)
|
|
30785
30983
|
return deny("invalid native permission request");
|
|
30786
30984
|
try {
|
|
30787
|
-
const res = await hub.request({ t: "permission_request", tool: args?.tool_name, input: args?.input }, 3605000);
|
|
30788
|
-
|
|
30985
|
+
const res = await hub.request({ t: "permission_request", tool: args?.tool_name, input: args?.input, wrapperProtocol: 1 }, 3605000);
|
|
30986
|
+
if (res.ok !== true || res.behavior !== "allow")
|
|
30987
|
+
return deny(typeof res.message === "string" ? res.message : "permission denied");
|
|
30988
|
+
if (res.wrapperProtocol !== undefined && res.wrapperProtocol !== 1)
|
|
30989
|
+
return deny("unsupported host execution response");
|
|
30990
|
+
const updated = res.wrapperProtocol === 1 ? res.updatedInput : undefined;
|
|
30991
|
+
if (res.wrapperProtocol === 1 && (args.tool_name !== "Bash" || !updated || typeof updated !== "object" || Array.isArray(updated) || typeof updated.command !== "string"))
|
|
30992
|
+
return deny("invalid host execution response");
|
|
30993
|
+
return text5(JSON.stringify({ behavior: "allow", updatedInput: updated ?? args?.input }));
|
|
30789
30994
|
} catch {
|
|
30790
30995
|
return deny("hub permission unavailable");
|
|
30791
30996
|
}
|