@staix/agent-hub 0.12.37 → 0.12.39
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 +12 -6
- package/docs/agent-notes/adapters.md +19 -8
- package/docs/agent-notes/benchmarks.md +1 -1
- package/docs/agent-notes/bus.md +3 -2
- package/docs/agent-notes/daemon.md +6 -3
- package/docs/agent-notes/tasks.md +4 -3
- package/docs/agent-notes/tests.md +3 -3
- package/docs/operations.md +45 -17
- package/docs/quickstart.md +19 -5
- package/docs/security.md +7 -3
- package/docs/smoke.md +22 -2
- package/docs/specs/2026-09-19-agent-hub-design.md +40 -9
- package/docs/verification/2026-10-19-agy-p1-t0.md +55 -0
- package/docs/verification/2026-10-19-bus-defer-reset.md +9 -0
- package/docs/verification/2026-10-19-kimi-agy-reviewer-isolation.md +220 -0
- package/docs/verification/2026-10-20-0.12.38.md +56 -0
- package/docs/verification/2026-10-20-0.12.39.md +55 -0
- package/docs/verification/2026-10-20-jcode-tui-0.98.1.md +39 -0
- package/docs/verified.json +22 -14
- package/package.json +1 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +1759 -437
- package/src/adapters/acp.ts +20 -2
- package/src/adapters/agy/definition.ts +61 -0
- package/src/adapters/agy/entry.ts +76 -0
- package/src/adapters/agy/hooks.ts +81 -0
- package/src/adapters/claude-channel.ts +1 -0
- package/src/adapters/codex-review-isolation.ts +30 -16
- package/src/adapters/definitions/builtin-metadata.ts +8 -8
- package/src/adapters/definitions/native-cli-entry-claude.ts +3 -0
- package/src/adapters/definitions/native-cli-entry-codex.ts +5 -1
- package/src/adapters/definitions/native-cli-entry-jcode.ts +82 -11
- package/src/adapters/definitions/native-cli-entry-kimi.ts +12 -4
- package/src/adapters/definitions/native-cli-entry-local.ts +3 -0
- package/src/adapters/definitions/native-cli-entry-pi.ts +7 -1
- package/src/adapters/definitions/native-cli-entry-qwen.ts +3 -0
- package/src/adapters/definitions/native-cli-entry-types.ts +1 -0
- package/src/adapters/definitions/native-cli-entry.ts +2 -0
- package/src/adapters/definitions/native-cli-preview-common.ts +1 -0
- package/src/adapters/definitions/native-cli-preview-jcode.ts +7 -7
- package/src/adapters/definitions/native-cli-retained.ts +15 -0
- package/src/adapters/definitions/native-daemon-policy.ts +1 -1
- package/src/adapters/definitions/qwen.ts +1 -1
- package/src/adapters/definitions/reviewer-variants.ts +4 -3
- package/src/adapters/jcode.ts +58 -1
- package/src/adapters/kimi-review.ts +161 -0
- package/src/adapters/registry-types.ts +29 -1
- package/src/adapters/registry.ts +16 -7
- package/src/adapters/reviewer-mcp.ts +85 -0
- package/src/adapters/reviewer-print.ts +83 -20
- package/src/cli/agy-mcp.ts +370 -0
- package/src/cli/console-state.ts +6 -0
- package/src/cli/console.ts +2 -0
- package/src/cli/facts-hook.ts +15 -2
- package/src/cli/init.ts +7 -0
- package/src/cli/launch.ts +3 -1
- package/src/cli/main.ts +5 -4
- package/src/cli/output.ts +5 -0
- package/src/cli/preview.ts +2 -2
- package/src/hub/bus.ts +17 -6
- package/src/hub/control-client.ts +7 -1
- package/src/hub/daemon.ts +182 -24
- package/src/hub/native-hook-launch.ts +37 -0
- package/src/hub/peer-catalog.ts +2 -3
- package/src/hub/peers.ts +2 -2
- package/src/hub/reviewer-pool.ts +12 -6
- package/src/hub/routing.ts +3 -0
- package/src/hub/start-mode.ts +1 -1
- package/src/hub/tasks.ts +7 -0
- package/src/ui/index.html +6 -1
- package/templates/AGENTS.block.md +1 -1
|
@@ -456,12 +456,12 @@ class BasePeer {
|
|
|
456
456
|
this._state = next;
|
|
457
457
|
this.onState?.(next);
|
|
458
458
|
}
|
|
459
|
-
touch() {
|
|
459
|
+
touch(ms = this.watchdogMs) {
|
|
460
460
|
this.clearWatchdog();
|
|
461
461
|
this.timer = setTimeout(() => {
|
|
462
462
|
if (this._state === "busy")
|
|
463
463
|
this.onWatchdog();
|
|
464
|
-
},
|
|
464
|
+
}, ms);
|
|
465
465
|
this.timer.unref?.();
|
|
466
466
|
}
|
|
467
467
|
onWatchdog() {
|
|
@@ -505,6 +505,10 @@ var init_reviewer_print = __esm(() => {
|
|
|
505
505
|
hubNative = true;
|
|
506
506
|
generation = 0;
|
|
507
507
|
child;
|
|
508
|
+
childStop;
|
|
509
|
+
childCleanup;
|
|
510
|
+
childCurrent;
|
|
511
|
+
reviewMcpClose;
|
|
508
512
|
activeDeliveryId;
|
|
509
513
|
batch;
|
|
510
514
|
preparation;
|
|
@@ -514,8 +518,23 @@ var init_reviewer_print = __esm(() => {
|
|
|
514
518
|
}
|
|
515
519
|
reviewing;
|
|
516
520
|
reviewSeq = 0;
|
|
521
|
+
runningBinding;
|
|
522
|
+
reviewBinding(task) {
|
|
523
|
+
return this.reviewing === task && this.runningBinding?.task === task ? this.runningBinding.binding : undefined;
|
|
524
|
+
}
|
|
517
525
|
reviewToken(task) {
|
|
518
|
-
|
|
526
|
+
if (this.reviewing !== task)
|
|
527
|
+
return;
|
|
528
|
+
try {
|
|
529
|
+
if (this.childCurrent?.() === false) {
|
|
530
|
+
this.abortReview("review policy changed");
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
} catch {
|
|
534
|
+
this.abortReview("review policy changed");
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
return this.reviewSeq;
|
|
519
538
|
}
|
|
520
539
|
constructor(id, opts) {
|
|
521
540
|
super(id, opts.watchdogMs);
|
|
@@ -550,6 +569,7 @@ var init_reviewer_print = __esm(() => {
|
|
|
550
569
|
}
|
|
551
570
|
async stop(reason) {
|
|
552
571
|
this.generation++;
|
|
572
|
+
this.reviewMcpClose?.();
|
|
553
573
|
this.receipt = undefined;
|
|
554
574
|
this.batch = undefined;
|
|
555
575
|
this.preparation?.abort.abort();
|
|
@@ -566,6 +586,7 @@ var init_reviewer_print = __esm(() => {
|
|
|
566
586
|
abortReview(reason) {
|
|
567
587
|
const generation = ++this.generation, batch = this.batch, preparation = this.preparation;
|
|
568
588
|
this.reviewing = undefined;
|
|
589
|
+
this.reviewMcpClose?.();
|
|
569
590
|
this.receipt = undefined;
|
|
570
591
|
this.clearWatchdog();
|
|
571
592
|
preparation?.abort.abort();
|
|
@@ -694,23 +715,28 @@ var init_reviewer_print = __esm(() => {
|
|
|
694
715
|
}
|
|
695
716
|
}
|
|
696
717
|
}
|
|
697
|
-
prepare(sessionId) {
|
|
718
|
+
prepare(sessionId, input, task, mcpConfig) {
|
|
698
719
|
const abort = new AbortController;
|
|
699
720
|
const done = (async () => {
|
|
721
|
+
if (this.opts.backend.taskMcp && !this.opts.taskMcp)
|
|
722
|
+
throw new Error("task-bound reviewer MCP is unavailable");
|
|
700
723
|
const launch = await this.opts.backend.prepare({
|
|
701
724
|
peer: this.id,
|
|
702
725
|
cwd: this.opts.cwd,
|
|
703
726
|
sessionId,
|
|
704
|
-
mcpConfig: this.opts.mcpConfig,
|
|
727
|
+
mcpConfig: this.opts.backend.taskMcp ? mcpConfig ?? '{"mcpServers":{}}' : this.opts.mcpConfig,
|
|
705
728
|
systemPrompt: this.opts.systemPrompt,
|
|
706
729
|
privatePaths: this.opts.privatePaths ?? [],
|
|
707
730
|
command: this.opts.command,
|
|
708
|
-
signal: abort.signal
|
|
731
|
+
signal: abort.signal,
|
|
732
|
+
...input === undefined ? {} : { input },
|
|
733
|
+
...task === undefined ? {} : { task }
|
|
709
734
|
});
|
|
710
735
|
if ("unavailable" in launch)
|
|
711
736
|
throw new Error(launch.unavailable);
|
|
712
737
|
const proof = launch.isolation;
|
|
713
|
-
|
|
738
|
+
const policy = this.opts.backend.nativePolicy ? proof?.enforcement === "native-policy" && proof.freshSession === true && proof.hubToolsOnly === true : proof?.strictMcp === true && proof.ephemeral === true;
|
|
739
|
+
if (abort.signal.aborted || !proof || proof.backend !== this.opts.backend.id || proof.readOnly !== true || !policy || !proof.binary || !proof.version || !proof.profile || !proof.evidence.length || !launch.command.length) {
|
|
714
740
|
await launch.cleanup?.();
|
|
715
741
|
throw new Error("reviewer isolation is unavailable or preparation was cancelled");
|
|
716
742
|
}
|
|
@@ -725,19 +751,58 @@ var init_reviewer_print = __esm(() => {
|
|
|
725
751
|
return done;
|
|
726
752
|
}
|
|
727
753
|
async review(env, task, generation) {
|
|
728
|
-
|
|
754
|
+
if (this.opts.admit?.(env) === false)
|
|
755
|
+
throw new Error("review admission changed");
|
|
756
|
+
let facts;
|
|
729
757
|
try {
|
|
730
|
-
|
|
731
|
-
|
|
758
|
+
facts = this.opts.facts?.(env);
|
|
759
|
+
} catch (error) {
|
|
760
|
+
this.opts.log?.(`[${this.id}] review facts for task #${task} failed: ${error.message}`);
|
|
761
|
+
}
|
|
762
|
+
const input = `${frame(env)}${facts ? `
|
|
763
|
+
|
|
764
|
+
${facts}` : ""}`;
|
|
765
|
+
let launch;
|
|
766
|
+
const revision = Number(env.refs?.reviewRevision);
|
|
767
|
+
const binding = Object.freeze({ head: env.refs?.commit, flowRevision: Number.isSafeInteger(revision) && revision > 0 ? revision : undefined });
|
|
768
|
+
const mcp = this.opts.backend.taskMcp ? this.opts.taskMcp?.(task, () => generation === this.generation && this.reviewToken(task) !== undefined && this.opts.bindingCurrent?.(task, binding) !== false) : undefined;
|
|
769
|
+
let mcpClosed = false;
|
|
770
|
+
const closeMcp = () => {
|
|
771
|
+
if (mcpClosed)
|
|
772
|
+
return;
|
|
773
|
+
mcpClosed = true;
|
|
774
|
+
mcp?.close();
|
|
775
|
+
if (this.reviewMcpClose === closeMcp)
|
|
776
|
+
this.reviewMcpClose = undefined;
|
|
777
|
+
};
|
|
778
|
+
this.reviewMcpClose = closeMcp;
|
|
779
|
+
let ownedChild, releasing;
|
|
780
|
+
let policyTimer;
|
|
781
|
+
const release = () => releasing ??= Promise.resolve().then(() => launch?.cleanup?.()).then(() => {});
|
|
782
|
+
try {
|
|
783
|
+
launch = await this.prepare(randomUUID(), input, task, mcp?.config);
|
|
784
|
+
if (generation !== this.generation || this.opts.admit?.(env) === false || launch.current?.() === false)
|
|
785
|
+
throw new Error("reviewer isolation or admission changed before child spawn");
|
|
732
786
|
this.receipt = launch.isolation;
|
|
733
787
|
const [cmd, ...prefix] = launch.command;
|
|
734
788
|
let quota = false;
|
|
735
789
|
return await new Promise((resolve, reject) => {
|
|
736
|
-
const child = spawn(cmd, [...prefix, ...launch.args], {
|
|
790
|
+
const child = spawn(cmd, [...prefix, ...launch.args], {
|
|
791
|
+
cwd: launch.cwd ?? this.opts.cwd,
|
|
792
|
+
detached: true,
|
|
793
|
+
env: peerChildEnv(this.id, launch.envMode === "replace" ? launch.env : { ...process.env, ...launch.env }),
|
|
794
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
795
|
+
});
|
|
737
796
|
this.child = child;
|
|
797
|
+
ownedChild = child;
|
|
798
|
+
this.childStop = launch.stop;
|
|
799
|
+
this.childCleanup = release;
|
|
800
|
+
this.childCurrent = launch.watchCurrent ? launch.current : undefined;
|
|
738
801
|
child.once("error", (error) => {
|
|
739
|
-
if (this.child === child)
|
|
802
|
+
if (this.child === child) {
|
|
740
803
|
this.child = undefined;
|
|
804
|
+
this.childStop = undefined;
|
|
805
|
+
}
|
|
741
806
|
reject(error);
|
|
742
807
|
});
|
|
743
808
|
child.stdin.on("error", () => {});
|
|
@@ -747,17 +812,14 @@ var init_reviewer_print = __esm(() => {
|
|
|
747
812
|
child.stdin.destroy();
|
|
748
813
|
return;
|
|
749
814
|
}
|
|
815
|
+
this.runningBinding = { task, binding };
|
|
750
816
|
this.reviewing = task;
|
|
751
817
|
this.reviewSeq++;
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
}
|
|
758
|
-
child.stdin.end(`${frame(env)}${facts ? `
|
|
759
|
-
|
|
760
|
-
${facts}` : ""}`);
|
|
818
|
+
if (this.childCurrent)
|
|
819
|
+
policyTimer = setInterval(() => {
|
|
820
|
+
this.reviewToken(task);
|
|
821
|
+
}, 100);
|
|
822
|
+
child.stdin.end(input);
|
|
761
823
|
});
|
|
762
824
|
let buffer = "", outputBytes = 0;
|
|
763
825
|
child.stdout.on("data", (chunk) => {
|
|
@@ -793,18 +855,29 @@ ${facts}` : ""}`);
|
|
|
793
855
|
this.touch();
|
|
794
856
|
});
|
|
795
857
|
child.once("exit", (code) => {
|
|
796
|
-
if (this.child === child)
|
|
858
|
+
if (this.child === child) {
|
|
797
859
|
this.reviewing = undefined;
|
|
798
|
-
|
|
799
|
-
|
|
860
|
+
this.runningBinding = undefined;
|
|
861
|
+
}
|
|
862
|
+
(launch.stop ? launch.stop(child) : stopOwnedProcess(child, { group: true })).then(() => {
|
|
863
|
+
if (this.child === child) {
|
|
800
864
|
this.child = undefined;
|
|
865
|
+
this.childStop = undefined;
|
|
866
|
+
}
|
|
801
867
|
resolve({ code, quota });
|
|
802
868
|
}, (error) => reject(new GroupCleanupError(error.message)));
|
|
803
869
|
});
|
|
804
870
|
});
|
|
805
871
|
} finally {
|
|
872
|
+
closeMcp();
|
|
873
|
+
if (policyTimer)
|
|
874
|
+
clearInterval(policyTimer);
|
|
806
875
|
try {
|
|
807
|
-
|
|
876
|
+
if (!ownedChild || this.child !== ownedChild) {
|
|
877
|
+
await release();
|
|
878
|
+
if (this.childCleanup === release)
|
|
879
|
+
this.childCleanup = undefined;
|
|
880
|
+
}
|
|
808
881
|
} catch {
|
|
809
882
|
this.opts.log?.(`[${this.id}] reviewer isolation cleanup incomplete`);
|
|
810
883
|
}
|
|
@@ -818,12 +891,18 @@ ${facts}` : ""}`);
|
|
|
818
891
|
}
|
|
819
892
|
async kill() {
|
|
820
893
|
this.reviewing = undefined;
|
|
894
|
+
this.runningBinding = undefined;
|
|
821
895
|
const child = this.child;
|
|
822
896
|
if (!child)
|
|
823
897
|
return;
|
|
824
|
-
await stopOwnedProcess(child, { group: true });
|
|
825
|
-
if (this.child === child)
|
|
898
|
+
await (this.childStop ? this.childStop(child) : stopOwnedProcess(child, { group: true }));
|
|
899
|
+
if (this.child === child) {
|
|
900
|
+
await this.childCleanup?.();
|
|
826
901
|
this.child = undefined;
|
|
902
|
+
this.childStop = undefined;
|
|
903
|
+
this.childCleanup = undefined;
|
|
904
|
+
this.childCurrent = undefined;
|
|
905
|
+
}
|
|
827
906
|
}
|
|
828
907
|
};
|
|
829
908
|
});
|
|
@@ -1047,19 +1126,27 @@ function configFiles(cwd, env) {
|
|
|
1047
1126
|
}
|
|
1048
1127
|
function hostCredentialRoots(env, cwd) {
|
|
1049
1128
|
const home = resolve3(cwd, env.HOME || homedir());
|
|
1050
|
-
const homes = [...new Set([home,
|
|
1051
|
-
const roots = homes.flatMap((base) => HOST_CREDENTIAL_PATHS.map((entry) => join3(base, entry)));
|
|
1052
|
-
const configured = HOST_CREDENTIAL_ENV.flatMap((key) => env[key] ? [env[key]] : []);
|
|
1129
|
+
const homes = [...new Set([home, canonicalLabelled(home, "HOME")])];
|
|
1130
|
+
const roots = homes.flatMap((base) => HOST_CREDENTIAL_PATHS.map((entry) => ({ path: join3(base, entry), label: `~/${entry}` })));
|
|
1131
|
+
const configured = HOST_CREDENTIAL_ENV.flatMap((key) => env[key] ? [{ path: env[key], label: `$${key}` }] : []);
|
|
1053
1132
|
if (env.KUBECONFIG)
|
|
1054
|
-
configured.push(...env.KUBECONFIG.split(delimiter).filter(Boolean));
|
|
1133
|
+
configured.push(...env.KUBECONFIG.split(delimiter).filter(Boolean).map((path) => ({ path, label: "$KUBECONFIG" })));
|
|
1055
1134
|
if (configured.length > 64)
|
|
1056
1135
|
throw new Error("credential root bound exceeded");
|
|
1057
|
-
for (const entry of configured) {
|
|
1136
|
+
for (const { path: entry, label } of configured) {
|
|
1058
1137
|
if (Buffer.byteLength(entry) > 4096 || /\p{Cc}/u.test(entry))
|
|
1059
|
-
throw new
|
|
1060
|
-
roots.push(entry.startsWith("~/") ? resolve3(home, entry.slice(2)) : resolve3(cwd, entry));
|
|
1138
|
+
throw new CredentialRootError(label, "unsupported");
|
|
1139
|
+
roots.push({ path: entry.startsWith("~/") ? resolve3(home, entry.slice(2)) : resolve3(cwd, entry), label });
|
|
1140
|
+
}
|
|
1141
|
+
const seen = new Set;
|
|
1142
|
+
return roots.filter((root) => !seen.has(root.path) && seen.add(root.path));
|
|
1143
|
+
}
|
|
1144
|
+
function canonicalLabelled(lexical, label) {
|
|
1145
|
+
try {
|
|
1146
|
+
return credentialCanonicalPath(lexical);
|
|
1147
|
+
} catch (error) {
|
|
1148
|
+
throw new CredentialRootError(label, error.code === "ENOENT" ? "dangling" : "unreadable");
|
|
1061
1149
|
}
|
|
1062
|
-
return [...new Set(roots)];
|
|
1063
1150
|
}
|
|
1064
1151
|
function credentialCanonicalPath(lexical) {
|
|
1065
1152
|
let ancestor = lexical;
|
|
@@ -1076,15 +1163,13 @@ function credentialCanonicalPath(lexical) {
|
|
|
1076
1163
|
return join3(realPath(ancestor), relative(ancestor, lexical));
|
|
1077
1164
|
}
|
|
1078
1165
|
function denyCredentialRoots(filesystem, roots, cwd) {
|
|
1079
|
-
for (const lexical of roots) {
|
|
1166
|
+
for (const { path: lexical, label } of roots) {
|
|
1080
1167
|
if (/[?*\[\]{}]/.test(lexical))
|
|
1081
|
-
throw new
|
|
1082
|
-
const targets = new Set([lexical,
|
|
1168
|
+
throw new CredentialRootError(label, "unsupported");
|
|
1169
|
+
const targets = new Set([lexical, canonicalLabelled(lexical, label)]);
|
|
1083
1170
|
for (const target of targets) {
|
|
1084
|
-
if (/[?*\[\]{}]/.test(target))
|
|
1085
|
-
throw new
|
|
1086
|
-
if (inside(cwd, target))
|
|
1087
|
-
throw new Error("credential root overlaps checkout");
|
|
1171
|
+
if (/[?*\[\]{}]/.test(target) || inside(cwd, target))
|
|
1172
|
+
throw new CredentialRootError(label, "unsupported");
|
|
1088
1173
|
filesystem[target] = "deny";
|
|
1089
1174
|
filesystem[`${target.replace(/\/$/, "")}/**`] = "deny";
|
|
1090
1175
|
}
|
|
@@ -1337,12 +1422,16 @@ AHUB_CODEX_HOST_ALIAS_DENIED` || existsSync2(writeFile))
|
|
|
1337
1422
|
evidence: ["model-free-mcp-inventory-disabled-ambient", "hub-tools-exact-allowlist", "plugins-and-hooks-disabled", "apps-disabled-effective-feature-readback", "web-search-disabled", "project-untrusted", "native-public-read-private-deny-write-deny", "native-glob-and-symlink-private-deny", "portable-core-name-and-state-deny", "standard-host-credential-roots-denied", "binary-config-stable", "binary-hash-once-metadata-fence", "session-ephemeral"]
|
|
1338
1423
|
}
|
|
1339
1424
|
};
|
|
1340
|
-
} catch {
|
|
1425
|
+
} catch (error) {
|
|
1341
1426
|
if (root) {
|
|
1342
1427
|
try {
|
|
1343
1428
|
rmSync(root, { recursive: true, force: true });
|
|
1344
1429
|
} catch {}
|
|
1345
1430
|
}
|
|
1431
|
+
if (error instanceof CredentialRootError) {
|
|
1432
|
+
const why = { dangling: "a dangling link", unreadable: "an unreadable path", unsupported: "unsupported syntax or overlapping the checkout" }[error.kind];
|
|
1433
|
+
return { unavailable: `Codex reviewer isolation receipt unavailable (credential root ${error.label} is ${why}; remove or repair it)` };
|
|
1434
|
+
}
|
|
1346
1435
|
return { unavailable: "Codex reviewer isolation receipt unavailable (version, MCP inventory, sandbox, configuration or process proof)" };
|
|
1347
1436
|
}
|
|
1348
1437
|
}
|
|
@@ -1353,7 +1442,7 @@ var VERSION = "0.146.0", TOOLS, CAP, MAX_SERVERS = 64, digest = (value) => creat
|
|
|
1353
1442
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
1354
1443
|
throw new Error("invalid metadata");
|
|
1355
1444
|
return value;
|
|
1356
|
-
}, HOST_CREDENTIAL_PATHS, HOST_CREDENTIAL_ENV, codexReviewerBackend;
|
|
1445
|
+
}, HOST_CREDENTIAL_PATHS, HOST_CREDENTIAL_ENV, CredentialRootError, codexReviewerBackend;
|
|
1357
1446
|
var init_codex_review_isolation = __esm(() => {
|
|
1358
1447
|
init_platform();
|
|
1359
1448
|
init_child_process();
|
|
@@ -1362,6 +1451,15 @@ var init_codex_review_isolation = __esm(() => {
|
|
|
1362
1451
|
CAP = 128 * 1024;
|
|
1363
1452
|
HOST_CREDENTIAL_PATHS = [".config", ".ssh", ".aws", ".azure", ".kube", ".docker", ".gnupg", ".codex", ".claude", ".kimi", ".kimi-code", ".gemini", ".git-credentials", ".vault-token", "Library/Keychains", ".local/share/keyrings", ".password-store"];
|
|
1364
1453
|
HOST_CREDENTIAL_ENV = ["XDG_CONFIG_HOME", "CODEX_HOME", "CLAUDE_CONFIG_DIR", "KIMI_CODE_HOME", "KIMI_HOME", "GEMINI_CLI_HOME", "GNUPGHOME", "AWS_SHARED_CREDENTIALS_FILE", "AWS_CONFIG_FILE", "AZURE_CONFIG_DIR", "DOCKER_CONFIG", "GOOGLE_APPLICATION_CREDENTIALS"];
|
|
1454
|
+
CredentialRootError = class CredentialRootError extends Error {
|
|
1455
|
+
label;
|
|
1456
|
+
kind;
|
|
1457
|
+
constructor(label, kind) {
|
|
1458
|
+
super("credential root refused");
|
|
1459
|
+
this.label = label;
|
|
1460
|
+
this.kind = kind;
|
|
1461
|
+
}
|
|
1462
|
+
};
|
|
1365
1463
|
codexReviewerBackend = {
|
|
1366
1464
|
id: "codex",
|
|
1367
1465
|
prepare,
|
|
@@ -1372,10 +1470,271 @@ var init_codex_review_isolation = __esm(() => {
|
|
|
1372
1470
|
};
|
|
1373
1471
|
});
|
|
1374
1472
|
|
|
1473
|
+
// src/adapters/kimi-review.ts
|
|
1474
|
+
import { spawn as spawn3 } from "child_process";
|
|
1475
|
+
import { createHash as createHash3 } from "crypto";
|
|
1476
|
+
import { existsSync as existsSync3, lstatSync as lstatSync3, mkdirSync as mkdirSync2, mkdtempSync as mkdtempSync2, readFileSync as readFileSync3, rmSync as rmSync2, symlinkSync as symlinkSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1477
|
+
import { homedir as homedir2, tmpdir as tmpdir2 } from "os";
|
|
1478
|
+
import { basename as basename3, dirname as dirname4, join as join4, resolve as resolve4 } from "path";
|
|
1479
|
+
function assertCleanCwdAncestors(...candidates) {
|
|
1480
|
+
const seen = new Set;
|
|
1481
|
+
for (const candidate of candidates) {
|
|
1482
|
+
let ancestor = resolve4(candidate);
|
|
1483
|
+
for (;; ) {
|
|
1484
|
+
if (!seen.has(ancestor)) {
|
|
1485
|
+
seen.add(ancestor);
|
|
1486
|
+
for (const marker of [".git", ".kimi-code", "AGENTS.md"]) {
|
|
1487
|
+
if (lstatSync3(join4(ancestor, marker), { throwIfNoEntry: false }) !== undefined) {
|
|
1488
|
+
throw new Error("private cwd ancestor contains ambient configuration");
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
const parent = dirname4(ancestor);
|
|
1493
|
+
if (parent === ancestor)
|
|
1494
|
+
break;
|
|
1495
|
+
ancestor = parent;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
function managedConfig(raw) {
|
|
1500
|
+
const config = Bun.TOML.parse(raw);
|
|
1501
|
+
if (!object2(config.providers) || !object2(config.models) || typeof config.default_model !== "string")
|
|
1502
|
+
throw new Error("managed Kimi configuration required");
|
|
1503
|
+
const provider = config.providers[MANAGED], model = config.models[config.default_model];
|
|
1504
|
+
if (!object2(provider) || provider.type !== "kimi" || provider.api_key && provider.api_key !== "" || !object2(provider.oauth) || provider.oauth.storage !== "file" || !object2(model) || model.provider !== MANAGED)
|
|
1505
|
+
throw new Error("managed file-backed Kimi OAuth required");
|
|
1506
|
+
const oauth = provider.oauth;
|
|
1507
|
+
const key = oauth.key;
|
|
1508
|
+
const lock = !key || key === "kimi-code" || key === "oauth/kimi-code" ? "kimi-code" : typeof key === "string" && key.startsWith("oauth/") ? key.slice(6) : key;
|
|
1509
|
+
if (typeof lock !== "string" || !lock || basename3(lock) !== lock || lock.startsWith("."))
|
|
1510
|
+
throw new Error("unsupported OAuth slot");
|
|
1511
|
+
if (typeof provider.base_url !== "string")
|
|
1512
|
+
throw new Error("managed Kimi endpoint required");
|
|
1513
|
+
const endpoint = new URL(provider.base_url);
|
|
1514
|
+
if (endpoint.protocol !== "https:" || !["api.kimi.ai", "api.kimi.com"].includes(endpoint.hostname) || endpoint.username || endpoint.password)
|
|
1515
|
+
throw new Error("unsupported managed Kimi endpoint");
|
|
1516
|
+
if (oauth.oauth_host !== undefined) {
|
|
1517
|
+
const auth = new URL(String(oauth.oauth_host));
|
|
1518
|
+
if (auth.protocol !== "https:" || !["auth.kimi.ai", "auth.kimi.com"].includes(auth.hostname) || auth.username || auth.password)
|
|
1519
|
+
throw new Error("unsupported OAuth endpoint");
|
|
1520
|
+
}
|
|
1521
|
+
const lines = [
|
|
1522
|
+
"telemetry = false",
|
|
1523
|
+
"hooks = []",
|
|
1524
|
+
`default_model = ${scalar(config.default_model)}`,
|
|
1525
|
+
...config.thinking === undefined ? [] : [`thinking = ${scalar(config.thinking)}`],
|
|
1526
|
+
`[providers.${scalar(MANAGED)}]`,
|
|
1527
|
+
'type = "kimi"',
|
|
1528
|
+
`base_url = ${scalar(provider.base_url)}`,
|
|
1529
|
+
`[providers.${scalar(MANAGED)}.oauth]`
|
|
1530
|
+
];
|
|
1531
|
+
for (const name of ["storage", "key", "oauth_host"])
|
|
1532
|
+
if (oauth[name] !== undefined)
|
|
1533
|
+
lines.push(`${name} = ${scalar(oauth[name])}`);
|
|
1534
|
+
lines.push(`[models.${scalar(config.default_model)}]`);
|
|
1535
|
+
const modelFields = ["provider", "model", "protocol", "max_context_size", "max_input_size", "capabilities", "display_name", "support_efforts", "default_effort", "beta_api", "adaptive_thinking", "overrides"];
|
|
1536
|
+
if (Object.keys(model).some((name) => !modelFields.includes(name)))
|
|
1537
|
+
throw new Error("unsupported managed model setting");
|
|
1538
|
+
if (model.overrides !== undefined && (!object2(model.overrides) || Object.keys(model.overrides).some((name) => !modelFields.includes(name) || ["provider", "overrides"].includes(name))))
|
|
1539
|
+
throw new Error("unsupported managed model override");
|
|
1540
|
+
for (const name of modelFields)
|
|
1541
|
+
if (model[name] !== undefined)
|
|
1542
|
+
lines.push(`${name} = ${scalar(model[name])}`);
|
|
1543
|
+
return { config: lines.join(`
|
|
1544
|
+
`) + `
|
|
1545
|
+
`, lock };
|
|
1546
|
+
}
|
|
1547
|
+
function mcpConfig(context) {
|
|
1548
|
+
const config = JSON.parse(context.mcpConfig);
|
|
1549
|
+
if (!object2(config) || !object2(config.mcpServers))
|
|
1550
|
+
throw new Error("review MCP unavailable");
|
|
1551
|
+
if (context.task === undefined && Object.keys(config.mcpServers).length === 0)
|
|
1552
|
+
return context.mcpConfig;
|
|
1553
|
+
if (Object.keys(config.mcpServers).join() !== "agent-hub")
|
|
1554
|
+
throw new Error("ambient MCP refused");
|
|
1555
|
+
const server = config.mcpServers["agent-hub"];
|
|
1556
|
+
if (!object2(server) || Object.keys(server).some((k) => !["transport", "url", "headers"].includes(k)) || server.transport !== "http" || typeof server.url !== "string" || !object2(server.headers) || Object.keys(server.headers).join() !== "Authorization" || typeof server.headers.Authorization !== "string" || !server.headers.Authorization.startsWith("Bearer "))
|
|
1557
|
+
throw new Error("task-bound HTTP MCP required");
|
|
1558
|
+
const url = new URL(server.url);
|
|
1559
|
+
if (url.protocol !== "http:" || url.hostname !== "127.0.0.1" || !url.port || url.pathname !== "/mcp" || url.username || url.password || url.search || url.hash)
|
|
1560
|
+
throw new Error("local review MCP required");
|
|
1561
|
+
return context.mcpConfig;
|
|
1562
|
+
}
|
|
1563
|
+
async function version(command, cwd, env, signal) {
|
|
1564
|
+
return await new Promise((accept, reject) => {
|
|
1565
|
+
const child = spawn3(command[0], [...command.slice(1), "--version"], { cwd, env, detached: true, stdio: ["ignore", "pipe", "ignore"] });
|
|
1566
|
+
let text = "", done = false;
|
|
1567
|
+
const finish = async (code) => {
|
|
1568
|
+
if (done)
|
|
1569
|
+
return;
|
|
1570
|
+
done = true;
|
|
1571
|
+
clearTimeout(timer);
|
|
1572
|
+
signal.removeEventListener("abort", abort);
|
|
1573
|
+
try {
|
|
1574
|
+
await stopOwnedProcess(child, { group: true });
|
|
1575
|
+
} catch {
|
|
1576
|
+
reject(new Error("version probe cleanup failed"));
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
if (code !== 0 || signal.aborted)
|
|
1580
|
+
reject(new Error("version probe failed"));
|
|
1581
|
+
else
|
|
1582
|
+
accept(text.trim());
|
|
1583
|
+
};
|
|
1584
|
+
const abort = () => {
|
|
1585
|
+
finish(null);
|
|
1586
|
+
};
|
|
1587
|
+
const timer = setTimeout(abort, 5000);
|
|
1588
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
1589
|
+
child.once("spawn", () => trackGroup(child));
|
|
1590
|
+
child.stdout.on("data", (bytes) => {
|
|
1591
|
+
text += bytes.toString();
|
|
1592
|
+
if (text.length > 4096)
|
|
1593
|
+
abort();
|
|
1594
|
+
});
|
|
1595
|
+
child.once("error", () => {
|
|
1596
|
+
finish(null);
|
|
1597
|
+
});
|
|
1598
|
+
child.once("close", (code) => {
|
|
1599
|
+
finish(code);
|
|
1600
|
+
});
|
|
1601
|
+
if (signal.aborted)
|
|
1602
|
+
abort();
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
async function stopKimiReview(child, locks, graceMs = 1e4) {
|
|
1606
|
+
const locked = () => locks.some((file) => existsSync3(file));
|
|
1607
|
+
if (child.exitCode === null && child.signalCode === null && locked()) {
|
|
1608
|
+
child.kill("SIGTERM");
|
|
1609
|
+
const end = Date.now() + graceMs;
|
|
1610
|
+
while (child.exitCode === null && child.signalCode === null && locked() && Date.now() < end)
|
|
1611
|
+
await Bun.sleep(50);
|
|
1612
|
+
if (child.exitCode === null && child.signalCode === null && locked())
|
|
1613
|
+
throw new Error("Kimi OAuth refresh still locked; graceful stop incomplete");
|
|
1614
|
+
}
|
|
1615
|
+
await stopOwnedProcess(child, { group: true });
|
|
1616
|
+
}
|
|
1617
|
+
var VERSION2 = "2.2.0", MANAGED = "managed:kimi-code", object2 = (v) => !!v && typeof v === "object" && !Array.isArray(v), hash = (text) => createHash3("sha256").update(text).digest("hex"), read = (file, limit = 262144) => {
|
|
1618
|
+
const stat = lstatSync3(file);
|
|
1619
|
+
if (!stat.isFile() || stat.size > limit)
|
|
1620
|
+
throw new Error("unsupported reviewer file");
|
|
1621
|
+
return readFileSync3(file, "utf8");
|
|
1622
|
+
}, scalar = (value, depth = 0) => {
|
|
1623
|
+
if (depth > 4)
|
|
1624
|
+
throw new Error("managed model setting too deeply nested");
|
|
1625
|
+
if (typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value))
|
|
1626
|
+
return JSON.stringify(value);
|
|
1627
|
+
if (Array.isArray(value) && value.every((v) => typeof v === "string"))
|
|
1628
|
+
return JSON.stringify(value);
|
|
1629
|
+
if (object2(value) && Object.keys(value).length <= 32)
|
|
1630
|
+
return `{ ${Object.entries(value).map(([key, item]) => `${JSON.stringify(key)} = ${scalar(item, depth + 1)}`).join(", ")} }`;
|
|
1631
|
+
throw new Error("unsupported managed model setting");
|
|
1632
|
+
}, kimiReviewerBackend;
|
|
1633
|
+
var init_kimi_review = __esm(() => {
|
|
1634
|
+
init_child_process();
|
|
1635
|
+
init_project();
|
|
1636
|
+
kimiReviewerBackend = {
|
|
1637
|
+
id: "kimi",
|
|
1638
|
+
taskMcp: true,
|
|
1639
|
+
nativePolicy: true,
|
|
1640
|
+
async prepare(context) {
|
|
1641
|
+
let own;
|
|
1642
|
+
try {
|
|
1643
|
+
if (context.signal.aborted)
|
|
1644
|
+
throw new Error("cancelled");
|
|
1645
|
+
const sourcePath = process.env.KIMI_CODE_HOME ?? join4(process.env.HOME ?? homedir2(), ".kimi-code"), source = realPath(sourcePath);
|
|
1646
|
+
const sourceConfig = join4(source, "config.toml"), original = read(sourceConfig), selected = managedConfig(original);
|
|
1647
|
+
const sources = ["credentials", "oauth", "device_id"].map((name) => realPath(join4(source, name)));
|
|
1648
|
+
if (!sources.every((file, index) => index < 2 ? lstatSync3(file).isDirectory() : lstatSync3(file).isFile()))
|
|
1649
|
+
throw new Error("native OAuth directories and device identity required");
|
|
1650
|
+
const mcp = mcpConfig(context);
|
|
1651
|
+
if (Buffer.byteLength(context.input ?? "") > 1048576)
|
|
1652
|
+
throw new Error("review facts exceed limit");
|
|
1653
|
+
const lexicalRoot = mkdtempSync2(join4(process.env.TMPDIR ?? tmpdir2(), "ahub-kimi-review-"));
|
|
1654
|
+
own = realPath(lexicalRoot);
|
|
1655
|
+
assertCleanCwdAncestors(join4(lexicalRoot, "workspace"), join4(own, "workspace"));
|
|
1656
|
+
const root = own, home = join4(root, "home"), tool = join4(root, "tool"), cwd = join4(root, "workspace"), skills = join4(root, "empty-skills"), temporary = join4(root, "tmp");
|
|
1657
|
+
for (const dir of [home, tool, cwd, skills, temporary])
|
|
1658
|
+
mkdirSync2(dir, { mode: 448 });
|
|
1659
|
+
["credentials", "oauth", "device_id"].forEach((name, index) => symlinkSync2(sources[index], join4(tool, name), index < 2 ? "dir" : "file"));
|
|
1660
|
+
const agentFile = join4(cwd, "reviewer.md"), configFile = join4(tool, "config.toml"), mcpFile = join4(tool, "mcp.json");
|
|
1661
|
+
writeFileSync2(configFile, selected.config, { mode: 384 });
|
|
1662
|
+
writeFileSync2(mcpFile, mcp, { mode: 384 });
|
|
1663
|
+
writeFileSync2(agentFile, `---
|
|
1664
|
+
name: hub-reviewer
|
|
1665
|
+
description: Fresh agent-hub reviewer
|
|
1666
|
+
tools:
|
|
1667
|
+
- mcp__agent-hub__hub_review
|
|
1668
|
+
- mcp__agent-hub__hub_task_show
|
|
1669
|
+
subagents: []
|
|
1670
|
+
---
|
|
1671
|
+
|
|
1672
|
+
${context.systemPrompt}
|
|
1673
|
+
|
|
1674
|
+
Use only the supplied public facts and hub_task_show. Never claim to have read evidence that was not supplied.
|
|
1675
|
+
|
|
1676
|
+
${context.input ?? "No review is active; startup self-check only."}
|
|
1677
|
+
`, { mode: 384 });
|
|
1678
|
+
const env = { PATH: process.env.PATH ?? "/usr/bin:/bin", HOME: home, KIMI_CODE_HOME: tool, TMPDIR: `${temporary}/`, XDG_CONFIG_HOME: join4(home, ".config"), LANG: process.env.LANG ?? "en_US.UTF-8", TERM: "dumb", ...process.env.USER ? { USER: process.env.USER } : {} };
|
|
1679
|
+
const native = context.command ?? [Bun.which("kimi", { PATH: process.env.PATH }) ?? "kimi"];
|
|
1680
|
+
const executable = realPath(Bun.which(native[0], { PATH: process.env.PATH }) ?? resolve4(native[0]));
|
|
1681
|
+
const command = [executable, ...native.slice(1)];
|
|
1682
|
+
const executableStat = lstatSync3(executable);
|
|
1683
|
+
if (await version(command, cwd, env, context.signal) !== VERSION2)
|
|
1684
|
+
throw new Error("unsupported Kimi version; receipt covers 2.2.0");
|
|
1685
|
+
const fingerprints = [configFile, mcpFile, agentFile].map((file) => hash(read(file, 2097152)));
|
|
1686
|
+
let cleaned = false;
|
|
1687
|
+
const cleanup = () => {
|
|
1688
|
+
if (cleaned)
|
|
1689
|
+
return;
|
|
1690
|
+
rmSync2(root, { recursive: true, force: true });
|
|
1691
|
+
cleaned = true;
|
|
1692
|
+
};
|
|
1693
|
+
return {
|
|
1694
|
+
command,
|
|
1695
|
+
args: ["-p", "Review the supplied task facts and record the evidence-based verdict with hub_review.", "--output-format", "stream-json", "--agent-file", agentFile, "--skills-dir", skills],
|
|
1696
|
+
cwd,
|
|
1697
|
+
env,
|
|
1698
|
+
envMode: "replace",
|
|
1699
|
+
cleanup,
|
|
1700
|
+
watchCurrent: true,
|
|
1701
|
+
stop: (child) => stopKimiReview(child, [join4(sources[1], "kimi-code.lock"), join4(sources[1], `${selected.lock}.lock`)]),
|
|
1702
|
+
current: () => {
|
|
1703
|
+
try {
|
|
1704
|
+
assertCleanCwdAncestors(join4(lexicalRoot, "workspace"), cwd);
|
|
1705
|
+
const now = lstatSync3(executable);
|
|
1706
|
+
return !cleaned && !context.signal.aborted && realPath(sourcePath) === source && now.dev === executableStat.dev && now.ino === executableStat.ino && now.size === executableStat.size && now.mtimeMs === executableStat.mtimeMs && now.ctimeMs === executableStat.ctimeMs && hash(read(sourceConfig)) === hash(original) && [configFile, mcpFile, agentFile].every((file, index) => hash(read(file, 2097152)) === fingerprints[index]) && ["credentials", "oauth", "device_id"].every((name, index) => realPath(join4(source, name)) === sources[index] && realPath(join4(tool, name)) === sources[index]);
|
|
1707
|
+
} catch {
|
|
1708
|
+
return false;
|
|
1709
|
+
}
|
|
1710
|
+
},
|
|
1711
|
+
isolation: {
|
|
1712
|
+
backend: "kimi",
|
|
1713
|
+
version: VERSION2,
|
|
1714
|
+
binary: executable,
|
|
1715
|
+
profile: "private-config-exact-two-tools-shared-native-oauth",
|
|
1716
|
+
readOnly: true,
|
|
1717
|
+
strictMcp: true,
|
|
1718
|
+
ephemeral: true,
|
|
1719
|
+
enforcement: "native-policy",
|
|
1720
|
+
freshSession: true,
|
|
1721
|
+
hubToolsOnly: true,
|
|
1722
|
+
evidence: ["#533 person ruling2026-10-19: native execution-time exact tool allowlist, no kernel sandbox", "private config/cwd/empty skills; shared OAuth directory symlinks and native lock/atomic rename", "task-bound in-process hub MCP; no full control token", "residual: interruption mid-refresh may require native re-login"]
|
|
1723
|
+
}
|
|
1724
|
+
};
|
|
1725
|
+
} catch (error) {
|
|
1726
|
+
if (own)
|
|
1727
|
+
rmSync2(own, { recursive: true, force: true });
|
|
1728
|
+
return { unavailable: `Kimi reviewer unavailable: ${error.message}` };
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
});
|
|
1733
|
+
|
|
1375
1734
|
// src/adapters/definitions/native-context.ts
|
|
1376
1735
|
function claudeContext(value, sessionId, measuredAt) {
|
|
1377
|
-
const c =
|
|
1378
|
-
const current =
|
|
1736
|
+
const c = object3(value);
|
|
1737
|
+
const current = object3(c.current_usage);
|
|
1379
1738
|
const parts = [current.input_tokens, current.cache_creation_input_tokens, current.cache_read_input_tokens];
|
|
1380
1739
|
const window = positive(c.context_window_size) ? c.context_window_size : null;
|
|
1381
1740
|
const sum = c.current_usage != null && parts.every(nonnegative) ? parts.reduce((sum, n) => sum + n, 0) : null;
|
|
@@ -1384,7 +1743,7 @@ function claudeContext(value, sessionId, measuredAt) {
|
|
|
1384
1743
|
return { source: "claude_statusline", sessionId, measuredAt, tokens, window, used };
|
|
1385
1744
|
}
|
|
1386
1745
|
function codexContext(value, sessionId, measuredAt) {
|
|
1387
|
-
const c =
|
|
1746
|
+
const c = object3(value), last = object3(c.last);
|
|
1388
1747
|
const window = positive(c.modelContextWindow) ? c.modelContextWindow : null;
|
|
1389
1748
|
const tokens = nonnegative(last.totalTokens) ? last.totalTokens : null;
|
|
1390
1749
|
return { source: "codex_token_usage", sessionId, measuredAt, tokens, window, used: tokens !== null && window !== null ? Math.min(1, tokens / window) : null };
|
|
@@ -1395,16 +1754,16 @@ function acpContext(usage, sessionId, measuredAt) {
|
|
|
1395
1754
|
return { source: "acp_usage_update", sessionId, measuredAt, tokens, window, used: tokens !== null && window !== null ? Math.min(1, tokens / window) : null };
|
|
1396
1755
|
}
|
|
1397
1756
|
function acpContextValue(value, sessionId, measuredAt) {
|
|
1398
|
-
const raw =
|
|
1757
|
+
const raw = object3(value);
|
|
1399
1758
|
return acpContext({ used: raw.used, size: raw.size }, sessionId, measuredAt);
|
|
1400
1759
|
}
|
|
1401
|
-
var nonnegative = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0, positive = (n) => nonnegative(n) && n > 0, record = (v) => v !== null && typeof v === "object" && !Array.isArray(v),
|
|
1760
|
+
var nonnegative = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0, positive = (n) => nonnegative(n) && n > 0, record = (v) => v !== null && typeof v === "object" && !Array.isArray(v), object3 = (v) => record(v) ? v : {};
|
|
1402
1761
|
|
|
1403
1762
|
// src/hub/context-window.ts
|
|
1404
1763
|
var init_context_window = () => {};
|
|
1405
1764
|
|
|
1406
1765
|
// src/adapters/codex-appserver.ts
|
|
1407
|
-
import { spawn as
|
|
1766
|
+
import { spawn as spawn4 } from "child_process";
|
|
1408
1767
|
function parse(raw) {
|
|
1409
1768
|
try {
|
|
1410
1769
|
return JSON.parse(raw);
|
|
@@ -1752,7 +2111,7 @@ ${renderDigest(envs, false)}`;
|
|
|
1752
2111
|
}
|
|
1753
2112
|
let gone = "";
|
|
1754
2113
|
const env = peerChildEnv(this.id, { ...process.env, ...this.opts.env ?? {} });
|
|
1755
|
-
this.proc =
|
|
2114
|
+
this.proc = spawn4(this.opts.bin ?? "codex", ["app-server", "--listen", `ws://127.0.0.1:${port}`, ...this.opts.extraArgs ?? []], {
|
|
1756
2115
|
cwd: this.opts.cwd,
|
|
1757
2116
|
env,
|
|
1758
2117
|
stdio: ["ignore", "ignore", "pipe"],
|
|
@@ -2152,7 +2511,7 @@ var init_qwen_native = __esm(() => {
|
|
|
2152
2511
|
});
|
|
2153
2512
|
|
|
2154
2513
|
// src/adapters/acp.ts
|
|
2155
|
-
import { spawn as
|
|
2514
|
+
import { spawn as spawn5 } from "child_process";
|
|
2156
2515
|
import { createInterface } from "readline";
|
|
2157
2516
|
function normalizeACPUsage(value, source = "usage_update") {
|
|
2158
2517
|
const unknown = { source, availability: "unsupported", shape: "none" };
|
|
@@ -2192,13 +2551,14 @@ function jsonObject(text) {
|
|
|
2192
2551
|
return;
|
|
2193
2552
|
}
|
|
2194
2553
|
}
|
|
2195
|
-
var HANDSHAKE_MS = 30000, TOOL_INPUT_CAP = 64, RpcTimeoutError, AcpPeer;
|
|
2554
|
+
var HANDSHAKE_MS = 30000, TOOL_INPUT_CAP = 64, TOOL_CEILING_MS, RpcTimeoutError, AcpPeer;
|
|
2196
2555
|
var init_acp = __esm(() => {
|
|
2197
2556
|
init_envelope();
|
|
2198
2557
|
init_kimi_native();
|
|
2199
2558
|
init_qwen_native();
|
|
2200
2559
|
init_qwen_native();
|
|
2201
2560
|
init_child_process();
|
|
2561
|
+
TOOL_CEILING_MS = 30 * 60000;
|
|
2202
2562
|
RpcTimeoutError = class RpcTimeoutError extends Error {
|
|
2203
2563
|
};
|
|
2204
2564
|
AcpPeer = class AcpPeer extends BasePeer {
|
|
@@ -2216,6 +2576,8 @@ var init_acp = __esm(() => {
|
|
|
2216
2576
|
toolInputs = new Map;
|
|
2217
2577
|
toolText = new Map;
|
|
2218
2578
|
toolTitles = new Map;
|
|
2579
|
+
openTools = new Map;
|
|
2580
|
+
lastSignAt = 0;
|
|
2219
2581
|
primed = false;
|
|
2220
2582
|
turn = 0;
|
|
2221
2583
|
stopping = false;
|
|
@@ -2314,7 +2676,7 @@ var init_acp = __esm(() => {
|
|
|
2314
2676
|
async start() {
|
|
2315
2677
|
this.stopping = false;
|
|
2316
2678
|
const [bin, ...args] = this.opts.cmd;
|
|
2317
|
-
const proc =
|
|
2679
|
+
const proc = spawn5(bin, args, { cwd: this.opts.cwd, env: peerChildEnv(this.id, { ...process.env, ...this.opts.env ?? {} }), stdio: ["pipe", "pipe", "pipe"], detached: true });
|
|
2318
2680
|
this.proc = proc;
|
|
2319
2681
|
trackGroup(proc);
|
|
2320
2682
|
proc.on("error", (e) => this.down(`spawn failed: ${e.message}`));
|
|
@@ -2434,6 +2796,7 @@ var init_acp = __esm(() => {
|
|
|
2434
2796
|
this.deliveryAccepted = false;
|
|
2435
2797
|
this.promptActivity = false;
|
|
2436
2798
|
this.chunks = [];
|
|
2799
|
+
this.openTools.clear();
|
|
2437
2800
|
this.activityOpen = true;
|
|
2438
2801
|
this.activityPrivate = envs.some((env) => env.private === true);
|
|
2439
2802
|
try {
|
|
@@ -2535,6 +2898,21 @@ ${renderDigest(envs, false)}` }] });
|
|
|
2535
2898
|
this.clearWatchdog();
|
|
2536
2899
|
return;
|
|
2537
2900
|
}
|
|
2901
|
+
if (this.openTools.size) {
|
|
2902
|
+
const ceiling = Math.max(this.watchdogMs, this.opts.toolCeilingMs ?? TOOL_CEILING_MS);
|
|
2903
|
+
const remaining = ceiling - (Date.now() - this.lastSignAt);
|
|
2904
|
+
if (remaining > 0) {
|
|
2905
|
+
this.touch(Math.min(this.watchdogMs, remaining));
|
|
2906
|
+
return;
|
|
2907
|
+
}
|
|
2908
|
+
try {
|
|
2909
|
+
this.activityPrivate ||= this.opts.isPrivateTurn?.() === true;
|
|
2910
|
+
} catch {
|
|
2911
|
+
this.activityPrivate = true;
|
|
2912
|
+
}
|
|
2913
|
+
const tools = this.activityPrivate ? "a tool call" : [...new Set(this.openTools.values())].map((title) => JSON.stringify(activityText(title).replace(/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu, " ").slice(0, 80))).join(", ");
|
|
2914
|
+
this.opts.log?.(`[${this.id}] open tool call (${tools}) silent past the ${Math.round(ceiling / 60000)} min tool ceiling; cancelling the turn`);
|
|
2915
|
+
}
|
|
2538
2916
|
this.endActivity("watchdog cancelled", true);
|
|
2539
2917
|
const cancelledTurn = this.turn;
|
|
2540
2918
|
this.cancelPermissions();
|
|
@@ -2608,8 +2986,10 @@ ${renderDigest(envs, false)}` }] });
|
|
|
2608
2986
|
} catch {
|
|
2609
2987
|
return;
|
|
2610
2988
|
}
|
|
2611
|
-
if (this.state === "busy" && !this.cancelWait)
|
|
2989
|
+
if (this.state === "busy" && !this.cancelWait) {
|
|
2990
|
+
this.lastSignAt = Date.now();
|
|
2612
2991
|
this.touch();
|
|
2992
|
+
}
|
|
2613
2993
|
if (msg.method === "session/update") {
|
|
2614
2994
|
if (msg.params?.sessionId && msg.params.sessionId !== this.sessionId)
|
|
2615
2995
|
return;
|
|
@@ -2640,7 +3020,11 @@ ${renderDigest(envs, false)}` }] });
|
|
|
2640
3020
|
if (text)
|
|
2641
3021
|
this.toolText.set(u.toolCallId, text);
|
|
2642
3022
|
if (u.status === "completed" || u.status === "failed")
|
|
2643
|
-
this.toolInputs.delete(u.toolCallId), this.toolText.delete(u.toolCallId), this.toolTitles.delete(u.toolCallId);
|
|
3023
|
+
this.toolInputs.delete(u.toolCallId), this.toolText.delete(u.toolCallId), this.toolTitles.delete(u.toolCallId), this.openTools.delete(u.toolCallId);
|
|
3024
|
+
else if (u.sessionUpdate === "tool_call" || this.openTools.has(u.toolCallId))
|
|
3025
|
+
this.openTools.set(u.toolCallId, this.toolTitles.get(u.toolCallId) ?? "tool");
|
|
3026
|
+
while (this.openTools.size > TOOL_INPUT_CAP)
|
|
3027
|
+
this.openTools.delete(this.openTools.keys().next().value);
|
|
2644
3028
|
for (const map of [this.toolInputs, this.toolText, this.toolTitles])
|
|
2645
3029
|
while (map.size > TOOL_INPUT_CAP)
|
|
2646
3030
|
map.delete(map.keys().next().value);
|
|
@@ -2744,10 +3128,10 @@ ${renderDigest(envs, false)}` }] });
|
|
|
2744
3128
|
});
|
|
2745
3129
|
|
|
2746
3130
|
// src/adapters/jcode.ts
|
|
2747
|
-
import { spawn as
|
|
2748
|
-
import { existsSync as
|
|
2749
|
-
import { tmpdir as
|
|
2750
|
-
import { isAbsolute as isAbsolute3, join as
|
|
3131
|
+
import { spawn as spawn6, spawnSync as spawnSync2 } from "child_process";
|
|
3132
|
+
import { closeSync, constants, existsSync as existsSync4, fstatSync, lstatSync as lstatSync4, mkdtempSync as mkdtempSync3, openSync, readSync, rmSync as rmSync3 } from "fs";
|
|
3133
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
3134
|
+
import { isAbsolute as isAbsolute3, join as join5 } from "path";
|
|
2751
3135
|
function jcodeCommand(command, kind, socket, cwd, model, profile) {
|
|
2752
3136
|
if (!command.length || command.some((word) => typeof word !== "string" || !word))
|
|
2753
3137
|
throw new Error("jcode_cmd must name an executable argv");
|
|
@@ -2765,6 +3149,72 @@ function jcodeCommand(command, kind, socket, cwd, model, profile) {
|
|
|
2765
3149
|
...kind === "serve" ? ["--temporary-server", "--owner-pid", String(process.pid), "--server-name", "agent-hub-jcode"] : ["--mcp-tools", "eager"]
|
|
2766
3150
|
];
|
|
2767
3151
|
}
|
|
3152
|
+
function jcodeTuiCommand(command, socket, sessionId, model, profile) {
|
|
3153
|
+
if (!command.length || command.some((word) => typeof word !== "string" || !word))
|
|
3154
|
+
throw new Error("jcode_cmd must name an executable argv");
|
|
3155
|
+
return [
|
|
3156
|
+
...command,
|
|
3157
|
+
"--socket",
|
|
3158
|
+
socket,
|
|
3159
|
+
"--resume",
|
|
3160
|
+
sessionId,
|
|
3161
|
+
"--no-update",
|
|
3162
|
+
"--no-selfdev",
|
|
3163
|
+
...model ? ["--model", model] : [],
|
|
3164
|
+
...profile ? ["--provider-profile", profile] : []
|
|
3165
|
+
];
|
|
3166
|
+
}
|
|
3167
|
+
function savedSession(home, id) {
|
|
3168
|
+
if (!/^[A-Za-z0-9_-]{1,128}$/.test(id))
|
|
3169
|
+
throw new Error("invalid native session identity");
|
|
3170
|
+
const directory = join5(home, "sessions"), info = lstatSync4(directory, { throwIfNoEntry: false });
|
|
3171
|
+
if (!info)
|
|
3172
|
+
return false;
|
|
3173
|
+
if (!info.isDirectory() || info.isSymbolicLink())
|
|
3174
|
+
throw new Error("unsafe native session directory");
|
|
3175
|
+
const file = join5(directory, `${id}.json`), entry = lstatSync4(file, { throwIfNoEntry: false });
|
|
3176
|
+
if (!entry)
|
|
3177
|
+
return false;
|
|
3178
|
+
if (!entry.isFile() || entry.isSymbolicLink())
|
|
3179
|
+
throw new Error("unsafe native session proof");
|
|
3180
|
+
let fd;
|
|
3181
|
+
try {
|
|
3182
|
+
fd = openSync(file, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
3183
|
+
} catch (error) {
|
|
3184
|
+
if (error.code === "ENOENT")
|
|
3185
|
+
return false;
|
|
3186
|
+
throw error;
|
|
3187
|
+
}
|
|
3188
|
+
try {
|
|
3189
|
+
const before = fstatSync(fd);
|
|
3190
|
+
if (!before.isFile() || before.nlink !== 1 || before.size > 32 * 1024 * 1024)
|
|
3191
|
+
throw new Error("unsafe native session proof");
|
|
3192
|
+
if (!before.size)
|
|
3193
|
+
return false;
|
|
3194
|
+
const bytes = Buffer.alloc(before.size);
|
|
3195
|
+
let offset = 0;
|
|
3196
|
+
while (offset < bytes.length) {
|
|
3197
|
+
const n = readSync(fd, bytes, offset, bytes.length - offset, offset);
|
|
3198
|
+
if (!n)
|
|
3199
|
+
return false;
|
|
3200
|
+
offset += n;
|
|
3201
|
+
}
|
|
3202
|
+
const after = fstatSync(fd);
|
|
3203
|
+
if (before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs)
|
|
3204
|
+
return false;
|
|
3205
|
+
let data;
|
|
3206
|
+
try {
|
|
3207
|
+
data = JSON.parse(bytes.toString("utf8"));
|
|
3208
|
+
} catch {
|
|
3209
|
+
return false;
|
|
3210
|
+
}
|
|
3211
|
+
if (!data || typeof data !== "object" || data.id !== id || !Array.isArray(data.messages))
|
|
3212
|
+
throw new Error("native saved session identity is unproven");
|
|
3213
|
+
return true;
|
|
3214
|
+
} finally {
|
|
3215
|
+
closeSync(fd);
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
2768
3218
|
function jcodeVersionSupported(version) {
|
|
2769
3219
|
const v = typeof version === "string" ? release(version) : undefined;
|
|
2770
3220
|
return !!v && compare(v, release(JCODE_MIN_VERSION)) >= 0 && compare(v, release(JCODE_BREAKING_VERSION)) < 0;
|
|
@@ -2776,6 +3226,8 @@ var init_jcode = __esm(() => {
|
|
|
2776
3226
|
JCODE_OWNED_ENV = { JCODE_DISABLE_CLAUDE_MCP: "1", JCODE_NO_TELEMETRY: "1" };
|
|
2777
3227
|
JcodePeer = class JcodePeer extends AcpPeer {
|
|
2778
3228
|
jcode;
|
|
3229
|
+
launchId = crypto.randomUUID();
|
|
3230
|
+
socketIdentity;
|
|
2779
3231
|
root;
|
|
2780
3232
|
socket;
|
|
2781
3233
|
server;
|
|
@@ -2786,7 +3238,7 @@ var init_jcode = __esm(() => {
|
|
|
2786
3238
|
if (!isAbsolute3(jcode.home))
|
|
2787
3239
|
throw new Error("jcode_home must be an absolute native data path");
|
|
2788
3240
|
jcodeCommand(jcode.command, "acp", "preview", jcode.cwd);
|
|
2789
|
-
const root =
|
|
3241
|
+
const root = mkdtempSync3(join5(tmpdir3(), "ahub-jcode-")), socket = join5(root, "j.sock");
|
|
2790
3242
|
const cmd = jcodeCommand(jcode.command, "acp", socket, jcode.cwd, jcode.launchModel, jcode.providerProfile);
|
|
2791
3243
|
super("jcode", { ...jcode, cmd, env: { ...jcode.env, JCODE_HOME: jcode.home, JCODE_SOCKET: socket, ...JCODE_OWNED_ENV }, mcpServers: [], requireLoadSessionEcho: true, expectedAgent: { name: "jcode", version: jcodeVersionSupported } });
|
|
2792
3244
|
this.jcode = jcode;
|
|
@@ -2794,15 +3246,15 @@ var init_jcode = __esm(() => {
|
|
|
2794
3246
|
this.socket = socket;
|
|
2795
3247
|
}
|
|
2796
3248
|
async start() {
|
|
2797
|
-
if (!
|
|
3249
|
+
if (!existsSync4(join5(this.jcode.home, "mcp.json")))
|
|
2798
3250
|
throw new Error("initialize Jcode's global MCP store with its native client before enabling this peer; the hub never initializes or copies it");
|
|
2799
|
-
if (
|
|
3251
|
+
if (existsSync4(this.socket)) {
|
|
2800
3252
|
this.preserveRoot = true;
|
|
2801
3253
|
throw new Error("Jcode private socket is occupied; no daemon was adopted or removed");
|
|
2802
3254
|
}
|
|
2803
3255
|
this.ending = false;
|
|
2804
3256
|
const [bin, ...args] = jcodeCommand(this.jcode.command, "serve", this.socket, this.jcode.cwd, this.jcode.launchModel, this.jcode.providerProfile);
|
|
2805
|
-
const server =
|
|
3257
|
+
const server = spawn6(bin, args, { cwd: this.jcode.cwd, detached: true, env: { ...peerChildEnv("jcode"), JCODE_HOME: this.jcode.home, JCODE_SOCKET: this.socket, JCODE_READY_FD: "3", ...JCODE_OWNED_ENV }, stdio: ["ignore", "ignore", "ignore", "pipe"] });
|
|
2806
3258
|
this.server = server;
|
|
2807
3259
|
trackGroup(server);
|
|
2808
3260
|
try {
|
|
@@ -2818,7 +3270,7 @@ var init_jcode = __esm(() => {
|
|
|
2818
3270
|
ready.once("data", () => {
|
|
2819
3271
|
clearTimeout(timer);
|
|
2820
3272
|
server.off("exit", fail);
|
|
2821
|
-
if (
|
|
3273
|
+
if (lstatSync4(this.socket, { throwIfNoEntry: false })?.isSocket())
|
|
2822
3274
|
resolve();
|
|
2823
3275
|
else
|
|
2824
3276
|
reject(new Error("Jcode readiness has no private socket"));
|
|
@@ -2828,6 +3280,8 @@ var init_jcode = __esm(() => {
|
|
|
2828
3280
|
if (!this.ending && this.server === server)
|
|
2829
3281
|
this.stop("Jcode owned daemon disconnected").catch(() => {});
|
|
2830
3282
|
});
|
|
3283
|
+
const identity = lstatSync4(this.socket);
|
|
3284
|
+
this.socketIdentity = { dev: identity.dev, ino: identity.ino };
|
|
2831
3285
|
await super.start();
|
|
2832
3286
|
} catch (error) {
|
|
2833
3287
|
await this.stop().catch(() => {});
|
|
@@ -2887,12 +3341,27 @@ var init_jcode = __esm(() => {
|
|
|
2887
3341
|
}
|
|
2888
3342
|
if (!failure && !this.server) {
|
|
2889
3343
|
if (!this.preserveRoot)
|
|
2890
|
-
|
|
3344
|
+
rmSync3(this.root, { recursive: true, force: true });
|
|
2891
3345
|
super.setState("offline");
|
|
2892
3346
|
}
|
|
2893
3347
|
if (failure)
|
|
2894
3348
|
throw failure;
|
|
2895
3349
|
}
|
|
3350
|
+
tuiLaunch(expected, options = {}) {
|
|
3351
|
+
const sessionId = super.recoveryMetadata().sessionId;
|
|
3352
|
+
const socket = lstatSync4(this.socket, { throwIfNoEntry: false });
|
|
3353
|
+
if (this.ending || !this.server || this.server.exitCode !== null || this.server.signalCode !== null || this.state === "offline" || !socket?.isSocket() || socket.dev !== this.socketIdentity?.dev || socket.ino !== this.socketIdentity.ino || typeof sessionId !== "string")
|
|
3354
|
+
throw new Error("owned Jcode launch is unavailable");
|
|
3355
|
+
const binding = { launchId: this.launchId, sessionId };
|
|
3356
|
+
if (expected && (expected.launchId !== binding.launchId || expected.sessionId !== binding.sessionId))
|
|
3357
|
+
throw new Error("Jcode launch changed");
|
|
3358
|
+
if (options.sessionId !== undefined && options.sessionId !== sessionId || options.model !== undefined && options.model !== this.jcode.launchModel || options.providerProfile !== undefined && options.providerProfile !== this.jcode.providerProfile)
|
|
3359
|
+
throw new Error("Jcode launch options differ from its running session");
|
|
3360
|
+
if (!savedSession(this.jcode.home, sessionId))
|
|
3361
|
+
return { state: "waiting", binding };
|
|
3362
|
+
const [cmd, ...args] = jcodeTuiCommand(this.jcode.command, this.socket, sessionId, this.jcode.launchModel, this.jcode.providerProfile);
|
|
3363
|
+
return { state: "ready", binding, launch: { cmd, args, env: { JCODE_HOME: this.jcode.home, JCODE_SOCKET: this.socket, ...JCODE_OWNED_ENV } } };
|
|
3364
|
+
}
|
|
2896
3365
|
recoveryMetadata() {
|
|
2897
3366
|
return { ...super.recoveryMetadata(), launch: { kind: "jcode", peer: "jcode", mode: "headless", ...this.jcode.launchModel ? { model: this.jcode.launchModel } : {}, ...this.jcode.providerProfile ? { providerProfile: this.jcode.providerProfile } : {} } };
|
|
2898
3367
|
}
|
|
@@ -2900,11 +3369,11 @@ var init_jcode = __esm(() => {
|
|
|
2900
3369
|
});
|
|
2901
3370
|
|
|
2902
3371
|
// src/pi/launch.ts
|
|
2903
|
-
import { join as
|
|
3372
|
+
import { join as join6 } from "path";
|
|
2904
3373
|
function buildPiLaunch(opts, inherited, extension, bridgePort, bridgeToken) {
|
|
2905
3374
|
const adapter = ADAPTERS.find((adapter) => adapter.start.daemonPort === "pi-rpc");
|
|
2906
|
-
const env = { ...peerChildEnv(opts.instance ?? adapter.kind, inherited), ...opts.hubStateDir !== undefined ? { AGENTHUB_STATE_DIR: opts.hubStateDir } : {}, PI_CODING_AGENT_DIR:
|
|
2907
|
-
const args = [...opts.mode === "headless" ? ["--mode", "rpc"] : [], "--provider", "agent-hub-local", "--model", opts.model ?? modelFor(opts.backend, opts.relay.models), "--models", opts.relay.models.map((model) => `agent-hub-local/${model.id}`).join(","), "--no-builtin-tools", "--no-skills", "--no-prompt-templates", "--no-extensions", "--extension", extension, ...opts.preamble ? ["--append-system-prompt", opts.preamble] : [], "--session-dir",
|
|
3375
|
+
const env = { ...peerChildEnv(opts.instance ?? adapter.kind, inherited), ...opts.hubStateDir !== undefined ? { AGENTHUB_STATE_DIR: opts.hubStateDir } : {}, PI_CODING_AGENT_DIR: join6(opts.stateDir, "pi"), AGENTHUB_PI_BRIDGE_URL: `http://127.0.0.1:${bridgePort}`, AGENTHUB_PI_BRIDGE_TOKEN: bridgeToken, AGENTHUB_PI_OWNER_TOKEN: bridgeToken, AGENTHUB_PI_RELAY_URL: opts.relay.url, AGENTHUB_PI_RELAY_TOKEN: opts.relay.token, AGENTHUB_PI_MODELS: JSON.stringify(opts.relay.models), AGENTHUB_PI_TOOLS: JSON.stringify(opts.tools), AGENTHUB_PI_MAX_STEPS: String(opts.maxSteps ?? 30) };
|
|
3376
|
+
const args = [...opts.mode === "headless" ? ["--mode", "rpc"] : [], "--provider", "agent-hub-local", "--model", opts.model ?? modelFor(opts.backend, opts.relay.models), "--models", opts.relay.models.map((model) => `agent-hub-local/${model.id}`).join(","), "--no-builtin-tools", "--no-skills", "--no-prompt-templates", "--no-extensions", "--extension", extension, ...opts.preamble ? ["--append-system-prompt", opts.preamble] : [], "--session-dir", join6(opts.stateDir, "pi-sessions")];
|
|
2908
3377
|
if (opts.sessionFile)
|
|
2909
3378
|
args.push("--session", opts.sessionFile);
|
|
2910
3379
|
else if (opts.sessionId)
|
|
@@ -2940,8 +3409,8 @@ var init_recovery_store = __esm(() => {
|
|
|
2940
3409
|
});
|
|
2941
3410
|
|
|
2942
3411
|
// src/pi/session.ts
|
|
2943
|
-
import { readFileSync as
|
|
2944
|
-
import { join as
|
|
3412
|
+
import { readFileSync as readFileSync4, openSync as openSync2, closeSync as closeSync2, fsyncSync, mkdirSync as mkdirSync3 } from "fs";
|
|
3413
|
+
import { join as join7, relative as relative2, resolve as resolve5, dirname as dirname5 } from "path";
|
|
2945
3414
|
function sessionReadFailure(error) {
|
|
2946
3415
|
if (error.code === "ENOENT")
|
|
2947
3416
|
throw new PiSessionUnresumable("missing", error.message, { cause: error });
|
|
@@ -2951,16 +3420,16 @@ function validatePiSession(stateDir, cwd, sessionFile, sessionId) {
|
|
|
2951
3420
|
let file, sessions;
|
|
2952
3421
|
try {
|
|
2953
3422
|
file = realPath(sessionFile);
|
|
2954
|
-
sessions = realPath(
|
|
3423
|
+
sessions = realPath(join7(stateDir, "pi-sessions"));
|
|
2955
3424
|
} catch (error) {
|
|
2956
3425
|
return sessionReadFailure(error);
|
|
2957
3426
|
}
|
|
2958
3427
|
const rel = relative2(sessions, file);
|
|
2959
|
-
if (!rel || rel.startsWith("..") ||
|
|
3428
|
+
if (!rel || rel.startsWith("..") || resolve5(sessions, rel) !== file)
|
|
2960
3429
|
throw new PiSessionUnresumable("outside_managed", "Pi session file is outside the managed project session directory");
|
|
2961
3430
|
let first;
|
|
2962
3431
|
try {
|
|
2963
|
-
first =
|
|
3432
|
+
first = readFileSync4(file, "utf8").split(`
|
|
2964
3433
|
`, 1)[0];
|
|
2965
3434
|
} catch (error) {
|
|
2966
3435
|
return sessionReadFailure(error);
|
|
@@ -3009,11 +3478,11 @@ var init_permission_mode = __esm(() => {
|
|
|
3009
3478
|
});
|
|
3010
3479
|
|
|
3011
3480
|
// src/hub/path-policy.ts
|
|
3012
|
-
import { accessSync, constants, lstatSync as
|
|
3013
|
-
import { dirname as
|
|
3481
|
+
import { accessSync, constants as constants2, lstatSync as lstatSync5, opendirSync, readlinkSync, statSync as statSync2 } from "fs";
|
|
3482
|
+
import { dirname as dirname6, isAbsolute as isAbsolute4, join as join8, relative as relative3, resolve as resolve6, sep as sep2 } from "path";
|
|
3014
3483
|
function existing(path) {
|
|
3015
3484
|
try {
|
|
3016
|
-
|
|
3485
|
+
lstatSync5(path);
|
|
3017
3486
|
return true;
|
|
3018
3487
|
} catch (error) {
|
|
3019
3488
|
if (error.code === "ENOENT")
|
|
@@ -3024,17 +3493,17 @@ function existing(path) {
|
|
|
3024
3493
|
function policyRealPath(path) {
|
|
3025
3494
|
let ancestor = path;
|
|
3026
3495
|
while (!existing(ancestor)) {
|
|
3027
|
-
const parent =
|
|
3496
|
+
const parent = dirname6(ancestor);
|
|
3028
3497
|
if (parent === ancestor)
|
|
3029
3498
|
throw new Error("path cannot be resolved");
|
|
3030
3499
|
ancestor = parent;
|
|
3031
3500
|
}
|
|
3032
|
-
return
|
|
3501
|
+
return join8(realPath(ancestor), relative3(ancestor, path));
|
|
3033
3502
|
}
|
|
3034
3503
|
function policyTargets(cwd, paths) {
|
|
3035
3504
|
const root = realPath(cwd);
|
|
3036
3505
|
return [...new Set(paths.flatMap((path) => {
|
|
3037
|
-
const lexical =
|
|
3506
|
+
const lexical = resolve6(root, path);
|
|
3038
3507
|
return [lexical, policyRealPath(lexical)];
|
|
3039
3508
|
}))];
|
|
3040
3509
|
}
|
|
@@ -3052,19 +3521,19 @@ function directAliasTarget(path) {
|
|
|
3052
3521
|
}
|
|
3053
3522
|
let ancestor = target;
|
|
3054
3523
|
while (!existing(ancestor)) {
|
|
3055
|
-
const parent =
|
|
3524
|
+
const parent = dirname6(ancestor);
|
|
3056
3525
|
if (parent === ancestor)
|
|
3057
3526
|
return;
|
|
3058
3527
|
ancestor = parent;
|
|
3059
3528
|
}
|
|
3060
|
-
if (!
|
|
3529
|
+
if (!lstatSync5(ancestor).isSymbolicLink())
|
|
3061
3530
|
return;
|
|
3062
|
-
target =
|
|
3531
|
+
target = resolve6(dirname6(ancestor), readlinkSync(ancestor), relative3(ancestor, target));
|
|
3063
3532
|
}
|
|
3064
3533
|
return;
|
|
3065
3534
|
}
|
|
3066
3535
|
function aliasScope(dir, lexical, actual, includeParents, budget) {
|
|
3067
|
-
accessSync(dir,
|
|
3536
|
+
accessSync(dir, constants2.R_OK);
|
|
3068
3537
|
const handle = opendirSync(dir);
|
|
3069
3538
|
let matched = false;
|
|
3070
3539
|
try {
|
|
@@ -3076,7 +3545,7 @@ function aliasScope(dir, lexical, actual, includeParents, budget) {
|
|
|
3076
3545
|
budget.entries++;
|
|
3077
3546
|
if (!entry.isSymbolicLink())
|
|
3078
3547
|
continue;
|
|
3079
|
-
const target = directAliasTarget(
|
|
3548
|
+
const target = directAliasTarget(join8(dir, entry.name));
|
|
3080
3549
|
if (target !== undefined && (beneath(lexical, target) || beneath(actual, target) || includeParents && (beneath(target, lexical) || beneath(target, actual))))
|
|
3081
3550
|
matched = true;
|
|
3082
3551
|
}
|
|
@@ -3172,7 +3641,7 @@ function globScope(root, lexical, actual, path, includeParents, budget, roots, a
|
|
|
3172
3641
|
if (++budget.entries > 4096)
|
|
3173
3642
|
throw new Error("policy glob discovery unavailable");
|
|
3174
3643
|
const next = advance(pattern, states, entry.name);
|
|
3175
|
-
if (next.size && visit(
|
|
3644
|
+
if (next.size && visit(join8(dir, entry.name), next, depth + 1, chain))
|
|
3176
3645
|
return true;
|
|
3177
3646
|
}
|
|
3178
3647
|
} finally {
|
|
@@ -3188,7 +3657,7 @@ function matchesPolicy(cwd, path, paths, includeParents = false) {
|
|
|
3188
3657
|
try {
|
|
3189
3658
|
if (path.length > 4096)
|
|
3190
3659
|
return true;
|
|
3191
|
-
const root = realPath(cwd), lexical =
|
|
3660
|
+
const root = realPath(cwd), lexical = resolve6(root, path), actual = policyRealPath(lexical);
|
|
3192
3661
|
includeParents &&= !existing(lexical) || statSync2(lexical).isDirectory();
|
|
3193
3662
|
const budget = { entries: 0 }, roots = new Set, exact = [];
|
|
3194
3663
|
if (paths.some((entry) => {
|
|
@@ -3223,7 +3692,7 @@ var init_path_policy = __esm(() => {
|
|
|
3223
3692
|
});
|
|
3224
3693
|
|
|
3225
3694
|
// src/local/deny.ts
|
|
3226
|
-
import { basename as
|
|
3695
|
+
import { basename as basename4, dirname as dirname7 } from "path";
|
|
3227
3696
|
function hubWriteRegexes(root, gitDirs = [], moduleGit) {
|
|
3228
3697
|
const git = hfsFoldedName(".git");
|
|
3229
3698
|
const re = (s) => `(regex ${sbplString(s)})`;
|
|
@@ -3235,7 +3704,7 @@ function hubWriteRegexes(root, gitDirs = [], moduleGit) {
|
|
|
3235
3704
|
const modules = moduleGit ? [
|
|
3236
3705
|
`(require-all ${re(`^${sbplEscape(moduleGit.root)}/(.*/)?${hfsFoldedName("config")}$`)} (require-not ${re(`^${sbplEscape(moduleGit.root)}/(.*/)?(refs|logs)/`)}))`,
|
|
3237
3706
|
`(require-all ${re(`^${sbplEscape(moduleGit.root)}/(.*/)?${hfsFoldedName("hooks")}(/|$)`)} (require-not ${re(`^${sbplEscape(moduleGit.root)}/(.*/)?(refs|logs)/`)}))`,
|
|
3238
|
-
...moduleGit.entries.map((d) => re(`^${sbplEscape(
|
|
3707
|
+
...moduleGit.entries.map((d) => re(`^${sbplEscape(dirname7(d))}/${hfsFoldedName(basename4(d))}$`))
|
|
3239
3708
|
] : [];
|
|
3240
3709
|
return [
|
|
3241
3710
|
`(require-all (subpath ${sbplString(root)}) ${re(`^.*/${hfsFoldedName(".agenthub")}(/|$)`)})`,
|
|
@@ -3267,10 +3736,10 @@ var init_deny = __esm(() => {
|
|
|
3267
3736
|
});
|
|
3268
3737
|
|
|
3269
3738
|
// src/local/sandbox.ts
|
|
3270
|
-
import { spawn as
|
|
3271
|
-
import { existsSync as
|
|
3272
|
-
import { homedir as
|
|
3273
|
-
import { dirname as
|
|
3739
|
+
import { spawn as spawn7, spawnSync as spawnSync3 } from "child_process";
|
|
3740
|
+
import { existsSync as existsSync5, mkdtempSync as mkdtempSync4, readdirSync, rmSync as rmSync4 } from "fs";
|
|
3741
|
+
import { homedir as homedir3, tmpdir as tmpdir4 } from "os";
|
|
3742
|
+
import { dirname as dirname8, join as join9, resolve as resolve7 } from "path";
|
|
3274
3743
|
function developerDir() {
|
|
3275
3744
|
const out = spawnSync3("xcode-select", ["-p"], { encoding: "utf8" });
|
|
3276
3745
|
let dir = out.status === 0 ? out.stdout.trim() : "";
|
|
@@ -3284,14 +3753,14 @@ function externalGitDirs(root) {
|
|
|
3284
3753
|
if (out.status !== 0)
|
|
3285
3754
|
return [];
|
|
3286
3755
|
const dirs = out.stdout.trim().split(`
|
|
3287
|
-
`).map((d) =>
|
|
3756
|
+
`).map((d) => resolve7(root, d));
|
|
3288
3757
|
return [...new Set(dirs)].filter((d) => !d.startsWith(`${root}/`));
|
|
3289
3758
|
}
|
|
3290
3759
|
function moduleGitDirs(root) {
|
|
3291
3760
|
const out = memoryGitSync(["-C", root, "rev-parse", "--git-common-dir"], { encoding: "utf8" });
|
|
3292
3761
|
if (out.status !== 0)
|
|
3293
3762
|
return;
|
|
3294
|
-
const modulesRoot =
|
|
3763
|
+
const modulesRoot = join9(resolve7(root, out.stdout.trim()), "modules");
|
|
3295
3764
|
const dirs = [];
|
|
3296
3765
|
const walk = (dir, depth) => {
|
|
3297
3766
|
if (depth > 8)
|
|
@@ -3305,31 +3774,31 @@ function moduleGitDirs(root) {
|
|
|
3305
3774
|
const names = new Set(entries.map((e) => e.name));
|
|
3306
3775
|
if (names.has("HEAD") && names.has("config")) {
|
|
3307
3776
|
dirs.push(dir);
|
|
3308
|
-
const nested =
|
|
3777
|
+
const nested = join9(dir, "modules");
|
|
3309
3778
|
if (names.has("modules"))
|
|
3310
3779
|
walk(nested, depth + 1);
|
|
3311
3780
|
return;
|
|
3312
3781
|
}
|
|
3313
3782
|
for (const e of entries)
|
|
3314
3783
|
if (e.isDirectory())
|
|
3315
|
-
walk(
|
|
3784
|
+
walk(join9(dir, e.name), depth + 1);
|
|
3316
3785
|
};
|
|
3317
3786
|
walk(modulesRoot, 0);
|
|
3318
3787
|
const entries = new Set([modulesRoot]);
|
|
3319
3788
|
for (const d of dirs)
|
|
3320
|
-
for (let p = d;p.length > modulesRoot.length; p =
|
|
3789
|
+
for (let p = d;p.length > modulesRoot.length; p = dirname8(p))
|
|
3321
3790
|
entries.add(p);
|
|
3322
3791
|
return { root: modulesRoot, entries: [...entries] };
|
|
3323
3792
|
}
|
|
3324
3793
|
function profile(cwd, network, readAllow = [], deny = [], readOnly = []) {
|
|
3325
|
-
const home =
|
|
3326
|
-
const inHome = (p) => p.startsWith("~/") ?
|
|
3794
|
+
const home = homedir3();
|
|
3795
|
+
const inHome = (p) => p.startsWith("~/") ? join9(home, p.slice(2)) : p;
|
|
3327
3796
|
const root = realPath(cwd);
|
|
3328
3797
|
const gitDirs = externalGitDirs(root);
|
|
3329
3798
|
const moduleGit = moduleGitDirs(root);
|
|
3330
3799
|
const creds = [".ssh", ".aws", ".gnupg", ".config/gh", ".config/gcloud", ".kube", ".docker", ".netrc", ".npmrc", ".omniroute", ".claude", ".codex", ".kimi-code", "Library/Keychains"];
|
|
3331
3800
|
const dev = developerDir();
|
|
3332
|
-
const readable = [root, ...HOME_READABLE.map((p) =>
|
|
3801
|
+
const readable = [root, ...HOME_READABLE.map((p) => join9(home, p)), ...readAllow.map(inHome), ...gitDirs, ...dev ? [dev] : []];
|
|
3333
3802
|
const subpaths = (paths) => paths.map((p) => `(subpath ${q(p)})`).join(" ");
|
|
3334
3803
|
const globals = (names) => names.map((n) => `(global-name ${q(n)})`).join(" ");
|
|
3335
3804
|
const proxy = typeof network === "object" ? `(allow network-outbound (remote ip ${q(`localhost:${network.proxyPort}`)}))` : undefined;
|
|
@@ -3347,14 +3816,14 @@ function profile(cwd, network, readAllow = [], deny = [], readOnly = []) {
|
|
|
3347
3816
|
'(allow file-ioctl (regex #"^/dev/"))',
|
|
3348
3817
|
...proxy ? [proxy] : network ? ["(allow network*)"] : []
|
|
3349
3818
|
];
|
|
3350
|
-
const places = `${creds.map((c) => `(subpath ${q(
|
|
3819
|
+
const places = `${creds.map((c) => `(subpath ${q(join9(home, c))})`).join(" ")} ${denyRegexes(root, deny, false).join(" ")}`;
|
|
3351
3820
|
return [
|
|
3352
3821
|
"(version 1)",
|
|
3353
3822
|
...start,
|
|
3354
3823
|
"(deny file-write*)",
|
|
3355
3824
|
...readOnly.length ? ['(allow file-write* (literal "/dev/null"))'] : [`(allow file-write* (subpath ${q(root)}) (regex #"^/dev/") ${gitDirs.map((d) => `(subpath ${q(d)})`).join(" ")})`],
|
|
3356
3825
|
`(deny file-write* ${hubWriteRegexes(root, gitDirs, moduleGit).join(" ")})`,
|
|
3357
|
-
`(deny file-read* file-write* ${creds.map((c) => `(subpath ${q(
|
|
3826
|
+
`(deny file-read* file-write* ${creds.map((c) => `(subpath ${q(join9(home, c))})`).join(" ")})`,
|
|
3358
3827
|
`(deny file-read* file-write* ${denyRegexes(root, deny).join(" ")})`,
|
|
3359
3828
|
...network ? [`(allow file-read* ${CA_BUNDLES.map((p) => `(literal ${q(p)})`).join(" ")} (regex ${q("/certifi/cacert\\.pem$")}))`, `(deny file-read* file-write* ${places})`] : []
|
|
3360
3829
|
].join(`
|
|
@@ -3368,11 +3837,11 @@ function sandboxedExec(argv, opts) {
|
|
|
3368
3837
|
if (opts.signal?.aborted)
|
|
3369
3838
|
return Promise.resolve({ code: null, output: "error: command cancelled before execution" });
|
|
3370
3839
|
return new Promise((resolve) => {
|
|
3371
|
-
const own = realPath(
|
|
3840
|
+
const own = realPath(mkdtempSync4(join9(tmpdir4(), "ahub-cmd-")));
|
|
3372
3841
|
const sandbox = `${opts.profile}
|
|
3373
3842
|
(allow file-read* ${opts.readOnlyData ? "" : "file-write* "}process-exec (subpath ${q(own)}))`;
|
|
3374
|
-
const env = { PATH: process.env.PATH ?? "/usr/bin:/bin", HOME:
|
|
3375
|
-
const child =
|
|
3843
|
+
const env = { PATH: process.env.PATH ?? "/usr/bin:/bin", HOME: homedir3(), LANG: process.env.LANG ?? "en_US.UTF-8", TERM: "dumb", ...opts.env, TMPDIR: `${own}/` };
|
|
3844
|
+
const child = spawn7(SANDBOX_EXEC, ["-p", sandbox, ...argv], { cwd: opts.cwd, env, stdio: ["ignore", "pipe", "pipe"], detached: true });
|
|
3376
3845
|
let output = "";
|
|
3377
3846
|
const take = (d) => {
|
|
3378
3847
|
if (output.length < OUTPUT_CAP)
|
|
@@ -3391,11 +3860,11 @@ function sandboxedExec(argv, opts) {
|
|
|
3391
3860
|
opts.signal?.addEventListener("abort", abort, { once: true });
|
|
3392
3861
|
const dropOwn = () => {
|
|
3393
3862
|
try {
|
|
3394
|
-
|
|
3863
|
+
rmSync4(own, { recursive: true, force: true });
|
|
3395
3864
|
} catch {
|
|
3396
3865
|
try {
|
|
3397
3866
|
spawnSync3("chflags", ["-R", "nouchg", own]);
|
|
3398
|
-
|
|
3867
|
+
rmSync4(own, { recursive: true, force: true });
|
|
3399
3868
|
} catch {}
|
|
3400
3869
|
}
|
|
3401
3870
|
};
|
|
@@ -3424,7 +3893,7 @@ function proxyEnv(network) {
|
|
|
3424
3893
|
const url = `http://127.0.0.1:${network.proxyPort}`;
|
|
3425
3894
|
return { HTTPS_PROXY: url, https_proxy: url, HTTP_PROXY: url, http_proxy: url, ALL_PROXY: url, all_proxy: url, NO_PROXY: "", no_proxy: "" };
|
|
3426
3895
|
}
|
|
3427
|
-
var SANDBOX_EXEC = "/usr/bin/sandbox-exec", OUTPUT_CAP = 20000, sandboxAvailable = () => process.platform === "darwin" &&
|
|
3896
|
+
var SANDBOX_EXEC = "/usr/bin/sandbox-exec", OUTPUT_CAP = 20000, sandboxAvailable = () => process.platform === "darwin" && existsSync5(SANDBOX_EXEC), q, HOME_READABLE, CA_BUNDLES, SYSTEM_READABLE, MACH_SERVICES, NETWORK_MACH_SERVICES, PROXY_MACH_SERVICES;
|
|
3428
3897
|
var init_sandbox = __esm(() => {
|
|
3429
3898
|
init_project();
|
|
3430
3899
|
init_git();
|
|
@@ -3439,29 +3908,29 @@ var init_sandbox = __esm(() => {
|
|
|
3439
3908
|
});
|
|
3440
3909
|
|
|
3441
3910
|
// src/local/tools.ts
|
|
3442
|
-
import { lstatSync as
|
|
3443
|
-
import { dirname as
|
|
3911
|
+
import { lstatSync as lstatSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync5, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
3912
|
+
import { dirname as dirname9, join as join10, relative as relative4, resolve as resolve8 } from "path";
|
|
3444
3913
|
function guardPath(ctx, path, mode) {
|
|
3445
3914
|
const root = realPath(ctx.cwd);
|
|
3446
|
-
const abs =
|
|
3915
|
+
const abs = resolve8(root, path);
|
|
3447
3916
|
let existing = abs;
|
|
3448
3917
|
while (!lexists(existing))
|
|
3449
|
-
existing =
|
|
3918
|
+
existing = dirname9(existing);
|
|
3450
3919
|
let realExisting;
|
|
3451
3920
|
try {
|
|
3452
3921
|
realExisting = realPath(existing);
|
|
3453
3922
|
} catch {
|
|
3454
3923
|
throw new Error(`${path} goes through a dangling symlink`);
|
|
3455
3924
|
}
|
|
3456
|
-
const real =
|
|
3925
|
+
const real = join10(realExisting, relative4(existing, abs));
|
|
3457
3926
|
const rel = relative4(root, real);
|
|
3458
|
-
if (rel.startsWith("..") ||
|
|
3927
|
+
if (rel.startsWith("..") || resolve8(root, rel) !== real)
|
|
3459
3928
|
throw new Error(`${path} is outside the project directory`);
|
|
3460
3929
|
if (isDenied(rel, ctx.deny))
|
|
3461
3930
|
throw new Error(`${path} is on the secrets denylist`);
|
|
3462
3931
|
if (mode === "write" && isHubWritePath(rel))
|
|
3463
3932
|
throw new Error(`${path} is not writable by the local worker`);
|
|
3464
|
-
if (mode === "write" && ctx.readOnly?.length && (
|
|
3933
|
+
if (mode === "write" && ctx.readOnly?.length && (lstatSync6(real, { throwIfNoEntry: false })?.nlink ?? 1) > 1)
|
|
3465
3934
|
throw new Error("hardlinked write target cannot be verified with data.readonly");
|
|
3466
3935
|
if (mode === "write" && matchesPolicy(root, path, ctx.readOnly ?? []))
|
|
3467
3936
|
throw new Error("target is protected by data.readonly");
|
|
@@ -3511,7 +3980,7 @@ async function runTool(name, rawArgs, ctx) {
|
|
|
3511
3980
|
const file = guardPath(ctx, String(a.path), "read");
|
|
3512
3981
|
if (statSync3(file).size > 5000000)
|
|
3513
3982
|
return "error: file is larger than 5 MB; use bash (head, grep, sed -n) to look at parts of it";
|
|
3514
|
-
const lines =
|
|
3983
|
+
const lines = readFileSync5(file, "utf8").split(`
|
|
3515
3984
|
`);
|
|
3516
3985
|
const from = Math.max(1, Number(a.offset) || 1);
|
|
3517
3986
|
const out = lines.slice(from - 1, from - 1 + (Number(a.limit) || 2000)).map((l, i) => `${from + i} ${l}`).join(`
|
|
@@ -3532,14 +4001,14 @@ ${preview(content)}`, "write", ctx.signal, file, undefined, String(a.path)), "er
|
|
|
3532
4001
|
file = guardPath(ctx, String(a.path), "write");
|
|
3533
4002
|
if (file !== approvedTarget)
|
|
3534
4003
|
return "error: path target changed during approval; inspect before retrying";
|
|
3535
|
-
|
|
3536
|
-
|
|
4004
|
+
mkdirSync4(dirname9(file), { recursive: true });
|
|
4005
|
+
writeFileSync3(file, content);
|
|
3537
4006
|
return `wrote ${a.path}`;
|
|
3538
4007
|
}
|
|
3539
4008
|
case "edit": {
|
|
3540
4009
|
let file = guardPath(ctx, String(a.path), "write");
|
|
3541
4010
|
const approvedTarget = file;
|
|
3542
|
-
let text =
|
|
4011
|
+
let text = readFileSync5(file, "utf8");
|
|
3543
4012
|
let count = text.split(String(a.old)).length - 1;
|
|
3544
4013
|
if (!a.old || count !== 1)
|
|
3545
4014
|
return `error: \`old\` must match exactly once, it matched ${count} times`;
|
|
@@ -3553,11 +4022,11 @@ ${preview(content)}`, "write", ctx.signal, file, undefined, String(a.path)), "er
|
|
|
3553
4022
|
file = guardPath(ctx, String(a.path), "write");
|
|
3554
4023
|
if (file !== approvedTarget)
|
|
3555
4024
|
return "error: path target changed during approval; inspect before retrying";
|
|
3556
|
-
text =
|
|
4025
|
+
text = readFileSync5(file, "utf8");
|
|
3557
4026
|
count = text.split(String(a.old)).length - 1;
|
|
3558
4027
|
if (count !== 1)
|
|
3559
4028
|
return `error: file changed during approval; \`old\` must match exactly once, it matched ${count} times`;
|
|
3560
|
-
|
|
4029
|
+
writeFileSync3(file, text.replace(String(a.old), () => String(a.new ?? "")));
|
|
3561
4030
|
return `edited ${a.path}`;
|
|
3562
4031
|
}
|
|
3563
4032
|
case "bash": {
|
|
@@ -3607,7 +4076,7 @@ ${preview(content)}`, "write", ctx.signal, file, undefined, String(a.path)), "er
|
|
|
3607
4076
|
}
|
|
3608
4077
|
var APPROVAL_EXPIRED_TEXT = "error: approval expired: no person answered; do not retry this call; hand the task off or stop", ApprovalWaitStop, approvalError = (picked, denied) => picked === true ? undefined : picked === "expired" ? APPROVAL_EXPIRED_TEXT : picked === "aborted" ? "error: approval withdrawn because the turn ended; no operation was executed" : denied, lexists = (p) => {
|
|
3609
4078
|
try {
|
|
3610
|
-
|
|
4079
|
+
lstatSync6(p);
|
|
3611
4080
|
return true;
|
|
3612
4081
|
} catch {
|
|
3613
4082
|
return false;
|
|
@@ -3661,9 +4130,9 @@ var PI_CEILING_KIND = "tool-step-ceiling", PI_CEILING_UNIT = "tool-step";
|
|
|
3661
4130
|
|
|
3662
4131
|
// src/adapters/pi.ts
|
|
3663
4132
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3664
|
-
import { existsSync as
|
|
3665
|
-
import { join as
|
|
3666
|
-
import { spawn as
|
|
4133
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5 } from "fs";
|
|
4134
|
+
import { join as join11, resolve as resolve9 } from "path";
|
|
4135
|
+
import { spawn as spawn8 } from "child_process";
|
|
3667
4136
|
function ownerStillAlive(pid, signature) {
|
|
3668
4137
|
const current = processSignature(pid);
|
|
3669
4138
|
if (current !== undefined)
|
|
@@ -3779,7 +4248,7 @@ var init_pi = __esm(() => {
|
|
|
3779
4248
|
get recoveryReady() {
|
|
3780
4249
|
if (this.state === "busy" || this.starting || this.activeTools)
|
|
3781
4250
|
return false;
|
|
3782
|
-
if (this.sessionFile && !
|
|
4251
|
+
if (this.sessionFile && !existsSync6(this.sessionFile) && (this.activityObserved || this.persistedOnce))
|
|
3783
4252
|
return false;
|
|
3784
4253
|
if (this.opts.mode === "headless")
|
|
3785
4254
|
return this.state === "idle" || !this.proc || this.proc.exitCode !== null || this.proc.signalCode !== null;
|
|
@@ -3798,7 +4267,7 @@ var init_pi = __esm(() => {
|
|
|
3798
4267
|
return this.ownerClaimed && this.ownerPid && this.ownerSignature ? `${this.ownerPid}:${this.ownerSignature}:${this.sessionId ?? ""}` : undefined;
|
|
3799
4268
|
}
|
|
3800
4269
|
recoveryMetadata() {
|
|
3801
|
-
const exists = !!this.sessionFile &&
|
|
4270
|
+
const exists = !!this.sessionFile && existsSync6(this.sessionFile);
|
|
3802
4271
|
if (exists) {
|
|
3803
4272
|
this.persistedOnce = true;
|
|
3804
4273
|
this.verifiedEmptyResume = undefined;
|
|
@@ -3811,14 +4280,14 @@ var init_pi = __esm(() => {
|
|
|
3811
4280
|
if (this.state === "offline") {
|
|
3812
4281
|
if (this.canReuseVerifiedEmptyResume())
|
|
3813
4282
|
return this.recoveryMetadata();
|
|
3814
|
-
if (this.recoveryReady && this.sessionFile &&
|
|
4283
|
+
if (this.recoveryReady && this.sessionFile && existsSync6(this.sessionFile))
|
|
3815
4284
|
return this.recoveryMetadata();
|
|
3816
4285
|
throw new Error("Pi source session is offline and has no verified persisted resume state; inspect preserved history, then a person may run ahub pi --replace-session --session-file <managed-file>");
|
|
3817
4286
|
}
|
|
3818
4287
|
this.emptyResumeVerified = false;
|
|
3819
4288
|
if (this.state !== "idle" || this.stopping || this.activeTools)
|
|
3820
4289
|
throw new Error("Pi must settle before session capture");
|
|
3821
|
-
if (this.sessionFile &&
|
|
4290
|
+
if (this.sessionFile && existsSync6(this.sessionFile))
|
|
3822
4291
|
return this.recoveryMetadata();
|
|
3823
4292
|
const snapshot = this.opts.mode === "headless" ? (await this.waitRpc("get_state", 15000)).data : await this.sendTui({ type: "get_session_state" });
|
|
3824
4293
|
if (this.state !== "idle" || this.activeTools || snapshot?.sessionId !== this.sessionId || snapshot?.sessionFile !== this.sessionFile)
|
|
@@ -3833,7 +4302,7 @@ var init_pi = __esm(() => {
|
|
|
3833
4302
|
return captured;
|
|
3834
4303
|
}
|
|
3835
4304
|
canReuseVerifiedEmptyResume() {
|
|
3836
|
-
return !!this.verifiedEmptyResume && this.recoveryReady && !this.activityObserved && !this.persistedOnce && this.sessionId === this.verifiedEmptyResume.sessionId && (!this.sessionFile || !
|
|
4305
|
+
return !!this.verifiedEmptyResume && this.recoveryReady && !this.activityObserved && !this.persistedOnce && this.sessionId === this.verifiedEmptyResume.sessionId && (!this.sessionFile || !existsSync6(this.sessionFile));
|
|
3837
4306
|
}
|
|
3838
4307
|
noteActivity() {
|
|
3839
4308
|
this.activityObserved = true;
|
|
@@ -4025,9 +4494,9 @@ var init_pi = __esm(() => {
|
|
|
4025
4494
|
this.approvalAnswerEpoch = undefined;
|
|
4026
4495
|
this.approvalStopReason = "";
|
|
4027
4496
|
this.approvalAbortSent = false;
|
|
4028
|
-
|
|
4029
|
-
const sessions =
|
|
4030
|
-
|
|
4497
|
+
mkdirSync5(this.opts.stateDir, { recursive: true });
|
|
4498
|
+
const sessions = join11(this.opts.stateDir, "pi-sessions");
|
|
4499
|
+
mkdirSync5(sessions, { recursive: true, mode: 448 });
|
|
4031
4500
|
if (this.opts.sessionFile) {
|
|
4032
4501
|
try {
|
|
4033
4502
|
validatePiSession(this.opts.stateDir, this.opts.cwd, this.opts.sessionFile, this.opts.sessionId);
|
|
@@ -4084,7 +4553,7 @@ var init_pi = __esm(() => {
|
|
|
4084
4553
|
return new Response("not found", { status: 404 });
|
|
4085
4554
|
} });
|
|
4086
4555
|
this.server = bridge;
|
|
4087
|
-
const extension =
|
|
4556
|
+
const extension = resolve9(join11(import.meta.dir, "../pi/extension.ts"));
|
|
4088
4557
|
const inherited = {};
|
|
4089
4558
|
for (const key of ["PATH", "HOME", "USER", "SHELL", "TMPDIR", "TERM", "TERM_PROGRAM", "LANG", "LC_ALL", "LC_CTYPE", "NO_COLOR", "CODEX_HOME"])
|
|
4090
4559
|
if (process.env[key])
|
|
@@ -4094,7 +4563,7 @@ var init_pi = __esm(() => {
|
|
|
4094
4563
|
if (this.opts.mode === "tui") {
|
|
4095
4564
|
return;
|
|
4096
4565
|
}
|
|
4097
|
-
this.proc =
|
|
4566
|
+
this.proc = spawn8(bin, args, { cwd: this.opts.cwd, env, stdio: ["pipe", "pipe", "pipe"], detached: true });
|
|
4098
4567
|
trackGroup(this.proc);
|
|
4099
4568
|
this.proc.stdout.on("data", (chunk) => this.onOutput(String(chunk)));
|
|
4100
4569
|
this.proc.stderr.on("data", (chunk) => this.opts.log?.(`[${this.id}] ${String(chunk).trimEnd()}`));
|
|
@@ -4126,7 +4595,7 @@ var init_pi = __esm(() => {
|
|
|
4126
4595
|
this.ownerToken = String(this._tuiLaunch?.env.AGENTHUB_PI_OWNER_TOKEN ?? "");
|
|
4127
4596
|
this.ownerClaimed = true;
|
|
4128
4597
|
}
|
|
4129
|
-
this.persistedOnce =
|
|
4598
|
+
this.persistedOnce = existsSync6(this.sessionFile);
|
|
4130
4599
|
if (this.opts.sessionId && this.sessionId !== this.opts.sessionId || this.opts.sessionFile && this.sessionFile !== this.opts.sessionFile)
|
|
4131
4600
|
throw new Error("Pi startup session identity mismatch");
|
|
4132
4601
|
this.started = true;
|
|
@@ -5865,7 +6334,7 @@ var init_signals = __esm(() => {
|
|
|
5865
6334
|
});
|
|
5866
6335
|
|
|
5867
6336
|
// src/models/route/labels.ts
|
|
5868
|
-
import { createHash as
|
|
6337
|
+
import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
|
|
5869
6338
|
|
|
5870
6339
|
class RouteLabelTracker {
|
|
5871
6340
|
sink;
|
|
@@ -6015,7 +6484,7 @@ function safeJson(value) {
|
|
|
6015
6484
|
return "";
|
|
6016
6485
|
}
|
|
6017
6486
|
}
|
|
6018
|
-
var discreteSeverity = (value) => value >= 1 ? 1 : value >= 0.7 ? 0.7 : value >= 0.3 ? 0.3 : 0, sha256 = (value) =>
|
|
6487
|
+
var discreteSeverity = (value) => value >= 1 ? 1 : value >= 0.7 ? 0.7 : value >= 0.3 ? 0.3 : 0, sha256 = (value) => createHash4("sha256").update(value).digest("hex");
|
|
6019
6488
|
var init_labels = __esm(() => {
|
|
6020
6489
|
init_signals();
|
|
6021
6490
|
});
|
|
@@ -6883,9 +7352,9 @@ var init_shipped_roles = __esm(() => {
|
|
|
6883
7352
|
});
|
|
6884
7353
|
|
|
6885
7354
|
// src/hub/roles.ts
|
|
6886
|
-
import { createHash as
|
|
6887
|
-
import { constants as
|
|
6888
|
-
import { join as
|
|
7355
|
+
import { createHash as createHash5 } from "crypto";
|
|
7356
|
+
import { constants as constants3, closeSync as closeSync3, fstatSync as fstatSync2, lstatSync as lstatSync7, openSync as openSync3, readSync as readSync2, readdirSync as readdirSync2 } from "fs";
|
|
7357
|
+
import { join as join12 } from "path";
|
|
6889
7358
|
function parseRole(text, stem, layer) {
|
|
6890
7359
|
if (Buffer.byteLength(text) > 8192 || text.includes("\r") || !ID.test(stem))
|
|
6891
7360
|
throw new Error("invalid role size, encoding or file id");
|
|
@@ -6929,27 +7398,27 @@ function parseRole(text, stem, layer) {
|
|
|
6929
7398
|
}
|
|
6930
7399
|
const reviewStart = sections[4].index + sections[4][0].length;
|
|
6931
7400
|
const review = body.slice(reviewStart, sections[5].index).trim();
|
|
6932
|
-
return { id: stem, name: header.name, group: header.group, summary: header.summary, source: header.source, base: list("base", BASES2), classes: list("classes", CLASSES2), body, review, layer, hash:
|
|
7401
|
+
return { id: stem, name: header.name, group: header.group, summary: header.summary, source: header.source, base: list("base", BASES2), classes: list("classes", CLASSES2), body, review, layer, hash: createHash5("sha256").update(text).digest("hex") };
|
|
6933
7402
|
}
|
|
6934
7403
|
function projectRole(file) {
|
|
6935
|
-
const fd =
|
|
7404
|
+
const fd = openSync3(file, constants3.O_RDONLY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
|
|
6936
7405
|
try {
|
|
6937
|
-
const before =
|
|
7406
|
+
const before = fstatSync2(fd);
|
|
6938
7407
|
if (!before.isFile() || before.nlink !== 1 || before.size > 8192)
|
|
6939
7408
|
throw new Error("role must be a bounded regular file");
|
|
6940
7409
|
const bytes = Buffer.alloc(8193);
|
|
6941
7410
|
let length = 0, n = 0;
|
|
6942
|
-
while ((n =
|
|
7411
|
+
while ((n = readSync2(fd, bytes, length, bytes.length - length, null)) > 0) {
|
|
6943
7412
|
length += n;
|
|
6944
7413
|
if (length > 8192)
|
|
6945
7414
|
throw new Error("role exceeds file cap");
|
|
6946
7415
|
}
|
|
6947
|
-
const after =
|
|
7416
|
+
const after = fstatSync2(fd);
|
|
6948
7417
|
if (before.size !== after.size || before.mtimeMs !== after.mtimeMs || length !== after.size)
|
|
6949
7418
|
throw new Error("role changed during read");
|
|
6950
7419
|
return bytes.subarray(0, length).toString("utf8");
|
|
6951
7420
|
} finally {
|
|
6952
|
-
|
|
7421
|
+
closeSync3(fd);
|
|
6953
7422
|
}
|
|
6954
7423
|
}
|
|
6955
7424
|
function loadRoleCatalog(cwd) {
|
|
@@ -6964,9 +7433,9 @@ function loadRoleCatalog(cwd) {
|
|
|
6964
7433
|
if (!cwd)
|
|
6965
7434
|
return { roles, errors };
|
|
6966
7435
|
try {
|
|
6967
|
-
const config =
|
|
7436
|
+
const config = join12(cwd, ".agenthub"), dir = join12(config, "roles");
|
|
6968
7437
|
for (const path of [config, dir]) {
|
|
6969
|
-
const stat =
|
|
7438
|
+
const stat = lstatSync7(path);
|
|
6970
7439
|
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
6971
7440
|
throw new Error("unsafe role directory");
|
|
6972
7441
|
}
|
|
@@ -6976,7 +7445,7 @@ function loadRoleCatalog(cwd) {
|
|
|
6976
7445
|
for (const file of files) {
|
|
6977
7446
|
const id = file.slice(0, -3);
|
|
6978
7447
|
try {
|
|
6979
|
-
roles.set(id, parseRole(projectRole(
|
|
7448
|
+
roles.set(id, parseRole(projectRole(join12(dir, file)), id, "project"));
|
|
6980
7449
|
} catch {
|
|
6981
7450
|
errors.push({ id: ID.test(id) ? id : "invalid-file-id", reason: "invalid or unsafe project role" });
|
|
6982
7451
|
}
|
|
@@ -7878,21 +8347,748 @@ function reviewerVariant(kind, displayName, reviewer, display) {
|
|
|
7878
8347
|
display
|
|
7879
8348
|
};
|
|
7880
8349
|
}
|
|
7881
|
-
var codexVariant, CODEX_REVIEWER_VARIANT, KIMI_REVIEWER_VARIANT,
|
|
8350
|
+
var codexVariant, CODEX_REVIEWER_VARIANT, KIMI_REVIEWER_VARIANT, AGY_REVIEWER_VARIANT;
|
|
7882
8351
|
var init_reviewer_variants = __esm(() => {
|
|
7883
8352
|
init_claude_print();
|
|
7884
8353
|
init_codex_review_isolation();
|
|
8354
|
+
init_kimi_review();
|
|
7885
8355
|
init_native_factories();
|
|
7886
8356
|
init_native_display();
|
|
7887
8357
|
codexVariant = reviewerVariant("codex", "Codex reviewer", { backend: codexReviewerBackend, defaultPool: true, declaration: "explicit" }, CODEX_DISPLAY);
|
|
7888
8358
|
CODEX_REVIEWER_VARIANT = { ...codexVariant, start: { ...codexVariant.start, create: createCodexReviewer } };
|
|
7889
|
-
KIMI_REVIEWER_VARIANT = reviewerVariant("kimi", "Kimi reviewer", {
|
|
7890
|
-
|
|
7891
|
-
...reviewerVariant("agy", "agy reviewer", { defaultPool: false, declaration: "automatic", unavailable: "agy
|
|
8359
|
+
KIMI_REVIEWER_VARIANT = reviewerVariant("kimi", "Kimi reviewer", { backend: kimiReviewerBackend, defaultPool: true, declaration: "explicit" }, KIMI_DISPLAY);
|
|
8360
|
+
AGY_REVIEWER_VARIANT = {
|
|
8361
|
+
...reviewerVariant("agy", "agy reviewer", { defaultPool: false, declaration: "automatic", unavailable: "agy is an implementer peer (#485)" }, { palette: { fg: "#6558a6", bg: "#f1effa" } }),
|
|
7892
8362
|
optional: true
|
|
7893
8363
|
};
|
|
7894
8364
|
});
|
|
7895
8365
|
|
|
8366
|
+
// src/adapters/agy/hooks.ts
|
|
8367
|
+
import { closeSync as closeSync4, constants as constants4, fstatSync as fstatSync3, lstatSync as lstatSync8, openSync as openSync4, readSync as readSync3 } from "fs";
|
|
8368
|
+
import { homedir as homedir4 } from "os";
|
|
8369
|
+
import { isAbsolute as isAbsolute5, join as join13, relative as relative5 } from "path";
|
|
8370
|
+
import { createHash as createHash6 } from "crypto";
|
|
8371
|
+
function decodeAgyHook(event, value) {
|
|
8372
|
+
if (!EVENTS.includes(event) || !record3(value) || typeof value.conversationId !== "string" || !UUID.test(value.conversationId) || typeof value.transcriptPath !== "string" || !isAbsolute5(value.transcriptPath) || value.transcriptPath.length > 4096 || !Array.isArray(value.workspacePaths) || !value.workspacePaths.length || value.workspacePaths.length > 32 || !value.workspacePaths.every((path) => typeof path === "string" && path.length <= 4096 && isAbsolute5(path)))
|
|
8373
|
+
return;
|
|
8374
|
+
if (event === "Stop" && typeof value.fullyIdle !== "boolean")
|
|
8375
|
+
return;
|
|
8376
|
+
const call = record3(value.toolCall) ? value.toolCall : undefined;
|
|
8377
|
+
if ((event === "PreToolUse" || event === "PostToolUse") && (!call || typeof call.name !== "string" || call.name.length > 128 || !record3(call.args)))
|
|
8378
|
+
return;
|
|
8379
|
+
const native = call?.args;
|
|
8380
|
+
const observedRead = call?.name === "view_file" && typeof native?.AbsolutePath === "string";
|
|
8381
|
+
const phase = { SessionStart: "session", PreInvocation: "start", PostInvocation: "post", PreToolUse: "pre", PostToolUse: "post", Stop: "stop" }[event];
|
|
8382
|
+
return {
|
|
8383
|
+
event,
|
|
8384
|
+
phase,
|
|
8385
|
+
sessionId: value.conversationId,
|
|
8386
|
+
transcriptPath: value.transcriptPath,
|
|
8387
|
+
workspacePaths: value.workspacePaths,
|
|
8388
|
+
tool: observedRead ? "Read" : typeof call?.name === "string" ? call.name : "",
|
|
8389
|
+
input: observedRead ? { file_path: native.AbsolutePath } : native ?? {},
|
|
8390
|
+
...Number.isSafeInteger(value.stepIdx) && value.stepIdx >= 0 ? { toolUseId: `${value.conversationId}:${value.stepIdx}` } : {},
|
|
8391
|
+
...Number.isSafeInteger(value.invocationNum) && value.invocationNum >= 0 ? { invocation: value.invocationNum } : {},
|
|
8392
|
+
...event === "Stop" ? { fullyIdle: value.fullyIdle, terminationReason: typeof value.terminationReason === "string" ? value.terminationReason : "" } : {}
|
|
8393
|
+
};
|
|
8394
|
+
}
|
|
8395
|
+
function encodeAgyHook(event, text) {
|
|
8396
|
+
if (text && (event === "SessionStart" || event === "PreInvocation"))
|
|
8397
|
+
return { injectSteps: [{ ephemeralMessage: text }] };
|
|
8398
|
+
return;
|
|
8399
|
+
}
|
|
8400
|
+
function readAgyTranscript(session, transcript) {
|
|
8401
|
+
const empty = { factIds: [] };
|
|
8402
|
+
if (!UUID.test(session))
|
|
8403
|
+
return empty;
|
|
8404
|
+
const home = process.env.HOME ?? homedir4();
|
|
8405
|
+
const candidates = ["antigravity-cli", "antigravity", "antigravity-ide"].map((surface) => join13(home, ".gemini", surface, "brain", session, ".system_generated/logs/transcript_full.jsonl"));
|
|
8406
|
+
if (!candidates.includes(transcript))
|
|
8407
|
+
return empty;
|
|
8408
|
+
let fd;
|
|
8409
|
+
try {
|
|
8410
|
+
let cursor = home;
|
|
8411
|
+
for (const name of relative5(home, transcript).split("/")) {
|
|
8412
|
+
cursor = join13(cursor, name);
|
|
8413
|
+
if (lstatSync8(cursor).isSymbolicLink())
|
|
8414
|
+
return empty;
|
|
8415
|
+
}
|
|
8416
|
+
fd = openSync4(transcript, constants4.O_RDONLY | constants4.O_NOFOLLOW);
|
|
8417
|
+
const stat = fstatSync3(fd);
|
|
8418
|
+
if (!stat.isFile() || stat.nlink !== 1)
|
|
8419
|
+
return empty;
|
|
8420
|
+
const cap = 4 * 1024 * 1024;
|
|
8421
|
+
for (let window = 64 * 1024;; window = Math.min(cap, window * 2)) {
|
|
8422
|
+
const start = Math.max(0, stat.size - window), from = start > 0 ? start - 1 : 0;
|
|
8423
|
+
const buffer = Buffer.alloc(stat.size - from);
|
|
8424
|
+
let bytes = 0;
|
|
8425
|
+
while (bytes < buffer.length) {
|
|
8426
|
+
const count = readSync3(fd, buffer, bytes, buffer.length - bytes, from + bytes);
|
|
8427
|
+
if (!count)
|
|
8428
|
+
break;
|
|
8429
|
+
bytes += count;
|
|
8430
|
+
}
|
|
8431
|
+
const lines = buffer.subarray(0, bytes).toString("utf8").split(`
|
|
8432
|
+
`);
|
|
8433
|
+
if (start)
|
|
8434
|
+
lines.shift();
|
|
8435
|
+
const out = { factIds: [] };
|
|
8436
|
+
for (const line of lines) {
|
|
8437
|
+
let row;
|
|
8438
|
+
try {
|
|
8439
|
+
row = JSON.parse(line);
|
|
8440
|
+
} catch {
|
|
8441
|
+
continue;
|
|
8442
|
+
}
|
|
8443
|
+
if (!record3(row) || row.status !== "DONE")
|
|
8444
|
+
continue;
|
|
8445
|
+
if (row.source === "SYSTEM_SDK" && row.type === "EPHEMERAL_MESSAGE" && typeof row.content === "string") {
|
|
8446
|
+
for (const match of row.content.matchAll(/agent-hub facts \[([a-zA-Z0-9-]{1,128})\]/g))
|
|
8447
|
+
out.factIds.push(match[1]);
|
|
8448
|
+
}
|
|
8449
|
+
if (row.source === "MODEL" && row.type === "PLANNER_RESPONSE" && Number.isSafeInteger(row.step_index) && typeof row.content === "string" && row.content.trim().length > 0 && (!Array.isArray(row.tool_calls) || row.tool_calls.length === 0))
|
|
8450
|
+
out.completion = { id: createHash6("sha256").update(`${session}:${row.step_index}:${line}`).digest("hex"), index: row.step_index };
|
|
8451
|
+
}
|
|
8452
|
+
if (out.completion || start === 0 || window === cap)
|
|
8453
|
+
return out;
|
|
8454
|
+
}
|
|
8455
|
+
} catch {
|
|
8456
|
+
return empty;
|
|
8457
|
+
} finally {
|
|
8458
|
+
if (fd !== undefined)
|
|
8459
|
+
closeSync4(fd);
|
|
8460
|
+
}
|
|
8461
|
+
}
|
|
8462
|
+
var EVENTS, UUID, record3 = (value) => !!value && typeof value === "object" && !Array.isArray(value), AGY_FACTS_HOOK;
|
|
8463
|
+
var init_hooks = __esm(() => {
|
|
8464
|
+
EVENTS = ["SessionStart", "PreInvocation", "PostInvocation", "PreToolUse", "PostToolUse", "Stop"];
|
|
8465
|
+
UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
8466
|
+
AGY_FACTS_HOOK = { decode: decodeAgyHook, encode: encodeAgyHook, read: readAgyTranscript };
|
|
8467
|
+
});
|
|
8468
|
+
|
|
8469
|
+
// src/adapters/agy/definition.ts
|
|
8470
|
+
function agyLaunch(ctx) {
|
|
8471
|
+
const argv = ["agy", ...ctx.userArgs], command = argv.map(shellQuote).join(" ");
|
|
8472
|
+
const metadata = { packageEntrypoint: ctx.entrypoint, command, argv, env: {}, kind: "agy", peer: ctx.instance };
|
|
8473
|
+
return { cmd: "agy", args: ctx.userArgs, metadata };
|
|
8474
|
+
}
|
|
8475
|
+
var DISPLAY, shellQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`, AGY_DEFINITION;
|
|
8476
|
+
var init_definition = __esm(() => {
|
|
8477
|
+
init_reviewer_variants();
|
|
8478
|
+
init_hooks();
|
|
8479
|
+
DISPLAY = { palette: { fg: "#6558a6", bg: "#f1effa" } };
|
|
8480
|
+
AGY_DEFINITION = {
|
|
8481
|
+
api: 1,
|
|
8482
|
+
kind: "agy",
|
|
8483
|
+
displayName: "Antigravity",
|
|
8484
|
+
family: "agy",
|
|
8485
|
+
transport: "ws-pull",
|
|
8486
|
+
roles: ["implementer", "verifier"],
|
|
8487
|
+
optional: true,
|
|
8488
|
+
defaultOnRoster: false,
|
|
8489
|
+
trust: { onPrem: false, hubSandboxed: false },
|
|
8490
|
+
start: {
|
|
8491
|
+
forms: ["tui"],
|
|
8492
|
+
defaultForm: "tui",
|
|
8493
|
+
ownsTerminal: (form) => form === "tui",
|
|
8494
|
+
launch: agyLaunch,
|
|
8495
|
+
conductorStartable: false,
|
|
8496
|
+
hubSwitches: [],
|
|
8497
|
+
ownedFlags: ["--new", "--continue", "--conversation", "--resume"],
|
|
8498
|
+
conductorStartArgs: (args) => args.length ? new Error("conductor starts accept no launch overrides; use ahub agy") : {}
|
|
8499
|
+
},
|
|
8500
|
+
identity: { mcp: "channel-plugin" },
|
|
8501
|
+
attach: {
|
|
8502
|
+
pullOnlyForms: ["tui"],
|
|
8503
|
+
recoveryLaunch: (instance) => ({ kind: "agy", peer: instance }),
|
|
8504
|
+
pullOnlyNotice: "pull-only: messages wait for hub_inbox; permission modes and session recovery are not managed"
|
|
8505
|
+
},
|
|
8506
|
+
permission: {
|
|
8507
|
+
modes: {},
|
|
8508
|
+
unmanaged: "Antigravity selects its own permission mode in its TUI; no hub permission mode",
|
|
8509
|
+
unmanagedForms: ["tui"],
|
|
8510
|
+
unmanagedNote: "Antigravity approves tools in its own TUI; no hub permission mode",
|
|
8511
|
+
unmanagedStatusNote: "unmanaged native permission policy"
|
|
8512
|
+
},
|
|
8513
|
+
session: {
|
|
8514
|
+
idField: "sessionId",
|
|
8515
|
+
currentId: () => {
|
|
8516
|
+
return;
|
|
8517
|
+
},
|
|
8518
|
+
freshOnRestore: true,
|
|
8519
|
+
recoveryRefusal: () => "Antigravity session continuity and resume are unavailable until native behavior is verified"
|
|
8520
|
+
},
|
|
8521
|
+
turnEnd: "native-hook",
|
|
8522
|
+
cli: { command: "agy", authority: "person", startWords: () => ["agy"] },
|
|
8523
|
+
display: DISPLAY,
|
|
8524
|
+
routing: { allowedRoles: ["implementer", "verifier"] },
|
|
8525
|
+
reviewer: { defaultPool: false, declaration: "automatic", unavailable: "agy is an implementer peer (#485)" },
|
|
8526
|
+
reviewerVariant: AGY_REVIEWER_VARIANT,
|
|
8527
|
+
hooks: { factsHook: AGY_FACTS_HOOK }
|
|
8528
|
+
};
|
|
8529
|
+
});
|
|
8530
|
+
|
|
8531
|
+
// src/adapters/definitions/native-cli-preview-common.ts
|
|
8532
|
+
function publicArg(arg) {
|
|
8533
|
+
const flags = new Set(["--settings", "--model", "--mcp-config", "--session-id", "--session-file", "--resume", "--continue", "--print", "--safe", "--new"]);
|
|
8534
|
+
const key = arg.split("=", 1)[0];
|
|
8535
|
+
return flags.has(key) ? arg.includes("=") ? key + "=" + REDACTED : key : REDACTED;
|
|
8536
|
+
}
|
|
8537
|
+
function nativeCommand(command, trusted) {
|
|
8538
|
+
return command.map((value, i) => value === trusted[i] ? value : REDACTED);
|
|
8539
|
+
}
|
|
8540
|
+
function previewState(args, baseEnvNames) {
|
|
8541
|
+
const unresolved = [];
|
|
8542
|
+
const envNames = [...baseEnvNames];
|
|
8543
|
+
const value = (flag) => {
|
|
8544
|
+
const at = args.indexOf(flag);
|
|
8545
|
+
if (at < 0)
|
|
8546
|
+
return;
|
|
8547
|
+
const next = args[at + 1];
|
|
8548
|
+
if (!next || next.startsWith("--"))
|
|
8549
|
+
throw new Error("missing option value");
|
|
8550
|
+
return next;
|
|
8551
|
+
};
|
|
8552
|
+
return { unresolved, envNames, value };
|
|
8553
|
+
}
|
|
8554
|
+
function finishPreview(ctx, launch, envNames, unresolved, metadata, settings) {
|
|
8555
|
+
return { tool: ctx.tool, ...metadata ?? {}, cmd: launch.cmd, args: launch.args, settings, envNames: [...new Set(envNames)].sort(), ...launch.warning ? { warning: launch.warning } : {}, unresolved, redaction: "Arbitrary user arguments, settings, commands and configured executable overrides are redacted. Environment values are never displayed.", needs: ["Native executable, accounts and runtime readiness are not checked."] };
|
|
8556
|
+
}
|
|
8557
|
+
var REDACTED = "[redacted]", UNRESOLVED = "[unresolved]";
|
|
8558
|
+
|
|
8559
|
+
// src/hub/status-roster.ts
|
|
8560
|
+
function rosterInStatus(status, peer) {
|
|
8561
|
+
const rows = status?.roster;
|
|
8562
|
+
if (!Array.isArray(rows))
|
|
8563
|
+
return;
|
|
8564
|
+
const row = rows.find((r) => !!r && typeof r === "object" && r.peer === peer);
|
|
8565
|
+
return typeof row?.on === "boolean" ? row.on : undefined;
|
|
8566
|
+
}
|
|
8567
|
+
function bundleInStatus(status) {
|
|
8568
|
+
const value = status?.pluginBundle;
|
|
8569
|
+
return typeof value === "string" && value.startsWith("/") && /[\\/]plugins[\\/]agent-hub[\\/]server\.js$/.test(value) ? value : undefined;
|
|
8570
|
+
}
|
|
8571
|
+
|
|
8572
|
+
// src/cli/agy-mcp.ts
|
|
8573
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
8574
|
+
import { closeSync as closeSync5, fstatSync as fstatSync4, lstatSync as lstatSync9, mkdirSync as mkdirSync6, openSync as openSync5, readFileSync as readFileSync6, renameSync, rmdirSync, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8575
|
+
import { isAbsolute as isAbsolute6, join as join14, resolve as resolve10 } from "path";
|
|
8576
|
+
import { isDeepStrictEqual } from "util";
|
|
8577
|
+
function object4(value) {
|
|
8578
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
8579
|
+
}
|
|
8580
|
+
function jsonFile(value, name) {
|
|
8581
|
+
if (!object4(value))
|
|
8582
|
+
throw new Error(`${name} must be a JSON object`);
|
|
8583
|
+
return value;
|
|
8584
|
+
}
|
|
8585
|
+
function parseJson(bytes, name) {
|
|
8586
|
+
try {
|
|
8587
|
+
return jsonFile(JSON.parse(bytes.toString("utf8")), name);
|
|
8588
|
+
} catch (error) {
|
|
8589
|
+
if (error instanceof SyntaxError)
|
|
8590
|
+
throw new Error(`${name} is invalid JSON`);
|
|
8591
|
+
throw error;
|
|
8592
|
+
}
|
|
8593
|
+
}
|
|
8594
|
+
function shellQuote2(value) {
|
|
8595
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
8596
|
+
}
|
|
8597
|
+
function shellUnquote(value) {
|
|
8598
|
+
if (!value.startsWith("'") || !value.endsWith("'"))
|
|
8599
|
+
return;
|
|
8600
|
+
let out = "";
|
|
8601
|
+
for (let index = 1;index < value.length - 1; ) {
|
|
8602
|
+
if (value.startsWith("'\\''", index)) {
|
|
8603
|
+
out += "'";
|
|
8604
|
+
index += 4;
|
|
8605
|
+
} else if (value[index] === "'")
|
|
8606
|
+
return;
|
|
8607
|
+
else
|
|
8608
|
+
out += value[index++];
|
|
8609
|
+
}
|
|
8610
|
+
return out;
|
|
8611
|
+
}
|
|
8612
|
+
function validateOptions(options) {
|
|
8613
|
+
const peer = options.peer ?? "agy";
|
|
8614
|
+
if (!/^[a-z][a-z0-9-]{0,31}$/.test(peer))
|
|
8615
|
+
throw new Error("agy peer id is invalid");
|
|
8616
|
+
for (const [name, value] of Object.entries({ projectRoot: options.projectRoot, stateDir: options.stateDir, serverJs: options.serverJs, hookScript: options.hookScript })) {
|
|
8617
|
+
if (typeof value !== "string" || !isAbsolute6(value) || value.length > 4096 || /[\u0000-\u001f\u007f]/.test(value))
|
|
8618
|
+
throw new Error(`${name} must be an absolute control-free path`);
|
|
8619
|
+
}
|
|
8620
|
+
for (const [name, value] of Object.entries({ instanceId: options.instanceId, launchId: options.launchId })) {
|
|
8621
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9._:-]{1,128}$/.test(value))
|
|
8622
|
+
throw new Error(`${name} is invalid`);
|
|
8623
|
+
}
|
|
8624
|
+
const root = resolve10(options.projectRoot);
|
|
8625
|
+
return { root, peer };
|
|
8626
|
+
}
|
|
8627
|
+
function safeDirectory(path) {
|
|
8628
|
+
const stat = lstatSync9(path, { throwIfNoEntry: false });
|
|
8629
|
+
if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
|
|
8630
|
+
throw new Error(`${path} must be a project directory, not a link`);
|
|
8631
|
+
return stat;
|
|
8632
|
+
}
|
|
8633
|
+
function safeFile(path) {
|
|
8634
|
+
const stat = lstatSync9(path, { throwIfNoEntry: false });
|
|
8635
|
+
if (!stat)
|
|
8636
|
+
return;
|
|
8637
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1 || stat.size > MAX_CONFIG_BYTES)
|
|
8638
|
+
throw new Error(`${path} must be a bounded regular file, not a link`);
|
|
8639
|
+
const bytes = readFileSync6(path);
|
|
8640
|
+
if (bytes.length > MAX_CONFIG_BYTES)
|
|
8641
|
+
throw new Error(`${path} exceeds the configuration size limit`);
|
|
8642
|
+
return { stat, bytes };
|
|
8643
|
+
}
|
|
8644
|
+
function pluginDirectories(root) {
|
|
8645
|
+
const directories = [];
|
|
8646
|
+
for (const relative of [".", ".agents", ".agents/plugins", PLUGIN_DIR]) {
|
|
8647
|
+
const path = relative === "." ? root : join14(root, relative);
|
|
8648
|
+
const stat = safeDirectory(path);
|
|
8649
|
+
if (!stat)
|
|
8650
|
+
throw new Error(`${path} must be an existing project directory`);
|
|
8651
|
+
directories.push({ path, stat });
|
|
8652
|
+
}
|
|
8653
|
+
return directories;
|
|
8654
|
+
}
|
|
8655
|
+
function verifyPluginDirectory(plan, target) {
|
|
8656
|
+
if (!plan.canonicalPluginDir || !plan.directories)
|
|
8657
|
+
throw new Error("managed agy plugin directory identity is unavailable");
|
|
8658
|
+
if (realPath(plan.root) !== plan.canonicalRoot || realPath(plan.pluginDir) !== plan.canonicalPluginDir || plan.canonicalPluginDir !== join14(plan.canonicalRoot, PLUGIN_DIR)) {
|
|
8659
|
+
throw new Error("managed agy plugin path changed; inspect it before writing");
|
|
8660
|
+
}
|
|
8661
|
+
for (const expected of plan.directories) {
|
|
8662
|
+
const current = safeDirectory(expected.path);
|
|
8663
|
+
if (!current || current.dev !== expected.stat.dev || current.ino !== expected.stat.ino)
|
|
8664
|
+
throw new Error("managed agy plugin directory changed; inspect it before writing");
|
|
8665
|
+
}
|
|
8666
|
+
if (!FILES.some((file) => join14(plan.root, file) === target)) {
|
|
8667
|
+
throw new Error("atomic write target is outside the managed agy plugin files");
|
|
8668
|
+
}
|
|
8669
|
+
}
|
|
8670
|
+
function hookCommand(phase, options, peer) {
|
|
8671
|
+
return `AGENTHUB_HOOK_EVENT='${phase}' AGENTHUB_INSTANCE_ID='${options.instanceId}' AGENTHUB_LAUNCH_ID='${options.launchId}' AGENTHUB_HOOK_PURPOSE='facts' AGENTHUB_PEER_ID=${shellQuote2(peer)} AGENTHUB_STATE_DIR=${shellQuote2(options.stateDir)} AGENTHUB_PROJECT_DIR=${shellQuote2(resolve10(options.projectRoot))} bun ${shellQuote2(options.hookScript)}`;
|
|
8672
|
+
}
|
|
8673
|
+
function desired(options, root, peer) {
|
|
8674
|
+
const events = {};
|
|
8675
|
+
for (const phase of EVENTS2) {
|
|
8676
|
+
if (phase === "PostInvocation") {
|
|
8677
|
+
events[phase] = [];
|
|
8678
|
+
continue;
|
|
8679
|
+
}
|
|
8680
|
+
const command = { type: "command", command: hookCommand(phase, options, peer), timeout: 5 };
|
|
8681
|
+
events[phase] = phase === "PreToolUse" || phase === "PostToolUse" ? [{ matcher: ".*", hooks: [command] }] : [command];
|
|
8682
|
+
}
|
|
8683
|
+
return {
|
|
8684
|
+
[MANIFEST_FILE]: { name: PLUGIN_KEY, description: MANAGED_DESCRIPTION },
|
|
8685
|
+
[MCP_FILE]: { mcpServers: { [PLUGIN_KEY]: { command: "bun", args: ["run", options.serverJs], env: { AGENTHUB_PEER_ID: peer, AGENTHUB_STATE_DIR: options.stateDir, AGENTHUB_PROJECT_DIR: root } } } },
|
|
8686
|
+
[HOOKS_FILE]: { [PLUGIN_KEY]: events }
|
|
8687
|
+
};
|
|
8688
|
+
}
|
|
8689
|
+
function parseManagedHookCommand(command, phase, options, peer) {
|
|
8690
|
+
if (typeof command !== "string")
|
|
8691
|
+
return;
|
|
8692
|
+
const prefix = `AGENTHUB_HOOK_EVENT='${phase}' AGENTHUB_INSTANCE_ID='`;
|
|
8693
|
+
const middle = `' AGENTHUB_LAUNCH_ID='`;
|
|
8694
|
+
const suffix = `' AGENTHUB_HOOK_PURPOSE='facts' AGENTHUB_PEER_ID=${shellQuote2(peer)} AGENTHUB_STATE_DIR=${shellQuote2(options.stateDir)} AGENTHUB_PROJECT_DIR=${shellQuote2(resolve10(options.projectRoot))} bun `;
|
|
8695
|
+
if (!command.startsWith(prefix))
|
|
8696
|
+
return;
|
|
8697
|
+
const rest = command.slice(prefix.length);
|
|
8698
|
+
const launchSplit = rest.indexOf(middle);
|
|
8699
|
+
if (launchSplit < 1)
|
|
8700
|
+
return;
|
|
8701
|
+
const instanceId = rest.slice(0, launchSplit);
|
|
8702
|
+
const afterInstance = rest.slice(launchSplit + middle.length);
|
|
8703
|
+
const pathSplit = afterInstance.indexOf(suffix);
|
|
8704
|
+
if (pathSplit < 1)
|
|
8705
|
+
return;
|
|
8706
|
+
const launchId = afterInstance.slice(0, pathSplit);
|
|
8707
|
+
const hookScript = shellUnquote(afterInstance.slice(pathSplit + suffix.length));
|
|
8708
|
+
if (!/^[A-Za-z0-9._:-]{1,128}$/.test(instanceId) || !/^[A-Za-z0-9._:-]{1,128}$/.test(launchId) || !hookScript || !isAbsolute6(hookScript) || !hookScript.endsWith("/src/cli/facts-hook.ts"))
|
|
8709
|
+
return;
|
|
8710
|
+
return { instanceId, launchId };
|
|
8711
|
+
}
|
|
8712
|
+
function managedHookLaunch(value, options, peer) {
|
|
8713
|
+
if (Object.keys(value).length !== 1 || !Object.hasOwn(value, PLUGIN_KEY))
|
|
8714
|
+
return;
|
|
8715
|
+
const events = value[PLUGIN_KEY];
|
|
8716
|
+
if (!object4(events) || Object.keys(events).length !== EVENTS2.length || EVENTS2.some((phase) => !Object.hasOwn(events, phase)))
|
|
8717
|
+
return;
|
|
8718
|
+
let identity;
|
|
8719
|
+
for (const phase of EVENTS2) {
|
|
8720
|
+
const rows = events[phase];
|
|
8721
|
+
if (phase === "PostInvocation") {
|
|
8722
|
+
if (!Array.isArray(rows) || rows.length !== 0)
|
|
8723
|
+
return;
|
|
8724
|
+
continue;
|
|
8725
|
+
}
|
|
8726
|
+
if (!Array.isArray(rows) || rows.length !== 1 || !object4(rows[0]))
|
|
8727
|
+
return;
|
|
8728
|
+
let command;
|
|
8729
|
+
if (phase === "PreToolUse" || phase === "PostToolUse") {
|
|
8730
|
+
const row = rows[0];
|
|
8731
|
+
if (Object.keys(row).sort().join(",") !== "hooks,matcher" || row.matcher !== ".*" || !Array.isArray(row.hooks) || row.hooks.length !== 1 || !object4(row.hooks[0]))
|
|
8732
|
+
return;
|
|
8733
|
+
command = row.hooks[0];
|
|
8734
|
+
} else
|
|
8735
|
+
command = rows[0];
|
|
8736
|
+
if (!object4(command) || Object.keys(command).sort().join(",") !== "command,timeout,type" || command.type !== "command" || command.timeout !== 5)
|
|
8737
|
+
return;
|
|
8738
|
+
const found = parseManagedHookCommand(command.command, phase, options, peer);
|
|
8739
|
+
if (!found || identity && (identity.instanceId !== found.instanceId || identity.launchId !== found.launchId))
|
|
8740
|
+
return;
|
|
8741
|
+
identity = found;
|
|
8742
|
+
}
|
|
8743
|
+
return identity;
|
|
8744
|
+
}
|
|
8745
|
+
function nextDocuments(current, wanted) {
|
|
8746
|
+
const nextMcp = current[MCP_FILE] ? { ...current[MCP_FILE] } : {};
|
|
8747
|
+
const currentServers = nextMcp.mcpServers;
|
|
8748
|
+
const servers = currentServers === undefined ? {} : currentServers;
|
|
8749
|
+
if (!object4(servers))
|
|
8750
|
+
throw new Error(`${MCP_FILE} mcpServers must be an object`);
|
|
8751
|
+
nextMcp.mcpServers = { ...servers, [PLUGIN_KEY]: wanted[MCP_FILE].mcpServers[PLUGIN_KEY] };
|
|
8752
|
+
return { [MANIFEST_FILE]: wanted[MANIFEST_FILE], [MCP_FILE]: nextMcp, [HOOKS_FILE]: wanted[HOOKS_FILE] };
|
|
8753
|
+
}
|
|
8754
|
+
function internalPlan(options) {
|
|
8755
|
+
const { root, peer } = validateOptions(options);
|
|
8756
|
+
const rootStat = safeDirectory(root);
|
|
8757
|
+
if (!rootStat)
|
|
8758
|
+
throw new Error("project root must be an existing directory");
|
|
8759
|
+
const canonicalRoot = realPath(root);
|
|
8760
|
+
const pluginDir = join14(root, PLUGIN_DIR);
|
|
8761
|
+
let parentsPresent = true;
|
|
8762
|
+
for (const relative of [".agents", ".agents/plugins", PLUGIN_DIR]) {
|
|
8763
|
+
if (!parentsPresent)
|
|
8764
|
+
break;
|
|
8765
|
+
parentsPresent = !!safeDirectory(join14(root, relative));
|
|
8766
|
+
}
|
|
8767
|
+
let directories;
|
|
8768
|
+
let canonicalPluginDir;
|
|
8769
|
+
if (parentsPresent) {
|
|
8770
|
+
directories = pluginDirectories(root);
|
|
8771
|
+
canonicalPluginDir = realPath(pluginDir);
|
|
8772
|
+
if (canonicalPluginDir !== join14(canonicalRoot, PLUGIN_DIR))
|
|
8773
|
+
throw new Error("managed agy plugin path traverses a symlink");
|
|
8774
|
+
}
|
|
8775
|
+
const snapshots = Object.fromEntries(FILES.map((relative) => {
|
|
8776
|
+
const path = join14(root, relative);
|
|
8777
|
+
const found = parentsPresent ? safeFile(path) : undefined;
|
|
8778
|
+
return [relative, found];
|
|
8779
|
+
}));
|
|
8780
|
+
const current = {};
|
|
8781
|
+
for (const relative of FILES) {
|
|
8782
|
+
const found = snapshots[relative];
|
|
8783
|
+
current[relative] = found ? parseJson(found.bytes, relative) : undefined;
|
|
8784
|
+
}
|
|
8785
|
+
const manifest = current[MANIFEST_FILE];
|
|
8786
|
+
if (manifest && (Object.keys(manifest).sort().join(",") !== "description,name" || manifest.name !== PLUGIN_KEY || manifest.description !== MANAGED_DESCRIPTION)) {
|
|
8787
|
+
throw new Error(`${MANIFEST_FILE} is a foreign agent-hub plugin manifest; it was not changed`);
|
|
8788
|
+
}
|
|
8789
|
+
const mcp = current[MCP_FILE];
|
|
8790
|
+
if (mcp) {
|
|
8791
|
+
if (Object.hasOwn(mcp, "mcpServers") && !object4(mcp.mcpServers))
|
|
8792
|
+
throw new Error(`${MCP_FILE} mcpServers must be an object`);
|
|
8793
|
+
const entry = mcp.mcpServers?.[PLUGIN_KEY];
|
|
8794
|
+
if (entry !== undefined && (!object4(entry) || Object.keys(entry).sort().join(",") !== "args,command,env" || entry.command !== "bun" || !Array.isArray(entry.args) || entry.args.length !== 2 || entry.args[0] !== "run" || typeof entry.args[1] !== "string" || !isAbsolute6(entry.args[1]) || !object4(entry.env) || Object.keys(entry.env).sort().join(",") !== "AGENTHUB_PEER_ID,AGENTHUB_PROJECT_DIR,AGENTHUB_STATE_DIR" || entry.env.AGENTHUB_PEER_ID !== peer || entry.env.AGENTHUB_PROJECT_DIR !== root || entry.env.AGENTHUB_STATE_DIR !== options.stateDir)) {
|
|
8795
|
+
throw new Error(`${MCP_FILE} agent-hub is a foreign MCP entry; it was not changed`);
|
|
8796
|
+
}
|
|
8797
|
+
}
|
|
8798
|
+
const hooks = current[HOOKS_FILE];
|
|
8799
|
+
const oldLaunch = hooks ? managedHookLaunch(hooks, options, peer) : undefined;
|
|
8800
|
+
if (hooks && !oldLaunch)
|
|
8801
|
+
throw new Error(`${HOOKS_FILE} agent-hub is a foreign hooks entry; it was not changed`);
|
|
8802
|
+
const wanted = desired(options, root, peer);
|
|
8803
|
+
const docs = nextDocuments(current, wanted);
|
|
8804
|
+
const targets = FILES.map((relative) => {
|
|
8805
|
+
const old = snapshots[relative];
|
|
8806
|
+
const next = Buffer.from(`${JSON.stringify(docs[relative], null, 2)}
|
|
8807
|
+
`);
|
|
8808
|
+
return { path: join14(root, relative), relative, ...old ? { current: old.bytes, stat: old.stat } : {}, next, changed: !old || !isDeepStrictEqual(current[relative], docs[relative]) };
|
|
8809
|
+
});
|
|
8810
|
+
const git = memoryGitSync(["ls-files", "--", ...FILES], { cwd: root, encoding: "utf8", timeout: 3000 });
|
|
8811
|
+
const tracked = git.status === 0 && !!git.stdout.trim();
|
|
8812
|
+
const changed = targets.some((target) => target.changed);
|
|
8813
|
+
return { file: pluginDir, tracked, changed, action: changed ? "create or update the managed agent-hub plugin" : "keep the managed agent-hub plugin", root, pluginDir, canonicalRoot, ...canonicalPluginDir ? { canonicalPluginDir } : {}, ...directories ? { directories } : {}, targets };
|
|
8814
|
+
}
|
|
8815
|
+
function identityMatches(path, before) {
|
|
8816
|
+
const now = lstatSync9(path, { throwIfNoEntry: false });
|
|
8817
|
+
return before ? !!now && now.dev === before.dev && now.ino === before.ino : !now;
|
|
8818
|
+
}
|
|
8819
|
+
function atomicWrite(path, bytes, expected, plan) {
|
|
8820
|
+
const temporary = `${path}.${randomUUID5()}.tmp`;
|
|
8821
|
+
let fd;
|
|
8822
|
+
let tempIdentity;
|
|
8823
|
+
try {
|
|
8824
|
+
verifyPluginDirectory(plan, path);
|
|
8825
|
+
fd = openSync5(temporary, "wx", 384);
|
|
8826
|
+
const tempStat = fstatSync4(fd);
|
|
8827
|
+
tempIdentity = { dev: tempStat.dev, ino: tempStat.ino };
|
|
8828
|
+
writeFileSync4(fd, bytes);
|
|
8829
|
+
closeSync5(fd);
|
|
8830
|
+
fd = undefined;
|
|
8831
|
+
verifyPluginDirectory(plan, path);
|
|
8832
|
+
if (!identityMatches(path, expected))
|
|
8833
|
+
throw new Error(`${path} changed while the managed plugin was being written`);
|
|
8834
|
+
renameSync(temporary, path);
|
|
8835
|
+
const stat = lstatSync9(path);
|
|
8836
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1)
|
|
8837
|
+
throw new Error(`${path} changed while the managed plugin was written`);
|
|
8838
|
+
return { dev: stat.dev, ino: stat.ino };
|
|
8839
|
+
} finally {
|
|
8840
|
+
if (fd !== undefined)
|
|
8841
|
+
closeSync5(fd);
|
|
8842
|
+
try {
|
|
8843
|
+
const current = lstatSync9(temporary, { throwIfNoEntry: false });
|
|
8844
|
+
if (current && tempIdentity && current.dev === tempIdentity.dev && current.ino === tempIdentity.ino)
|
|
8845
|
+
unlinkSync(temporary);
|
|
8846
|
+
} catch {}
|
|
8847
|
+
}
|
|
8848
|
+
}
|
|
8849
|
+
function rollback(plan, written, createdDirs) {
|
|
8850
|
+
for (const { target, own } of written.reverse()) {
|
|
8851
|
+
try {
|
|
8852
|
+
const now = lstatSync9(target.path, { throwIfNoEntry: false });
|
|
8853
|
+
if (!now || now.dev !== own.dev || now.ino !== own.ino)
|
|
8854
|
+
continue;
|
|
8855
|
+
if (target.current)
|
|
8856
|
+
atomicWrite(target.path, target.current, now, plan);
|
|
8857
|
+
else
|
|
8858
|
+
unlinkSync(target.path);
|
|
8859
|
+
} catch {}
|
|
8860
|
+
}
|
|
8861
|
+
for (const { path, dev, ino } of createdDirs.reverse()) {
|
|
8862
|
+
try {
|
|
8863
|
+
const now = lstatSync9(path);
|
|
8864
|
+
if (now.isDirectory() && !now.isSymbolicLink() && now.dev === dev && now.ino === ino)
|
|
8865
|
+
rmdirSync(path);
|
|
8866
|
+
} catch {}
|
|
8867
|
+
}
|
|
8868
|
+
}
|
|
8869
|
+
function ensureAgyMcp(options) {
|
|
8870
|
+
const plan = internalPlan(options);
|
|
8871
|
+
if (!plan.changed)
|
|
8872
|
+
return { changed: false, tracked: plan.tracked };
|
|
8873
|
+
const createdDirs = [];
|
|
8874
|
+
const written = [];
|
|
8875
|
+
try {
|
|
8876
|
+
for (const relative of [".agents", ".agents/plugins", PLUGIN_DIR]) {
|
|
8877
|
+
const path = join14(plan.root, relative);
|
|
8878
|
+
const prior = safeDirectory(path);
|
|
8879
|
+
if (prior)
|
|
8880
|
+
continue;
|
|
8881
|
+
mkdirSync6(path, { mode: 448 });
|
|
8882
|
+
const stat = safeDirectory(path);
|
|
8883
|
+
createdDirs.push({ path, dev: stat.dev, ino: stat.ino });
|
|
8884
|
+
}
|
|
8885
|
+
plan.directories = pluginDirectories(plan.root);
|
|
8886
|
+
plan.canonicalPluginDir = realPath(plan.pluginDir);
|
|
8887
|
+
if (plan.canonicalRoot !== realPath(plan.root) || plan.canonicalPluginDir !== join14(plan.canonicalRoot, PLUGIN_DIR))
|
|
8888
|
+
throw new Error("managed agy plugin path changed; inspect it before writing");
|
|
8889
|
+
for (const target of plan.targets) {
|
|
8890
|
+
if (!target.changed)
|
|
8891
|
+
continue;
|
|
8892
|
+
if (!identityMatches(target.path, target.stat))
|
|
8893
|
+
throw new Error(`${target.path} changed while the managed plugin was being prepared`);
|
|
8894
|
+
const own = atomicWrite(target.path, target.next, target.stat, plan);
|
|
8895
|
+
written.push({ target, own });
|
|
8896
|
+
}
|
|
8897
|
+
} catch (error) {
|
|
8898
|
+
rollback(plan, written, createdDirs);
|
|
8899
|
+
throw error;
|
|
8900
|
+
}
|
|
8901
|
+
return { changed: true, tracked: plan.tracked };
|
|
8902
|
+
}
|
|
8903
|
+
function cleanupAgyMcp(options) {
|
|
8904
|
+
const plan = internalPlan(options);
|
|
8905
|
+
const present = plan.targets.filter((target) => target.current !== undefined);
|
|
8906
|
+
if (!present.length)
|
|
8907
|
+
return;
|
|
8908
|
+
if (present.length !== FILES.length)
|
|
8909
|
+
throw new Error("managed agy plugin is incomplete; inspect it before cleanup");
|
|
8910
|
+
const currentHooks = parseJson(readFileSync6(join14(plan.root, HOOKS_FILE)), HOOKS_FILE);
|
|
8911
|
+
const launch = managedHookLaunch(currentHooks, options, options.peer ?? "agy");
|
|
8912
|
+
if (!launch || launch.instanceId !== options.instanceId || launch.launchId !== options.launchId)
|
|
8913
|
+
return;
|
|
8914
|
+
if (plan.changed)
|
|
8915
|
+
return;
|
|
8916
|
+
const deleted = [];
|
|
8917
|
+
try {
|
|
8918
|
+
for (const target of plan.targets) {
|
|
8919
|
+
const stat = lstatSync9(target.path);
|
|
8920
|
+
if (!target.stat || stat.dev !== target.stat.dev || stat.ino !== target.stat.ino)
|
|
8921
|
+
throw new Error(`${target.path} changed before agy plugin cleanup`);
|
|
8922
|
+
}
|
|
8923
|
+
for (const target of plan.targets) {
|
|
8924
|
+
if (!identityMatches(target.path, target.stat))
|
|
8925
|
+
throw new Error(`${target.path} changed before agy plugin cleanup`);
|
|
8926
|
+
if (target.relative === MCP_FILE) {
|
|
8927
|
+
const currentMcp = parseJson(target.current, MCP_FILE);
|
|
8928
|
+
const servers = { ...currentMcp.mcpServers };
|
|
8929
|
+
delete servers[PLUGIN_KEY];
|
|
8930
|
+
const nextMcp = { ...currentMcp, mcpServers: servers };
|
|
8931
|
+
const retain = Object.keys(servers).length > 0 || Object.keys(currentMcp).some((key) => key !== "mcpServers");
|
|
8932
|
+
if (retain) {
|
|
8933
|
+
const own = atomicWrite(target.path, Buffer.from(`${JSON.stringify(nextMcp, null, 2)}
|
|
8934
|
+
`), target.stat, plan);
|
|
8935
|
+
deleted.push({ path: target.path, bytes: target.current, own });
|
|
8936
|
+
} else {
|
|
8937
|
+
unlinkSync(target.path);
|
|
8938
|
+
deleted.push({ path: target.path, bytes: target.current });
|
|
8939
|
+
}
|
|
8940
|
+
} else {
|
|
8941
|
+
unlinkSync(target.path);
|
|
8942
|
+
deleted.push({ path: target.path, bytes: target.current });
|
|
8943
|
+
}
|
|
8944
|
+
}
|
|
8945
|
+
} catch (error) {
|
|
8946
|
+
for (const item of deleted.reverse()) {
|
|
8947
|
+
try {
|
|
8948
|
+
const current = lstatSync9(item.path, { throwIfNoEntry: false });
|
|
8949
|
+
if (item.own) {
|
|
8950
|
+
if (current?.dev === item.own.dev && current.ino === item.own.ino)
|
|
8951
|
+
atomicWrite(item.path, item.bytes, current, plan);
|
|
8952
|
+
} else if (!current)
|
|
8953
|
+
atomicWrite(item.path, item.bytes, undefined, plan);
|
|
8954
|
+
} catch {}
|
|
8955
|
+
}
|
|
8956
|
+
throw error;
|
|
8957
|
+
}
|
|
8958
|
+
}
|
|
8959
|
+
var PLUGIN_DIR = ".agents/plugins/agent-hub", PLUGIN_KEY = "agent-hub", MANIFEST_FILE, MCP_FILE, HOOKS_FILE, FILES, EVENTS2, MANAGED_DESCRIPTION = "Managed by ahub agy launcher.", MAX_CONFIG_BYTES;
|
|
8960
|
+
var init_agy_mcp = __esm(() => {
|
|
8961
|
+
init_git();
|
|
8962
|
+
init_project();
|
|
8963
|
+
MANIFEST_FILE = `${PLUGIN_DIR}/plugin.json`;
|
|
8964
|
+
MCP_FILE = `${PLUGIN_DIR}/mcp_config.json`;
|
|
8965
|
+
HOOKS_FILE = `${PLUGIN_DIR}/hooks.json`;
|
|
8966
|
+
FILES = [MANIFEST_FILE, MCP_FILE, HOOKS_FILE];
|
|
8967
|
+
EVENTS2 = ["SessionStart", "PreInvocation", "PostInvocation", "PreToolUse", "PostToolUse", "Stop"];
|
|
8968
|
+
MAX_CONFIG_BYTES = 256 * 1024;
|
|
8969
|
+
});
|
|
8970
|
+
|
|
8971
|
+
// src/adapters/agy/entry.ts
|
|
8972
|
+
import { spawn as spawn9, spawnSync as spawnSync4 } from "child_process";
|
|
8973
|
+
import { resolve as resolve11 } from "path";
|
|
8974
|
+
function agyTuiSpawnOptions(cwd, env) {
|
|
8975
|
+
return { cwd, env, stdio: "inherit" };
|
|
8976
|
+
}
|
|
8977
|
+
function runAgyTui(argv, cwd, env) {
|
|
8978
|
+
const child = spawn9("agy", argv, agyTuiSpawnOptions(cwd, env));
|
|
8979
|
+
return new Promise((resolveExit, reject) => {
|
|
8980
|
+
const forward = (signal) => {
|
|
8981
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
8982
|
+
return;
|
|
8983
|
+
try {
|
|
8984
|
+
child.kill(signal);
|
|
8985
|
+
} catch {}
|
|
8986
|
+
};
|
|
8987
|
+
const onInterrupt = () => forward("SIGINT");
|
|
8988
|
+
const onTerminate = () => forward("SIGTERM");
|
|
8989
|
+
const onHangup = () => forward("SIGHUP");
|
|
8990
|
+
const cleanup = () => {
|
|
8991
|
+
process.off("SIGINT", onInterrupt);
|
|
8992
|
+
process.off("SIGTERM", onTerminate);
|
|
8993
|
+
process.off("SIGHUP", onHangup);
|
|
8994
|
+
};
|
|
8995
|
+
process.on("SIGINT", onInterrupt);
|
|
8996
|
+
process.on("SIGTERM", onTerminate);
|
|
8997
|
+
process.on("SIGHUP", onHangup);
|
|
8998
|
+
child.once("error", (error) => {
|
|
8999
|
+
cleanup();
|
|
9000
|
+
reject(error);
|
|
9001
|
+
});
|
|
9002
|
+
child.once("close", (code) => {
|
|
9003
|
+
cleanup();
|
|
9004
|
+
resolveExit(code ?? 1);
|
|
9005
|
+
});
|
|
9006
|
+
});
|
|
9007
|
+
}
|
|
9008
|
+
async function runAgyEntry(ctx) {
|
|
9009
|
+
const { args, cwd, stateDir, cliDir, connect, fail } = ctx;
|
|
9010
|
+
if (ctx.instance)
|
|
9011
|
+
fail("extra Antigravity TUI instances are not verified in P1");
|
|
9012
|
+
const preview = args.includes("--print-command") || args.includes("--dry-run");
|
|
9013
|
+
const unattended = ctx.unattendedEnv || args.includes("--unattended");
|
|
9014
|
+
const nativeArgs = args.filter((arg) => !["--print-command", "--dry-run", "--unattended"].includes(arg));
|
|
9015
|
+
if (nativeArgs.some((arg) => ["--print", "-p", "--prompt", "--input-format", "--output-format", "--conversation", "--continue", "-c", "--new", "--mode", "--dangerously-skip-permissions"].some((flag) => arg === flag || arg.startsWith(`${flag}=`))))
|
|
9016
|
+
fail("agy P1 supports a fresh TUI only; headless, resume and permission flags need a verified mapping");
|
|
9017
|
+
if (unattended)
|
|
9018
|
+
fail("agy P1 native permissions are unmanaged; unattended mode has no verified prompt mapping");
|
|
9019
|
+
const argv = nativeArgs;
|
|
9020
|
+
if (preview) {
|
|
9021
|
+
console.log(JSON.stringify({ command: "agy", args: argv.map(publicArg), mode: "tui", permission: "unmanaged", mcp: ".agents/plugins/agent-hub/", writes: false }));
|
|
9022
|
+
return;
|
|
9023
|
+
}
|
|
9024
|
+
const version = spawnSync4("agy", ["--version"], { encoding: "utf8", timeout: 5000, maxBuffer: 4096, env: peerChildEnv("agy") });
|
|
9025
|
+
if (version.status !== 0 || version.stdout.trim() !== "1.4.2")
|
|
9026
|
+
fail("agy P1 requires the verified Antigravity CLI 1.4.2");
|
|
9027
|
+
const hub = await connect();
|
|
9028
|
+
let options;
|
|
9029
|
+
let pluginWritten = false;
|
|
9030
|
+
try {
|
|
9031
|
+
if (!hub.features.includes("native_hook_launch"))
|
|
9032
|
+
fail("running hub lacks the verified native hook launcher; upgrade or restart it before launching agy");
|
|
9033
|
+
const status = (await hub.request({ t: "status" })).status;
|
|
9034
|
+
if (rosterInStatus(status, "agy") !== true)
|
|
9035
|
+
fail("agy is off roster; add it with ahub roster add agy");
|
|
9036
|
+
const admitted = await hub.request({ t: "native_hook_launch", op: "begin", peer: "agy" });
|
|
9037
|
+
if (!admitted.ok || typeof admitted.launchId !== "string" || typeof admitted.instanceId !== "string")
|
|
9038
|
+
fail(admitted.error ?? "native hook launch was not admitted");
|
|
9039
|
+
options = {
|
|
9040
|
+
projectRoot: cwd,
|
|
9041
|
+
stateDir,
|
|
9042
|
+
serverJs: bundleInStatus(status) ?? resolve11(cliDir, "../../plugins/agent-hub/server.js"),
|
|
9043
|
+
hookScript: resolve11(cliDir, "facts-hook.ts"),
|
|
9044
|
+
peer: "agy",
|
|
9045
|
+
launchId: admitted.launchId,
|
|
9046
|
+
instanceId: admitted.instanceId
|
|
9047
|
+
};
|
|
9048
|
+
const written = ensureAgyMcp(options);
|
|
9049
|
+
pluginWritten = written.changed;
|
|
9050
|
+
if (written.tracked)
|
|
9051
|
+
console.error("The managed agy plugin is tracked; review its local machine paths before committing");
|
|
9052
|
+
console.error("Antigravity: pull-only TUI; native permissions unmanaged, no hub sandbox");
|
|
9053
|
+
const env = { ...ctx.helpers.nativeLaunchEnv("agy", ctx.helpers.childEnv()), AGENTHUB_STATE_DIR: stateDir, AGENTHUB_PROJECT_DIR: cwd };
|
|
9054
|
+
delete env.AGENTHUB_CHANNEL;
|
|
9055
|
+
const code = await runAgyTui(argv, cwd, env);
|
|
9056
|
+
if (code !== 0)
|
|
9057
|
+
process.exitCode = code;
|
|
9058
|
+
} finally {
|
|
9059
|
+
if (options) {
|
|
9060
|
+
await hub.request({ t: "native_hook_launch", op: "end", peer: "agy", launchId: options.launchId }).catch(() => {});
|
|
9061
|
+
if (pluginWritten)
|
|
9062
|
+
try {
|
|
9063
|
+
cleanupAgyMcp(options);
|
|
9064
|
+
} catch {
|
|
9065
|
+
console.error("Managed agy plugin cleanup refused: files changed or ownership could not be verified");
|
|
9066
|
+
}
|
|
9067
|
+
}
|
|
9068
|
+
hub.close();
|
|
9069
|
+
}
|
|
9070
|
+
}
|
|
9071
|
+
var init_entry = __esm(() => {
|
|
9072
|
+
init_agy_mcp();
|
|
9073
|
+
init_child_process();
|
|
9074
|
+
});
|
|
9075
|
+
|
|
9076
|
+
// src/adapters/definitions/native-cli-retained.ts
|
|
9077
|
+
function retainedLaunchWarning(status, peer, definition) {
|
|
9078
|
+
const keys = definition.cli.launchConfigKeys;
|
|
9079
|
+
const retained = status?.retainedConfig;
|
|
9080
|
+
if (!keys?.length || !Array.isArray(retained))
|
|
9081
|
+
return;
|
|
9082
|
+
const pending = retained.flatMap((row) => {
|
|
9083
|
+
if (!row || typeof row !== "object")
|
|
9084
|
+
return [];
|
|
9085
|
+
const key = row.key;
|
|
9086
|
+
return typeof key === "string" && keys.some((candidate) => key === candidate || key.startsWith(`${candidate}.`)) ? [key] : [];
|
|
9087
|
+
});
|
|
9088
|
+
const unique = [...new Set(pending)];
|
|
9089
|
+
return unique.length ? `ahub: config retained: ${unique.join(", ")}; starting ${peer} with the running value; run ahub settings reload to apply the file's value` : undefined;
|
|
9090
|
+
}
|
|
9091
|
+
|
|
7896
9092
|
// src/hub/claude-state.ts
|
|
7897
9093
|
function claudeStateFile(base, peer) {
|
|
7898
9094
|
return peer.includes("-") && PEER2.test(peer) ? `${base}.${peer}.json` : `${base}.json`;
|
|
@@ -7903,9 +9099,9 @@ var init_claude_state = __esm(() => {
|
|
|
7903
9099
|
});
|
|
7904
9100
|
|
|
7905
9101
|
// src/adapters/definitions/native-cli-entry-claude.ts
|
|
7906
|
-
import { readFileSync as
|
|
7907
|
-
import { join as
|
|
7908
|
-
import { randomUUID as
|
|
9102
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
9103
|
+
import { join as join15 } from "path";
|
|
9104
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
7909
9105
|
async function runClaudeEntry(ctx) {
|
|
7910
9106
|
const { args, cwd, stateDir, cliDir, unattendedEnv, projectConfig, fail } = ctx;
|
|
7911
9107
|
const peer = ctx.instance ?? "claude";
|
|
@@ -7914,6 +9110,10 @@ async function runClaudeEntry(ctx) {
|
|
|
7914
9110
|
return console.log(JSON.stringify({ peer, start: "hub", note: "a headless reviewer the hub runs per review" }, null, 2));
|
|
7915
9111
|
const hub = await ctx.connect();
|
|
7916
9112
|
try {
|
|
9113
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9114
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9115
|
+
if (warning)
|
|
9116
|
+
console.error(warning);
|
|
7917
9117
|
const res = await hub.request({ t: "start", peer, operationId: process.env.AGENTHUB_RECOVERY_OPERATION });
|
|
7918
9118
|
if (res.ok !== true)
|
|
7919
9119
|
fail(String(res.error ?? `${peer} was not started`));
|
|
@@ -7935,20 +9135,20 @@ async function runClaudeEntry(ctx) {
|
|
|
7935
9135
|
process.env.AGENTHUB_INSTANCE_ID = control.instanceId;
|
|
7936
9136
|
const terminal = await recordTerminalLaunch(peer, cwd, stateDir, control.instanceId, ctx.instance ? { executionCwd: cwd } : undefined);
|
|
7937
9137
|
if (!terminal)
|
|
7938
|
-
process.env.AGENTHUB_LAUNCH_ID =
|
|
9138
|
+
process.env.AGENTHUB_LAUNCH_ID = randomUUID6();
|
|
7939
9139
|
}
|
|
7940
9140
|
let original;
|
|
7941
|
-
for (const file of [
|
|
9141
|
+
for (const file of [join15(cwd, ".claude", "settings.local.json"), join15(cwd, ".claude", "settings.json"), join15(process.env.HOME ?? "", ".claude", "settings.json")]) {
|
|
7942
9142
|
try {
|
|
7943
|
-
original ??= JSON.parse(
|
|
9143
|
+
original ??= JSON.parse(readFileSync7(file, "utf8")).statusLine;
|
|
7944
9144
|
} catch {}
|
|
7945
9145
|
}
|
|
7946
|
-
const facts = claudeObservationHooks(projectConfig(), { script:
|
|
9146
|
+
const facts = claudeObservationHooks(projectConfig(), { script: join15(cliDir, "facts-hook.ts"), stateDir });
|
|
7947
9147
|
let previousLaunch;
|
|
7948
9148
|
try {
|
|
7949
|
-
previousLaunch = JSON.parse(
|
|
9149
|
+
previousLaunch = JSON.parse(readFileSync7(join15(stateDir, claudeStateFile("claude-launch", peer)), "utf8"));
|
|
7950
9150
|
} catch {}
|
|
7951
|
-
const launch = buildLaunch("claude", args, { unattended: unattendedEnv, statusLine: { script:
|
|
9151
|
+
const launch = buildLaunch("claude", args, { unattended: unattendedEnv, statusLine: { script: join15(cliDir, "statusline-tee.ts"), stateDir, ...original ? { original } : {} }, ...facts ? { facts } : {} });
|
|
7952
9152
|
let result;
|
|
7953
9153
|
try {
|
|
7954
9154
|
let launchRecord;
|
|
@@ -7992,6 +9192,10 @@ async function runCodexEntry(ctx) {
|
|
|
7992
9192
|
if (recovering && !ctx.instance && before?.instanceId)
|
|
7993
9193
|
await recordTerminalLaunch(peer, cwd, stateDir, before.instanceId);
|
|
7994
9194
|
const hub = await connect();
|
|
9195
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9196
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9197
|
+
if (warning)
|
|
9198
|
+
console.error(warning);
|
|
7995
9199
|
const res = await hub.request({ t: "start", peer, args: { unattended: launch0.unattended === true }, operationId: process.env.AGENTHUB_RECOVERY_OPERATION });
|
|
7996
9200
|
hub.close();
|
|
7997
9201
|
if (!res.ok)
|
|
@@ -8004,43 +9208,99 @@ async function runCodexEntry(ctx) {
|
|
|
8004
9208
|
const control = readControl(stateDir);
|
|
8005
9209
|
if ((!recovering || ctx.instance) && control?.instanceId)
|
|
8006
9210
|
await recordTerminalLaunch(peer, cwd, stateDir, control.instanceId, ctx.instance ? { executionCwd: ctx.executionCwd } : undefined);
|
|
8007
|
-
const
|
|
9211
|
+
const runningCodexBin = res.retainedLaunchConfig?.codex_bin;
|
|
9212
|
+
const launch = buildLaunch("codex", args, { unattended: unattendedEnv, instance: peer, proxyUrl: res.proxyUrl, codexBin: typeof runningCodexBin === "string" ? runningCodexBin : projectConfig().codex_bin });
|
|
8008
9213
|
if (launch0.warning)
|
|
8009
9214
|
console.error(launch0.warning);
|
|
8010
9215
|
exec(launch.cmd, launch.args, "codex");
|
|
8011
9216
|
}
|
|
8012
|
-
|
|
8013
|
-
// src/hub/status-roster.ts
|
|
8014
|
-
function rosterInStatus(status, peer) {
|
|
8015
|
-
const rows = status?.roster;
|
|
8016
|
-
if (!Array.isArray(rows))
|
|
8017
|
-
return;
|
|
8018
|
-
const row = rows.find((r) => !!r && typeof r === "object" && r.peer === peer);
|
|
8019
|
-
return typeof row?.on === "boolean" ? row.on : undefined;
|
|
8020
|
-
}
|
|
8021
|
-
function bundleInStatus(status) {
|
|
8022
|
-
const value = status?.pluginBundle;
|
|
8023
|
-
return typeof value === "string" && value.startsWith("/") && /[\\/]plugins[\\/]agent-hub[\\/]server\.js$/.test(value) ? value : undefined;
|
|
8024
|
-
}
|
|
9217
|
+
var init_native_cli_entry_codex = () => {};
|
|
8025
9218
|
|
|
8026
9219
|
// src/adapters/definitions/native-cli-entry-jcode.ts
|
|
8027
|
-
import { resolve as
|
|
9220
|
+
import { resolve as resolve12 } from "path";
|
|
9221
|
+
async function waitForJcodeTui(hub, fail, signal, options, first) {
|
|
9222
|
+
let binding = first?.binding, announced = false, current = first;
|
|
9223
|
+
while (!signal.aborted) {
|
|
9224
|
+
if (!current) {
|
|
9225
|
+
const reply = await hub.request({ t: "native_tui_launch", peer: "jcode", binding, args: options });
|
|
9226
|
+
if (signal.aborted)
|
|
9227
|
+
return;
|
|
9228
|
+
if (!reply.ok || !reply.binding || !["waiting", "ready"].includes(reply.state))
|
|
9229
|
+
fail("Jcode launch changed or its native saved session is unavailable; retry from a terminal");
|
|
9230
|
+
current = reply;
|
|
9231
|
+
}
|
|
9232
|
+
if (binding && (binding.launchId !== current.binding.launchId || binding.sessionId !== current.binding.sessionId))
|
|
9233
|
+
fail("Jcode launch changed while waiting");
|
|
9234
|
+
binding = current.binding;
|
|
9235
|
+
if (current.state === "ready")
|
|
9236
|
+
return current;
|
|
9237
|
+
if (!announced) {
|
|
9238
|
+
console.log(WAITING);
|
|
9239
|
+
announced = true;
|
|
9240
|
+
}
|
|
9241
|
+
await new Promise((resolve) => {
|
|
9242
|
+
const finish = () => {
|
|
9243
|
+
clearTimeout(timer);
|
|
9244
|
+
signal.removeEventListener("abort", finish);
|
|
9245
|
+
resolve();
|
|
9246
|
+
};
|
|
9247
|
+
const timer = setTimeout(finish, 200);
|
|
9248
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
9249
|
+
if (signal.aborted)
|
|
9250
|
+
finish();
|
|
9251
|
+
});
|
|
9252
|
+
current = undefined;
|
|
9253
|
+
}
|
|
9254
|
+
}
|
|
8028
9255
|
async function runJcodeEntry(ctx) {
|
|
8029
9256
|
const { args, cwd, stateDir, cliDir, unattendedEnv, connect, fail, takeFlags } = ctx;
|
|
8030
|
-
const { launcherPreview, qwenSessionId, configureJcodeMcp } = ctx.helpers;
|
|
8031
|
-
|
|
9257
|
+
const { launcherPreview, qwenSessionId, configureJcodeMcp, readControl, startModeOf } = ctx.helpers;
|
|
9258
|
+
const print = args.includes("--print-command");
|
|
9259
|
+
if (args.includes("--dry-run") || print) {
|
|
9260
|
+
if (!print || args.includes("--dry-run") || !ctx.person) {
|
|
9261
|
+
try {
|
|
9262
|
+
return console.log(JSON.stringify(launcherPreview("jcode", args, cwd, stateDir, unattendedEnv), null, 2));
|
|
9263
|
+
} catch {
|
|
9264
|
+
fail("cannot preview Jcode launch: invalid arguments or configuration (details withheld)");
|
|
9265
|
+
}
|
|
9266
|
+
}
|
|
9267
|
+
}
|
|
9268
|
+
const { one, rest } = takeFlags(args.filter((arg) => arg !== "--print-command"), ["--mode", "--model", "--provider-profile", "--session-id"], []);
|
|
9269
|
+
if (rest.length || one["--mode"] !== undefined && !["headless", "tui"].includes(one["--mode"]))
|
|
9270
|
+
fail("usage: ahub jcode [--mode tui|headless] [--model <alias>] [--provider-profile <name>] [--session-id <id>]");
|
|
9271
|
+
if (one["--session-id"] !== undefined && !qwenSessionId(one["--session-id"]))
|
|
9272
|
+
fail("invalid Jcode session identity");
|
|
9273
|
+
const control = readControl(stateDir);
|
|
9274
|
+
const mode = one["--mode"] ?? (ctx.instance ? "headless" : control?.startModes?.jcode ?? startModeOf(ctx.projectConfig().peers, "jcode"));
|
|
9275
|
+
if (print && (mode === "headless" || !control?.features?.includes("native_tui_launch"))) {
|
|
8032
9276
|
try {
|
|
8033
9277
|
return console.log(JSON.stringify(launcherPreview("jcode", args, cwd, stateDir, unattendedEnv), null, 2));
|
|
8034
9278
|
} catch {
|
|
8035
|
-
fail("cannot preview Jcode launch: invalid arguments or configuration (details withheld)");
|
|
9279
|
+
return fail("cannot preview Jcode launch: invalid arguments or configuration (details withheld)");
|
|
8036
9280
|
}
|
|
8037
9281
|
}
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
9282
|
+
if (mode === "tui" && !ctx.person)
|
|
9283
|
+
fail("Jcode's private TUI is person-only; run ahub jcode in your terminal");
|
|
9284
|
+
const hub = await connect(), abort = new AbortController;
|
|
9285
|
+
let interrupted = false;
|
|
9286
|
+
const interruptedWait = () => {
|
|
9287
|
+
if (abort.signal.aborted && !interrupted)
|
|
9288
|
+
fail("hub connection closed while waiting for Jcode; retry from a terminal");
|
|
9289
|
+
return abort.signal.aborted;
|
|
9290
|
+
};
|
|
9291
|
+
const interrupt = () => {
|
|
9292
|
+
interrupted = true;
|
|
9293
|
+
abort.abort();
|
|
9294
|
+
hub.close();
|
|
9295
|
+
process.exitCode = 130;
|
|
9296
|
+
};
|
|
9297
|
+
process.once("SIGINT", interrupt);
|
|
9298
|
+
const oldClose = hub.onClose;
|
|
9299
|
+
hub.onClose = (code, reason) => {
|
|
9300
|
+
if (!abort.signal.aborted)
|
|
9301
|
+
abort.abort();
|
|
9302
|
+
oldClose?.(code, reason);
|
|
9303
|
+
};
|
|
8044
9304
|
try {
|
|
8045
9305
|
const snapshot = (await hub.request({ t: "status" })).status;
|
|
8046
9306
|
const rostered = rosterInStatus(snapshot, "jcode");
|
|
@@ -8048,17 +9308,63 @@ async function runJcodeEntry(ctx) {
|
|
|
8048
9308
|
fail("cannot read jcode's roster membership from the running hub; upgrade or restart it, then retry (nothing was started)");
|
|
8049
9309
|
if (!rostered)
|
|
8050
9310
|
fail("jcode is off roster; add it with ahub roster add jcode");
|
|
8051
|
-
const
|
|
9311
|
+
const warning = retainedLaunchWarning(snapshot, "jcode", ctx.definition);
|
|
9312
|
+
if (warning)
|
|
9313
|
+
console.error(warning);
|
|
9314
|
+
const options = { model: one["--model"], providerProfile: one["--provider-profile"], sessionId: one["--session-id"] };
|
|
9315
|
+
if (print) {
|
|
9316
|
+
if (!hub.features.includes("native_tui_launch") || mode !== "tui")
|
|
9317
|
+
return console.log(JSON.stringify(launcherPreview("jcode", args, cwd, stateDir, unattendedEnv), null, 2));
|
|
9318
|
+
const reply = await hub.request({ t: "native_tui_launch", peer: "jcode", args: options });
|
|
9319
|
+
if (!reply.ok || reply.state !== "ready")
|
|
9320
|
+
fail("Jcode's selected native session is not saved and ready; run ahub jcode to wait for its first hub turn");
|
|
9321
|
+
return console.log(JSON.stringify(reply.launch, null, 2));
|
|
9322
|
+
}
|
|
9323
|
+
if (mode === "tui") {
|
|
9324
|
+
if (!hub.features.includes("native_tui_launch"))
|
|
9325
|
+
fail("the running hub does not support Jcode TUI launch; upgrade it before retrying");
|
|
9326
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
9327
|
+
if (one["--mode"] || Object.values(options).some((value) => value !== undefined))
|
|
9328
|
+
fail("run ahub jcode with these options in a terminal");
|
|
9329
|
+
const reply = await hub.request({ t: "peer_start", peer: "jcode" });
|
|
9330
|
+
if (!reply.ok)
|
|
9331
|
+
fail(reply.error);
|
|
9332
|
+
console.log(reply.command ?? "Jcode terminal requested");
|
|
9333
|
+
return;
|
|
9334
|
+
}
|
|
9335
|
+
}
|
|
9336
|
+
const written = configureJcodeMcp(cwd, stateDir, bundleInStatus(snapshot) ?? resolve12(cliDir, "../../plugins/agent-hub/server.js"));
|
|
8052
9337
|
if (written.tracked)
|
|
8053
9338
|
console.error(".jcode/mcp.json is tracked; review this local machine-path change before committing it");
|
|
8054
|
-
const reply = await hub.request({ t: "start", peer: "jcode", args: { mode
|
|
9339
|
+
const reply = await hub.request({ t: "start", peer: "jcode", args: { mode, ...options }, operationId: process.env.AGENTHUB_RECOVERY_OPERATION }, 65000);
|
|
9340
|
+
if (interruptedWait())
|
|
9341
|
+
return;
|
|
8055
9342
|
if (!reply.ok)
|
|
8056
9343
|
fail(reply.error);
|
|
8057
|
-
|
|
9344
|
+
if (mode === "headless") {
|
|
9345
|
+
console.log(reply.already ? "jcode is already attached" : "jcode attached (headless); native policy unmanaged, NO hub sandbox");
|
|
9346
|
+
return;
|
|
9347
|
+
}
|
|
9348
|
+
const ready = await waitForJcodeTui(hub, fail, abort.signal, options);
|
|
9349
|
+
if (interruptedWait() || !ready)
|
|
9350
|
+
return;
|
|
9351
|
+
const instanceId = typeof snapshot?.instanceId === "string" ? snapshot.instanceId : undefined;
|
|
9352
|
+
if (!instanceId)
|
|
9353
|
+
return fail("cannot bind Jcode's terminal to the running hub");
|
|
9354
|
+
await ctx.helpers.recordTerminalLaunch("jcode", cwd, stateDir, instanceId);
|
|
9355
|
+
const confirmed = await hub.request({ t: "native_tui_launch", peer: "jcode", binding: ready.binding, args: options });
|
|
9356
|
+
if (interruptedWait())
|
|
9357
|
+
return;
|
|
9358
|
+
if (!confirmed.ok || confirmed.state !== "ready")
|
|
9359
|
+
fail("Jcode launch changed before its terminal opened");
|
|
9360
|
+
ctx.execWithEnv(confirmed.launch.cmd, confirmed.launch.args, { ...ctx.helpers.childEnv(), ...confirmed.launch.env });
|
|
8058
9361
|
} finally {
|
|
9362
|
+
process.removeListener("SIGINT", interrupt);
|
|
9363
|
+
hub.onClose = oldClose;
|
|
8059
9364
|
hub.close();
|
|
8060
9365
|
}
|
|
8061
9366
|
}
|
|
9367
|
+
var WAITING = "jcode's TUI opens after its first hub turn; send it work, or Ctrl-C";
|
|
8062
9368
|
var init_native_cli_entry_jcode = () => {};
|
|
8063
9369
|
|
|
8064
9370
|
// src/adapters/definitions/native-cli-entry-qwen.ts
|
|
@@ -8081,6 +9387,10 @@ async function runQwenEntry(ctx) {
|
|
|
8081
9387
|
fail("Qwen TUI is not offered before verification; usage: ahub qwen [--mode headless] [--model <alias>] [--session-id <id>]");
|
|
8082
9388
|
const hub = await connect();
|
|
8083
9389
|
try {
|
|
9390
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9391
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9392
|
+
if (warning)
|
|
9393
|
+
console.error(warning);
|
|
8084
9394
|
const reply = await hub.request({ t: "start", peer, args: { mode: "headless", ...one["--model"] ? { model: one["--model"] } : {}, ...one["--session-id"] ? { sessionId: one["--session-id"] } : {} }, operationId: process.env.AGENTHUB_RECOVERY_OPERATION }, 35000);
|
|
8085
9395
|
if (!reply.ok)
|
|
8086
9396
|
fail(reply.error);
|
|
@@ -8089,14 +9399,15 @@ async function runQwenEntry(ctx) {
|
|
|
8089
9399
|
hub.close();
|
|
8090
9400
|
}
|
|
8091
9401
|
}
|
|
9402
|
+
var init_native_cli_entry_qwen = () => {};
|
|
8092
9403
|
|
|
8093
9404
|
// src/adapters/definitions/native-cli-entry-kimi.ts
|
|
8094
|
-
import { resolve as
|
|
8095
|
-
import { spawnSync as
|
|
9405
|
+
import { resolve as resolve13 } from "path";
|
|
9406
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
8096
9407
|
async function runKimiEntry(ctx) {
|
|
8097
9408
|
const peer = ctx.instance ?? "kimi";
|
|
8098
9409
|
const { args, cwd, stateDir, cliDir, unattendedEnv, projectConfig, connect, fail, takeFlags } = ctx;
|
|
8099
|
-
const { launcherPreview, startModeOf, buildKimiTuiLaunch, configureKimiMcp, nativeLaunchEnv, childEnv } = ctx.helpers;
|
|
9410
|
+
const { launcherPreview, readControl, startModeOf, buildKimiTuiLaunch, configureKimiMcp, nativeLaunchEnv, childEnv } = ctx.helpers;
|
|
8100
9411
|
if (args.includes("--print-command") || args.includes("--dry-run")) {
|
|
8101
9412
|
try {
|
|
8102
9413
|
return console.log(JSON.stringify(launcherPreview("kimi", args, cwd, stateDir, unattendedEnv), null, 2));
|
|
@@ -8105,7 +9416,8 @@ async function runKimiEntry(ctx) {
|
|
|
8105
9416
|
}
|
|
8106
9417
|
}
|
|
8107
9418
|
const { one, rest } = takeFlags(args, ["--mode", "--model"], []);
|
|
8108
|
-
const
|
|
9419
|
+
const acceptedMode = readControl(stateDir)?.startModes?.[peer];
|
|
9420
|
+
const mode = one["--mode"] ?? (ctx.instance ? "headless" : acceptedMode ?? startModeOf(projectConfig().peers, "kimi")), model = one["--model"];
|
|
8109
9421
|
if (ctx.instance && mode !== "headless")
|
|
8110
9422
|
fail("extra Kimi instances require headless mode");
|
|
8111
9423
|
if (rest.length || !["tui", "headless"].includes(mode))
|
|
@@ -8115,6 +9427,10 @@ async function runKimiEntry(ctx) {
|
|
|
8115
9427
|
fail("Kimi's TUI needs a terminal for these options; run in a terminal or add --mode headless");
|
|
8116
9428
|
const hub = await connect();
|
|
8117
9429
|
try {
|
|
9430
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9431
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9432
|
+
if (warning)
|
|
9433
|
+
console.error(warning);
|
|
8118
9434
|
const reply = await hub.request({ t: "peer_start", peer }, 35000);
|
|
8119
9435
|
if (!reply.ok)
|
|
8120
9436
|
fail(reply.error);
|
|
@@ -8126,25 +9442,29 @@ async function runKimiEntry(ctx) {
|
|
|
8126
9442
|
const hub = await connect();
|
|
8127
9443
|
try {
|
|
8128
9444
|
if (mode === "tui") {
|
|
8129
|
-
const launch = buildKimiTuiLaunch(projectConfig().kimi_cmd, model);
|
|
8130
9445
|
const snapshot = (await hub.request({ t: "status" })).status;
|
|
8131
9446
|
const rostered = rosterInStatus(snapshot, peer);
|
|
8132
9447
|
if (rostered === undefined)
|
|
8133
9448
|
fail("cannot read kimi's roster membership from the running hub; upgrade or restart it, then retry (nothing was started)");
|
|
8134
9449
|
if (!rostered)
|
|
8135
9450
|
fail("kimi is off roster; add it with ahub roster add kimi");
|
|
9451
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9452
|
+
if (warning)
|
|
9453
|
+
console.error(warning);
|
|
8136
9454
|
const owner = snapshot?.peers?.[peer];
|
|
8137
9455
|
if (owner && !["offline"].includes(owner.state) && !owner.pullOnly)
|
|
8138
9456
|
fail("Kimi is running headless; run ahub stop kimi before starting its TUI");
|
|
8139
9457
|
const admission = await hub.request({ t: "start", peer, args: { mode: "tui" }, operationId: process.env.AGENTHUB_RECOVERY_OPERATION });
|
|
8140
9458
|
if (!admission.ok)
|
|
8141
9459
|
fail(admission.error);
|
|
9460
|
+
const runningCommand = admission.retainedLaunchConfig?.kimi_cmd;
|
|
9461
|
+
const launch = buildKimiTuiLaunch(Array.isArray(runningCommand) && runningCommand.every((arg) => typeof arg === "string") ? runningCommand : projectConfig().kimi_cmd, model);
|
|
8142
9462
|
let result;
|
|
8143
9463
|
try {
|
|
8144
|
-
const written = configureKimiMcp(cwd, stateDir, bundleInStatus(snapshot) ??
|
|
9464
|
+
const written = configureKimiMcp(cwd, stateDir, bundleInStatus(snapshot) ?? resolve13(cliDir, "../../plugins/agent-hub/server.js"), "tui");
|
|
8145
9465
|
if (written.tracked)
|
|
8146
9466
|
console.error(".kimi-code/mcp.json is tracked; the agent-hub entry contains machine paths. Review this local change before committing it.");
|
|
8147
|
-
result =
|
|
9467
|
+
result = spawnSync5(launch.cmd, launch.args, { cwd, stdio: "inherit", env: { ...nativeLaunchEnv("kimi", childEnv()), AGENTHUB_STATE_DIR: stateDir, AGENTHUB_PROJECT_DIR: cwd } });
|
|
8148
9468
|
} finally {
|
|
8149
9469
|
if (admission.admissionId)
|
|
8150
9470
|
await hub.request({ t: "kimi_launch_end", admissionId: admission.admissionId }).catch(() => {});
|
|
@@ -8153,11 +9473,15 @@ async function runKimiEntry(ctx) {
|
|
|
8153
9473
|
fail(`cannot run ${launch.cmd}: ${result.error.message}`);
|
|
8154
9474
|
process.exit(result.status ?? 1);
|
|
8155
9475
|
}
|
|
8156
|
-
const
|
|
9476
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9477
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9478
|
+
if (warning)
|
|
9479
|
+
console.error(warning);
|
|
9480
|
+
const owner = snapshot?.peers?.[peer];
|
|
8157
9481
|
if (owner?.pullOnly && owner.state !== "offline")
|
|
8158
9482
|
fail("Kimi runs in a terminal; end it there before starting a headless one");
|
|
8159
9483
|
if (!ctx.instance)
|
|
8160
|
-
configureKimiMcp(cwd, stateDir,
|
|
9484
|
+
configureKimiMcp(cwd, stateDir, resolve13(cliDir, "../../plugins/agent-hub/server.js"), "headless");
|
|
8161
9485
|
const res = await hub.request({ t: "start", peer, args: { mode: "headless", model }, operationId: process.env.AGENTHUB_RECOVERY_OPERATION });
|
|
8162
9486
|
if (!res.ok)
|
|
8163
9487
|
fail(res.error);
|
|
@@ -8224,7 +9548,8 @@ async function runPiEntry(ctx) {
|
|
|
8224
9548
|
}
|
|
8225
9549
|
}
|
|
8226
9550
|
const flags = parsePiFlags(args, fail);
|
|
8227
|
-
const
|
|
9551
|
+
const acceptedMode = ctx.helpers.readControl(stateDir)?.startModes?.[peer];
|
|
9552
|
+
const options = { ...flags, mode: flags.mode ?? (ctx.instance ? "headless" : acceptedMode ?? startModeOf(projectConfig().peers, "pi")) };
|
|
8228
9553
|
if (ctx.instance && options.mode !== "headless")
|
|
8229
9554
|
fail("extra Pi instances require headless mode");
|
|
8230
9555
|
if (flags.mode === undefined && options.mode === "tui" && !(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
@@ -8232,6 +9557,10 @@ async function runPiEntry(ctx) {
|
|
|
8232
9557
|
fail("Pi's TUI needs a terminal, and these options are not passed to one the hub opens: run this in a terminal, or add --mode headless");
|
|
8233
9558
|
const hub = await connect();
|
|
8234
9559
|
try {
|
|
9560
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9561
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9562
|
+
if (warning)
|
|
9563
|
+
console.error(warning);
|
|
8235
9564
|
const reply = await hub.request({ t: "peer_start", peer }, 35000);
|
|
8236
9565
|
if (reply.ok !== true) {
|
|
8237
9566
|
const error = String(reply.error ?? "pi start refused");
|
|
@@ -8247,6 +9576,10 @@ async function runPiEntry(ctx) {
|
|
|
8247
9576
|
if (recovering && before?.instanceId)
|
|
8248
9577
|
await recordTerminalLaunch(peer, cwd, stateDir, before.instanceId);
|
|
8249
9578
|
const hub = await connect();
|
|
9579
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9580
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9581
|
+
if (warning)
|
|
9582
|
+
console.error(warning);
|
|
8250
9583
|
const res = await hub.request({ t: "start", peer, args: options, operationId: process.env.AGENTHUB_RECOVERY_OPERATION });
|
|
8251
9584
|
hub.close();
|
|
8252
9585
|
if (!res.ok)
|
|
@@ -8280,21 +9613,31 @@ async function runLocalEntry(ctx) {
|
|
|
8280
9613
|
if (args.some((a, i) => i % 2 === 0 && !["--route", "--model"].includes(a)))
|
|
8281
9614
|
fail("usage: ahub local [--route <id> | --model <id>]");
|
|
8282
9615
|
const hub = await connect();
|
|
9616
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9617
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9618
|
+
if (warning)
|
|
9619
|
+
console.error(warning);
|
|
8283
9620
|
const res = await hub.request({ t: "start", peer, args: { route: opt("--route"), model: opt("--model") }, operationId: process.env.AGENTHUB_RECOVERY_OPERATION });
|
|
8284
9621
|
hub.close();
|
|
8285
9622
|
if (!res.ok)
|
|
8286
9623
|
fail(res.error);
|
|
8287
9624
|
console.log(res.already ? `${peer} is already attached` : `${peer} attached on ${res.model}. Give it work with: ahub say @${peer} "..."`);
|
|
8288
9625
|
}
|
|
9626
|
+
var init_native_cli_entry_local = () => {};
|
|
8289
9627
|
|
|
8290
9628
|
// src/adapters/definitions/native-cli-entry.ts
|
|
8291
9629
|
var builtinCliEntries;
|
|
8292
9630
|
var init_native_cli_entry = __esm(() => {
|
|
9631
|
+
init_entry();
|
|
8293
9632
|
init_native_cli_entry_claude();
|
|
9633
|
+
init_native_cli_entry_codex();
|
|
8294
9634
|
init_native_cli_entry_jcode();
|
|
9635
|
+
init_native_cli_entry_qwen();
|
|
8295
9636
|
init_native_cli_entry_kimi();
|
|
8296
9637
|
init_native_cli_entry_pi();
|
|
9638
|
+
init_native_cli_entry_local();
|
|
8297
9639
|
builtinCliEntries = Object.freeze([
|
|
9640
|
+
{ command: "agy", lifecycleAdmission: true, execute: runAgyEntry },
|
|
8298
9641
|
{ command: "claude", lifecycleAdmission: true, execute: runClaudeEntry },
|
|
8299
9642
|
{ command: "codex", lifecycleAdmission: true, execute: runCodexEntry },
|
|
8300
9643
|
{ command: "jcode", lifecycleAdmission: true, execute: runJcodeEntry },
|
|
@@ -8305,37 +9648,9 @@ var init_native_cli_entry = __esm(() => {
|
|
|
8305
9648
|
]);
|
|
8306
9649
|
});
|
|
8307
9650
|
|
|
8308
|
-
// src/adapters/definitions/native-cli-preview-common.ts
|
|
8309
|
-
function publicArg(arg) {
|
|
8310
|
-
const flags = new Set(["--settings", "--model", "--mcp-config", "--session-id", "--session-file", "--resume", "--continue", "--print", "--safe", "--new"]);
|
|
8311
|
-
const key = arg.split("=", 1)[0];
|
|
8312
|
-
return flags.has(key) ? arg.includes("=") ? key + "=" + REDACTED : key : REDACTED;
|
|
8313
|
-
}
|
|
8314
|
-
function nativeCommand(command, trusted) {
|
|
8315
|
-
return command.map((value, i) => value === trusted[i] ? value : REDACTED);
|
|
8316
|
-
}
|
|
8317
|
-
function previewState(args, baseEnvNames) {
|
|
8318
|
-
const unresolved = [];
|
|
8319
|
-
const envNames = [...baseEnvNames];
|
|
8320
|
-
const value = (flag) => {
|
|
8321
|
-
const at = args.indexOf(flag);
|
|
8322
|
-
if (at < 0)
|
|
8323
|
-
return;
|
|
8324
|
-
const next = args[at + 1];
|
|
8325
|
-
if (!next || next.startsWith("--"))
|
|
8326
|
-
throw new Error("missing option value");
|
|
8327
|
-
return next;
|
|
8328
|
-
};
|
|
8329
|
-
return { unresolved, envNames, value };
|
|
8330
|
-
}
|
|
8331
|
-
function finishPreview(ctx, launch, envNames, unresolved, metadata, settings) {
|
|
8332
|
-
return { tool: ctx.tool, ...metadata ?? {}, cmd: launch.cmd, args: launch.args, settings, envNames: [...new Set(envNames)].sort(), ...launch.warning ? { warning: launch.warning } : {}, unresolved, redaction: "Arbitrary user arguments, settings, commands and configured executable overrides are redacted. Environment values are never displayed.", needs: ["Native executable, accounts and runtime readiness are not checked."] };
|
|
8333
|
-
}
|
|
8334
|
-
var REDACTED = "[redacted]", UNRESOLVED = "[unresolved]";
|
|
8335
|
-
|
|
8336
9651
|
// src/adapters/definitions/native-cli-preview-claude.ts
|
|
8337
|
-
import { readFileSync as
|
|
8338
|
-
import { join as
|
|
9652
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
9653
|
+
import { join as join16 } from "path";
|
|
8339
9654
|
function previewClaude(ctx) {
|
|
8340
9655
|
const { args, cwd, stateDir, cliDir, unattended, config } = ctx;
|
|
8341
9656
|
const { buildLaunch, claudeObservationHooks } = ctx.helpers;
|
|
@@ -8343,13 +9658,13 @@ function previewClaude(ctx) {
|
|
|
8343
9658
|
let envNames = state.envNames;
|
|
8344
9659
|
let launch;
|
|
8345
9660
|
let original;
|
|
8346
|
-
for (const file of [
|
|
9661
|
+
for (const file of [join16(cwd, ".claude/settings.local.json"), join16(cwd, ".claude/settings.json"), join16(process.env.HOME ?? "", ".claude/settings.json")]) {
|
|
8347
9662
|
try {
|
|
8348
|
-
original ??= JSON.parse(
|
|
9663
|
+
original ??= JSON.parse(readFileSync8(file, "utf8")).statusLine;
|
|
8349
9664
|
} catch {}
|
|
8350
9665
|
}
|
|
8351
|
-
const facts = claudeObservationHooks(config, { script:
|
|
8352
|
-
launch = buildLaunch("claude", args, { preview: true, unattended, statusLine: { script:
|
|
9666
|
+
const facts = claudeObservationHooks(config, { script: join16(cliDir, "facts-hook.ts"), stateDir });
|
|
9667
|
+
launch = buildLaunch("claude", args, { preview: true, unattended, statusLine: { script: join16(cliDir, "statusline-tee.ts"), stateDir, ...original ? { original: { command: original.command ? REDACTED : "", ...typeof original.refreshInterval === "number" && Number.isFinite(original.refreshInterval) ? { refreshInterval: original.refreshInterval } : {}, ...typeof original.padding === "number" && Number.isFinite(original.padding) ? { padding: original.padding } : {} } } : {} }, ...facts ? { facts } : {} });
|
|
8353
9668
|
const safeArgs = args.map((arg, i) => {
|
|
8354
9669
|
if (args[i - 1] === "--settings")
|
|
8355
9670
|
return "{}";
|
|
@@ -8360,7 +9675,7 @@ function previewClaude(ctx) {
|
|
|
8360
9675
|
return publicArg(arg);
|
|
8361
9676
|
});
|
|
8362
9677
|
const publicLaunch = buildLaunch("claude", safeArgs, { preview: true, unattended, statusLine: {
|
|
8363
|
-
script:
|
|
9678
|
+
script: join16(cliDir, "statusline-tee.ts"),
|
|
8364
9679
|
stateDir,
|
|
8365
9680
|
...original ? { original: { command: original.command ? REDACTED : "", ...typeof original.refreshInterval === "number" && Number.isFinite(original.refreshInterval) ? { refreshInterval: original.refreshInterval } : {}, ...typeof original.padding === "number" && Number.isFinite(original.padding) ? { padding: original.padding } : {} } } : {}
|
|
8366
9681
|
}, facts });
|
|
@@ -8393,7 +9708,7 @@ function previewCodex(ctx) {
|
|
|
8393
9708
|
var init_native_cli_preview_codex = () => {};
|
|
8394
9709
|
|
|
8395
9710
|
// src/adapters/definitions/native-cli-preview-kimi.ts
|
|
8396
|
-
import { join as
|
|
9711
|
+
import { join as join17 } from "path";
|
|
8397
9712
|
function previewKimi(ctx) {
|
|
8398
9713
|
const { args, cwd, stateDir, cliDir, config } = ctx;
|
|
8399
9714
|
const { childEnv, startModeOf, buildKimiLaunch, buildKimiTuiLaunch, nativeLaunchEnv, kimiMcpPlan } = ctx.helpers;
|
|
@@ -8416,14 +9731,14 @@ function previewKimi(ctx) {
|
|
|
8416
9731
|
launch = { ...native, cmd: native.cmd === "kimi" ? "kimi" : REDACTED, args: native.args.map((arg) => arg === "--model" || arg === REDACTED ? arg : REDACTED) };
|
|
8417
9732
|
envNames = Object.keys(nativeLaunchEnv("kimi", childEnv())).concat(["AGENTHUB_STATE_DIR", "AGENTHUB_PROJECT_DIR"]);
|
|
8418
9733
|
}
|
|
8419
|
-
const { file, action, changed, tracked } = kimiMcpPlan(cwd, stateDir,
|
|
9734
|
+
const { file, action, changed, tracked } = kimiMcpPlan(cwd, stateDir, join17(cliDir, "../../plugins/agent-hub/server.js"), mode);
|
|
8420
9735
|
kimiMetadata = { mode, mcp: { file, action, changed, tracked } };
|
|
8421
9736
|
return finishPreview(ctx, launch, envNames, unresolved, kimiMetadata);
|
|
8422
9737
|
}
|
|
8423
9738
|
var init_native_cli_preview_kimi = () => {};
|
|
8424
9739
|
|
|
8425
9740
|
// src/adapters/definitions/native-cli-preview-pi.ts
|
|
8426
|
-
import { join as
|
|
9741
|
+
import { join as join18, resolve as resolve14 } from "path";
|
|
8427
9742
|
function previewPi(ctx) {
|
|
8428
9743
|
const { args, stateDir, cliDir, config } = ctx;
|
|
8429
9744
|
const { relayModelIds, buildPiLaunch, startModeOf } = ctx.helpers;
|
|
@@ -8458,7 +9773,7 @@ function previewPi(ctx) {
|
|
|
8458
9773
|
tools: [],
|
|
8459
9774
|
preamble: REDACTED,
|
|
8460
9775
|
maxSteps: config.pi.max_steps
|
|
8461
|
-
}, {},
|
|
9776
|
+
}, {}, resolve14(join18(cliDir, "../pi/extension.ts")), UNRESOLVED, UNRESOLVED);
|
|
8462
9777
|
launch = pi;
|
|
8463
9778
|
envNames = Object.keys(pi.env).concat(["PATH", "HOME", "USER", "SHELL", "TMPDIR", "TERM", "TERM_PROGRAM", "LANG", "LC_ALL", "LC_CTYPE", "NO_COLOR", "CODEX_HOME"].filter((key) => process.env[key] !== undefined));
|
|
8464
9779
|
if (mode === "tui")
|
|
@@ -8471,7 +9786,7 @@ function previewPi(ctx) {
|
|
|
8471
9786
|
var init_native_cli_preview_pi = () => {};
|
|
8472
9787
|
|
|
8473
9788
|
// src/adapters/definitions/native-cli-preview-qwen.ts
|
|
8474
|
-
import { join as
|
|
9789
|
+
import { join as join19 } from "path";
|
|
8475
9790
|
function previewQwen(ctx) {
|
|
8476
9791
|
const { tool, args, cwd, stateDir, cliDir, config } = ctx;
|
|
8477
9792
|
const state = previewState(args, ctx.baseEnvNames), { unresolved, value } = state;
|
|
@@ -8494,7 +9809,7 @@ function previewQwen(ctx) {
|
|
|
8494
9809
|
form: "headless",
|
|
8495
9810
|
projectRoot: cwd,
|
|
8496
9811
|
stateDir,
|
|
8497
|
-
entrypoint:
|
|
9812
|
+
entrypoint: join19(cliDir, "main.js"),
|
|
8498
9813
|
userArgs: [],
|
|
8499
9814
|
unattended: false,
|
|
8500
9815
|
env: {},
|
|
@@ -8510,23 +9825,23 @@ var init_native_cli_preview_qwen = () => {};
|
|
|
8510
9825
|
// src/adapters/definitions/native-cli-preview-jcode.ts
|
|
8511
9826
|
function previewJcode(ctx) {
|
|
8512
9827
|
const { args, config } = ctx;
|
|
8513
|
-
const { jcodeCommand, qwenSessionId } = ctx.helpers;
|
|
9828
|
+
const { jcodeCommand, jcodeTuiCommand, qwenSessionId, startModeOf } = ctx.helpers;
|
|
8514
9829
|
const state = previewState(args, ctx.baseEnvNames), { unresolved, value } = state;
|
|
8515
9830
|
let envNames = state.envNames;
|
|
8516
9831
|
let launch;
|
|
8517
|
-
const mode = value("--mode") ?? "
|
|
8518
|
-
if (mode !== "headless")
|
|
8519
|
-
throw new Error("Jcode
|
|
9832
|
+
const mode = value("--mode") ?? startModeOf(config.peers, "jcode"), model = value("--model"), profile = value("--provider-profile"), session = value("--session-id");
|
|
9833
|
+
if (mode !== "headless" && mode !== "tui")
|
|
9834
|
+
throw new Error("invalid Jcode start mode");
|
|
8520
9835
|
for (let i = 0;i < args.length; i += 2)
|
|
8521
9836
|
if (!["--model", "--mode", "--provider-profile", "--session-id"].includes(args[i]) || !args[i + 1])
|
|
8522
9837
|
throw new Error("unknown Jcode option");
|
|
8523
9838
|
if (session !== undefined && !qwenSessionId(session))
|
|
8524
9839
|
throw new Error("invalid Jcode session identity");
|
|
8525
|
-
const cmd = jcodeCommand(nativeCommand(config.jcode_cmd, ["jcode"]), "acp", UNRESOLVED, UNRESOLVED, model ? REDACTED : undefined, profile || config.jcode_provider_profile ? REDACTED : undefined);
|
|
9840
|
+
const cmd = mode === "tui" ? jcodeTuiCommand(nativeCommand(config.jcode_cmd, ["jcode"]), UNRESOLVED, UNRESOLVED, model ? REDACTED : undefined, profile || config.jcode_provider_profile ? REDACTED : undefined) : jcodeCommand(nativeCommand(config.jcode_cmd, ["jcode"]), "acp", UNRESOLVED, UNRESOLVED, model ? REDACTED : undefined, profile || config.jcode_provider_profile ? REDACTED : undefined);
|
|
8526
9841
|
launch = { cmd: cmd[0], args: cmd.slice(1) };
|
|
8527
9842
|
unresolved.push({ field: "ownedDaemon", reason: "private daemon/socket and source-pinned native identity verified at start; preview starts nothing" });
|
|
8528
9843
|
unresolved.push({ field: "hubTools", reason: "project MCP bridge required; native tool-call receipt remains unverified" });
|
|
8529
|
-
return finishPreview(ctx, launch, envNames, unresolved,
|
|
9844
|
+
return { ...finishPreview(ctx, launch, envNames, unresolved), mode };
|
|
8530
9845
|
}
|
|
8531
9846
|
var init_native_cli_preview_jcode = () => {};
|
|
8532
9847
|
|
|
@@ -8556,7 +9871,7 @@ function kimiAcpCommand(command, model) {
|
|
|
8556
9871
|
}
|
|
8557
9872
|
|
|
8558
9873
|
// src/adapters/definitions/native-daemon-policy.ts
|
|
8559
|
-
var claudeRecoveryLaunch = (instance) => ({ kind: "claude-channel", peer: instance }), kimiRecoveryLaunch = (instance) => ({ kind: "kimi-tui", peer: instance, mode: "tui" }), claudeRetainedTerminalNotice = (instance) => `Stop ${instance}'s existing work in its retained terminal; removal detaches the hub connection and does not stop that terminal's work.`, piRetainedTerminalNotice = (instance, form) => form === "tui" ? claudeRetainedTerminalNotice(instance) : undefined, codexRetainedTerminalNotice = (_instance, _form, resumeCommand) => `Check codex's existing terminal and stop any remaining work. Its app-server and proxy stay available, but Codex 0.146.0 exits its TUI on disconnect and does not reconnect automatically; after ahub roster add codex, resume the previous session with ${resumeCommand}.`, codexRosterReturnNotice = (resumeCommand) => `Codex 0.146.0 needs a manual return: resume the previous native session with ${resumeCommand}.`, jcodeStartPreflight = (request) => request.form !== undefined && request.form !== "headless"
|
|
9874
|
+
var claudeRecoveryLaunch = (instance) => ({ kind: "claude-channel", peer: instance }), kimiRecoveryLaunch = (instance) => ({ kind: "kimi-tui", peer: instance, mode: "tui" }), claudeRetainedTerminalNotice = (instance) => `Stop ${instance}'s existing work in its retained terminal; removal detaches the hub connection and does not stop that terminal's work.`, piRetainedTerminalNotice = (instance, form) => form === "tui" ? claudeRetainedTerminalNotice(instance) : undefined, codexRetainedTerminalNotice = (_instance, _form, resumeCommand) => `Check codex's existing terminal and stop any remaining work. Its app-server and proxy stay available, but Codex 0.146.0 exits its TUI on disconnect and does not reconnect automatically; after ahub roster add codex, resume the previous session with ${resumeCommand}.`, codexRosterReturnNotice = (resumeCommand) => `Codex 0.146.0 needs a manual return: resume the previous native session with ${resumeCommand}.`, jcodeStartPreflight = (request) => request.form !== undefined && request.form !== "headless" && request.form !== "tui" ? "invalid Jcode start mode" : undefined, claudeObservedVersion = (context) => context.observedVersion, codexObservedVersion = (context) => context.peer?.version ?? context.observedVersion;
|
|
8560
9875
|
|
|
8561
9876
|
// src/adapters/definitions/native-config-policy.ts
|
|
8562
9877
|
function piConfigSchema(v) {
|
|
@@ -8602,8 +9917,8 @@ var init_native_config_policy = __esm(() => {
|
|
|
8602
9917
|
});
|
|
8603
9918
|
|
|
8604
9919
|
// src/adapters/definitions/native-policy-aux.ts
|
|
8605
|
-
import { closeSync as
|
|
8606
|
-
import { isAbsolute as
|
|
9920
|
+
import { closeSync as closeSync6, constants as constants5, fstatSync as fstatSync5, openSync as openSync6, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
|
|
9921
|
+
import { isAbsolute as isAbsolute7, join as join20, normalize } from "path";
|
|
8607
9922
|
function claudeCrashPlan(_meta) {
|
|
8608
9923
|
return { how: "claude: the Claude Code plugin reconnects by itself while that session is still open" };
|
|
8609
9924
|
}
|
|
@@ -8648,13 +9963,13 @@ function localCrashPlan(meta) {
|
|
|
8648
9963
|
}
|
|
8649
9964
|
function claudePlugins(root, cwd) {
|
|
8650
9965
|
const enabled = new Map;
|
|
8651
|
-
for (const file of [
|
|
9966
|
+
for (const file of [join20(root, "settings.json"), ...cwd ? [join20(cwd, ".claude", "settings.json"), join20(cwd, ".claude", "settings.local.json")] : []]) {
|
|
8652
9967
|
const map = json(file)?.enabledPlugins;
|
|
8653
9968
|
if (map && typeof map === "object")
|
|
8654
9969
|
for (const [key, on] of Object.entries(map))
|
|
8655
9970
|
enabled.set(key, on === true);
|
|
8656
9971
|
}
|
|
8657
|
-
const installed = json(
|
|
9972
|
+
const installed = json(join20(root, "plugins", "installed_plugins.json"))?.plugins ?? {};
|
|
8658
9973
|
const out = [];
|
|
8659
9974
|
for (const [key, on] of enabled) {
|
|
8660
9975
|
if (!on)
|
|
@@ -8663,12 +9978,12 @@ function claudePlugins(root, cwd) {
|
|
|
8663
9978
|
const entries = Array.isArray(installed[key]) ? installed[key] : [];
|
|
8664
9979
|
const entry = entries.find((e) => e?.scope === "user") ?? entries.find((e) => e?.projectPath === cwd);
|
|
8665
9980
|
if (name && entry)
|
|
8666
|
-
out.push({ name, version:
|
|
9981
|
+
out.push({ name, version: version2(entry.version) });
|
|
8667
9982
|
}
|
|
8668
9983
|
return out;
|
|
8669
9984
|
}
|
|
8670
9985
|
function codexPlugins(root) {
|
|
8671
|
-
const config = text(
|
|
9986
|
+
const config = text(join20(root, "config.toml"));
|
|
8672
9987
|
if (config === undefined)
|
|
8673
9988
|
return [];
|
|
8674
9989
|
const found = new Map, disabled = new Set;
|
|
@@ -8687,9 +10002,9 @@ function codexPlugins(root) {
|
|
|
8687
10002
|
continue;
|
|
8688
10003
|
let versions = [];
|
|
8689
10004
|
try {
|
|
8690
|
-
versions = readdirSync3(
|
|
10005
|
+
versions = readdirSync3(join20(root, "plugins", "cache", market, plugin));
|
|
8691
10006
|
} catch {}
|
|
8692
|
-
out.push({ name, version: versions.length === 1 ?
|
|
10007
|
+
out.push({ name, version: versions.length === 1 ? version2(versions[0]) : null });
|
|
8693
10008
|
}
|
|
8694
10009
|
return out;
|
|
8695
10010
|
}
|
|
@@ -8702,21 +10017,21 @@ function parsePiContinuity(value) {
|
|
|
8702
10017
|
return { sessionId: value.sessionId, ...value.sessionFile === undefined ? {} : { sessionFile: value.sessionFile }, ...value.backend === undefined ? {} : { backend: value.backend }, ...value.model === undefined ? {} : { model: value.model } };
|
|
8703
10018
|
}
|
|
8704
10019
|
function absoluteContinuityPath(value) {
|
|
8705
|
-
return typeof value === "string" && value.length > 0 && value.length <= 4096 && !/[\x00-\x1f\x7f]/.test(value) &&
|
|
10020
|
+
return typeof value === "string" && value.length > 0 && value.length <= 4096 && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute7(value) && normalize(value) === value;
|
|
8706
10021
|
}
|
|
8707
10022
|
var str3 = (value) => typeof value === "string" && value ? value : undefined, PLUGINS, CLAUDE_ONLY, text = (file) => {
|
|
8708
10023
|
let fd;
|
|
8709
10024
|
try {
|
|
8710
|
-
fd =
|
|
8711
|
-
const st =
|
|
10025
|
+
fd = openSync6(file, constants5.O_RDONLY | constants5.O_NONBLOCK);
|
|
10026
|
+
const st = fstatSync5(fd);
|
|
8712
10027
|
if (!st.isFile() || st.size > 1024 * 1024)
|
|
8713
10028
|
return;
|
|
8714
|
-
return
|
|
10029
|
+
return readFileSync9(fd, "utf8");
|
|
8715
10030
|
} catch {
|
|
8716
10031
|
return;
|
|
8717
10032
|
} finally {
|
|
8718
10033
|
if (fd !== undefined)
|
|
8719
|
-
|
|
10034
|
+
closeSync6(fd);
|
|
8720
10035
|
}
|
|
8721
10036
|
}, json = (file) => {
|
|
8722
10037
|
try {
|
|
@@ -8725,7 +10040,7 @@ var str3 = (value) => typeof value === "string" && value ? value : undefined, PL
|
|
|
8725
10040
|
} catch {
|
|
8726
10041
|
return;
|
|
8727
10042
|
}
|
|
8728
|
-
},
|
|
10043
|
+
}, version2 = (v) => typeof v === "string" && /^[0-9A-Za-z.+_-]{1,40}$/.test(v) ? v : null, claudePluginInventory = (ctx) => claudePlugins(ctx.env.CLAUDE_CONFIG_DIR?.trim() || join20(ctx.home, ".claude"), ctx.cwd), codexPluginInventory = (ctx) => codexPlugins(ctx.env.CODEX_HOME?.trim() || join20(ctx.home, ".codex")), codexToolSemantic = (name, _command) => name.toLowerCase() === "filechange" ? "edit" : undefined;
|
|
8729
10044
|
var init_native_policy_aux = __esm(() => {
|
|
8730
10045
|
PLUGINS = new Map([["claude-mem", "claude-mem"], ["ponytail", "ponytail"], ["open-code-review", "open-code-review"]]);
|
|
8731
10046
|
CLAUDE_ONLY = new Map([["codex@openai-codex", "codex-plugin"]]);
|
|
@@ -8771,7 +10086,7 @@ var init_launch2 = __esm(() => {
|
|
|
8771
10086
|
});
|
|
8772
10087
|
|
|
8773
10088
|
// src/cli/shell-quote.ts
|
|
8774
|
-
function
|
|
10089
|
+
function shellQuote3(value) {
|
|
8775
10090
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
8776
10091
|
}
|
|
8777
10092
|
|
|
@@ -8786,26 +10101,26 @@ function nativeLaunchArguments(tool, ctx, policy) {
|
|
|
8786
10101
|
}
|
|
8787
10102
|
function nativeLaunchPlan(launch, ctx) {
|
|
8788
10103
|
const argv = [launch.cmd, ...launch.args];
|
|
8789
|
-
return { ...launch, metadata: { packageEntrypoint: ctx.entrypoint, argv, env: {}, command: argv.map(
|
|
10104
|
+
return { ...launch, metadata: { packageEntrypoint: ctx.entrypoint, argv, env: {}, command: argv.map(shellQuote3).join(" ") } };
|
|
8790
10105
|
}
|
|
8791
10106
|
var init_native_cli_launch_common = __esm(() => {
|
|
8792
10107
|
init_launch2();
|
|
8793
10108
|
});
|
|
8794
10109
|
|
|
8795
10110
|
// src/adapters/definitions/native-cli-claude-launch.ts
|
|
8796
|
-
import { mkdirSync as
|
|
8797
|
-
import { randomUUID as
|
|
8798
|
-
import { resolve as
|
|
10111
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync5, renameSync as renameSync2, unlinkSync as unlinkSync2, readFileSync as readFileSync10 } from "fs";
|
|
10112
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
10113
|
+
import { resolve as resolve15, join as join21 } from "path";
|
|
8799
10114
|
function claudeChannel(userArgs) {
|
|
8800
|
-
const bundle =
|
|
10115
|
+
const bundle = resolve15(import.meta.dir, "../../../plugins/agent-hub/server.js");
|
|
8801
10116
|
for (let i = 0;i < userArgs.length; i++) {
|
|
8802
10117
|
const arg = userArgs[i];
|
|
8803
10118
|
const path = arg === "--mcp-config" ? userArgs[i + 1] : arg.startsWith("--mcp-config=") ? arg.slice("--mcp-config=".length) : undefined;
|
|
8804
10119
|
if (!path)
|
|
8805
10120
|
continue;
|
|
8806
10121
|
try {
|
|
8807
|
-
const server = JSON.parse(
|
|
8808
|
-
if (server?.command === "bun" && Array.isArray(server.args) && server.args.some((value) => typeof value === "string" &&
|
|
10122
|
+
const server = JSON.parse(readFileSync10(resolve15(path), "utf8")).mcpServers?.["agent-hub"];
|
|
10123
|
+
if (server?.command === "bun" && Array.isArray(server.args) && server.args.some((value) => typeof value === "string" && resolve15(value) === bundle))
|
|
8809
10124
|
return "server:agent-hub";
|
|
8810
10125
|
} catch {}
|
|
8811
10126
|
}
|
|
@@ -8828,7 +10143,7 @@ function claudeNativeLaunch(ctx) {
|
|
|
8828
10143
|
throw new Error("--settings requires a JSON object or readable file so the hub permission hook can be installed");
|
|
8829
10144
|
let caller;
|
|
8830
10145
|
try {
|
|
8831
|
-
caller = JSON.parse(value.trim().startsWith("{") ? value :
|
|
10146
|
+
caller = JSON.parse(value.trim().startsWith("{") ? value : readFileSync10(resolve15(value), "utf8"));
|
|
8832
10147
|
if (!caller || typeof caller !== "object" || Array.isArray(caller))
|
|
8833
10148
|
throw new Error("not an object");
|
|
8834
10149
|
} catch {
|
|
@@ -8857,14 +10172,14 @@ function claudeNativeLaunch(ctx) {
|
|
|
8857
10172
|
const stateDir = ctx.facts?.stateDir ?? ctx.statusLine?.stateDir;
|
|
8858
10173
|
if (!stateDir)
|
|
8859
10174
|
throw new Error("Claude session settings require the hub state directory");
|
|
8860
|
-
const file =
|
|
10175
|
+
const file = join21(stateDir, `claude-settings-${randomUUID7()}.json`);
|
|
8861
10176
|
try {
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
10177
|
+
mkdirSync7(stateDir, { recursive: true, mode: 448 });
|
|
10178
|
+
writeFileSync5(`${file}.tmp`, settingsArgs[1], { mode: 384, flag: "wx" });
|
|
10179
|
+
renameSync2(`${file}.tmp`, file);
|
|
8865
10180
|
} catch {
|
|
8866
10181
|
try {
|
|
8867
|
-
|
|
10182
|
+
unlinkSync2(`${file}.tmp`);
|
|
8868
10183
|
} catch {}
|
|
8869
10184
|
throw new Error("cannot write private Claude session settings in the hub state directory");
|
|
8870
10185
|
}
|
|
@@ -8914,11 +10229,11 @@ var init_native_cli_codex_launch = __esm(() => {
|
|
|
8914
10229
|
});
|
|
8915
10230
|
|
|
8916
10231
|
// src/hub/events.ts
|
|
8917
|
-
import { appendFileSync, closeSync as
|
|
10232
|
+
import { appendFileSync, closeSync as closeSync7, existsSync as existsSync7, fstatSync as fstatSync6, openSync as openSync7, readFileSync as readFileSync11, readSync as readSync4 } from "fs";
|
|
8918
10233
|
function readEvents(file, since = 0) {
|
|
8919
|
-
if (!
|
|
10234
|
+
if (!existsSync7(file))
|
|
8920
10235
|
return [];
|
|
8921
|
-
return
|
|
10236
|
+
return readFileSync11(file, "utf8").split(`
|
|
8922
10237
|
`).flatMap((line) => {
|
|
8923
10238
|
if (!line.trim())
|
|
8924
10239
|
return [];
|
|
@@ -8935,14 +10250,14 @@ var init_events = () => {};
|
|
|
8935
10250
|
|
|
8936
10251
|
// src/cli/recovery-session-file.ts
|
|
8937
10252
|
import { statSync as statSync4 } from "fs";
|
|
8938
|
-
import { homedir as
|
|
8939
|
-
import { join as
|
|
10253
|
+
import { homedir as homedir5 } from "os";
|
|
10254
|
+
import { join as join22 } from "path";
|
|
8940
10255
|
function checkedSessionPath(binding, kind) {
|
|
8941
10256
|
if (kind === "rollout")
|
|
8942
|
-
return
|
|
8943
|
-
const config = binding.launch.env.CLAUDE_CONFIG_DIR ??
|
|
10257
|
+
return join22(binding.launch.env.CODEX_HOME ?? join22(homedir5(), ".codex"), "sessions");
|
|
10258
|
+
const config = binding.launch.env.CLAUDE_CONFIG_DIR ?? join22(homedir5(), ".claude");
|
|
8944
10259
|
const slug = binding.projectRoot.replace(/[^a-zA-Z0-9]/g, "-");
|
|
8945
|
-
return
|
|
10260
|
+
return join22(config, "projects", slug, `${binding.sessionId}.jsonl`);
|
|
8946
10261
|
}
|
|
8947
10262
|
function recordedFileState(file) {
|
|
8948
10263
|
try {
|
|
@@ -8955,17 +10270,17 @@ var init_recovery_session_file = () => {};
|
|
|
8955
10270
|
|
|
8956
10271
|
// src/adapters/definitions/native-recovery.ts
|
|
8957
10272
|
import { readdirSync as readdirSync4 } from "fs";
|
|
8958
|
-
import { basename as
|
|
10273
|
+
import { basename as basename5, join as join23 } from "path";
|
|
8959
10274
|
var rolloutState = (binding) => {
|
|
8960
10275
|
try {
|
|
8961
|
-
return readdirSync4(checkedSessionPath(binding, "rollout"), { recursive: true }).some((name) => String(name).endsWith(".jsonl") &&
|
|
10276
|
+
return readdirSync4(checkedSessionPath(binding, "rollout"), { recursive: true }).some((name) => String(name).endsWith(".jsonl") && basename5(String(name)).includes(binding.sessionId));
|
|
8962
10277
|
} catch (error) {
|
|
8963
10278
|
return error.code === "ENOENT" ? false : "unknown";
|
|
8964
10279
|
}
|
|
8965
10280
|
}, transcriptState = (binding) => {
|
|
8966
10281
|
const state = recordedFileState(checkedSessionPath(binding, "transcript"));
|
|
8967
10282
|
return state === "unknown" ? "unknown" : state === "found";
|
|
8968
|
-
}, startupArgs = (peer) => Object.fromEntries(["mode", "backend", "model", "route"].flatMap((key) => typeof peer.args?.[key] === "string" ? [[key, peer.args[key]]] : [])), text2 = (value) => typeof value === "string" && value.length > 0,
|
|
10283
|
+
}, startupArgs = (peer) => Object.fromEntries(["mode", "backend", "model", "route"].flatMap((key) => typeof peer.args?.[key] === "string" ? [[key, peer.args[key]]] : [])), text2 = (value) => typeof value === "string" && value.length > 0, object5 = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {}, CODEX_RECOVERY_SESSION, CLAUDE_RECOVERY_SESSION, PI_RECOVERY_SESSION, KIMI_RECOVERY_SESSION, LOCAL_RECOVERY_SESSION, JCODE_RECOVERY_SESSION, QWEN_RECOVERY_SESSION;
|
|
8969
10284
|
var init_native_recovery = __esm(() => {
|
|
8970
10285
|
init_events();
|
|
8971
10286
|
init_recovery_session_file();
|
|
@@ -8979,7 +10294,7 @@ var init_native_recovery = __esm(() => {
|
|
|
8979
10294
|
if (phase !== "plan")
|
|
8980
10295
|
return false;
|
|
8981
10296
|
let current, started = false, turned = false;
|
|
8982
|
-
for (const e of readEvents(
|
|
10297
|
+
for (const e of readEvents(join23(stateDir, "events.jsonl"))) {
|
|
8983
10298
|
if (e.type === "native_thread" && e.peer === binding.peer) {
|
|
8984
10299
|
current = e.thread;
|
|
8985
10300
|
if (e.thread === binding.sessionId && e.fresh)
|
|
@@ -9020,7 +10335,7 @@ var init_native_recovery = __esm(() => {
|
|
|
9020
10335
|
return args;
|
|
9021
10336
|
},
|
|
9022
10337
|
crashIdentity: (row) => {
|
|
9023
|
-
const value =
|
|
10338
|
+
const value = object5(row), args = object5(value.args), session = object5(value.session);
|
|
9024
10339
|
const sessionId = [value.sessionId, value.agentSessionId, value.resumeSessionId, session.id, session.sessionId].find(text2);
|
|
9025
10340
|
const sessionFile = [value.sessionFile, value.sessionPath, args.sessionFile, session.file, session.path].find(text2);
|
|
9026
10341
|
return { ...sessionId ? { sessionId } : {}, ...sessionFile ? { sessionFile } : {} };
|
|
@@ -9076,8 +10391,8 @@ function claudeWindows(rateLimits) {
|
|
|
9076
10391
|
}
|
|
9077
10392
|
|
|
9078
10393
|
// src/adapters/definitions/builtin-metadata.ts
|
|
9079
|
-
function compatibilityLaunch(kind, ctx) {
|
|
9080
|
-
const mode = kind === "kimi" || kind === "pi" ? ["--mode", ctx.form] : [];
|
|
10394
|
+
function compatibilityLaunch(kind, ctx, explicitMode = false) {
|
|
10395
|
+
const mode = explicitMode || kind === "kimi" || kind === "pi" ? ["--mode", ctx.form] : [];
|
|
9081
10396
|
const unattended = ctx.unattended && (kind === "claude" || kind === "codex") ? ["--unattended"] : [];
|
|
9082
10397
|
const args = [
|
|
9083
10398
|
ctx.entrypoint,
|
|
@@ -9094,12 +10409,12 @@ function compatibilityLaunch(kind, ctx) {
|
|
|
9094
10409
|
...ctx.env.CODEX_HOME !== undefined ? { CODEX_HOME: ctx.env.CODEX_HOME } : {},
|
|
9095
10410
|
...ctx.env.CLAUDE_CONFIG_DIR !== undefined ? { CLAUDE_CONFIG_DIR: ctx.env.CLAUDE_CONFIG_DIR } : {}
|
|
9096
10411
|
};
|
|
9097
|
-
const prefix = Object.entries(env).map(([name, value]) => `${name}=${
|
|
10412
|
+
const prefix = Object.entries(env).map(([name, value]) => `${name}=${shellQuote3(value)}`);
|
|
9098
10413
|
return {
|
|
9099
10414
|
cmd: process.execPath,
|
|
9100
10415
|
args,
|
|
9101
10416
|
...unattended.length ? { unattended: true } : {},
|
|
9102
|
-
metadata: { packageEntrypoint: ctx.entrypoint, argv, env, command: [...prefix, ...argv.map(
|
|
10417
|
+
metadata: { packageEntrypoint: ctx.entrypoint, argv, env, command: [...prefix, ...argv.map(shellQuote3)].join(" ") }
|
|
9103
10418
|
};
|
|
9104
10419
|
}
|
|
9105
10420
|
function sessionId(field) {
|
|
@@ -9223,7 +10538,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
9223
10538
|
session: { nativeRevert: true, idField: "threadId", currentId: sessionId("threadId"), configDirEnv: "CODEX_HOME", zeroTurnFresh: true, launchKind: "codex", launchRecord: true, ...CODEX_RECOVERY_SESSION, crashPlan: codexCrashPlan, unrecordedTerminal: "always" },
|
|
9224
10539
|
turnEnd: "adapter",
|
|
9225
10540
|
budget: { account: "codex", checkpoint: true, reader: { source: "codex rateLimits", read: codexWindows }, context: { source: "codex_token_usage", read: codexContext } },
|
|
9226
|
-
cli: { command: "codex", retainedTerminalNotice: codexRetainedTerminalNotice, rosterReturnNotice: codexRosterReturnNotice, stopNotice: () => "this peer belongs to a native terminal; end it in its terminal", startWords: () => ["codex"] },
|
|
10541
|
+
cli: { command: "codex", launchConfigKeys: ["codex_bin"], retainedTerminalNotice: codexRetainedTerminalNotice, rosterReturnNotice: codexRosterReturnNotice, stopNotice: () => "this peer belongs to a native terminal; end it in its terminal", startWords: () => ["codex"] },
|
|
9227
10542
|
display: CODEX_DISPLAY,
|
|
9228
10543
|
hooks: { recallPlatform: "codex", recallSource: "codex", recallInjected: true, pluginInventory: codexPluginInventory, classifyTool: codexToolSemantic }
|
|
9229
10544
|
},
|
|
@@ -9258,7 +10573,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
9258
10573
|
capabilities: { watch: true },
|
|
9259
10574
|
init: { mcpFile: ".kimi-code/mcp.json" },
|
|
9260
10575
|
hooks: { recallPlatform: "kimi", recallSource: "kimi" },
|
|
9261
|
-
cli: { command: "kimi", stopNotice: (form) => form === "tui" ? "Kimi belongs to its own TUI terminal; end it in that terminal" : "", startWords: (form) => ["kimi", "--mode", form] },
|
|
10576
|
+
cli: { command: "kimi", launchConfigKeys: ["kimi_cmd", "peers.kimi.start_mode"], stopNotice: (form) => form === "tui" ? "Kimi belongs to its own TUI terminal; end it in that terminal" : "", startWords: (form) => ["kimi", "--mode", form] },
|
|
9262
10577
|
display: KIMI_DISPLAY
|
|
9263
10578
|
},
|
|
9264
10579
|
{
|
|
@@ -9280,7 +10595,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
9280
10595
|
reporting: { tokensNotReported: true },
|
|
9281
10596
|
configuration: { schema: piConfigSchema, routingSchema: piRoutingSchema, settings: PI_SETTINGS, restartReasons: PI_RESTART_REASONS, validateEffective: piEffectiveConfig },
|
|
9282
10597
|
routing: { localAllowedRefusal: "local_allowed = false also excludes pi for this class", longContext: "backend-limit", preferredTier: true, handoffPriority: 0, trace: "backend", backendLabel: "pi" },
|
|
9283
|
-
cli: { command: "pi", previewArgs: ["--mode", "tui"], retainedTerminalNotice: piRetainedTerminalNotice, personOnlyFlags: ["--replace-session"], startWords: (form) => ["pi", "--mode", form] },
|
|
10598
|
+
cli: { command: "pi", launchConfigKeys: ["pi.cmd", "pi.backend", "pi.dgx_coding", "pi.dgx_fast", "pi.max_steps", "routing.pi", "peers.pi.start_mode"], previewArgs: ["--mode", "tui"], retainedTerminalNotice: piRetainedTerminalNotice, personOnlyFlags: ["--replace-session"], startWords: (form) => ["pi", "--mode", form] },
|
|
9284
10599
|
display: PI_DISPLAY
|
|
9285
10600
|
},
|
|
9286
10601
|
{
|
|
@@ -9300,7 +10615,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
9300
10615
|
budget: { account: "local", quotaExempt: true, instrumented: true },
|
|
9301
10616
|
configuration: { schema: localConfigSchema, routingSchema: localRoutingSchema, restartReasons: LOCAL_RESTART_REASONS },
|
|
9302
10617
|
routing: { localAllowedRefusal: "local_allowed = false for this class", longContext: "skip-owner", preferredTier: true, handoffPriority: 1, trace: "route" },
|
|
9303
|
-
cli: { command: "local", startWords: () => ["local"] },
|
|
10618
|
+
cli: { command: "local", launchConfigKeys: ["local.deny", "local.bash_network", "local.network_allow", "local.max_steps", "local.read_allow", "routing.local"], startWords: () => ["local"] },
|
|
9304
10619
|
display: LOCAL_DISPLAY
|
|
9305
10620
|
},
|
|
9306
10621
|
{
|
|
@@ -9313,7 +10628,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
9313
10628
|
optional: true,
|
|
9314
10629
|
defaultOnRoster: false,
|
|
9315
10630
|
trust: { onPrem: false, hubSandboxed: false },
|
|
9316
|
-
start: { create: createJcode, preflight: jcodeStartPreflight, daemonPort: "owned-acp", forms:
|
|
10631
|
+
start: { create: createJcode, preflight: jcodeStartPreflight, daemonPort: "owned-acp", forms: dualForms, defaultForm: "tui", ownsTerminal: terminal, explicitHeadlessMode: true, launch: (ctx) => compatibilityLaunch("jcode", ctx, true), conductorStartable: true, hubSwitches: [], ownedFlags: [] },
|
|
9317
10632
|
identity: { mcp: "project-file" },
|
|
9318
10633
|
permission: { modes: {}, unmanaged: "Jcode native policy is unmanaged; no hub mode mapping", grants: {
|
|
9319
10634
|
ask: "no verified hub mode mapping; native policy only",
|
|
@@ -9323,7 +10638,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
9323
10638
|
session: { idField: "sessionId", currentId: sessionId("sessionId"), launchKind: "jcode", ...JCODE_RECOVERY_SESSION, crashPlan: jcodeCrashPlan },
|
|
9324
10639
|
turnEnd: "adapter",
|
|
9325
10640
|
budget: { account: "jcode" },
|
|
9326
|
-
cli: { command: "jcode", authority: "person", startWords: () => ["jcode"] },
|
|
10641
|
+
cli: { command: "jcode", authority: "person", launchConfigKeys: ["jcode_cmd", "jcode_home", "jcode_provider_profile"], startWords: (mode) => ["jcode", "--mode", mode] },
|
|
9327
10642
|
display: JCODE_DISPLAY,
|
|
9328
10643
|
hooks: { recallPlatform: "jcode" }
|
|
9329
10644
|
}
|
|
@@ -9514,7 +10829,7 @@ var init_qwen = __esm(() => {
|
|
|
9514
10829
|
throw new Error("Qwen TUI is not offered before its native verification; use ahub qwen --mode headless");
|
|
9515
10830
|
const launch = buildQwenLaunch(context.nativeCommand ?? ["qwen", "--acp"], context.model);
|
|
9516
10831
|
const argv = [launch.cmd, ...launch.args];
|
|
9517
|
-
return { ...launch, metadata: { packageEntrypoint: context.entrypoint, argv, command: argv.map(
|
|
10832
|
+
return { ...launch, metadata: { packageEntrypoint: context.entrypoint, argv, command: argv.map(shellQuote3).join(" "), env: {} } };
|
|
9518
10833
|
},
|
|
9519
10834
|
create: (context) => {
|
|
9520
10835
|
if (!("acp" in context.daemon))
|
|
@@ -9555,7 +10870,7 @@ var init_qwen = __esm(() => {
|
|
|
9555
10870
|
turnEnd: "adapter",
|
|
9556
10871
|
budget: { account: "qwen" },
|
|
9557
10872
|
capabilities: { watch: true },
|
|
9558
|
-
cli: { command: "qwen", startWords: () => ["qwen"] },
|
|
10873
|
+
cli: { command: "qwen", launchConfigKeys: ["qwen_cmd"], startWords: () => ["qwen"] },
|
|
9559
10874
|
display: QWEN_DISPLAY,
|
|
9560
10875
|
hooks: { agentIdentity: (init) => {
|
|
9561
10876
|
qwenNativeIdentity("qwen", true, init);
|
|
@@ -9644,7 +10959,8 @@ function assertNativeCapabilityScope(definition) {
|
|
|
9644
10959
|
["start.daemonPort pi-rpc", definition.start.daemonPort === "pi-rpc"],
|
|
9645
10960
|
["routing", definition.routing !== undefined],
|
|
9646
10961
|
["reviewerVariant", definition.reviewerVariant !== undefined],
|
|
9647
|
-
["reviewer", definition.reviewer !== undefined]
|
|
10962
|
+
["reviewer", definition.reviewer !== undefined],
|
|
10963
|
+
["hooks.factsHook", declared(definition.hooks, "factsHook")]
|
|
9648
10964
|
];
|
|
9649
10965
|
const field = restricted.find(([, present]) => present)?.[0];
|
|
9650
10966
|
if (field)
|
|
@@ -9652,6 +10968,7 @@ function assertNativeCapabilityScope(definition) {
|
|
|
9652
10968
|
}
|
|
9653
10969
|
var ADAPTER_API = 1, KNOWN_PEERS, OPTIONAL_PEERS, SUPPORTED_PEERS, fixedBuiltinDefinitions, ADAPTERS, declarationOnlyDefinitions, definitions, REVIEW_POOL_KINDS, DEFAULT_REVIEW_POOL, QUOTA_HANDOFF_PLATFORMS, ROLE_ORDER, CONDUCTOR_START_PRIORITY, CONDUCTOR_START_PEERS, ADAPTER_DISPLAY, BUILTIN_CLI_ENTRIES;
|
|
9654
10970
|
var init_registry = __esm(() => {
|
|
10971
|
+
init_definition();
|
|
9655
10972
|
init_reviewer_variants();
|
|
9656
10973
|
init_native_cli_entry();
|
|
9657
10974
|
init_native_cli_preview();
|
|
@@ -9662,18 +10979,19 @@ var init_registry = __esm(() => {
|
|
|
9662
10979
|
init_native_cli_doctor();
|
|
9663
10980
|
init_native_display();
|
|
9664
10981
|
KNOWN_PEERS = ["claude", "codex", "kimi", "pi", "local"];
|
|
9665
|
-
OPTIONAL_PEERS = ["qwen", "jcode"];
|
|
10982
|
+
OPTIONAL_PEERS = ["qwen", "jcode", "agy"];
|
|
9666
10983
|
SUPPORTED_PEERS = [...KNOWN_PEERS, ...OPTIONAL_PEERS];
|
|
9667
|
-
fixedBuiltinDefinitions = new Set([...NATIVE_BUILTIN_DEFINITIONS_WITHOUT_QWEN, QWEN_DEFINITION, AGY_DEFINITION]);
|
|
10984
|
+
fixedBuiltinDefinitions = new Set([...NATIVE_BUILTIN_DEFINITIONS_WITHOUT_QWEN, QWEN_DEFINITION, AGY_DEFINITION, AGY_REVIEWER_VARIANT]);
|
|
9668
10985
|
for (const definition of [...fixedBuiltinDefinitions])
|
|
9669
10986
|
if (definition.reviewerVariant)
|
|
9670
10987
|
fixedBuiltinDefinitions.add(definition.reviewerVariant);
|
|
9671
10988
|
ADAPTERS = immutable([
|
|
9672
10989
|
...NATIVE_BUILTIN_DEFINITIONS_WITHOUT_QWEN.filter((definition) => definition.kind !== "jcode"),
|
|
9673
10990
|
QWEN_DEFINITION,
|
|
9674
|
-
...NATIVE_BUILTIN_DEFINITIONS_WITHOUT_QWEN.filter((definition) => definition.kind === "jcode")
|
|
10991
|
+
...NATIVE_BUILTIN_DEFINITIONS_WITHOUT_QWEN.filter((definition) => definition.kind === "jcode"),
|
|
10992
|
+
AGY_DEFINITION
|
|
9675
10993
|
]);
|
|
9676
|
-
declarationOnlyDefinitions = immutable([
|
|
10994
|
+
declarationOnlyDefinitions = immutable([]);
|
|
9677
10995
|
definitions = new Map;
|
|
9678
10996
|
for (const definition of [...ADAPTERS, ...declarationOnlyDefinitions]) {
|
|
9679
10997
|
assertDaemonEngine(definition);
|
|
@@ -9689,7 +11007,7 @@ var init_registry = __esm(() => {
|
|
|
9689
11007
|
REVIEW_POOL_KINDS = Object.freeze([...definitions.values()].filter((definition) => !!(definition.reviewerVariant ?? definition).reviewer).map((definition) => definition.kind));
|
|
9690
11008
|
DEFAULT_REVIEW_POOL = Object.freeze(ADAPTERS.filter((definition) => (definition.reviewerVariant ?? definition).reviewer?.defaultPool === true).map((definition) => definition.kind));
|
|
9691
11009
|
QUOTA_HANDOFF_PLATFORMS = Object.freeze(Object.fromEntries(ADAPTERS.flatMap((adapter) => adapter.hooks?.recallPlatform ? [[adapter.kind, adapter.hooks.recallPlatform]] : [])));
|
|
9692
|
-
ROLE_ORDER = ["claude", "codex", "kimi", "local", "pi", "qwen", "jcode"];
|
|
11010
|
+
ROLE_ORDER = ["claude", "codex", "kimi", "local", "pi", "qwen", "jcode", "agy"];
|
|
9693
11011
|
CONDUCTOR_START_PRIORITY = ["local", "kimi", "pi"];
|
|
9694
11012
|
CONDUCTOR_START_PEERS = [
|
|
9695
11013
|
...CONDUCTOR_START_PRIORITY,
|
|
@@ -16873,8 +18191,8 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
16873
18191
|
|
|
16874
18192
|
// src/hub/instance-config.ts
|
|
16875
18193
|
init_registry();
|
|
16876
|
-
import { readFileSync as
|
|
16877
|
-
import { join as
|
|
18194
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
18195
|
+
import { join as join26 } from "path";
|
|
16878
18196
|
|
|
16879
18197
|
// src/hub/conductor.ts
|
|
16880
18198
|
init_registry();
|
|
@@ -16895,7 +18213,7 @@ var STDOUT_LIMIT = 512 * 1024;
|
|
|
16895
18213
|
// package.json
|
|
16896
18214
|
var package_default = {
|
|
16897
18215
|
name: "@staix/agent-hub",
|
|
16898
|
-
version: "0.12.
|
|
18216
|
+
version: "0.12.39",
|
|
16899
18217
|
description: "Native multi-agent hub: Claude Code, Codex, Kimi Code, Pi and local inference as peers in one project",
|
|
16900
18218
|
license: "MIT",
|
|
16901
18219
|
type: "module",
|
|
@@ -16945,7 +18263,7 @@ var package_default = {
|
|
|
16945
18263
|
};
|
|
16946
18264
|
|
|
16947
18265
|
// src/version.ts
|
|
16948
|
-
var
|
|
18266
|
+
var VERSION3 = package_default.version;
|
|
16949
18267
|
|
|
16950
18268
|
// src/hub/github-import.ts
|
|
16951
18269
|
init_sdlc();
|
|
@@ -17036,7 +18354,7 @@ var USER2 = Symbol("shared-user-shard");
|
|
|
17036
18354
|
// node_modules/@staix/agent-mem/src/mcp.ts
|
|
17037
18355
|
var string = (maxLength, minLength = 1) => ({ type: "string", minLength, maxLength });
|
|
17038
18356
|
var limit = { type: "integer", minimum: 1, maximum: 100 };
|
|
17039
|
-
var
|
|
18357
|
+
var object6 = (properties, required = []) => ({ type: "object", properties, required, additionalProperties: false });
|
|
17040
18358
|
var tool2 = (name, description, inputSchema, readOnlyHint = true) => ({
|
|
17041
18359
|
name,
|
|
17042
18360
|
description,
|
|
@@ -17044,20 +18362,20 @@ var tool2 = (name, description, inputSchema, readOnlyHint = true) => ({
|
|
|
17044
18362
|
annotations: { readOnlyHint, destructiveHint: false, openWorldHint: false }
|
|
17045
18363
|
});
|
|
17046
18364
|
var MCP_TOOLS = [
|
|
17047
|
-
tool2("amem_search", "Search screened memory in this project. Capture results are recall, not curated evidence.",
|
|
17048
|
-
tool2("amem_show", "Show a screened memory entry in this project by its exact id or unique prefix.",
|
|
17049
|
-
tool2("amem_timeline", "Read screened memory near an entry or time in this project.",
|
|
17050
|
-
tool2("amem_graph", "Read the screened curated-memory neighbourhood. Capture is never graph evidence.",
|
|
17051
|
-
tool2("amem_work_state_read", "Read project work lists; closed lists/items can be included explicitly.",
|
|
17052
|
-
tool2("amem_work_state_write", "Append scalar project work-list fields; null clears, task selects an item, list status done closes.",
|
|
18365
|
+
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"])),
|
|
18366
|
+
tool2("amem_show", "Show a screened memory entry in this project by its exact id or unique prefix.", object6({ id: string(80) }, ["id"])),
|
|
18367
|
+
tool2("amem_timeline", "Read screened memory near an entry or time in this project.", object6({ id: string(80), since: string(40), limit })),
|
|
18368
|
+
tool2("amem_graph", "Read the screened curated-memory neighbourhood. Capture is never graph evidence.", object6({ start: string(500), hops: { type: "integer", minimum: 1, maximum: 2 }, limit }, ["start"])),
|
|
18369
|
+
tool2("amem_work_state_read", "Read project work lists; closed lists/items can be included explicitly.", object6({ list: string(200), includeClosed: { type: "boolean" } })),
|
|
18370
|
+
tool2("amem_work_state_write", "Append scalar project work-list fields; null clears, task selects an item, list status done closes.", object6({ list: string(200), fields: {
|
|
17053
18371
|
type: "object",
|
|
17054
18372
|
minProperties: 1,
|
|
17055
18373
|
maxProperties: 64,
|
|
17056
18374
|
additionalProperties: { type: ["string", "number", "boolean", "null"], maxLength: 4000 }
|
|
17057
18375
|
} }, ["list", "fields"]), false),
|
|
17058
|
-
tool2("amem_remember", "Propose a general memory candidate for person acceptance. Never writes directly to curated notes.",
|
|
17059
|
-
tool2("amem_vault_candidates", "List screened durable curated notes eligible for a person to move to the vault. Read only.",
|
|
17060
|
-
tool2("amem_capacity", "Read this machine's bounded capture capacity proposal; applying it is person-only.",
|
|
18376
|
+
tool2("amem_remember", "Propose a general memory candidate for person acceptance. Never writes directly to curated notes.", object6({ text: string(3000), title: string(200), kind: { type: "string", enum: ["decision", "finding", "contract", "fail"] } }, ["text"]), false),
|
|
18377
|
+
tool2("amem_vault_candidates", "List screened durable curated notes eligible for a person to move to the vault. Read only.", object6({ limit })),
|
|
18378
|
+
tool2("amem_capacity", "Read this machine's bounded capture capacity proposal; applying it is person-only.", object6({}))
|
|
17061
18379
|
];
|
|
17062
18380
|
// node_modules/@staix/agent-mem/src/mcp-memory.ts
|
|
17063
18381
|
init_capture();
|
|
@@ -17084,14 +18402,14 @@ init_generation();
|
|
|
17084
18402
|
// src/hub/routing.ts
|
|
17085
18403
|
init_registry();
|
|
17086
18404
|
init_board();
|
|
17087
|
-
import { join as
|
|
18405
|
+
import { join as join24, resolve as resolve16 } from "path";
|
|
17088
18406
|
|
|
17089
18407
|
// src/models/route/config.ts
|
|
17090
18408
|
var TYPES = new Set(["stage", "plan_execute", "advisor", "escalation"]);
|
|
17091
18409
|
|
|
17092
18410
|
// src/hub/routing.ts
|
|
17093
18411
|
init_stage();
|
|
17094
|
-
var TEMPLATE =
|
|
18412
|
+
var TEMPLATE = join24(import.meta.dir, "..", "..", "templates", "routing.toml");
|
|
17095
18413
|
var cache = new Map;
|
|
17096
18414
|
|
|
17097
18415
|
// src/hub/memory-policy.ts
|
|
@@ -17118,8 +18436,8 @@ var OWNERSHIP_EVENTS = new Set(["assigned", "escalated", "reassigned", "unassign
|
|
|
17118
18436
|
// src/hub/config-trust.ts
|
|
17119
18437
|
init_child_process();
|
|
17120
18438
|
init_git();
|
|
17121
|
-
import { lstatSync as
|
|
17122
|
-
import { dirname as
|
|
18439
|
+
import { lstatSync as lstatSync10, statSync as statSync5 } from "fs";
|
|
18440
|
+
import { dirname as dirname10, join as join25 } from "path";
|
|
17123
18441
|
var MACHINE_LOCAL = [
|
|
17124
18442
|
"kimi_cmd",
|
|
17125
18443
|
"permission_modes",
|
|
@@ -17151,7 +18469,7 @@ function configTracked(cwd, name) {
|
|
|
17151
18469
|
return;
|
|
17152
18470
|
let opened;
|
|
17153
18471
|
try {
|
|
17154
|
-
opened = statSync5(
|
|
18472
|
+
opened = statSync5(join25(cwd, ".agenthub", name));
|
|
17155
18473
|
} catch {}
|
|
17156
18474
|
return r.stdout.split("\x00").filter(Boolean).some((row) => {
|
|
17157
18475
|
const path = row.slice(row.indexOf("\t") + 1);
|
|
@@ -17160,7 +18478,7 @@ function configTracked(cwd, name) {
|
|
|
17160
18478
|
if (!opened)
|
|
17161
18479
|
return path === `.agenthub/${name}`;
|
|
17162
18480
|
try {
|
|
17163
|
-
const s = statSync5(
|
|
18481
|
+
const s = statSync5(join25(cwd, path));
|
|
17164
18482
|
return !!opened && s.dev === opened.dev && s.ino === opened.ino;
|
|
17165
18483
|
} catch {
|
|
17166
18484
|
return false;
|
|
@@ -17293,7 +18611,7 @@ var INSTANCE_CONFIG_FILES = ["config.json", "config.local.json"];
|
|
|
17293
18611
|
function readInstanceConfigLayers(cwd) {
|
|
17294
18612
|
return INSTANCE_CONFIG_FILES.flatMap((name) => {
|
|
17295
18613
|
try {
|
|
17296
|
-
return [{ name, raw: JSON.parse(
|
|
18614
|
+
return [{ name, raw: JSON.parse(readFileSync12(join26(cwd, ".agenthub", name), "utf8")) }];
|
|
17297
18615
|
} catch (error) {
|
|
17298
18616
|
if (error.code === "ENOENT")
|
|
17299
18617
|
return [];
|
|
@@ -17323,8 +18641,8 @@ function readInstanceConfig(cwd, layers = readInstanceConfigLayers(cwd)) {
|
|
|
17323
18641
|
|
|
17324
18642
|
// src/hub/control-client.ts
|
|
17325
18643
|
init_project();
|
|
17326
|
-
import { readFileSync as
|
|
17327
|
-
import { join as
|
|
18644
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
18645
|
+
import { join as join27 } from "path";
|
|
17328
18646
|
function stateDirFor(cwd) {
|
|
17329
18647
|
return projectContext(cwd).stateDir;
|
|
17330
18648
|
}
|
|
@@ -17332,10 +18650,11 @@ var PROTOCOL = 16;
|
|
|
17332
18650
|
var RECOVERY_SOURCE_PROTOCOLS = [9, 10, 11, 12, 13, 14, 15, PROTOCOL];
|
|
17333
18651
|
function readControl(stateDir) {
|
|
17334
18652
|
try {
|
|
17335
|
-
const status = JSON.parse(
|
|
17336
|
-
const token =
|
|
18653
|
+
const status = JSON.parse(readFileSync13(join27(stateDir, "status.json"), "utf8"));
|
|
18654
|
+
const token = readFileSync13(join27(stateDir, "control-token"), "utf8").trim();
|
|
17337
18655
|
if (!Number.isInteger(status.controlPort) || status.controlPort < 1 || status.controlPort > 65535 || !token)
|
|
17338
18656
|
return;
|
|
18657
|
+
const startModes = status.startModes && typeof status.startModes === "object" && !Array.isArray(status.startModes) ? Object.fromEntries(Object.entries(status.startModes).filter((entry) => entry[1] === "tui" || entry[1] === "headless")) : undefined;
|
|
17339
18658
|
return {
|
|
17340
18659
|
url: `ws://127.0.0.1:${status.controlPort}`,
|
|
17341
18660
|
token,
|
|
@@ -17345,6 +18664,7 @@ function readControl(stateDir) {
|
|
|
17345
18664
|
protocol: status.protocol,
|
|
17346
18665
|
pid: status.pid,
|
|
17347
18666
|
features: Array.isArray(status.features) ? status.features.filter((feature) => typeof feature === "string") : [],
|
|
18667
|
+
...startModes ? { startModes } : {},
|
|
17348
18668
|
...typeof status.pidSignature === "string" && status.pidSignature ? { pidSignature: status.pidSignature } : {}
|
|
17349
18669
|
};
|
|
17350
18670
|
} catch {
|
|
@@ -18811,7 +20131,7 @@ ${content.join(`
|
|
|
18811
20131
|
}
|
|
18812
20132
|
|
|
18813
20133
|
// node_modules/zod/v4/core/versions.js
|
|
18814
|
-
var
|
|
20134
|
+
var version3 = {
|
|
18815
20135
|
major: 4,
|
|
18816
20136
|
minor: 6,
|
|
18817
20137
|
patch: 5
|
|
@@ -18823,7 +20143,7 @@ var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => {
|
|
|
18823
20143
|
inst ?? (inst = {});
|
|
18824
20144
|
inst._zod.def = def;
|
|
18825
20145
|
inst._zod.bag = inst._zod.bag || {};
|
|
18826
|
-
inst._zod.version =
|
|
20146
|
+
inst._zod.version = version3;
|
|
18827
20147
|
const defChecks = inst._zod.def.checks;
|
|
18828
20148
|
const checks = inst._zod.traits.has("$ZodCheck") ? [inst, ...defChecks ?? []] : defChecks?.length ? [...defChecks] : [];
|
|
18829
20149
|
for (const ch of checks) {
|
|
@@ -23131,7 +24451,7 @@ var ZodObject = /* @__PURE__ */ $constructor("ZodObject", (inst, def) => {
|
|
|
23131
24451
|
return required(ZodNonOptional, this, args[0]);
|
|
23132
24452
|
}
|
|
23133
24453
|
});
|
|
23134
|
-
function
|
|
24454
|
+
function object8(shape, params) {
|
|
23135
24455
|
const def = {
|
|
23136
24456
|
type: "object",
|
|
23137
24457
|
shape: shape ?? {},
|
|
@@ -23192,7 +24512,7 @@ var ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
|
|
|
23192
24512
|
inst.keyType = def.keyType;
|
|
23193
24513
|
inst.valueType = def.valueType;
|
|
23194
24514
|
});
|
|
23195
|
-
function
|
|
24515
|
+
function record4(keyType, valueType, params) {
|
|
23196
24516
|
if (!valueType || !valueType._zod) {
|
|
23197
24517
|
return new ZodRecord({
|
|
23198
24518
|
type: "record",
|
|
@@ -23476,31 +24796,31 @@ var TaskCreationParamsSchema = looseObject({
|
|
|
23476
24796
|
ttl: number2().optional(),
|
|
23477
24797
|
pollInterval: number2().optional()
|
|
23478
24798
|
});
|
|
23479
|
-
var TaskMetadataSchema =
|
|
24799
|
+
var TaskMetadataSchema = object8({
|
|
23480
24800
|
ttl: number2().optional()
|
|
23481
24801
|
});
|
|
23482
|
-
var RelatedTaskMetadataSchema =
|
|
24802
|
+
var RelatedTaskMetadataSchema = object8({
|
|
23483
24803
|
taskId: string3()
|
|
23484
24804
|
});
|
|
23485
24805
|
var RequestMetaSchema = looseObject({
|
|
23486
24806
|
progressToken: ProgressTokenSchema.optional(),
|
|
23487
24807
|
[RELATED_TASK_META_KEY]: RelatedTaskMetadataSchema.optional()
|
|
23488
24808
|
});
|
|
23489
|
-
var BaseRequestParamsSchema =
|
|
24809
|
+
var BaseRequestParamsSchema = object8({
|
|
23490
24810
|
_meta: RequestMetaSchema.optional()
|
|
23491
24811
|
});
|
|
23492
24812
|
var TaskAugmentedRequestParamsSchema = BaseRequestParamsSchema.extend({
|
|
23493
24813
|
task: TaskMetadataSchema.optional()
|
|
23494
24814
|
});
|
|
23495
24815
|
var isTaskAugmentedRequestParams = (value) => TaskAugmentedRequestParamsSchema.safeParse(value).success;
|
|
23496
|
-
var RequestSchema =
|
|
24816
|
+
var RequestSchema = object8({
|
|
23497
24817
|
method: string3(),
|
|
23498
24818
|
params: BaseRequestParamsSchema.loose().optional()
|
|
23499
24819
|
});
|
|
23500
|
-
var NotificationsParamsSchema =
|
|
24820
|
+
var NotificationsParamsSchema = object8({
|
|
23501
24821
|
_meta: RequestMetaSchema.optional()
|
|
23502
24822
|
});
|
|
23503
|
-
var NotificationSchema =
|
|
24823
|
+
var NotificationSchema = object8({
|
|
23504
24824
|
method: string3(),
|
|
23505
24825
|
params: NotificationsParamsSchema.loose().optional()
|
|
23506
24826
|
});
|
|
@@ -23508,18 +24828,18 @@ var ResultSchema = looseObject({
|
|
|
23508
24828
|
_meta: RequestMetaSchema.optional()
|
|
23509
24829
|
});
|
|
23510
24830
|
var RequestIdSchema = union([string3(), number2().int()]);
|
|
23511
|
-
var JSONRPCRequestSchema =
|
|
24831
|
+
var JSONRPCRequestSchema = object8({
|
|
23512
24832
|
jsonrpc: literal(JSONRPC_VERSION),
|
|
23513
24833
|
id: RequestIdSchema,
|
|
23514
24834
|
...RequestSchema.shape
|
|
23515
24835
|
}).strict();
|
|
23516
24836
|
var isJSONRPCRequest = (value) => JSONRPCRequestSchema.safeParse(value).success;
|
|
23517
|
-
var JSONRPCNotificationSchema =
|
|
24837
|
+
var JSONRPCNotificationSchema = object8({
|
|
23518
24838
|
jsonrpc: literal(JSONRPC_VERSION),
|
|
23519
24839
|
...NotificationSchema.shape
|
|
23520
24840
|
}).strict();
|
|
23521
24841
|
var isJSONRPCNotification = (value) => JSONRPCNotificationSchema.safeParse(value).success;
|
|
23522
|
-
var JSONRPCResultResponseSchema =
|
|
24842
|
+
var JSONRPCResultResponseSchema = object8({
|
|
23523
24843
|
jsonrpc: literal(JSONRPC_VERSION),
|
|
23524
24844
|
id: RequestIdSchema,
|
|
23525
24845
|
result: ResultSchema
|
|
@@ -23536,10 +24856,10 @@ var ErrorCode;
|
|
|
23536
24856
|
ErrorCode[ErrorCode["InternalError"] = -32603] = "InternalError";
|
|
23537
24857
|
ErrorCode[ErrorCode["UrlElicitationRequired"] = -32042] = "UrlElicitationRequired";
|
|
23538
24858
|
})(ErrorCode || (ErrorCode = {}));
|
|
23539
|
-
var JSONRPCErrorResponseSchema =
|
|
24859
|
+
var JSONRPCErrorResponseSchema = object8({
|
|
23540
24860
|
jsonrpc: literal(JSONRPC_VERSION),
|
|
23541
24861
|
id: RequestIdSchema.optional(),
|
|
23542
|
-
error:
|
|
24862
|
+
error: object8({
|
|
23543
24863
|
code: number2().int(),
|
|
23544
24864
|
message: string3(),
|
|
23545
24865
|
data: unknown().optional()
|
|
@@ -23562,16 +24882,16 @@ var CancelledNotificationSchema = NotificationSchema.extend({
|
|
|
23562
24882
|
method: literal("notifications/cancelled"),
|
|
23563
24883
|
params: CancelledNotificationParamsSchema
|
|
23564
24884
|
});
|
|
23565
|
-
var IconSchema =
|
|
24885
|
+
var IconSchema = object8({
|
|
23566
24886
|
src: string3(),
|
|
23567
24887
|
mimeType: string3().optional(),
|
|
23568
24888
|
sizes: array(string3()).optional(),
|
|
23569
24889
|
theme: _enum(["light", "dark"]).optional()
|
|
23570
24890
|
});
|
|
23571
|
-
var IconsSchema =
|
|
24891
|
+
var IconsSchema = object8({
|
|
23572
24892
|
icons: array(IconSchema).optional()
|
|
23573
24893
|
});
|
|
23574
|
-
var BaseMetadataSchema =
|
|
24894
|
+
var BaseMetadataSchema = object8({
|
|
23575
24895
|
name: string3(),
|
|
23576
24896
|
title: string3().optional()
|
|
23577
24897
|
});
|
|
@@ -23582,9 +24902,9 @@ var ImplementationSchema = BaseMetadataSchema.extend({
|
|
|
23582
24902
|
websiteUrl: string3().optional(),
|
|
23583
24903
|
description: string3().optional()
|
|
23584
24904
|
});
|
|
23585
|
-
var FormElicitationCapabilitySchema = intersection(
|
|
24905
|
+
var FormElicitationCapabilitySchema = intersection(object8({
|
|
23586
24906
|
applyDefaults: boolean2().optional()
|
|
23587
|
-
}),
|
|
24907
|
+
}), record4(string3(), unknown()));
|
|
23588
24908
|
var ElicitationCapabilitySchema = preprocess((value) => {
|
|
23589
24909
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
23590
24910
|
if (Object.keys(value).length === 0) {
|
|
@@ -23592,10 +24912,10 @@ var ElicitationCapabilitySchema = preprocess((value) => {
|
|
|
23592
24912
|
}
|
|
23593
24913
|
}
|
|
23594
24914
|
return value;
|
|
23595
|
-
}, intersection(
|
|
24915
|
+
}, intersection(object8({
|
|
23596
24916
|
form: FormElicitationCapabilitySchema.optional(),
|
|
23597
24917
|
url: AssertObjectSchema.optional()
|
|
23598
|
-
}),
|
|
24918
|
+
}), record4(string3(), unknown()).optional()));
|
|
23599
24919
|
var ClientTasksCapabilitySchema = looseObject({
|
|
23600
24920
|
list: AssertObjectSchema.optional(),
|
|
23601
24921
|
cancel: AssertObjectSchema.optional(),
|
|
@@ -23617,18 +24937,18 @@ var ServerTasksCapabilitySchema = looseObject({
|
|
|
23617
24937
|
}).optional()
|
|
23618
24938
|
}).optional()
|
|
23619
24939
|
});
|
|
23620
|
-
var ClientCapabilitiesSchema =
|
|
23621
|
-
experimental:
|
|
23622
|
-
sampling:
|
|
24940
|
+
var ClientCapabilitiesSchema = object8({
|
|
24941
|
+
experimental: record4(string3(), AssertObjectSchema).optional(),
|
|
24942
|
+
sampling: object8({
|
|
23623
24943
|
context: AssertObjectSchema.optional(),
|
|
23624
24944
|
tools: AssertObjectSchema.optional()
|
|
23625
24945
|
}).optional(),
|
|
23626
24946
|
elicitation: ElicitationCapabilitySchema.optional(),
|
|
23627
|
-
roots:
|
|
24947
|
+
roots: object8({
|
|
23628
24948
|
listChanged: boolean2().optional()
|
|
23629
24949
|
}).optional(),
|
|
23630
24950
|
tasks: ClientTasksCapabilitySchema.optional(),
|
|
23631
|
-
extensions:
|
|
24951
|
+
extensions: record4(string3(), AssertObjectSchema).optional()
|
|
23632
24952
|
});
|
|
23633
24953
|
var InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
|
|
23634
24954
|
protocolVersion: string3(),
|
|
@@ -23639,22 +24959,22 @@ var InitializeRequestSchema = RequestSchema.extend({
|
|
|
23639
24959
|
method: literal("initialize"),
|
|
23640
24960
|
params: InitializeRequestParamsSchema
|
|
23641
24961
|
});
|
|
23642
|
-
var ServerCapabilitiesSchema =
|
|
23643
|
-
experimental:
|
|
24962
|
+
var ServerCapabilitiesSchema = object8({
|
|
24963
|
+
experimental: record4(string3(), AssertObjectSchema).optional(),
|
|
23644
24964
|
logging: AssertObjectSchema.optional(),
|
|
23645
24965
|
completions: AssertObjectSchema.optional(),
|
|
23646
|
-
prompts:
|
|
24966
|
+
prompts: object8({
|
|
23647
24967
|
listChanged: boolean2().optional()
|
|
23648
24968
|
}).optional(),
|
|
23649
|
-
resources:
|
|
24969
|
+
resources: object8({
|
|
23650
24970
|
subscribe: boolean2().optional(),
|
|
23651
24971
|
listChanged: boolean2().optional()
|
|
23652
24972
|
}).optional(),
|
|
23653
|
-
tools:
|
|
24973
|
+
tools: object8({
|
|
23654
24974
|
listChanged: boolean2().optional()
|
|
23655
24975
|
}).optional(),
|
|
23656
24976
|
tasks: ServerTasksCapabilitySchema.optional(),
|
|
23657
|
-
extensions:
|
|
24977
|
+
extensions: record4(string3(), AssertObjectSchema).optional()
|
|
23658
24978
|
});
|
|
23659
24979
|
var InitializeResultSchema = ResultSchema.extend({
|
|
23660
24980
|
protocolVersion: string3(),
|
|
@@ -23670,12 +24990,12 @@ var PingRequestSchema = RequestSchema.extend({
|
|
|
23670
24990
|
method: literal("ping"),
|
|
23671
24991
|
params: BaseRequestParamsSchema.optional()
|
|
23672
24992
|
});
|
|
23673
|
-
var ProgressSchema =
|
|
24993
|
+
var ProgressSchema = object8({
|
|
23674
24994
|
progress: number2(),
|
|
23675
24995
|
total: optional(number2()),
|
|
23676
24996
|
message: optional(string3())
|
|
23677
24997
|
});
|
|
23678
|
-
var ProgressNotificationParamsSchema =
|
|
24998
|
+
var ProgressNotificationParamsSchema = object8({
|
|
23679
24999
|
...NotificationsParamsSchema.shape,
|
|
23680
25000
|
...ProgressSchema.shape,
|
|
23681
25001
|
progressToken: ProgressTokenSchema
|
|
@@ -23694,7 +25014,7 @@ var PaginatedResultSchema = ResultSchema.extend({
|
|
|
23694
25014
|
nextCursor: CursorSchema.optional()
|
|
23695
25015
|
});
|
|
23696
25016
|
var TaskStatusSchema = _enum(["working", "input_required", "completed", "failed", "cancelled"]);
|
|
23697
|
-
var TaskSchema =
|
|
25017
|
+
var TaskSchema = object8({
|
|
23698
25018
|
taskId: string3(),
|
|
23699
25019
|
status: TaskStatusSchema,
|
|
23700
25020
|
ttl: union([number2(), _null3()]),
|
|
@@ -23738,10 +25058,10 @@ var CancelTaskRequestSchema = RequestSchema.extend({
|
|
|
23738
25058
|
})
|
|
23739
25059
|
});
|
|
23740
25060
|
var CancelTaskResultSchema = ResultSchema.merge(TaskSchema);
|
|
23741
|
-
var ResourceContentsSchema =
|
|
25061
|
+
var ResourceContentsSchema = object8({
|
|
23742
25062
|
uri: string3(),
|
|
23743
25063
|
mimeType: optional(string3()),
|
|
23744
|
-
_meta:
|
|
25064
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23745
25065
|
});
|
|
23746
25066
|
var TextResourceContentsSchema = ResourceContentsSchema.extend({
|
|
23747
25067
|
text: string3()
|
|
@@ -23758,12 +25078,12 @@ var BlobResourceContentsSchema = ResourceContentsSchema.extend({
|
|
|
23758
25078
|
blob: Base64Schema
|
|
23759
25079
|
});
|
|
23760
25080
|
var RoleSchema = _enum(["user", "assistant"]);
|
|
23761
|
-
var AnnotationsSchema =
|
|
25081
|
+
var AnnotationsSchema = object8({
|
|
23762
25082
|
audience: array(RoleSchema).optional(),
|
|
23763
25083
|
priority: number2().min(0).max(1).optional(),
|
|
23764
25084
|
lastModified: datetime2({ offset: true }).optional()
|
|
23765
25085
|
});
|
|
23766
|
-
var ResourceSchema =
|
|
25086
|
+
var ResourceSchema = object8({
|
|
23767
25087
|
...BaseMetadataSchema.shape,
|
|
23768
25088
|
...IconsSchema.shape,
|
|
23769
25089
|
uri: string3(),
|
|
@@ -23773,7 +25093,7 @@ var ResourceSchema = object6({
|
|
|
23773
25093
|
annotations: AnnotationsSchema.optional(),
|
|
23774
25094
|
_meta: optional(looseObject({}))
|
|
23775
25095
|
});
|
|
23776
|
-
var ResourceTemplateSchema =
|
|
25096
|
+
var ResourceTemplateSchema = object8({
|
|
23777
25097
|
...BaseMetadataSchema.shape,
|
|
23778
25098
|
...IconsSchema.shape,
|
|
23779
25099
|
uriTemplate: string3(),
|
|
@@ -23826,12 +25146,12 @@ var ResourceUpdatedNotificationSchema = NotificationSchema.extend({
|
|
|
23826
25146
|
method: literal("notifications/resources/updated"),
|
|
23827
25147
|
params: ResourceUpdatedNotificationParamsSchema
|
|
23828
25148
|
});
|
|
23829
|
-
var PromptArgumentSchema =
|
|
25149
|
+
var PromptArgumentSchema = object8({
|
|
23830
25150
|
name: string3(),
|
|
23831
25151
|
description: optional(string3()),
|
|
23832
25152
|
required: optional(boolean2())
|
|
23833
25153
|
});
|
|
23834
|
-
var PromptSchema =
|
|
25154
|
+
var PromptSchema = object8({
|
|
23835
25155
|
...BaseMetadataSchema.shape,
|
|
23836
25156
|
...IconsSchema.shape,
|
|
23837
25157
|
description: optional(string3()),
|
|
@@ -23846,44 +25166,44 @@ var ListPromptsResultSchema = PaginatedResultSchema.extend({
|
|
|
23846
25166
|
});
|
|
23847
25167
|
var GetPromptRequestParamsSchema = BaseRequestParamsSchema.extend({
|
|
23848
25168
|
name: string3(),
|
|
23849
|
-
arguments:
|
|
25169
|
+
arguments: record4(string3(), string3()).optional()
|
|
23850
25170
|
});
|
|
23851
25171
|
var GetPromptRequestSchema = RequestSchema.extend({
|
|
23852
25172
|
method: literal("prompts/get"),
|
|
23853
25173
|
params: GetPromptRequestParamsSchema
|
|
23854
25174
|
});
|
|
23855
|
-
var TextContentSchema =
|
|
25175
|
+
var TextContentSchema = object8({
|
|
23856
25176
|
type: literal("text"),
|
|
23857
25177
|
text: string3(),
|
|
23858
25178
|
annotations: AnnotationsSchema.optional(),
|
|
23859
|
-
_meta:
|
|
25179
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23860
25180
|
});
|
|
23861
|
-
var ImageContentSchema =
|
|
25181
|
+
var ImageContentSchema = object8({
|
|
23862
25182
|
type: literal("image"),
|
|
23863
25183
|
data: Base64Schema,
|
|
23864
25184
|
mimeType: string3(),
|
|
23865
25185
|
annotations: AnnotationsSchema.optional(),
|
|
23866
|
-
_meta:
|
|
25186
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23867
25187
|
});
|
|
23868
|
-
var AudioContentSchema =
|
|
25188
|
+
var AudioContentSchema = object8({
|
|
23869
25189
|
type: literal("audio"),
|
|
23870
25190
|
data: Base64Schema,
|
|
23871
25191
|
mimeType: string3(),
|
|
23872
25192
|
annotations: AnnotationsSchema.optional(),
|
|
23873
|
-
_meta:
|
|
25193
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23874
25194
|
});
|
|
23875
|
-
var ToolUseContentSchema =
|
|
25195
|
+
var ToolUseContentSchema = object8({
|
|
23876
25196
|
type: literal("tool_use"),
|
|
23877
25197
|
name: string3(),
|
|
23878
25198
|
id: string3(),
|
|
23879
|
-
input:
|
|
23880
|
-
_meta:
|
|
25199
|
+
input: record4(string3(), unknown()),
|
|
25200
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23881
25201
|
});
|
|
23882
|
-
var EmbeddedResourceSchema =
|
|
25202
|
+
var EmbeddedResourceSchema = object8({
|
|
23883
25203
|
type: literal("resource"),
|
|
23884
25204
|
resource: union([TextResourceContentsSchema, BlobResourceContentsSchema]),
|
|
23885
25205
|
annotations: AnnotationsSchema.optional(),
|
|
23886
|
-
_meta:
|
|
25206
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23887
25207
|
});
|
|
23888
25208
|
var ResourceLinkSchema = ResourceSchema.extend({
|
|
23889
25209
|
type: literal("resource_link")
|
|
@@ -23895,7 +25215,7 @@ var ContentBlockSchema = union([
|
|
|
23895
25215
|
ResourceLinkSchema,
|
|
23896
25216
|
EmbeddedResourceSchema
|
|
23897
25217
|
]);
|
|
23898
|
-
var PromptMessageSchema =
|
|
25218
|
+
var PromptMessageSchema = object8({
|
|
23899
25219
|
role: RoleSchema,
|
|
23900
25220
|
content: ContentBlockSchema
|
|
23901
25221
|
});
|
|
@@ -23907,33 +25227,33 @@ var PromptListChangedNotificationSchema = NotificationSchema.extend({
|
|
|
23907
25227
|
method: literal("notifications/prompts/list_changed"),
|
|
23908
25228
|
params: NotificationsParamsSchema.optional()
|
|
23909
25229
|
});
|
|
23910
|
-
var ToolAnnotationsSchema =
|
|
25230
|
+
var ToolAnnotationsSchema = object8({
|
|
23911
25231
|
title: string3().optional(),
|
|
23912
25232
|
readOnlyHint: boolean2().optional(),
|
|
23913
25233
|
destructiveHint: boolean2().optional(),
|
|
23914
25234
|
idempotentHint: boolean2().optional(),
|
|
23915
25235
|
openWorldHint: boolean2().optional()
|
|
23916
25236
|
});
|
|
23917
|
-
var ToolExecutionSchema =
|
|
25237
|
+
var ToolExecutionSchema = object8({
|
|
23918
25238
|
taskSupport: _enum(["required", "optional", "forbidden"]).optional()
|
|
23919
25239
|
});
|
|
23920
|
-
var ToolSchema =
|
|
25240
|
+
var ToolSchema = object8({
|
|
23921
25241
|
...BaseMetadataSchema.shape,
|
|
23922
25242
|
...IconsSchema.shape,
|
|
23923
25243
|
description: string3().optional(),
|
|
23924
|
-
inputSchema:
|
|
25244
|
+
inputSchema: object8({
|
|
23925
25245
|
type: literal("object"),
|
|
23926
|
-
properties:
|
|
25246
|
+
properties: record4(string3(), AssertObjectSchema).optional(),
|
|
23927
25247
|
required: array(string3()).optional()
|
|
23928
25248
|
}).catchall(unknown()),
|
|
23929
|
-
outputSchema:
|
|
25249
|
+
outputSchema: object8({
|
|
23930
25250
|
type: literal("object"),
|
|
23931
|
-
properties:
|
|
25251
|
+
properties: record4(string3(), AssertObjectSchema).optional(),
|
|
23932
25252
|
required: array(string3()).optional()
|
|
23933
25253
|
}).catchall(unknown()).optional(),
|
|
23934
25254
|
annotations: ToolAnnotationsSchema.optional(),
|
|
23935
25255
|
execution: ToolExecutionSchema.optional(),
|
|
23936
|
-
_meta:
|
|
25256
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23937
25257
|
});
|
|
23938
25258
|
var ListToolsRequestSchema = PaginatedRequestSchema.extend({
|
|
23939
25259
|
method: literal("tools/list")
|
|
@@ -23943,7 +25263,7 @@ var ListToolsResultSchema = PaginatedResultSchema.extend({
|
|
|
23943
25263
|
});
|
|
23944
25264
|
var CallToolResultSchema = ResultSchema.extend({
|
|
23945
25265
|
content: array(ContentBlockSchema).default([]),
|
|
23946
|
-
structuredContent:
|
|
25266
|
+
structuredContent: record4(string3(), unknown()).optional(),
|
|
23947
25267
|
isError: boolean2().optional()
|
|
23948
25268
|
});
|
|
23949
25269
|
var CompatibilityCallToolResultSchema = CallToolResultSchema.or(ResultSchema.extend({
|
|
@@ -23951,7 +25271,7 @@ var CompatibilityCallToolResultSchema = CallToolResultSchema.or(ResultSchema.ext
|
|
|
23951
25271
|
}));
|
|
23952
25272
|
var CallToolRequestParamsSchema = TaskAugmentedRequestParamsSchema.extend({
|
|
23953
25273
|
name: string3(),
|
|
23954
|
-
arguments:
|
|
25274
|
+
arguments: record4(string3(), unknown()).optional()
|
|
23955
25275
|
});
|
|
23956
25276
|
var CallToolRequestSchema = RequestSchema.extend({
|
|
23957
25277
|
method: literal("tools/call"),
|
|
@@ -23961,7 +25281,7 @@ var ToolListChangedNotificationSchema = NotificationSchema.extend({
|
|
|
23961
25281
|
method: literal("notifications/tools/list_changed"),
|
|
23962
25282
|
params: NotificationsParamsSchema.optional()
|
|
23963
25283
|
});
|
|
23964
|
-
var ListChangedOptionsBaseSchema =
|
|
25284
|
+
var ListChangedOptionsBaseSchema = object8({
|
|
23965
25285
|
autoRefresh: boolean2().default(true),
|
|
23966
25286
|
debounceMs: number2().int().nonnegative().default(300)
|
|
23967
25287
|
});
|
|
@@ -23982,25 +25302,25 @@ var LoggingMessageNotificationSchema = NotificationSchema.extend({
|
|
|
23982
25302
|
method: literal("notifications/message"),
|
|
23983
25303
|
params: LoggingMessageNotificationParamsSchema
|
|
23984
25304
|
});
|
|
23985
|
-
var ModelHintSchema =
|
|
25305
|
+
var ModelHintSchema = object8({
|
|
23986
25306
|
name: string3().optional()
|
|
23987
25307
|
});
|
|
23988
|
-
var ModelPreferencesSchema =
|
|
25308
|
+
var ModelPreferencesSchema = object8({
|
|
23989
25309
|
hints: array(ModelHintSchema).optional(),
|
|
23990
25310
|
costPriority: number2().min(0).max(1).optional(),
|
|
23991
25311
|
speedPriority: number2().min(0).max(1).optional(),
|
|
23992
25312
|
intelligencePriority: number2().min(0).max(1).optional()
|
|
23993
25313
|
});
|
|
23994
|
-
var ToolChoiceSchema =
|
|
25314
|
+
var ToolChoiceSchema = object8({
|
|
23995
25315
|
mode: _enum(["auto", "required", "none"]).optional()
|
|
23996
25316
|
});
|
|
23997
|
-
var ToolResultContentSchema =
|
|
25317
|
+
var ToolResultContentSchema = object8({
|
|
23998
25318
|
type: literal("tool_result"),
|
|
23999
25319
|
toolUseId: string3().describe("The unique identifier for the corresponding tool call."),
|
|
24000
25320
|
content: array(ContentBlockSchema).default([]),
|
|
24001
|
-
structuredContent:
|
|
25321
|
+
structuredContent: object8({}).loose().optional(),
|
|
24002
25322
|
isError: boolean2().optional(),
|
|
24003
|
-
_meta:
|
|
25323
|
+
_meta: record4(string3(), unknown()).optional()
|
|
24004
25324
|
});
|
|
24005
25325
|
var SamplingContentSchema = discriminatedUnion("type", [TextContentSchema, ImageContentSchema, AudioContentSchema]);
|
|
24006
25326
|
var SamplingMessageContentBlockSchema = discriminatedUnion("type", [
|
|
@@ -24010,10 +25330,10 @@ var SamplingMessageContentBlockSchema = discriminatedUnion("type", [
|
|
|
24010
25330
|
ToolUseContentSchema,
|
|
24011
25331
|
ToolResultContentSchema
|
|
24012
25332
|
]);
|
|
24013
|
-
var SamplingMessageSchema =
|
|
25333
|
+
var SamplingMessageSchema = object8({
|
|
24014
25334
|
role: RoleSchema,
|
|
24015
25335
|
content: union([SamplingMessageContentBlockSchema, array(SamplingMessageContentBlockSchema)]),
|
|
24016
|
-
_meta:
|
|
25336
|
+
_meta: record4(string3(), unknown()).optional()
|
|
24017
25337
|
});
|
|
24018
25338
|
var CreateMessageRequestParamsSchema = TaskAugmentedRequestParamsSchema.extend({
|
|
24019
25339
|
messages: array(SamplingMessageSchema),
|
|
@@ -24043,13 +25363,13 @@ var CreateMessageResultWithToolsSchema = ResultSchema.extend({
|
|
|
24043
25363
|
role: RoleSchema,
|
|
24044
25364
|
content: union([SamplingMessageContentBlockSchema, array(SamplingMessageContentBlockSchema)])
|
|
24045
25365
|
});
|
|
24046
|
-
var BooleanSchemaSchema =
|
|
25366
|
+
var BooleanSchemaSchema = object8({
|
|
24047
25367
|
type: literal("boolean"),
|
|
24048
25368
|
title: string3().optional(),
|
|
24049
25369
|
description: string3().optional(),
|
|
24050
25370
|
default: boolean2().optional()
|
|
24051
25371
|
});
|
|
24052
|
-
var StringSchemaSchema =
|
|
25372
|
+
var StringSchemaSchema = object8({
|
|
24053
25373
|
type: literal("string"),
|
|
24054
25374
|
title: string3().optional(),
|
|
24055
25375
|
description: string3().optional(),
|
|
@@ -24058,7 +25378,7 @@ var StringSchemaSchema = object6({
|
|
|
24058
25378
|
format: _enum(["email", "uri", "date", "date-time"]).optional(),
|
|
24059
25379
|
default: string3().optional()
|
|
24060
25380
|
});
|
|
24061
|
-
var NumberSchemaSchema =
|
|
25381
|
+
var NumberSchemaSchema = object8({
|
|
24062
25382
|
type: _enum(["number", "integer"]),
|
|
24063
25383
|
title: string3().optional(),
|
|
24064
25384
|
description: string3().optional(),
|
|
@@ -24066,24 +25386,24 @@ var NumberSchemaSchema = object6({
|
|
|
24066
25386
|
maximum: number2().optional(),
|
|
24067
25387
|
default: number2().optional()
|
|
24068
25388
|
});
|
|
24069
|
-
var UntitledSingleSelectEnumSchemaSchema =
|
|
25389
|
+
var UntitledSingleSelectEnumSchemaSchema = object8({
|
|
24070
25390
|
type: literal("string"),
|
|
24071
25391
|
title: string3().optional(),
|
|
24072
25392
|
description: string3().optional(),
|
|
24073
25393
|
enum: array(string3()),
|
|
24074
25394
|
default: string3().optional()
|
|
24075
25395
|
});
|
|
24076
|
-
var TitledSingleSelectEnumSchemaSchema =
|
|
25396
|
+
var TitledSingleSelectEnumSchemaSchema = object8({
|
|
24077
25397
|
type: literal("string"),
|
|
24078
25398
|
title: string3().optional(),
|
|
24079
25399
|
description: string3().optional(),
|
|
24080
|
-
oneOf: array(
|
|
25400
|
+
oneOf: array(object8({
|
|
24081
25401
|
const: string3(),
|
|
24082
25402
|
title: string3()
|
|
24083
25403
|
})),
|
|
24084
25404
|
default: string3().optional()
|
|
24085
25405
|
});
|
|
24086
|
-
var LegacyTitledEnumSchemaSchema =
|
|
25406
|
+
var LegacyTitledEnumSchemaSchema = object8({
|
|
24087
25407
|
type: literal("string"),
|
|
24088
25408
|
title: string3().optional(),
|
|
24089
25409
|
description: string3().optional(),
|
|
@@ -24092,26 +25412,26 @@ var LegacyTitledEnumSchemaSchema = object6({
|
|
|
24092
25412
|
default: string3().optional()
|
|
24093
25413
|
});
|
|
24094
25414
|
var SingleSelectEnumSchemaSchema = union([UntitledSingleSelectEnumSchemaSchema, TitledSingleSelectEnumSchemaSchema]);
|
|
24095
|
-
var UntitledMultiSelectEnumSchemaSchema =
|
|
25415
|
+
var UntitledMultiSelectEnumSchemaSchema = object8({
|
|
24096
25416
|
type: literal("array"),
|
|
24097
25417
|
title: string3().optional(),
|
|
24098
25418
|
description: string3().optional(),
|
|
24099
25419
|
minItems: number2().optional(),
|
|
24100
25420
|
maxItems: number2().optional(),
|
|
24101
|
-
items:
|
|
25421
|
+
items: object8({
|
|
24102
25422
|
type: literal("string"),
|
|
24103
25423
|
enum: array(string3())
|
|
24104
25424
|
}),
|
|
24105
25425
|
default: array(string3()).optional()
|
|
24106
25426
|
});
|
|
24107
|
-
var TitledMultiSelectEnumSchemaSchema =
|
|
25427
|
+
var TitledMultiSelectEnumSchemaSchema = object8({
|
|
24108
25428
|
type: literal("array"),
|
|
24109
25429
|
title: string3().optional(),
|
|
24110
25430
|
description: string3().optional(),
|
|
24111
25431
|
minItems: number2().optional(),
|
|
24112
25432
|
maxItems: number2().optional(),
|
|
24113
|
-
items:
|
|
24114
|
-
anyOf: array(
|
|
25433
|
+
items: object8({
|
|
25434
|
+
anyOf: array(object8({
|
|
24115
25435
|
const: string3(),
|
|
24116
25436
|
title: string3()
|
|
24117
25437
|
}))
|
|
@@ -24124,9 +25444,9 @@ var PrimitiveSchemaDefinitionSchema = union([EnumSchemaSchema, BooleanSchemaSche
|
|
|
24124
25444
|
var ElicitRequestFormParamsSchema = TaskAugmentedRequestParamsSchema.extend({
|
|
24125
25445
|
mode: literal("form").optional(),
|
|
24126
25446
|
message: string3(),
|
|
24127
|
-
requestedSchema:
|
|
25447
|
+
requestedSchema: object8({
|
|
24128
25448
|
type: literal("object"),
|
|
24129
|
-
properties:
|
|
25449
|
+
properties: record4(string3(), PrimitiveSchemaDefinitionSchema),
|
|
24130
25450
|
required: array(string3()).optional()
|
|
24131
25451
|
})
|
|
24132
25452
|
});
|
|
@@ -24150,24 +25470,24 @@ var ElicitationCompleteNotificationSchema = NotificationSchema.extend({
|
|
|
24150
25470
|
});
|
|
24151
25471
|
var ElicitResultSchema = ResultSchema.extend({
|
|
24152
25472
|
action: _enum(["accept", "decline", "cancel"]),
|
|
24153
|
-
content: preprocess((val) => val === null ? undefined : val,
|
|
25473
|
+
content: preprocess((val) => val === null ? undefined : val, record4(string3(), union([string3(), number2(), boolean2(), array(string3())])).optional())
|
|
24154
25474
|
});
|
|
24155
|
-
var ResourceTemplateReferenceSchema =
|
|
25475
|
+
var ResourceTemplateReferenceSchema = object8({
|
|
24156
25476
|
type: literal("ref/resource"),
|
|
24157
25477
|
uri: string3()
|
|
24158
25478
|
});
|
|
24159
|
-
var PromptReferenceSchema =
|
|
25479
|
+
var PromptReferenceSchema = object8({
|
|
24160
25480
|
type: literal("ref/prompt"),
|
|
24161
25481
|
name: string3()
|
|
24162
25482
|
});
|
|
24163
25483
|
var CompleteRequestParamsSchema = BaseRequestParamsSchema.extend({
|
|
24164
25484
|
ref: union([PromptReferenceSchema, ResourceTemplateReferenceSchema]),
|
|
24165
|
-
argument:
|
|
25485
|
+
argument: object8({
|
|
24166
25486
|
name: string3(),
|
|
24167
25487
|
value: string3()
|
|
24168
25488
|
}),
|
|
24169
|
-
context:
|
|
24170
|
-
arguments:
|
|
25489
|
+
context: object8({
|
|
25490
|
+
arguments: record4(string3(), string3()).optional()
|
|
24171
25491
|
}).optional()
|
|
24172
25492
|
});
|
|
24173
25493
|
var CompleteRequestSchema = RequestSchema.extend({
|
|
@@ -24181,10 +25501,10 @@ var CompleteResultSchema = ResultSchema.extend({
|
|
|
24181
25501
|
hasMore: optional(boolean2())
|
|
24182
25502
|
})
|
|
24183
25503
|
});
|
|
24184
|
-
var RootSchema =
|
|
25504
|
+
var RootSchema = object8({
|
|
24185
25505
|
uri: string3().startsWith("file://"),
|
|
24186
25506
|
name: string3().optional(),
|
|
24187
|
-
_meta:
|
|
25507
|
+
_meta: record4(string3(), unknown()).optional()
|
|
24188
25508
|
});
|
|
24189
25509
|
var ListRootsRequestSchema = RequestSchema.extend({
|
|
24190
25510
|
method: literal("roots/list"),
|
|
@@ -25754,8 +27074,8 @@ class StdioServerTransport {
|
|
|
25754
27074
|
}
|
|
25755
27075
|
|
|
25756
27076
|
// src/adapters/claude-channel.ts
|
|
25757
|
-
import { readFileSync as
|
|
25758
|
-
import { join as
|
|
27077
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
27078
|
+
import { join as join28 } from "path";
|
|
25759
27079
|
init_hub_tools();
|
|
25760
27080
|
init_envelope();
|
|
25761
27081
|
|
|
@@ -25794,7 +27114,7 @@ var channel = !toolsMode && process.env.AGENTHUB_CHANNEL === "1";
|
|
|
25794
27114
|
function roles2() {
|
|
25795
27115
|
const read = (name) => {
|
|
25796
27116
|
try {
|
|
25797
|
-
return JSON.parse(
|
|
27117
|
+
return JSON.parse(readFileSync14(join28(projectRoot2, ".agenthub", name), "utf8")).roles ?? {};
|
|
25798
27118
|
} catch {
|
|
25799
27119
|
return {};
|
|
25800
27120
|
}
|
|
@@ -25813,7 +27133,7 @@ function sessionProfile() {
|
|
|
25813
27133
|
for (const name of ["config.json", "config.local.json"]) {
|
|
25814
27134
|
let data;
|
|
25815
27135
|
try {
|
|
25816
|
-
data = JSON.parse(
|
|
27136
|
+
data = JSON.parse(readFileSync14(join28(projectRoot2, ".agenthub", name), "utf8"));
|
|
25817
27137
|
} catch (error) {
|
|
25818
27138
|
if (error.code === "ENOENT")
|
|
25819
27139
|
continue;
|
|
@@ -25840,7 +27160,7 @@ function sessionRoles() {
|
|
|
25840
27160
|
const read = (name) => {
|
|
25841
27161
|
let raw;
|
|
25842
27162
|
try {
|
|
25843
|
-
raw = JSON.parse(
|
|
27163
|
+
raw = JSON.parse(readFileSync14(join28(projectRoot2, ".agenthub", name), "utf8")).catalog_roles;
|
|
25844
27164
|
} catch {
|
|
25845
27165
|
return {};
|
|
25846
27166
|
}
|
|
@@ -25869,7 +27189,7 @@ var HELD = `another session is attached to the hub as "${peerId}"; this one is s
|
|
|
25869
27189
|
var INBOX_CAP = 200;
|
|
25870
27190
|
function peerHeld() {
|
|
25871
27191
|
try {
|
|
25872
|
-
const status = JSON.parse(
|
|
27192
|
+
const status = JSON.parse(readFileSync14(join28(stateDir, "status.json"), "utf8"));
|
|
25873
27193
|
const peer = status.peers?.[peerId];
|
|
25874
27194
|
if (channel && peer?.toolsOnly && !peer.claiming)
|
|
25875
27195
|
return false;
|
|
@@ -25904,7 +27224,7 @@ var TOOLS_INSTRUCTIONS = ["agent-hub task tools for this project. Messages from
|
|
|
25904
27224
|
`);
|
|
25905
27225
|
var log = (line) => console.error(`[agent-hub] ${line}`);
|
|
25906
27226
|
var text4 = (s) => ({ content: [{ type: "text", text: s }] });
|
|
25907
|
-
var server = new Server({ name: "agent-hub", version:
|
|
27227
|
+
var server = new Server({ name: "agent-hub", version: VERSION3 }, toolsMode ? { capabilities: { tools: {} }, instructions: TOOLS_INSTRUCTIONS } : { capabilities: channel ? { experimental: { "claude/channel": {} }, tools: {} } : { tools: {} }, instructions: INSTRUCTIONS });
|
|
25908
27228
|
var inbox = [];
|
|
25909
27229
|
var hub;
|
|
25910
27230
|
var detached;
|
|
@@ -26144,6 +27464,8 @@ async function callTool(req) {
|
|
|
26144
27464
|
return text4(`not sent: ${res.error}`);
|
|
26145
27465
|
if (res.recorded)
|
|
26146
27466
|
return text4("recorded only ([FYI]): it is on the hub console and log, and no peer spent a turn on it");
|
|
27467
|
+
if (res.dropped === "hop")
|
|
27468
|
+
return text4(res.notice);
|
|
26147
27469
|
const sent = `sent to: ${res.targets.join(", ") || "(no other peers attached)"}`;
|
|
26148
27470
|
return text4(typeof res.notice === "string" ? `${sent}; ${res.notice}` : sent);
|
|
26149
27471
|
}
|