@staix/agent-hub 0.18.0 → 0.20.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 +30 -0
- package/README.md +4 -4
- package/docs/agent-notes/README.md +6 -0
- package/docs/agent-notes/adapters.md +13 -10
- package/docs/agent-notes/bus.md +1 -1
- package/docs/agent-notes/daemon.md +11 -4
- package/docs/agent-notes/local-worker.md +3 -1
- package/docs/agent-notes/tasks.md +6 -1
- package/docs/agent-notes/tests.md +8 -4
- package/docs/dogfood.md +13 -4
- package/docs/memory.md +1 -1
- package/docs/operations.md +98 -28
- package/docs/quickstart.md +13 -5
- package/docs/sdlc.md +12 -0
- package/docs/security.md +24 -15
- package/docs/smoke.md +3 -3
- package/docs/specs/2026-09-19-agent-hub-design.md +101 -8
- 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/2026-10-27-0.20.0.md +75 -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 +25 -17
- package/package.json +2 -2
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +373 -72
- package/src/adapters/acp.ts +8 -0
- package/src/adapters/agy/entry.ts +1 -1
- package/src/adapters/channel-peer.ts +7 -5
- package/src/adapters/claude-channel.ts +7 -3
- package/src/adapters/claude-implementer.ts +6 -3
- package/src/adapters/codex-appserver.ts +11 -1
- package/src/adapters/compat.ts +32 -18
- package/src/adapters/conformance/enrollment.ts +4 -4
- package/src/adapters/conformance/factories.ts +2 -1
- package/src/adapters/conformance/fakes/pi-rpc.ts +1 -0
- package/src/adapters/conformance/kit.ts +10 -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/local-worker.ts +2 -0
- package/src/adapters/pi.ts +46 -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/adapters/reviewer-print.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/ci-watch.ts +140 -0
- package/src/hub/claude-launch-proof.ts +67 -2
- package/src/hub/claude-shell-wrapper.ts +349 -0
- package/src/hub/config-apply.ts +2 -2
- package/src/hub/config-edit.ts +2 -2
- package/src/hub/config-tier.ts +2 -2
- package/src/hub/config-trust.ts +1 -0
- package/src/hub/daemon.ts +282 -113
- 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/peers.ts +34 -0
- 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/settings.ts +1 -0
- package/src/hub/task-shell-command.ts +85 -0
- package/src/hub/task-shell-default.ts +79 -0
- package/src/hub/task-shell-proof.ts +171 -0
- package/src/hub/task-shell-sandbox.ts +61 -0
- package/src/hub/tasks.ts +274 -16
- package/src/local/deny.ts +112 -11
- package/src/local/sandbox.ts +70 -29
- 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" };
|
|
@@ -82,7 +85,8 @@ function memoryGitEnv(env = process.env, options = {}) {
|
|
|
82
85
|
}
|
|
83
86
|
function memoryGitSync(args, options) {
|
|
84
87
|
const argv = memoryGitArgv(args);
|
|
85
|
-
|
|
88
|
+
const timeout = gitTimeoutContext?.getStore();
|
|
89
|
+
return childProcess().spawnSync(argv[0], argv.slice(1), { ...timeout !== undefined ? { timeout, killSignal: "SIGKILL" } : {}, ...options, env: memoryGitEnv(options.env ?? process.env) });
|
|
86
90
|
}
|
|
87
91
|
function processSignature(pid) {
|
|
88
92
|
try {
|
|
@@ -118,11 +122,12 @@ var require2, processes, childProcess = () => processes ??= require2("node:child
|
|
|
118
122
|
const parent = dirname(absolute);
|
|
119
123
|
return parent === absolute ? absolute : join(canonical(parent), basename(absolute));
|
|
120
124
|
}
|
|
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],
|
|
125
|
+
}, 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], gitTimeoutContext;
|
|
122
126
|
var init_platform = __esm(() => {
|
|
123
127
|
require2 = createRequire(import.meta.url);
|
|
124
128
|
DENY_SEGMENTS = [".maru/secrets", ".agenthub/state", ".agenthub/archive"];
|
|
125
129
|
DENY_NAMES = ["^\\.env", "\\.pem$", "\\.key$", "^id_rsa", "^id_ed25519", "^credentials(\\.|$)", "^auth\\.json$", "^\\.netrc$", "^\\.npmrc$", "^\\.pypirc$"];
|
|
130
|
+
denyNamePatterns = new Map(DENY_NAMES.map((source) => [source, new RegExp(source)]));
|
|
126
131
|
SAFE_CONFIG = ["core.fsmonitor=false", "core.hooksPath=/dev/null", "diff.external=", "core.pager=cat", "credential.helper=", "core.askpass="];
|
|
127
132
|
CREDENTIAL_KEYS = new Set(["credential.helper=", "core.askpass="]);
|
|
128
133
|
});
|
|
@@ -291,13 +296,24 @@ var plain = (text) => text.length <= 64 && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/
|
|
|
291
296
|
var init_compat = __esm(() => {
|
|
292
297
|
init_project();
|
|
293
298
|
COMPAT = {
|
|
299
|
+
pi: {
|
|
300
|
+
label: "Pi",
|
|
301
|
+
verified: ["1.4.0"],
|
|
302
|
+
gate: "contract",
|
|
303
|
+
probe: ["pi", "--version"],
|
|
304
|
+
config: "pi.cmd",
|
|
305
|
+
parse: (out) => plain(out.trim()),
|
|
306
|
+
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.",
|
|
307
|
+
receipt: "docs/verification/2026-10-26-pi-140-compat.md"
|
|
308
|
+
},
|
|
294
309
|
"claude-implementer": {
|
|
295
310
|
label: "Claude headless implementer",
|
|
296
|
-
verified: ["2.1.309"],
|
|
311
|
+
verified: ["2.1.309", "2.1.310"],
|
|
297
312
|
gate: "contract",
|
|
298
313
|
probe: ["claude", "--version"],
|
|
299
314
|
parse: (out) => plain(out.trim().replace(/ \(Claude Code\)$/, "")),
|
|
300
|
-
trustNote: "The native prompt-tool
|
|
315
|
+
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.",
|
|
316
|
+
receipt: "docs/verification/2026-10-26-headless-claude-shell-context.md"
|
|
301
317
|
},
|
|
302
318
|
"codex-review": {
|
|
303
319
|
label: "Codex reviewer (codex-cli)",
|
|
@@ -306,19 +322,21 @@ var init_compat = __esm(() => {
|
|
|
306
322
|
probe: ["codex", "--version"],
|
|
307
323
|
config: "codex_bin",
|
|
308
324
|
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)."
|
|
325
|
+
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).",
|
|
326
|
+
receipt: "docs/verification/2026-10-24-codex-0164-reviewer-isolation.md"
|
|
310
327
|
},
|
|
311
328
|
"kimi-review": {
|
|
312
329
|
label: "Kimi reviewer (kimi-code)",
|
|
313
|
-
verified: ["2.2.0", "2.3.0", "2.3.1"],
|
|
330
|
+
verified: ["2.2.0", "2.3.0", "2.3.1", "2.3.2"],
|
|
314
331
|
gate: "isolation",
|
|
315
332
|
probe: ["kimi", "--version"],
|
|
316
333
|
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."
|
|
334
|
+
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.",
|
|
335
|
+
receipt: "docs/verification/2026-10-26-kimi-232-reviewer-compat.md"
|
|
318
336
|
},
|
|
319
337
|
jcode: {
|
|
320
338
|
label: "Jcode",
|
|
321
|
-
verified: ["0.98.1", "0.99.0"],
|
|
339
|
+
verified: ["0.98.1", "0.99.0", "0.99.1"],
|
|
322
340
|
line: { min: "0.98.0", below: "1.0.0" },
|
|
323
341
|
gate: "contract",
|
|
324
342
|
probe: ["jcode", "--quiet", "version", "--json"],
|
|
@@ -331,25 +349,28 @@ var init_compat = __esm(() => {
|
|
|
331
349
|
return;
|
|
332
350
|
}
|
|
333
351
|
},
|
|
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."
|
|
352
|
+
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.",
|
|
353
|
+
receipt: "docs/verification/2026-10-26-jcode-0.99.1.md"
|
|
335
354
|
},
|
|
336
355
|
agy: {
|
|
337
356
|
label: "agy",
|
|
338
|
-
verified: ["1.4.2"],
|
|
357
|
+
verified: ["1.4.2", "1.4.7"],
|
|
339
358
|
line: { min: "1.4.0", below: "1.5.0" },
|
|
340
359
|
gate: "contract",
|
|
341
360
|
probe: ["agy", "--version"],
|
|
342
361
|
parse: (out) => plain(out.trim()),
|
|
343
|
-
trustNote: "Only 1.4.2
|
|
362
|
+
trustNote: "Only 1.4.2 and 1.4.7 are T0-verified; the plugin MCP and hook contract of another version is unverified.",
|
|
363
|
+
receipt: "docs/verification/2026-10-26-agy-1.4.7.md"
|
|
344
364
|
},
|
|
345
365
|
qwen: {
|
|
346
366
|
label: "Qwen Code",
|
|
347
|
-
verified: ["0.25.0", "0.26.0"],
|
|
367
|
+
verified: ["0.25.0", "0.26.0", "0.26.1"],
|
|
348
368
|
gate: "contract",
|
|
349
369
|
probe: ["qwen", "--version"],
|
|
350
370
|
config: "qwen_cmd",
|
|
351
371
|
parse: (out) => plain(out.trim()),
|
|
352
|
-
trustNote: "Native ACP identity
|
|
372
|
+
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.",
|
|
373
|
+
receipt: "docs/verification/2026-10-26-qwen-0261-acp.md"
|
|
353
374
|
},
|
|
354
375
|
"qwen-dual": {
|
|
355
376
|
label: "Qwen Code dual-output TUI",
|
|
@@ -358,7 +379,8 @@ var init_compat = __esm(() => {
|
|
|
358
379
|
probe: ["qwen", "--version"],
|
|
359
380
|
config: "qwen_cmd",
|
|
360
381
|
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."
|
|
382
|
+
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.",
|
|
383
|
+
receipt: "docs/verification/2026-10-25-qwen-dual-output-t0.md"
|
|
362
384
|
}
|
|
363
385
|
};
|
|
364
386
|
MAX_BYTES = 64 * 1024;
|
|
@@ -1394,6 +1416,7 @@ var init_reviewer_print = __esm(() => {
|
|
|
1394
1416
|
ReviewerPrintPeer = class ReviewerPrintPeer extends BasePeer {
|
|
1395
1417
|
opts;
|
|
1396
1418
|
hubNative = true;
|
|
1419
|
+
hubOwned = true;
|
|
1397
1420
|
generation = 0;
|
|
1398
1421
|
child;
|
|
1399
1422
|
childCleanup;
|
|
@@ -2922,6 +2945,9 @@ var init_acp = __esm(() => {
|
|
|
2922
2945
|
};
|
|
2923
2946
|
AcpPeer = class AcpPeer extends BasePeer {
|
|
2924
2947
|
opts;
|
|
2948
|
+
deliveryOnly = true;
|
|
2949
|
+
hubOwned = true;
|
|
2950
|
+
form = "headless";
|
|
2925
2951
|
permissionMode;
|
|
2926
2952
|
nativePolicy;
|
|
2927
2953
|
modeUnknown = false;
|
|
@@ -3037,6 +3063,10 @@ var init_acp = __esm(() => {
|
|
|
3037
3063
|
recoveryMetadata() {
|
|
3038
3064
|
return { launch: { kind: "acp", ...this.opts.launchModel ? { model: this.opts.launchModel } : {} }, ...this.sessionId ? { sessionId: this.sessionId } : {} };
|
|
3039
3065
|
}
|
|
3066
|
+
get nativeSession() {
|
|
3067
|
+
const session = this.recoveryMetadata().sessionId;
|
|
3068
|
+
return typeof session === "string" ? session : undefined;
|
|
3069
|
+
}
|
|
3040
3070
|
async start() {
|
|
3041
3071
|
if (this.opts.requireOwnedGroup && this.proc)
|
|
3042
3072
|
throw new Error("Configured ACP still has a process; stop it before starting another");
|
|
@@ -3643,6 +3673,7 @@ var init_codex_appserver = __esm(() => {
|
|
|
3643
3673
|
TRACKED = new Set(["thread/start", "thread/resume"]);
|
|
3644
3674
|
CodexPeer = class CodexPeer extends BasePeer {
|
|
3645
3675
|
opts;
|
|
3676
|
+
frontendProxy;
|
|
3646
3677
|
permissionMode;
|
|
3647
3678
|
actionWaits = new Map;
|
|
3648
3679
|
get actionGated() {
|
|
@@ -3687,6 +3718,17 @@ var init_codex_appserver = __esm(() => {
|
|
|
3687
3718
|
super(id, opts.watchdogMs);
|
|
3688
3719
|
this.opts = opts;
|
|
3689
3720
|
this.permissionMode = opts.permissionMode ?? "ask";
|
|
3721
|
+
const owner = this;
|
|
3722
|
+
this.frontendProxy = {
|
|
3723
|
+
get url() {
|
|
3724
|
+
return owner.proxyUrl;
|
|
3725
|
+
},
|
|
3726
|
+
get attached() {
|
|
3727
|
+
return owner.proxyAttached;
|
|
3728
|
+
},
|
|
3729
|
+
setStartupPermissionMode: this.setStartupPermissionMode.bind(this),
|
|
3730
|
+
clearPermissionMode: this.clearPermissionMode.bind(this)
|
|
3731
|
+
};
|
|
3690
3732
|
}
|
|
3691
3733
|
recoveryMetadata() {
|
|
3692
3734
|
return {
|
|
@@ -3697,6 +3739,9 @@ var init_codex_appserver = __esm(() => {
|
|
|
3697
3739
|
get thread() {
|
|
3698
3740
|
return this.threadId;
|
|
3699
3741
|
}
|
|
3742
|
+
get nativeSession() {
|
|
3743
|
+
return this.threadId || undefined;
|
|
3744
|
+
}
|
|
3700
3745
|
get proxyUrl() {
|
|
3701
3746
|
return `ws://127.0.0.1:${this.server?.port ?? this.opts.proxyPort}`;
|
|
3702
3747
|
}
|
|
@@ -4303,6 +4348,9 @@ ${renderDigest(envs, false)}`;
|
|
|
4303
4348
|
link.up.send(JSON.stringify({ method: "thread/revert", id, params: { threadId: this.threadId, beforeTurnId: turnId } }));
|
|
4304
4349
|
});
|
|
4305
4350
|
}
|
|
4351
|
+
revertTurn(nativeTurn) {
|
|
4352
|
+
return this.revert(nativeTurn);
|
|
4353
|
+
}
|
|
4306
4354
|
readUsage() {
|
|
4307
4355
|
const link = this.link;
|
|
4308
4356
|
if (!this.opts.onUsage || !link || link.up.readyState !== WebSocket.OPEN)
|
|
@@ -4819,7 +4867,10 @@ function qwenDualNamesStateDir(input, stateDir) {
|
|
|
4819
4867
|
};
|
|
4820
4868
|
return seen(input, 0);
|
|
4821
4869
|
}
|
|
4822
|
-
|
|
4870
|
+
function isQwenTaskNotification(text) {
|
|
4871
|
+
return text.length <= NOTIFICATION_CAP && QWEN_TASK_NOTIFICATIONS.test(text);
|
|
4872
|
+
}
|
|
4873
|
+
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
4874
|
var init_qwen_dual = __esm(() => {
|
|
4824
4875
|
init_envelope();
|
|
4825
4876
|
init_child_process();
|
|
@@ -4828,6 +4879,9 @@ var init_qwen_dual = __esm(() => {
|
|
|
4828
4879
|
init_qwen_native();
|
|
4829
4880
|
SUBMIT_CAP = 256 * 1024;
|
|
4830
4881
|
READ_CAP = 1024 * 1024;
|
|
4882
|
+
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>`;
|
|
4883
|
+
QWEN_TASK_NOTIFICATIONS = new RegExp(`^${NOTIFICATION}(?:\\n\\n${NOTIFICATION})*$`);
|
|
4884
|
+
NOTIFICATION_CAP = 64 * 1024;
|
|
4831
4885
|
QwenDualPeer = class QwenDualPeer extends BasePeer {
|
|
4832
4886
|
opts;
|
|
4833
4887
|
permissionModeState = "unmanaged";
|
|
@@ -5276,6 +5330,13 @@ ${text}`;
|
|
|
5276
5330
|
if (!this.stopping && !this.detached && this.state === "offline")
|
|
5277
5331
|
this.setState("idle");
|
|
5278
5332
|
}
|
|
5333
|
+
isNotificationEvent(event) {
|
|
5334
|
+
const content = event.message?.content;
|
|
5335
|
+
if (!Array.isArray(content) || content.length !== 1)
|
|
5336
|
+
return false;
|
|
5337
|
+
const block = content[0];
|
|
5338
|
+
return !!block && typeof block === "object" && block.type === "text" && typeof block.text === "string" && isQwenTaskNotification(block.text);
|
|
5339
|
+
}
|
|
5279
5340
|
promptText(event) {
|
|
5280
5341
|
const content = event.message?.content;
|
|
5281
5342
|
if (typeof content === "string")
|
|
@@ -5286,6 +5347,8 @@ ${text}`;
|
|
|
5286
5347
|
return blocks.length ? blocks.map((block) => block.text).join("") : undefined;
|
|
5287
5348
|
}
|
|
5288
5349
|
onUser(event) {
|
|
5350
|
+
if (this.isNotificationEvent(event))
|
|
5351
|
+
return;
|
|
5289
5352
|
const text = this.promptText(event);
|
|
5290
5353
|
if (text === undefined)
|
|
5291
5354
|
return;
|
|
@@ -5808,29 +5871,73 @@ var init_path_policy = __esm(() => {
|
|
|
5808
5871
|
|
|
5809
5872
|
// src/local/deny.ts
|
|
5810
5873
|
import { basename as basename4, dirname as dirname8 } from "path";
|
|
5811
|
-
function hubWriteRegexes(root, gitDirs = [], moduleGit) {
|
|
5874
|
+
function hubWriteRegexes(root, gitDirs = [], moduleGit = [], worktreesRoot) {
|
|
5812
5875
|
const git = hfsFoldedName(".git");
|
|
5813
5876
|
const re = (s) => `(regex ${sbplString(s)})`;
|
|
5814
5877
|
const external = gitDirs.flatMap((d) => [
|
|
5815
5878
|
`(require-all ${re(`^${sbplEscape(d)}/[^/]+$`)} ${re(`^.*/(${hfsFoldedName("config")}|${hfsFoldedName("hooks")})$`)})`,
|
|
5879
|
+
`(require-all ${re(`^${sbplEscape(d)}/[^/]+$`)} ${CONFIG_WORKTREE_NAME.map(re).join(" ")})`,
|
|
5816
5880
|
`(subpath ${sbplString(`${d}/hooks`)})`,
|
|
5817
5881
|
`(require-all (subpath ${sbplString(d)}) ${re(`^.*/${hfsFoldedName("commondir")}$`)})`
|
|
5818
5882
|
]);
|
|
5819
|
-
const
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5883
|
+
const allDirs = moduleGit.flatMap((tree) => tree.dirs ?? []);
|
|
5884
|
+
const modules = moduleGit.flatMap((tree) => {
|
|
5885
|
+
const root = sbplEscape(tree.root);
|
|
5886
|
+
const found = tree.dirs ?? [];
|
|
5887
|
+
const inside = found.map((d) => `(require-not ${re(`^${sbplEscape(d)}(/|$)`)})`).join(" ");
|
|
5888
|
+
const anchored = found.flatMap((d) => {
|
|
5889
|
+
const dir = sbplEscape(d);
|
|
5890
|
+
return [
|
|
5891
|
+
`(require-all ${re(`^${dir}/[^/]+$`)} ${re(`^.*/${hfsFoldedName("config")}$`)})`,
|
|
5892
|
+
`(require-all ${re(`^${dir}/[^/]+$`)} ${CONFIG_WORKTREE_NAME.map(re).join(" ")})`,
|
|
5893
|
+
`(require-all ${re(`^${dir}/`)} ${re(hooksUnder(d))})`,
|
|
5894
|
+
`(require-all ${re(`^${dir}/worktrees/[^/]+/[^/]+$`)} ${CONFIG_WORKTREE_NAME.map(re).join(" ")})`,
|
|
5895
|
+
`(require-all ${re(`^${dir}/[^/]+$`)} ${re(`^.*/${hfsFoldedName("worktrees")}$`)})`,
|
|
5896
|
+
re(`^${dir}/worktrees/[^/]+$`),
|
|
5897
|
+
`(require-all ${re(`^${dir}/[^/]+$`)} ${re(`^.*/${hfsFoldedName("hooks")}$`)})`,
|
|
5898
|
+
`(require-all ${re(`^${dir}/worktrees/[^/]+/modules/.`)} ${allDirs.filter((f) => f.startsWith(`${d}/worktrees/`)).map((f) => `(require-not ${re(`^${sbplEscape(f)}(/|$)`)})`).join(" ")})`,
|
|
5899
|
+
`(require-all ${re(`^${dir}/worktrees/[^/]+/[^/]+$`)} ${re(`^.*/${hfsFoldedName("modules")}$`)})`
|
|
5900
|
+
];
|
|
5901
|
+
});
|
|
5902
|
+
return [
|
|
5903
|
+
`(require-all ${re(`^${root}/.`)} ${inside})`,
|
|
5904
|
+
...anchored,
|
|
5905
|
+
...tree.entries.map((d) => `(require-all ${re(`^${sbplEscape(dirname8(d))}/[^/]+$`)} ${re(`^.*/${hfsFoldedName(basename4(d))}$`)})`)
|
|
5906
|
+
];
|
|
5907
|
+
});
|
|
5908
|
+
const siblings = worktreesRoot ? (() => {
|
|
5909
|
+
const w = sbplEscape(worktreesRoot);
|
|
5910
|
+
return [
|
|
5911
|
+
`(require-all ${re(`^${sbplEscape(dirname8(worktreesRoot))}/[^/]+$`)} ${re(`^.*/${hfsFoldedName(basename4(worktreesRoot))}$`)})`,
|
|
5912
|
+
re(`^${w}/[^/]+$`),
|
|
5913
|
+
`(require-all ${re(`^${w}/[^/]+/[^/]+$`)} ${re(`^.*/${hfsFoldedName("config")}$`)})`,
|
|
5914
|
+
`(require-all ${re(`^${w}/[^/]+/[^/]+$`)} ${CONFIG_WORKTREE_NAME.map(re).join(" ")})`,
|
|
5915
|
+
`(require-all ${re(`^${w}/[^/]+/`)} ${re(hooksUnder(`${worktreesRoot}/x`))})`,
|
|
5916
|
+
`(require-all ${re(`^${w}/[^/]+/modules/.`)} ${moduleGit.flatMap((tree) => tree.dirs ?? []).filter((d) => d.startsWith(`${worktreesRoot}/`)).map((d) => `(require-not ${re(`^${sbplEscape(d)}(/|$)`)})`).join(" ")})`,
|
|
5917
|
+
`(require-all ${re(`^${w}/[^/]+/[^/]+$`)} ${re(`^.*/${hfsFoldedName("modules")}$`)})`
|
|
5918
|
+
];
|
|
5919
|
+
})() : [];
|
|
5824
5920
|
return [
|
|
5825
5921
|
`(require-all (subpath ${sbplString(root)}) ${re(`^.*/${hfsFoldedName(".agenthub")}(/|$)`)})`,
|
|
5826
5922
|
re(`^.*/${git}$`),
|
|
5827
5923
|
re(`^.*/${git}/${hfsFoldedName("config")}$`),
|
|
5924
|
+
`(require-all ${re(`^.*/${git}/[^/]+$`)} ${CONFIG_WORKTREE_NAME.map(re).join(" ")})`,
|
|
5828
5925
|
re(`^.*/${git}/${hfsFoldedName("hooks")}(/|$)`),
|
|
5829
5926
|
`(require-all ${re(`^.*/${git}/.*$`)} ${re(`^.*/${hfsFoldedName("commondir")}$`)})`,
|
|
5830
5927
|
...modules,
|
|
5928
|
+
...siblings,
|
|
5831
5929
|
...external
|
|
5832
5930
|
];
|
|
5833
5931
|
}
|
|
5932
|
+
function objectStoreRules(common) {
|
|
5933
|
+
const objects = `${common}/objects`, o = sbplEscape(objects);
|
|
5934
|
+
const re = (s) => `(regex ${sbplString(s)})`;
|
|
5935
|
+
return [
|
|
5936
|
+
`(deny file-write* (subpath ${sbplString(`${objects}/info`)}) (subpath ${sbplString(`${objects}/pack`)}) ${[objects, `${objects}/info`, `${objects}/pack`].map((p) => `(literal ${sbplString(p)})`).join(" ")})`,
|
|
5937
|
+
`(deny file-write-unlink ${re(`^${o}/[0-9a-f][0-9a-f]$`)})`,
|
|
5938
|
+
`(deny file-write-data file-write-unlink file-write-mode file-write-flags ${re(`^${o}/[0-9a-f][0-9a-f]/[0-9a-f]+$`)})`
|
|
5939
|
+
];
|
|
5940
|
+
}
|
|
5834
5941
|
function denyRegexes(root, extra = [], withNames = true) {
|
|
5835
5942
|
const names = DENY_NAMES.map((re) => {
|
|
5836
5943
|
const body = re.startsWith("^") ? re.slice(1) : `[^/]*${re}`;
|
|
@@ -5842,12 +5949,16 @@ function denyRegexes(root, extra = [], withNames = true) {
|
|
|
5842
5949
|
...extra.filter(Boolean).map((e) => `^${sbplEscape(root)}/.*${sbplEscape(e)}`)
|
|
5843
5950
|
].map((re) => `(regex ${sbplString(re)})`);
|
|
5844
5951
|
}
|
|
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}
|
|
5952
|
+
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) => {
|
|
5953
|
+
const depth = parent.split("/").filter(Boolean).length;
|
|
5954
|
+
return depth > MAX_ANCHOR_DEPTH ? `^.*/${hfsFoldedName("hooks")}(/|$)` : `^${"/[^/]+".repeat(depth)}/${hfsFoldedName("hooks")}(/|$)`;
|
|
5955
|
+
};
|
|
5846
5956
|
var init_deny = __esm(() => {
|
|
5847
5957
|
init_platform();
|
|
5848
5958
|
init_platform();
|
|
5849
5959
|
HFS_IGNORABLE_POINTS = ["\u200C", "\u200D", "\u200E", "\u200F", "\u202A", "\u202B", "\u202C", "\u202D", "\u202E", "\u206A", "\u206B", "\u206C", "\u206D", "\u206E", "\u206F", "\uFEFF"];
|
|
5850
5960
|
HFS_IGNORABLE = `(${HFS_IGNORABLE_POINTS.join("|")})`;
|
|
5961
|
+
CONFIG_WORKTREE_NAME = [`^.*/${hfsFoldedName("config")}\\.[^/]*$`, `^.*/[^/]*${hfsFoldedName("worktree")}$`];
|
|
5851
5962
|
});
|
|
5852
5963
|
|
|
5853
5964
|
// src/local/sandbox.ts
|
|
@@ -5871,39 +5982,70 @@ function externalGitDirs(root) {
|
|
|
5871
5982
|
`).map((d) => resolve9(root, d));
|
|
5872
5983
|
return [...new Set(dirs)].filter((d) => !d.startsWith(`${root}/`));
|
|
5873
5984
|
}
|
|
5874
|
-
function
|
|
5985
|
+
function worktreesRoot(root) {
|
|
5875
5986
|
const out = memoryGitSync(["-C", root, "rev-parse", "--git-common-dir"], { encoding: "utf8" });
|
|
5987
|
+
return out.status === 0 && out.stdout.trim() ? join12(resolve9(root, out.stdout.trim()), "worktrees") : undefined;
|
|
5988
|
+
}
|
|
5989
|
+
function moduleGitDirs(root) {
|
|
5990
|
+
const out = memoryGitSync(["-C", root, "rev-parse", "--git-common-dir", "--absolute-git-dir"], { encoding: "utf8" });
|
|
5876
5991
|
if (out.status !== 0)
|
|
5877
|
-
return;
|
|
5878
|
-
const
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5992
|
+
return [];
|
|
5993
|
+
const [commonRaw, ownRaw] = out.stdout.trim().split(`
|
|
5994
|
+
`);
|
|
5995
|
+
if (!commonRaw || !ownRaw)
|
|
5996
|
+
return [];
|
|
5997
|
+
const common = resolve9(root, commonRaw), own = resolve9(root, ownRaw);
|
|
5998
|
+
const roots = new Set([join12(common, "modules"), join12(own, "modules")]);
|
|
5999
|
+
try {
|
|
6000
|
+
for (const e of readdirSync(join12(common, "worktrees"), { withFileTypes: true })) {
|
|
6001
|
+
if (!e.isDirectory())
|
|
6002
|
+
continue;
|
|
6003
|
+
const modules = join12(common, "worktrees", e.name, "modules");
|
|
6004
|
+
if (existsSync6(modules))
|
|
6005
|
+
roots.add(modules);
|
|
5888
6006
|
}
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
6007
|
+
} catch {}
|
|
6008
|
+
const trees = [];
|
|
6009
|
+
const tree = (modulesRoot, nesting) => {
|
|
6010
|
+
const dirs = [];
|
|
6011
|
+
const walk = (dir, depth) => {
|
|
6012
|
+
if (depth > 8)
|
|
6013
|
+
return;
|
|
6014
|
+
let entries;
|
|
6015
|
+
try {
|
|
6016
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
6017
|
+
} catch {
|
|
6018
|
+
return;
|
|
6019
|
+
}
|
|
6020
|
+
const names = new Set(entries.map((e) => e.name));
|
|
6021
|
+
if (names.has("HEAD") && names.has("config")) {
|
|
6022
|
+
dirs.push(dir);
|
|
6023
|
+
return;
|
|
6024
|
+
}
|
|
6025
|
+
for (const e of entries)
|
|
6026
|
+
if (e.isDirectory())
|
|
6027
|
+
walk(join12(dir, e.name), depth + 1);
|
|
6028
|
+
};
|
|
6029
|
+
walk(modulesRoot, 0);
|
|
6030
|
+
const found = nesting >= MODULE_DEPTH ? [] : dirs;
|
|
6031
|
+
const entries = new Set([modulesRoot]);
|
|
6032
|
+
for (const d of found)
|
|
6033
|
+
for (let p = d;p.length > modulesRoot.length; p = dirname9(p))
|
|
6034
|
+
entries.add(p);
|
|
6035
|
+
trees.push({ root: modulesRoot, entries: [...entries], dirs: found });
|
|
6036
|
+
for (const d of found) {
|
|
6037
|
+
tree(join12(d, "modules"), nesting + 1);
|
|
6038
|
+
try {
|
|
6039
|
+
for (const e of readdirSync(join12(d, "worktrees"), { withFileTypes: true })) {
|
|
6040
|
+
if (e.isDirectory() && existsSync6(join12(d, "worktrees", e.name, "modules")))
|
|
6041
|
+
tree(join12(d, "worktrees", e.name, "modules"), nesting + 1);
|
|
6042
|
+
}
|
|
6043
|
+
} catch {}
|
|
5896
6044
|
}
|
|
5897
|
-
for (const e of entries)
|
|
5898
|
-
if (e.isDirectory())
|
|
5899
|
-
walk(join12(dir, e.name), depth + 1);
|
|
5900
6045
|
};
|
|
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] };
|
|
6046
|
+
for (const modulesRoot of roots)
|
|
6047
|
+
tree(modulesRoot, 0);
|
|
6048
|
+
return trees;
|
|
5907
6049
|
}
|
|
5908
6050
|
function profile(cwd, network, readAllow = [], deny = [], readOnly = []) {
|
|
5909
6051
|
const home = homedir4();
|
|
@@ -5911,6 +6053,7 @@ function profile(cwd, network, readAllow = [], deny = [], readOnly = []) {
|
|
|
5911
6053
|
const root = realPath(cwd);
|
|
5912
6054
|
const gitDirs = externalGitDirs(root);
|
|
5913
6055
|
const moduleGit = moduleGitDirs(root);
|
|
6056
|
+
const worktrees = worktreesRoot(root);
|
|
5914
6057
|
const creds = [".ssh", ".aws", ".gnupg", ".config/gh", ".config/gcloud", ".kube", ".docker", ".netrc", ".npmrc", ".omniroute", ".claude", ".codex", ".kimi-code", "Library/Keychains"];
|
|
5915
6058
|
const dev = developerDir();
|
|
5916
6059
|
const readable = [root, ...HOME_READABLE.map((p) => join12(home, p)), ...readAllow.map(inHome), ...gitDirs, ...dev ? [dev] : []];
|
|
@@ -5937,7 +6080,8 @@ function profile(cwd, network, readAllow = [], deny = [], readOnly = []) {
|
|
|
5937
6080
|
...start,
|
|
5938
6081
|
"(deny file-write*)",
|
|
5939
6082
|
...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(" ")})`,
|
|
6083
|
+
`(deny file-write* ${hubWriteRegexes(root, gitDirs, moduleGit, worktrees).join(" ")})`,
|
|
6084
|
+
...worktrees ? objectStoreRules(dirname9(worktrees)) : [],
|
|
5941
6085
|
`(deny file-read* file-write* ${creds.map((c) => `(subpath ${q(join12(home, c))})`).join(" ")})`,
|
|
5942
6086
|
`(deny file-read* file-write* ${denyRegexes(root, deny).join(" ")})`,
|
|
5943
6087
|
...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 +6156,7 @@ function proxyEnv(network) {
|
|
|
6012
6156
|
const url = `http://127.0.0.1:${network.proxyPort}`;
|
|
6013
6157
|
return { HTTPS_PROXY: url, https_proxy: url, HTTP_PROXY: url, http_proxy: url, ALL_PROXY: url, all_proxy: url, NO_PROXY: "", no_proxy: "" };
|
|
6014
6158
|
}
|
|
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;
|
|
6159
|
+
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
6160
|
var init_sandbox = __esm(() => {
|
|
6017
6161
|
init_project();
|
|
6018
6162
|
init_git();
|
|
@@ -6269,6 +6413,54 @@ import { randomUUID as randomUUID4 } from "crypto";
|
|
|
6269
6413
|
import { existsSync as existsSync7, mkdirSync as mkdirSync9 } from "fs";
|
|
6270
6414
|
import { join as join14, resolve as resolve11 } from "path";
|
|
6271
6415
|
import { spawn as spawn9 } from "child_process";
|
|
6416
|
+
function piVersionAsync(command, cwd, timeoutMs = 1e4) {
|
|
6417
|
+
const [bin, ...prefix] = command;
|
|
6418
|
+
if (!bin)
|
|
6419
|
+
return Promise.resolve(assessVersion("pi", undefined));
|
|
6420
|
+
return new Promise((resolve) => {
|
|
6421
|
+
let proc;
|
|
6422
|
+
try {
|
|
6423
|
+
proc = spawn9(bin, [...prefix, "--version"], { cwd, env: peerChildEnv("pi"), detached: true, stdio: ["ignore", "pipe", "ignore"] });
|
|
6424
|
+
} catch {
|
|
6425
|
+
resolve(assessVersion("pi", undefined));
|
|
6426
|
+
return;
|
|
6427
|
+
}
|
|
6428
|
+
let output = "", bytes = 0, done = false;
|
|
6429
|
+
const finish = async (code) => {
|
|
6430
|
+
if (done)
|
|
6431
|
+
return;
|
|
6432
|
+
done = true;
|
|
6433
|
+
clearTimeout(timer);
|
|
6434
|
+
try {
|
|
6435
|
+
await stopOwnedProcess(proc, { group: true });
|
|
6436
|
+
} catch {
|
|
6437
|
+
resolve(assessVersion("pi", undefined));
|
|
6438
|
+
return;
|
|
6439
|
+
}
|
|
6440
|
+
resolve(assessVersion("pi", code === 0 && bytes <= 64 * 1024 ? COMPAT.pi.parse(output) : undefined));
|
|
6441
|
+
};
|
|
6442
|
+
const timer = setTimeout(() => {
|
|
6443
|
+
finish(null);
|
|
6444
|
+
}, timeoutMs);
|
|
6445
|
+
proc.once("spawn", () => trackGroup(proc));
|
|
6446
|
+
proc.stdout.on("data", (chunk) => {
|
|
6447
|
+
if (done)
|
|
6448
|
+
return;
|
|
6449
|
+
bytes += chunk.length;
|
|
6450
|
+
if (bytes > 64 * 1024) {
|
|
6451
|
+
finish(null);
|
|
6452
|
+
return;
|
|
6453
|
+
}
|
|
6454
|
+
output += chunk.toString("utf8");
|
|
6455
|
+
});
|
|
6456
|
+
proc.once("error", () => {
|
|
6457
|
+
finish(null);
|
|
6458
|
+
});
|
|
6459
|
+
proc.once("close", (code) => {
|
|
6460
|
+
finish(code);
|
|
6461
|
+
});
|
|
6462
|
+
});
|
|
6463
|
+
}
|
|
6272
6464
|
function ownerStillAlive(pid, signature) {
|
|
6273
6465
|
const current = processSignature(pid);
|
|
6274
6466
|
if (current !== undefined)
|
|
@@ -6280,11 +6472,12 @@ function ownerStillAlive(pid, signature) {
|
|
|
6280
6472
|
return false;
|
|
6281
6473
|
}
|
|
6282
6474
|
}
|
|
6283
|
-
var PiToolSettlementFailure, PiPromptNotStarted, PiPromptUncertain, TERMINAL_OWNER_MAX_AGE_MS = 1000, PiPeer;
|
|
6475
|
+
var PiToolSettlementFailure, PiPromptNotStarted, PiPromptUncertain, PiVersionRefusal, TERMINAL_OWNER_MAX_AGE_MS = 1000, PiPeer;
|
|
6284
6476
|
var init_pi = __esm(() => {
|
|
6285
6477
|
init_launch();
|
|
6286
6478
|
init_envelope();
|
|
6287
6479
|
init_child_process();
|
|
6480
|
+
init_compat();
|
|
6288
6481
|
init_process_signature();
|
|
6289
6482
|
init_session();
|
|
6290
6483
|
init_tools();
|
|
@@ -6299,9 +6492,16 @@ var init_pi = __esm(() => {
|
|
|
6299
6492
|
};
|
|
6300
6493
|
PiPromptUncertain = class PiPromptUncertain extends Error {
|
|
6301
6494
|
};
|
|
6495
|
+
PiVersionRefusal = class PiVersionRefusal extends Error {
|
|
6496
|
+
constructor(verdict) {
|
|
6497
|
+
super(verdict.reason);
|
|
6498
|
+
}
|
|
6499
|
+
};
|
|
6302
6500
|
PiPeer = class PiPeer extends BasePeer {
|
|
6303
6501
|
opts;
|
|
6502
|
+
preflight;
|
|
6304
6503
|
hubNative = true;
|
|
6504
|
+
hubOwned = true;
|
|
6305
6505
|
proc;
|
|
6306
6506
|
server;
|
|
6307
6507
|
buffer = "";
|
|
@@ -6367,9 +6567,10 @@ var init_pi = __esm(() => {
|
|
|
6367
6567
|
tuiCommands = new Map;
|
|
6368
6568
|
tuiQueue = [];
|
|
6369
6569
|
tuiWaiters = [];
|
|
6370
|
-
constructor(id, opts) {
|
|
6570
|
+
constructor(id, opts, preflight) {
|
|
6371
6571
|
super(id, opts.watchdogMs);
|
|
6372
6572
|
this.opts = opts;
|
|
6573
|
+
this.preflight = preflight;
|
|
6373
6574
|
}
|
|
6374
6575
|
get tuiLaunch() {
|
|
6375
6576
|
return this._tuiLaunch;
|
|
@@ -6414,6 +6615,10 @@ var init_pi = __esm(() => {
|
|
|
6414
6615
|
const file = this.sessionFile && (exists || !empty) ? this.sessionFile : undefined;
|
|
6415
6616
|
return { launch: { kind: "pi", cwd: this.opts.cwd, mode: this.opts.mode, backend: this.opts.backend, ...this.opts.model ? { model: this.opts.model } : {}, ...file ? { sessionFile: file } : {} }, ...this.sessionId ? { sessionId: this.sessionId } : {}, ...file ? { sessionFile: file } : {} };
|
|
6416
6617
|
}
|
|
6618
|
+
get nativeSession() {
|
|
6619
|
+
const session = this.recoveryMetadata().sessionId;
|
|
6620
|
+
return typeof session === "string" ? session : undefined;
|
|
6621
|
+
}
|
|
6417
6622
|
async captureResume() {
|
|
6418
6623
|
if (this.state === "offline") {
|
|
6419
6624
|
if (this.canReuseVerifiedEmptyResume())
|
|
@@ -6615,6 +6820,8 @@ var init_pi = __esm(() => {
|
|
|
6615
6820
|
return Response.json({ decisions, ...reservation ? { reservation } : {} });
|
|
6616
6821
|
}
|
|
6617
6822
|
async startImpl() {
|
|
6823
|
+
if (this.preflight)
|
|
6824
|
+
await this.preflight();
|
|
6618
6825
|
this.terminalOwnerObservation = undefined;
|
|
6619
6826
|
this.stopping = false;
|
|
6620
6827
|
this.rosterDetached = false;
|
|
@@ -6776,6 +6983,9 @@ var init_pi = __esm(() => {
|
|
|
6776
6983
|
get mode() {
|
|
6777
6984
|
return this.opts.mode;
|
|
6778
6985
|
}
|
|
6986
|
+
get form() {
|
|
6987
|
+
return this.opts.mode;
|
|
6988
|
+
}
|
|
6779
6989
|
async detachForRoster() {
|
|
6780
6990
|
if (this.opts.mode !== "tui")
|
|
6781
6991
|
throw new Error("Pi roster detach requires a personal TUI");
|
|
@@ -10154,6 +10364,8 @@ var init_local_worker = __esm(() => {
|
|
|
10154
10364
|
LocalPeer = class LocalPeer extends BasePeer {
|
|
10155
10365
|
opts;
|
|
10156
10366
|
hubNative = true;
|
|
10367
|
+
deliveryOnly = true;
|
|
10368
|
+
hubOwned = true;
|
|
10157
10369
|
history = [];
|
|
10158
10370
|
sessionId = `agent-hub-local-${randomUUID6()}`;
|
|
10159
10371
|
routes;
|
|
@@ -10756,7 +10968,7 @@ import { randomUUID as randomUUID8 } from "crypto";
|
|
|
10756
10968
|
import { lstatSync as lstatSync11, mkdtempSync as mkdtempSync5, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
|
|
10757
10969
|
import { tmpdir as tmpdir5 } from "os";
|
|
10758
10970
|
import { join as join17 } from "path";
|
|
10759
|
-
function claudeImplementerArgs(turn, mcp, settings, prompt) {
|
|
10971
|
+
function claudeImplementerArgs(turn, mcp, settings, prompt, model) {
|
|
10760
10972
|
return [
|
|
10761
10973
|
"-p",
|
|
10762
10974
|
"--output-format",
|
|
@@ -10772,6 +10984,7 @@ function claudeImplementerArgs(turn, mcp, settings, prompt) {
|
|
|
10772
10984
|
"",
|
|
10773
10985
|
"--settings",
|
|
10774
10986
|
settings,
|
|
10987
|
+
...model ? ["--model", model] : [],
|
|
10775
10988
|
"--permission-mode",
|
|
10776
10989
|
"default",
|
|
10777
10990
|
"--permission-prompts",
|
|
@@ -10849,6 +11062,7 @@ var init_claude_implementer = __esm(() => {
|
|
|
10849
11062
|
ClaudeImplementerPeer = class ClaudeImplementerPeer extends BasePeer {
|
|
10850
11063
|
options;
|
|
10851
11064
|
hubNative = true;
|
|
11065
|
+
hubOwned = true;
|
|
10852
11066
|
generation = 0;
|
|
10853
11067
|
owned;
|
|
10854
11068
|
running;
|
|
@@ -11034,7 +11248,7 @@ var init_claude_implementer = __esm(() => {
|
|
|
11034
11248
|
const child = new OwnedPrintChild({
|
|
11035
11249
|
peer: this.id,
|
|
11036
11250
|
command: this.command,
|
|
11037
|
-
args: claudeImplementerArgs(turn, mcp, settings, this.options.systemPrompt),
|
|
11251
|
+
args: claudeImplementerArgs(turn, mcp, settings, this.options.systemPrompt, this.options.model),
|
|
11038
11252
|
cwd: this.options.cwd,
|
|
11039
11253
|
input: group.envs.map(frame).join(`
|
|
11040
11254
|
|
|
@@ -11166,7 +11380,14 @@ function createPi(ctx) {
|
|
|
11166
11380
|
if (ctx.permission === "read-only")
|
|
11167
11381
|
throw new Error("Pi has no verified read-only permission mapping");
|
|
11168
11382
|
init_pi();
|
|
11169
|
-
|
|
11383
|
+
const options = ctx.daemon.pi;
|
|
11384
|
+
return new PiPeer(ctx.instance, options, async () => {
|
|
11385
|
+
const verdict = await piVersionAsync(options.cmd ?? ["pi"], options.cwd);
|
|
11386
|
+
if (!verdict.ok)
|
|
11387
|
+
throw new PiVersionRefusal(verdict);
|
|
11388
|
+
if (verdict.note)
|
|
11389
|
+
options.log?.(verdict.note);
|
|
11390
|
+
});
|
|
11170
11391
|
}
|
|
11171
11392
|
function createLocal(ctx) {
|
|
11172
11393
|
if (!("local" in ctx.daemon))
|
|
@@ -11273,9 +11494,9 @@ var init_reviewer_variants = __esm(() => {
|
|
|
11273
11494
|
init_kimi_review();
|
|
11274
11495
|
init_native_factories();
|
|
11275
11496
|
init_native_display();
|
|
11276
|
-
codexVariant = reviewerVariant("codex", "Codex reviewer", { backend: codexReviewerBackend, defaultPool: true, declaration: "explicit" }, CODEX_DISPLAY);
|
|
11497
|
+
codexVariant = reviewerVariant("codex", "Codex reviewer", { backend: codexReviewerBackend, defaultPool: true, declaration: "explicit", scope: "code" }, CODEX_DISPLAY);
|
|
11277
11498
|
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);
|
|
11499
|
+
KIMI_REVIEWER_VARIANT = reviewerVariant("kimi", "Kimi reviewer", { backend: kimiReviewerBackend, defaultPool: true, declaration: "explicit", scope: "code" }, KIMI_DISPLAY);
|
|
11279
11500
|
AGY_REVIEWER_VARIANT = {
|
|
11280
11501
|
...reviewerVariant("agy", "agy reviewer", { defaultPool: false, declaration: "automatic", unavailable: "agy is an implementer peer (#485)" }, { palette: { fg: "#6558a6", bg: "#f1effa" } }),
|
|
11281
11502
|
optional: true
|
|
@@ -12760,8 +12981,8 @@ var init_builtin_metadata = __esm(() => {
|
|
|
12760
12981
|
trust: { onPrem: false, hubSandboxed: false },
|
|
12761
12982
|
start: { create: createClaudeImplementer, daemonPort: "claude-implementer", forms: headlessForms, defaultForm: "headless", conductorStartable: true, hubSwitches: [], ownedFlags: [] },
|
|
12762
12983
|
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;
|
|
12984
|
+
instances: { mode: "headless", roles: ["implementer", "verifier"], modelSelect: true },
|
|
12985
|
+
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
12986
|
session: { idField: "sessionId", currentId: sessionId("sessionId"), freshOnRestore: false, launchKind: "claude-implementer", crashPlan: () => ({ resume: {}, how: "Claude implementer resumes its recorded per-task sessions" }) },
|
|
12766
12987
|
turnEnd: "adapter",
|
|
12767
12988
|
budget: { account: "claude" },
|
|
@@ -13681,7 +13902,8 @@ function assertNativeCapabilityScope(definition) {
|
|
|
13681
13902
|
["hooks.factsHook", declared(definition.hooks, "factsHook")],
|
|
13682
13903
|
["cli.entry", declared(definition.cli, "entry")],
|
|
13683
13904
|
["cli.preview", declared(definition.cli, "preview")],
|
|
13684
|
-
["cli.usage", declared(definition.cli, "usage")]
|
|
13905
|
+
["cli.usage", declared(definition.cli, "usage")],
|
|
13906
|
+
["instances.modelSelect", declared(definition.instances, "modelSelect")]
|
|
13685
13907
|
];
|
|
13686
13908
|
const field = restricted.find(([, present]) => present)?.[0];
|
|
13687
13909
|
if (field)
|
|
@@ -13821,8 +14043,49 @@ var init_lifecycle = __esm(() => {
|
|
|
13821
14043
|
init_shard();
|
|
13822
14044
|
});
|
|
13823
14045
|
|
|
14046
|
+
// node_modules/@staix/agent-mem/src/memory/search-score.ts
|
|
14047
|
+
function sql(expr, rank) {
|
|
14048
|
+
const get = (part) => sql(part, rank);
|
|
14049
|
+
switch (expr.op) {
|
|
14050
|
+
case "rank":
|
|
14051
|
+
return rank;
|
|
14052
|
+
case "tier":
|
|
14053
|
+
return `CASE tier ${Object.entries(TIER_WEIGHTS).map(([name, value]) => `WHEN '${name}' THEN ${real(value)}`).join(" ")} ELSE ${real(DEFAULT_TIER_WEIGHT)} END`;
|
|
14054
|
+
case "age":
|
|
14055
|
+
return `((julianday(?) - julianday(at)) / ${real(MONTH_DAYS)})`;
|
|
14056
|
+
case "constant":
|
|
14057
|
+
return real(expr.value);
|
|
14058
|
+
case "negative":
|
|
14059
|
+
return `(-(${get(expr.value)}))`;
|
|
14060
|
+
case "reciprocal":
|
|
14061
|
+
return `(1.0 / ${get(expr.value)})`;
|
|
14062
|
+
case "add":
|
|
14063
|
+
return `(${get(expr.left)} + ${get(expr.right)})`;
|
|
14064
|
+
case "multiply":
|
|
14065
|
+
return expr.right.op === "reciprocal" ? `(${get(expr.left)} / ${get(expr.right.value)})` : `(${get(expr.left)} * ${get(expr.right)})`;
|
|
14066
|
+
case "max":
|
|
14067
|
+
return `max(${get(expr.left)}, ${get(expr.right)})`;
|
|
14068
|
+
}
|
|
14069
|
+
}
|
|
14070
|
+
function rankedScoreSql(rank = "bm25(docs)") {
|
|
14071
|
+
return sql(SCORE, rank);
|
|
14072
|
+
}
|
|
14073
|
+
var MONTH_DAYS = 30, TIER_WEIGHTS, DEFAULT_TIER_WEIGHT = 0.9, TIER_FACTOR, DENOMINATOR, RECENCY_FACTOR, SCORE, real = (value) => Number.isInteger(value) ? `${value}.0` : String(value);
|
|
14074
|
+
var init_search_score = __esm(() => {
|
|
14075
|
+
TIER_WEIGHTS = { long: 1, capture: 0.5 };
|
|
14076
|
+
TIER_FACTOR = { op: "tier" };
|
|
14077
|
+
DENOMINATOR = { op: "add", left: { op: "constant", value: 1 }, right: { op: "max", left: { op: "constant", value: 0 }, right: { op: "age" } } };
|
|
14078
|
+
RECENCY_FACTOR = { op: "reciprocal", value: DENOMINATOR };
|
|
14079
|
+
SCORE = { op: "multiply", left: { op: "multiply", left: { op: "negative", value: { op: "rank" } }, right: TIER_FACTOR }, right: RECENCY_FACTOR };
|
|
14080
|
+
});
|
|
14081
|
+
|
|
13824
14082
|
// node_modules/@staix/agent-mem/src/memory/search-fts.ts
|
|
13825
|
-
var
|
|
14083
|
+
var SCREENED_SCORE, SCREENED_SUBSTRING_SCORE;
|
|
14084
|
+
var init_search_fts = __esm(() => {
|
|
14085
|
+
init_search_score();
|
|
14086
|
+
SCREENED_SCORE = rankedScoreSql();
|
|
14087
|
+
SCREENED_SUBSTRING_SCORE = rankedScoreSql("-1.0");
|
|
14088
|
+
});
|
|
13826
14089
|
|
|
13827
14090
|
// node_modules/@staix/agent-mem/src/memory/capture-input.ts
|
|
13828
14091
|
var init_capture_input = __esm(() => {
|
|
@@ -14078,6 +14341,7 @@ var init_store = __esm(() => {
|
|
|
14078
14341
|
init_machine();
|
|
14079
14342
|
init_lifecycle();
|
|
14080
14343
|
init_machine();
|
|
14344
|
+
init_search_score();
|
|
14081
14345
|
init_search_fts();
|
|
14082
14346
|
init_shard();
|
|
14083
14347
|
init_capture();
|
|
@@ -21170,7 +21434,8 @@ var MACHINE_LOCAL = [
|
|
|
21170
21434
|
"local.read_allow",
|
|
21171
21435
|
"local.bash_network",
|
|
21172
21436
|
"local.network_allow",
|
|
21173
|
-
"terminal.open"
|
|
21437
|
+
"terminal.open",
|
|
21438
|
+
"tasks.auto_pull"
|
|
21174
21439
|
];
|
|
21175
21440
|
var ALL_MACHINE_LOCAL = [...MACHINE_LOCAL, "action_policy", "protect_instruction_files", "qwen_cmd", "jcode_cmd", "jcode_home", "jcode_provider_profile", "adapters", "adapter_modules", "text_projection", "instances"];
|
|
21176
21441
|
function configRefusal(cwd, name) {
|
|
@@ -21491,6 +21756,11 @@ init_platform();
|
|
|
21491
21756
|
init_store();
|
|
21492
21757
|
init_shard();
|
|
21493
21758
|
init_work_lists();
|
|
21759
|
+
init_capture();
|
|
21760
|
+
init_capture_input();
|
|
21761
|
+
init_config2();
|
|
21762
|
+
init_policy();
|
|
21763
|
+
init_private_spans();
|
|
21494
21764
|
|
|
21495
21765
|
// node_modules/@staix/agent-mem/src/index.ts
|
|
21496
21766
|
init_project2();
|
|
@@ -21502,7 +21772,7 @@ init_store();
|
|
|
21502
21772
|
init_sync();
|
|
21503
21773
|
init_project2();
|
|
21504
21774
|
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]`;
|
|
21775
|
+
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
21776
|
var MEMORY_USAGE = memoryUsage();
|
|
21507
21777
|
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
21778
|
var GOAL_USAGE = goalUsage();
|
|
@@ -21536,7 +21806,7 @@ var tool2 = (name, description, inputSchema, readOnlyHint = true) => ({
|
|
|
21536
21806
|
annotations: { readOnlyHint, destructiveHint: false, openWorldHint: false }
|
|
21537
21807
|
});
|
|
21538
21808
|
var MCP_TOOLS = [
|
|
21539
|
-
tool2("amem_search", "Search screened memory in this project. Capture results are recall, not curated evidence.", object6({ query: string(1000), limit, since: string(40), includeCapture: { type: "boolean" } }, ["query"])),
|
|
21809
|
+
tool2("amem_search", "Search screened memory in this project. Capture results are recall, not curated evidence.", object6({ query: string(1000), limit, since: string(40), includeCapture: { type: "boolean" }, explain: { type: "boolean" } }, ["query"])),
|
|
21540
21810
|
tool2("amem_evidence", "Return a bounded bundle of cited, live curated entries for a question. Untrusted memory data only: no answer or model call. Capture, repo-only notes, pending candidates and model answers are not evidence.", object6({ question: string(1000), limit: { type: "integer", minimum: 1, maximum: 20 }, maxBytes: { type: "integer", minimum: 256, maximum: 8192 } }, ["question"])),
|
|
21541
21811
|
tool2("amem_show", "Show a screened memory entry in this project by its exact id or unique prefix.", object6({ id: string(80) }, ["id"])),
|
|
21542
21812
|
tool2("amem_timeline", "Read screened memory near an entry or time in this project.", object6({ id: string(80), since: string(40), limit })),
|
|
@@ -21695,7 +21965,7 @@ init_sdlc_metrics();
|
|
|
21695
21965
|
// package.json
|
|
21696
21966
|
var package_default = {
|
|
21697
21967
|
name: "@staix/agent-hub",
|
|
21698
|
-
version: "0.
|
|
21968
|
+
version: "0.20.0",
|
|
21699
21969
|
description: "Native multi-agent hub: Claude Code, Codex, Kimi Code, Pi and local inference as peers in one project",
|
|
21700
21970
|
license: "MIT",
|
|
21701
21971
|
type: "module",
|
|
@@ -21712,7 +21982,7 @@ var package_default = {
|
|
|
21712
21982
|
bun: ">=1.4.2"
|
|
21713
21983
|
},
|
|
21714
21984
|
dependencies: {
|
|
21715
|
-
"@staix/agent-mem": "0.
|
|
21985
|
+
"@staix/agent-mem": "0.11.0"
|
|
21716
21986
|
},
|
|
21717
21987
|
devDependencies: {
|
|
21718
21988
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
@@ -21774,6 +22044,9 @@ init_sdlc();
|
|
|
21774
22044
|
init_envelope();
|
|
21775
22045
|
var inFlight = new Set;
|
|
21776
22046
|
|
|
22047
|
+
// src/hub/review-scope.ts
|
|
22048
|
+
var isReviewScope = (value) => value === "full" || value === "code";
|
|
22049
|
+
|
|
21777
22050
|
// src/hub/tasks.ts
|
|
21778
22051
|
init_review_findings();
|
|
21779
22052
|
init_roles();
|
|
@@ -21825,6 +22098,10 @@ init_registry();
|
|
|
21825
22098
|
// src/hub/task-sweep.ts
|
|
21826
22099
|
var GROOMING_CODES = new Set(["tests", "spec", "roster", "pull-only", "not-attached", "excluded", "owner-admission", "offline", "paused", "failing", "pii", "policy", "cancelled", "unavailable", "runner", "approvals", "quota", "dependency", "no-writes"]);
|
|
21827
22100
|
|
|
22101
|
+
// src/hub/ci-watch.ts
|
|
22102
|
+
init_sdlc();
|
|
22103
|
+
var CI_WATCH_DEADLINE_MS = 45 * 60000;
|
|
22104
|
+
|
|
21828
22105
|
// src/hub/tasks.ts
|
|
21829
22106
|
init_sdlc();
|
|
21830
22107
|
init_routing();
|
|
@@ -21834,6 +22111,7 @@ var QUIET_EVENTS = new Set(["answer", "reviewer changed", "idle sweep", "memory
|
|
|
21834
22111
|
QUIET_EVENTS.add("ready held");
|
|
21835
22112
|
QUIET_EVENTS.add("plan observed");
|
|
21836
22113
|
QUIET_EVENTS.add("planner changed");
|
|
22114
|
+
QUIET_EVENTS.add("shell branch bound");
|
|
21837
22115
|
var OWNERSHIP_EVENTS = new Set(["assigned", "escalated", "reassigned", "unassigned"]);
|
|
21838
22116
|
var GROOM_FYI_WINDOW_MS = 10 * 60000;
|
|
21839
22117
|
|
|
@@ -21915,7 +22193,8 @@ function validateInstances(raw, catalog) {
|
|
|
21915
22193
|
const path = `instances.${id}`, entry = fields2(descriptor.value, path);
|
|
21916
22194
|
const declaredKind = entry.kind?.value;
|
|
21917
22195
|
const jcodeFields = typeof declaredKind === "string" && resolve(declaredKind)?.instances?.nativeHome === true;
|
|
21918
|
-
|
|
22196
|
+
const modelSelect = typeof declaredKind === "string" && resolve(declaredKind)?.headlessVariant?.instances?.modelSelect === true;
|
|
22197
|
+
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
22198
|
return fail4(path, "unsupported field");
|
|
21920
22199
|
const kind = entry.kind?.value;
|
|
21921
22200
|
if (typeof kind !== "string")
|
|
@@ -21990,6 +22269,21 @@ function validateInstances(raw, catalog) {
|
|
|
21990
22269
|
return fail4("instances", `at most ${MAX_JCODE_INSTANCES} extra Jcode instances are supported per project`);
|
|
21991
22270
|
Object.assign(value, jcodeInstanceFields(entry, path));
|
|
21992
22271
|
}
|
|
22272
|
+
if (entry.model && modelSelect) {
|
|
22273
|
+
if (!headlessWriter)
|
|
22274
|
+
return fail4(`${path}.model`, "only a headless implementer takes a model");
|
|
22275
|
+
const word = entry.model.value;
|
|
22276
|
+
if (typeof word !== "string" || !NATIVE_WORD.test(word))
|
|
22277
|
+
return fail4(`${path}.model`, "must be one bounded printable word without spaces");
|
|
22278
|
+
value.model = word;
|
|
22279
|
+
}
|
|
22280
|
+
if (entry.review_scope) {
|
|
22281
|
+
if (!freshReviewer)
|
|
22282
|
+
return fail4(`${path}.review_scope`, "only a fresh reviewer instance has a review scope");
|
|
22283
|
+
if (!isReviewScope(entry.review_scope.value))
|
|
22284
|
+
return fail4(`${path}.review_scope`, 'must be "code" or "full"');
|
|
22285
|
+
value.review_scope = entry.review_scope.value;
|
|
22286
|
+
}
|
|
21993
22287
|
if (entry.readOnly)
|
|
21994
22288
|
value.readOnly = entry.readOnly.value;
|
|
21995
22289
|
if (entry.roles) {
|
|
@@ -30784,8 +31078,15 @@ async function callTool(req) {
|
|
|
30784
31078
|
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
31079
|
return deny("invalid native permission request");
|
|
30786
31080
|
try {
|
|
30787
|
-
const res = await hub.request({ t: "permission_request", tool: args?.tool_name, input: args?.input }, 3605000);
|
|
30788
|
-
|
|
31081
|
+
const res = await hub.request({ t: "permission_request", tool: args?.tool_name, input: args?.input, wrapperProtocol: 1 }, 3605000);
|
|
31082
|
+
if (res.ok !== true || res.behavior !== "allow")
|
|
31083
|
+
return deny(typeof res.message === "string" ? res.message : "permission denied");
|
|
31084
|
+
if (res.wrapperProtocol !== undefined && res.wrapperProtocol !== 1)
|
|
31085
|
+
return deny("unsupported host execution response");
|
|
31086
|
+
const updated = res.wrapperProtocol === 1 ? res.updatedInput : undefined;
|
|
31087
|
+
if (res.wrapperProtocol === 1 && (args.tool_name !== "Bash" || !updated || typeof updated !== "object" || Array.isArray(updated) || typeof updated.command !== "string"))
|
|
31088
|
+
return deny("invalid host execution response");
|
|
31089
|
+
return text5(JSON.stringify({ behavior: "allow", updatedInput: updated ?? args?.input }));
|
|
30789
31090
|
} catch {
|
|
30790
31091
|
return deny("hub permission unavailable");
|
|
30791
31092
|
}
|