@staix/agent-hub 0.12.37 → 0.12.38
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 +11 -0
- package/README.md +8 -6
- package/docs/agent-notes/adapters.md +16 -7
- package/docs/agent-notes/bus.md +3 -2
- package/docs/agent-notes/daemon.md +5 -3
- package/docs/agent-notes/tasks.md +4 -3
- package/docs/agent-notes/tests.md +2 -2
- package/docs/operations.md +21 -6
- package/docs/quickstart.md +13 -5
- package/docs/security.md +5 -1
- package/docs/smoke.md +22 -2
- package/docs/specs/2026-09-19-agent-hub-design.md +29 -5
- 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/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 +1507 -405
- 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 +5 -5
- 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 +2 -0
- 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.ts +2 -0
- package/src/adapters/definitions/native-cli-retained.ts +15 -0
- package/src/adapters/definitions/qwen.ts +1 -1
- package/src/adapters/definitions/reviewer-variants.ts +4 -3
- package/src/adapters/kimi-review.ts +161 -0
- package/src/adapters/registry-types.ts +29 -1
- package/src/adapters/registry.ts +15 -6
- 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 +1 -0
- package/src/cli/output.ts +5 -0
- package/src/hub/bus.ts +17 -6
- package/src/hub/control-client.ts +7 -1
- package/src/hub/daemon.ts +169 -23
- package/src/hub/native-hook-launch.ts +37 -0
- package/src/hub/peer-catalog.ts +2 -3
- package/src/hub/reviewer-pool.ts +12 -6
- package/src/hub/routing.ts +3 -0
- package/src/hub/tasks.ts +7 -0
- package/src/ui/index.html +6 -1
- package/templates/AGENTS.block.md +1 -1
|
@@ -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;
|
|
757
|
+
try {
|
|
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(() => {});
|
|
729
782
|
try {
|
|
730
|
-
|
|
731
|
-
|
|
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" };
|
|
@@ -2314,7 +2673,7 @@ var init_acp = __esm(() => {
|
|
|
2314
2673
|
async start() {
|
|
2315
2674
|
this.stopping = false;
|
|
2316
2675
|
const [bin, ...args] = this.opts.cmd;
|
|
2317
|
-
const proc =
|
|
2676
|
+
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
2677
|
this.proc = proc;
|
|
2319
2678
|
trackGroup(proc);
|
|
2320
2679
|
proc.on("error", (e) => this.down(`spawn failed: ${e.message}`));
|
|
@@ -2744,10 +3103,10 @@ ${renderDigest(envs, false)}` }] });
|
|
|
2744
3103
|
});
|
|
2745
3104
|
|
|
2746
3105
|
// src/adapters/jcode.ts
|
|
2747
|
-
import { spawn as
|
|
2748
|
-
import { existsSync as
|
|
2749
|
-
import { tmpdir as
|
|
2750
|
-
import { isAbsolute as isAbsolute3, join as
|
|
3106
|
+
import { spawn as spawn6, spawnSync as spawnSync2 } from "child_process";
|
|
3107
|
+
import { existsSync as existsSync4, lstatSync as lstatSync4, mkdtempSync as mkdtempSync3, rmSync as rmSync3 } from "fs";
|
|
3108
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
3109
|
+
import { isAbsolute as isAbsolute3, join as join5 } from "path";
|
|
2751
3110
|
function jcodeCommand(command, kind, socket, cwd, model, profile) {
|
|
2752
3111
|
if (!command.length || command.some((word) => typeof word !== "string" || !word))
|
|
2753
3112
|
throw new Error("jcode_cmd must name an executable argv");
|
|
@@ -2786,7 +3145,7 @@ var init_jcode = __esm(() => {
|
|
|
2786
3145
|
if (!isAbsolute3(jcode.home))
|
|
2787
3146
|
throw new Error("jcode_home must be an absolute native data path");
|
|
2788
3147
|
jcodeCommand(jcode.command, "acp", "preview", jcode.cwd);
|
|
2789
|
-
const root =
|
|
3148
|
+
const root = mkdtempSync3(join5(tmpdir3(), "ahub-jcode-")), socket = join5(root, "j.sock");
|
|
2790
3149
|
const cmd = jcodeCommand(jcode.command, "acp", socket, jcode.cwd, jcode.launchModel, jcode.providerProfile);
|
|
2791
3150
|
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
3151
|
this.jcode = jcode;
|
|
@@ -2794,15 +3153,15 @@ var init_jcode = __esm(() => {
|
|
|
2794
3153
|
this.socket = socket;
|
|
2795
3154
|
}
|
|
2796
3155
|
async start() {
|
|
2797
|
-
if (!
|
|
3156
|
+
if (!existsSync4(join5(this.jcode.home, "mcp.json")))
|
|
2798
3157
|
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 (
|
|
3158
|
+
if (existsSync4(this.socket)) {
|
|
2800
3159
|
this.preserveRoot = true;
|
|
2801
3160
|
throw new Error("Jcode private socket is occupied; no daemon was adopted or removed");
|
|
2802
3161
|
}
|
|
2803
3162
|
this.ending = false;
|
|
2804
3163
|
const [bin, ...args] = jcodeCommand(this.jcode.command, "serve", this.socket, this.jcode.cwd, this.jcode.launchModel, this.jcode.providerProfile);
|
|
2805
|
-
const server =
|
|
3164
|
+
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
3165
|
this.server = server;
|
|
2807
3166
|
trackGroup(server);
|
|
2808
3167
|
try {
|
|
@@ -2818,7 +3177,7 @@ var init_jcode = __esm(() => {
|
|
|
2818
3177
|
ready.once("data", () => {
|
|
2819
3178
|
clearTimeout(timer);
|
|
2820
3179
|
server.off("exit", fail);
|
|
2821
|
-
if (
|
|
3180
|
+
if (lstatSync4(this.socket, { throwIfNoEntry: false })?.isSocket())
|
|
2822
3181
|
resolve();
|
|
2823
3182
|
else
|
|
2824
3183
|
reject(new Error("Jcode readiness has no private socket"));
|
|
@@ -2887,7 +3246,7 @@ var init_jcode = __esm(() => {
|
|
|
2887
3246
|
}
|
|
2888
3247
|
if (!failure && !this.server) {
|
|
2889
3248
|
if (!this.preserveRoot)
|
|
2890
|
-
|
|
3249
|
+
rmSync3(this.root, { recursive: true, force: true });
|
|
2891
3250
|
super.setState("offline");
|
|
2892
3251
|
}
|
|
2893
3252
|
if (failure)
|
|
@@ -2900,11 +3259,11 @@ var init_jcode = __esm(() => {
|
|
|
2900
3259
|
});
|
|
2901
3260
|
|
|
2902
3261
|
// src/pi/launch.ts
|
|
2903
|
-
import { join as
|
|
3262
|
+
import { join as join6 } from "path";
|
|
2904
3263
|
function buildPiLaunch(opts, inherited, extension, bridgePort, bridgeToken) {
|
|
2905
3264
|
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",
|
|
3265
|
+
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) };
|
|
3266
|
+
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
3267
|
if (opts.sessionFile)
|
|
2909
3268
|
args.push("--session", opts.sessionFile);
|
|
2910
3269
|
else if (opts.sessionId)
|
|
@@ -2940,8 +3299,8 @@ var init_recovery_store = __esm(() => {
|
|
|
2940
3299
|
});
|
|
2941
3300
|
|
|
2942
3301
|
// src/pi/session.ts
|
|
2943
|
-
import { readFileSync as
|
|
2944
|
-
import { join as
|
|
3302
|
+
import { readFileSync as readFileSync4, openSync, closeSync, fsyncSync, mkdirSync as mkdirSync3 } from "fs";
|
|
3303
|
+
import { join as join7, relative as relative2, resolve as resolve5, dirname as dirname5 } from "path";
|
|
2945
3304
|
function sessionReadFailure(error) {
|
|
2946
3305
|
if (error.code === "ENOENT")
|
|
2947
3306
|
throw new PiSessionUnresumable("missing", error.message, { cause: error });
|
|
@@ -2951,16 +3310,16 @@ function validatePiSession(stateDir, cwd, sessionFile, sessionId) {
|
|
|
2951
3310
|
let file, sessions;
|
|
2952
3311
|
try {
|
|
2953
3312
|
file = realPath(sessionFile);
|
|
2954
|
-
sessions = realPath(
|
|
3313
|
+
sessions = realPath(join7(stateDir, "pi-sessions"));
|
|
2955
3314
|
} catch (error) {
|
|
2956
3315
|
return sessionReadFailure(error);
|
|
2957
3316
|
}
|
|
2958
3317
|
const rel = relative2(sessions, file);
|
|
2959
|
-
if (!rel || rel.startsWith("..") ||
|
|
3318
|
+
if (!rel || rel.startsWith("..") || resolve5(sessions, rel) !== file)
|
|
2960
3319
|
throw new PiSessionUnresumable("outside_managed", "Pi session file is outside the managed project session directory");
|
|
2961
3320
|
let first;
|
|
2962
3321
|
try {
|
|
2963
|
-
first =
|
|
3322
|
+
first = readFileSync4(file, "utf8").split(`
|
|
2964
3323
|
`, 1)[0];
|
|
2965
3324
|
} catch (error) {
|
|
2966
3325
|
return sessionReadFailure(error);
|
|
@@ -3009,11 +3368,11 @@ var init_permission_mode = __esm(() => {
|
|
|
3009
3368
|
});
|
|
3010
3369
|
|
|
3011
3370
|
// src/hub/path-policy.ts
|
|
3012
|
-
import { accessSync, constants, lstatSync as
|
|
3013
|
-
import { dirname as
|
|
3371
|
+
import { accessSync, constants, lstatSync as lstatSync5, opendirSync, readlinkSync, statSync as statSync2 } from "fs";
|
|
3372
|
+
import { dirname as dirname6, isAbsolute as isAbsolute4, join as join8, relative as relative3, resolve as resolve6, sep as sep2 } from "path";
|
|
3014
3373
|
function existing(path) {
|
|
3015
3374
|
try {
|
|
3016
|
-
|
|
3375
|
+
lstatSync5(path);
|
|
3017
3376
|
return true;
|
|
3018
3377
|
} catch (error) {
|
|
3019
3378
|
if (error.code === "ENOENT")
|
|
@@ -3024,17 +3383,17 @@ function existing(path) {
|
|
|
3024
3383
|
function policyRealPath(path) {
|
|
3025
3384
|
let ancestor = path;
|
|
3026
3385
|
while (!existing(ancestor)) {
|
|
3027
|
-
const parent =
|
|
3386
|
+
const parent = dirname6(ancestor);
|
|
3028
3387
|
if (parent === ancestor)
|
|
3029
3388
|
throw new Error("path cannot be resolved");
|
|
3030
3389
|
ancestor = parent;
|
|
3031
3390
|
}
|
|
3032
|
-
return
|
|
3391
|
+
return join8(realPath(ancestor), relative3(ancestor, path));
|
|
3033
3392
|
}
|
|
3034
3393
|
function policyTargets(cwd, paths) {
|
|
3035
3394
|
const root = realPath(cwd);
|
|
3036
3395
|
return [...new Set(paths.flatMap((path) => {
|
|
3037
|
-
const lexical =
|
|
3396
|
+
const lexical = resolve6(root, path);
|
|
3038
3397
|
return [lexical, policyRealPath(lexical)];
|
|
3039
3398
|
}))];
|
|
3040
3399
|
}
|
|
@@ -3052,14 +3411,14 @@ function directAliasTarget(path) {
|
|
|
3052
3411
|
}
|
|
3053
3412
|
let ancestor = target;
|
|
3054
3413
|
while (!existing(ancestor)) {
|
|
3055
|
-
const parent =
|
|
3414
|
+
const parent = dirname6(ancestor);
|
|
3056
3415
|
if (parent === ancestor)
|
|
3057
3416
|
return;
|
|
3058
3417
|
ancestor = parent;
|
|
3059
3418
|
}
|
|
3060
|
-
if (!
|
|
3419
|
+
if (!lstatSync5(ancestor).isSymbolicLink())
|
|
3061
3420
|
return;
|
|
3062
|
-
target =
|
|
3421
|
+
target = resolve6(dirname6(ancestor), readlinkSync(ancestor), relative3(ancestor, target));
|
|
3063
3422
|
}
|
|
3064
3423
|
return;
|
|
3065
3424
|
}
|
|
@@ -3076,7 +3435,7 @@ function aliasScope(dir, lexical, actual, includeParents, budget) {
|
|
|
3076
3435
|
budget.entries++;
|
|
3077
3436
|
if (!entry.isSymbolicLink())
|
|
3078
3437
|
continue;
|
|
3079
|
-
const target = directAliasTarget(
|
|
3438
|
+
const target = directAliasTarget(join8(dir, entry.name));
|
|
3080
3439
|
if (target !== undefined && (beneath(lexical, target) || beneath(actual, target) || includeParents && (beneath(target, lexical) || beneath(target, actual))))
|
|
3081
3440
|
matched = true;
|
|
3082
3441
|
}
|
|
@@ -3172,7 +3531,7 @@ function globScope(root, lexical, actual, path, includeParents, budget, roots, a
|
|
|
3172
3531
|
if (++budget.entries > 4096)
|
|
3173
3532
|
throw new Error("policy glob discovery unavailable");
|
|
3174
3533
|
const next = advance(pattern, states, entry.name);
|
|
3175
|
-
if (next.size && visit(
|
|
3534
|
+
if (next.size && visit(join8(dir, entry.name), next, depth + 1, chain))
|
|
3176
3535
|
return true;
|
|
3177
3536
|
}
|
|
3178
3537
|
} finally {
|
|
@@ -3188,7 +3547,7 @@ function matchesPolicy(cwd, path, paths, includeParents = false) {
|
|
|
3188
3547
|
try {
|
|
3189
3548
|
if (path.length > 4096)
|
|
3190
3549
|
return true;
|
|
3191
|
-
const root = realPath(cwd), lexical =
|
|
3550
|
+
const root = realPath(cwd), lexical = resolve6(root, path), actual = policyRealPath(lexical);
|
|
3192
3551
|
includeParents &&= !existing(lexical) || statSync2(lexical).isDirectory();
|
|
3193
3552
|
const budget = { entries: 0 }, roots = new Set, exact = [];
|
|
3194
3553
|
if (paths.some((entry) => {
|
|
@@ -3223,7 +3582,7 @@ var init_path_policy = __esm(() => {
|
|
|
3223
3582
|
});
|
|
3224
3583
|
|
|
3225
3584
|
// src/local/deny.ts
|
|
3226
|
-
import { basename as
|
|
3585
|
+
import { basename as basename4, dirname as dirname7 } from "path";
|
|
3227
3586
|
function hubWriteRegexes(root, gitDirs = [], moduleGit) {
|
|
3228
3587
|
const git = hfsFoldedName(".git");
|
|
3229
3588
|
const re = (s) => `(regex ${sbplString(s)})`;
|
|
@@ -3235,7 +3594,7 @@ function hubWriteRegexes(root, gitDirs = [], moduleGit) {
|
|
|
3235
3594
|
const modules = moduleGit ? [
|
|
3236
3595
|
`(require-all ${re(`^${sbplEscape(moduleGit.root)}/(.*/)?${hfsFoldedName("config")}$`)} (require-not ${re(`^${sbplEscape(moduleGit.root)}/(.*/)?(refs|logs)/`)}))`,
|
|
3237
3596
|
`(require-all ${re(`^${sbplEscape(moduleGit.root)}/(.*/)?${hfsFoldedName("hooks")}(/|$)`)} (require-not ${re(`^${sbplEscape(moduleGit.root)}/(.*/)?(refs|logs)/`)}))`,
|
|
3238
|
-
...moduleGit.entries.map((d) => re(`^${sbplEscape(
|
|
3597
|
+
...moduleGit.entries.map((d) => re(`^${sbplEscape(dirname7(d))}/${hfsFoldedName(basename4(d))}$`))
|
|
3239
3598
|
] : [];
|
|
3240
3599
|
return [
|
|
3241
3600
|
`(require-all (subpath ${sbplString(root)}) ${re(`^.*/${hfsFoldedName(".agenthub")}(/|$)`)})`,
|
|
@@ -3267,10 +3626,10 @@ var init_deny = __esm(() => {
|
|
|
3267
3626
|
});
|
|
3268
3627
|
|
|
3269
3628
|
// src/local/sandbox.ts
|
|
3270
|
-
import { spawn as
|
|
3271
|
-
import { existsSync as
|
|
3272
|
-
import { homedir as
|
|
3273
|
-
import { dirname as
|
|
3629
|
+
import { spawn as spawn7, spawnSync as spawnSync3 } from "child_process";
|
|
3630
|
+
import { existsSync as existsSync5, mkdtempSync as mkdtempSync4, readdirSync, rmSync as rmSync4 } from "fs";
|
|
3631
|
+
import { homedir as homedir3, tmpdir as tmpdir4 } from "os";
|
|
3632
|
+
import { dirname as dirname8, join as join9, resolve as resolve7 } from "path";
|
|
3274
3633
|
function developerDir() {
|
|
3275
3634
|
const out = spawnSync3("xcode-select", ["-p"], { encoding: "utf8" });
|
|
3276
3635
|
let dir = out.status === 0 ? out.stdout.trim() : "";
|
|
@@ -3284,14 +3643,14 @@ function externalGitDirs(root) {
|
|
|
3284
3643
|
if (out.status !== 0)
|
|
3285
3644
|
return [];
|
|
3286
3645
|
const dirs = out.stdout.trim().split(`
|
|
3287
|
-
`).map((d) =>
|
|
3646
|
+
`).map((d) => resolve7(root, d));
|
|
3288
3647
|
return [...new Set(dirs)].filter((d) => !d.startsWith(`${root}/`));
|
|
3289
3648
|
}
|
|
3290
3649
|
function moduleGitDirs(root) {
|
|
3291
3650
|
const out = memoryGitSync(["-C", root, "rev-parse", "--git-common-dir"], { encoding: "utf8" });
|
|
3292
3651
|
if (out.status !== 0)
|
|
3293
3652
|
return;
|
|
3294
|
-
const modulesRoot =
|
|
3653
|
+
const modulesRoot = join9(resolve7(root, out.stdout.trim()), "modules");
|
|
3295
3654
|
const dirs = [];
|
|
3296
3655
|
const walk = (dir, depth) => {
|
|
3297
3656
|
if (depth > 8)
|
|
@@ -3305,31 +3664,31 @@ function moduleGitDirs(root) {
|
|
|
3305
3664
|
const names = new Set(entries.map((e) => e.name));
|
|
3306
3665
|
if (names.has("HEAD") && names.has("config")) {
|
|
3307
3666
|
dirs.push(dir);
|
|
3308
|
-
const nested =
|
|
3667
|
+
const nested = join9(dir, "modules");
|
|
3309
3668
|
if (names.has("modules"))
|
|
3310
3669
|
walk(nested, depth + 1);
|
|
3311
3670
|
return;
|
|
3312
3671
|
}
|
|
3313
3672
|
for (const e of entries)
|
|
3314
3673
|
if (e.isDirectory())
|
|
3315
|
-
walk(
|
|
3674
|
+
walk(join9(dir, e.name), depth + 1);
|
|
3316
3675
|
};
|
|
3317
3676
|
walk(modulesRoot, 0);
|
|
3318
3677
|
const entries = new Set([modulesRoot]);
|
|
3319
3678
|
for (const d of dirs)
|
|
3320
|
-
for (let p = d;p.length > modulesRoot.length; p =
|
|
3679
|
+
for (let p = d;p.length > modulesRoot.length; p = dirname8(p))
|
|
3321
3680
|
entries.add(p);
|
|
3322
3681
|
return { root: modulesRoot, entries: [...entries] };
|
|
3323
3682
|
}
|
|
3324
3683
|
function profile(cwd, network, readAllow = [], deny = [], readOnly = []) {
|
|
3325
|
-
const home =
|
|
3326
|
-
const inHome = (p) => p.startsWith("~/") ?
|
|
3684
|
+
const home = homedir3();
|
|
3685
|
+
const inHome = (p) => p.startsWith("~/") ? join9(home, p.slice(2)) : p;
|
|
3327
3686
|
const root = realPath(cwd);
|
|
3328
3687
|
const gitDirs = externalGitDirs(root);
|
|
3329
3688
|
const moduleGit = moduleGitDirs(root);
|
|
3330
3689
|
const creds = [".ssh", ".aws", ".gnupg", ".config/gh", ".config/gcloud", ".kube", ".docker", ".netrc", ".npmrc", ".omniroute", ".claude", ".codex", ".kimi-code", "Library/Keychains"];
|
|
3331
3690
|
const dev = developerDir();
|
|
3332
|
-
const readable = [root, ...HOME_READABLE.map((p) =>
|
|
3691
|
+
const readable = [root, ...HOME_READABLE.map((p) => join9(home, p)), ...readAllow.map(inHome), ...gitDirs, ...dev ? [dev] : []];
|
|
3333
3692
|
const subpaths = (paths) => paths.map((p) => `(subpath ${q(p)})`).join(" ");
|
|
3334
3693
|
const globals = (names) => names.map((n) => `(global-name ${q(n)})`).join(" ");
|
|
3335
3694
|
const proxy = typeof network === "object" ? `(allow network-outbound (remote ip ${q(`localhost:${network.proxyPort}`)}))` : undefined;
|
|
@@ -3347,14 +3706,14 @@ function profile(cwd, network, readAllow = [], deny = [], readOnly = []) {
|
|
|
3347
3706
|
'(allow file-ioctl (regex #"^/dev/"))',
|
|
3348
3707
|
...proxy ? [proxy] : network ? ["(allow network*)"] : []
|
|
3349
3708
|
];
|
|
3350
|
-
const places = `${creds.map((c) => `(subpath ${q(
|
|
3709
|
+
const places = `${creds.map((c) => `(subpath ${q(join9(home, c))})`).join(" ")} ${denyRegexes(root, deny, false).join(" ")}`;
|
|
3351
3710
|
return [
|
|
3352
3711
|
"(version 1)",
|
|
3353
3712
|
...start,
|
|
3354
3713
|
"(deny file-write*)",
|
|
3355
3714
|
...readOnly.length ? ['(allow file-write* (literal "/dev/null"))'] : [`(allow file-write* (subpath ${q(root)}) (regex #"^/dev/") ${gitDirs.map((d) => `(subpath ${q(d)})`).join(" ")})`],
|
|
3356
3715
|
`(deny file-write* ${hubWriteRegexes(root, gitDirs, moduleGit).join(" ")})`,
|
|
3357
|
-
`(deny file-read* file-write* ${creds.map((c) => `(subpath ${q(
|
|
3716
|
+
`(deny file-read* file-write* ${creds.map((c) => `(subpath ${q(join9(home, c))})`).join(" ")})`,
|
|
3358
3717
|
`(deny file-read* file-write* ${denyRegexes(root, deny).join(" ")})`,
|
|
3359
3718
|
...network ? [`(allow file-read* ${CA_BUNDLES.map((p) => `(literal ${q(p)})`).join(" ")} (regex ${q("/certifi/cacert\\.pem$")}))`, `(deny file-read* file-write* ${places})`] : []
|
|
3360
3719
|
].join(`
|
|
@@ -3368,11 +3727,11 @@ function sandboxedExec(argv, opts) {
|
|
|
3368
3727
|
if (opts.signal?.aborted)
|
|
3369
3728
|
return Promise.resolve({ code: null, output: "error: command cancelled before execution" });
|
|
3370
3729
|
return new Promise((resolve) => {
|
|
3371
|
-
const own = realPath(
|
|
3730
|
+
const own = realPath(mkdtempSync4(join9(tmpdir4(), "ahub-cmd-")));
|
|
3372
3731
|
const sandbox = `${opts.profile}
|
|
3373
3732
|
(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 =
|
|
3733
|
+
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}/` };
|
|
3734
|
+
const child = spawn7(SANDBOX_EXEC, ["-p", sandbox, ...argv], { cwd: opts.cwd, env, stdio: ["ignore", "pipe", "pipe"], detached: true });
|
|
3376
3735
|
let output = "";
|
|
3377
3736
|
const take = (d) => {
|
|
3378
3737
|
if (output.length < OUTPUT_CAP)
|
|
@@ -3391,11 +3750,11 @@ function sandboxedExec(argv, opts) {
|
|
|
3391
3750
|
opts.signal?.addEventListener("abort", abort, { once: true });
|
|
3392
3751
|
const dropOwn = () => {
|
|
3393
3752
|
try {
|
|
3394
|
-
|
|
3753
|
+
rmSync4(own, { recursive: true, force: true });
|
|
3395
3754
|
} catch {
|
|
3396
3755
|
try {
|
|
3397
3756
|
spawnSync3("chflags", ["-R", "nouchg", own]);
|
|
3398
|
-
|
|
3757
|
+
rmSync4(own, { recursive: true, force: true });
|
|
3399
3758
|
} catch {}
|
|
3400
3759
|
}
|
|
3401
3760
|
};
|
|
@@ -3424,7 +3783,7 @@ function proxyEnv(network) {
|
|
|
3424
3783
|
const url = `http://127.0.0.1:${network.proxyPort}`;
|
|
3425
3784
|
return { HTTPS_PROXY: url, https_proxy: url, HTTP_PROXY: url, http_proxy: url, ALL_PROXY: url, all_proxy: url, NO_PROXY: "", no_proxy: "" };
|
|
3426
3785
|
}
|
|
3427
|
-
var SANDBOX_EXEC = "/usr/bin/sandbox-exec", OUTPUT_CAP = 20000, sandboxAvailable = () => process.platform === "darwin" &&
|
|
3786
|
+
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
3787
|
var init_sandbox = __esm(() => {
|
|
3429
3788
|
init_project();
|
|
3430
3789
|
init_git();
|
|
@@ -3439,29 +3798,29 @@ var init_sandbox = __esm(() => {
|
|
|
3439
3798
|
});
|
|
3440
3799
|
|
|
3441
3800
|
// src/local/tools.ts
|
|
3442
|
-
import { lstatSync as
|
|
3443
|
-
import { dirname as
|
|
3801
|
+
import { lstatSync as lstatSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync5, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
3802
|
+
import { dirname as dirname9, join as join10, relative as relative4, resolve as resolve8 } from "path";
|
|
3444
3803
|
function guardPath(ctx, path, mode) {
|
|
3445
3804
|
const root = realPath(ctx.cwd);
|
|
3446
|
-
const abs =
|
|
3805
|
+
const abs = resolve8(root, path);
|
|
3447
3806
|
let existing = abs;
|
|
3448
3807
|
while (!lexists(existing))
|
|
3449
|
-
existing =
|
|
3808
|
+
existing = dirname9(existing);
|
|
3450
3809
|
let realExisting;
|
|
3451
3810
|
try {
|
|
3452
3811
|
realExisting = realPath(existing);
|
|
3453
3812
|
} catch {
|
|
3454
3813
|
throw new Error(`${path} goes through a dangling symlink`);
|
|
3455
3814
|
}
|
|
3456
|
-
const real =
|
|
3815
|
+
const real = join10(realExisting, relative4(existing, abs));
|
|
3457
3816
|
const rel = relative4(root, real);
|
|
3458
|
-
if (rel.startsWith("..") ||
|
|
3817
|
+
if (rel.startsWith("..") || resolve8(root, rel) !== real)
|
|
3459
3818
|
throw new Error(`${path} is outside the project directory`);
|
|
3460
3819
|
if (isDenied(rel, ctx.deny))
|
|
3461
3820
|
throw new Error(`${path} is on the secrets denylist`);
|
|
3462
3821
|
if (mode === "write" && isHubWritePath(rel))
|
|
3463
3822
|
throw new Error(`${path} is not writable by the local worker`);
|
|
3464
|
-
if (mode === "write" && ctx.readOnly?.length && (
|
|
3823
|
+
if (mode === "write" && ctx.readOnly?.length && (lstatSync6(real, { throwIfNoEntry: false })?.nlink ?? 1) > 1)
|
|
3465
3824
|
throw new Error("hardlinked write target cannot be verified with data.readonly");
|
|
3466
3825
|
if (mode === "write" && matchesPolicy(root, path, ctx.readOnly ?? []))
|
|
3467
3826
|
throw new Error("target is protected by data.readonly");
|
|
@@ -3511,7 +3870,7 @@ async function runTool(name, rawArgs, ctx) {
|
|
|
3511
3870
|
const file = guardPath(ctx, String(a.path), "read");
|
|
3512
3871
|
if (statSync3(file).size > 5000000)
|
|
3513
3872
|
return "error: file is larger than 5 MB; use bash (head, grep, sed -n) to look at parts of it";
|
|
3514
|
-
const lines =
|
|
3873
|
+
const lines = readFileSync5(file, "utf8").split(`
|
|
3515
3874
|
`);
|
|
3516
3875
|
const from = Math.max(1, Number(a.offset) || 1);
|
|
3517
3876
|
const out = lines.slice(from - 1, from - 1 + (Number(a.limit) || 2000)).map((l, i) => `${from + i} ${l}`).join(`
|
|
@@ -3532,14 +3891,14 @@ ${preview(content)}`, "write", ctx.signal, file, undefined, String(a.path)), "er
|
|
|
3532
3891
|
file = guardPath(ctx, String(a.path), "write");
|
|
3533
3892
|
if (file !== approvedTarget)
|
|
3534
3893
|
return "error: path target changed during approval; inspect before retrying";
|
|
3535
|
-
|
|
3536
|
-
|
|
3894
|
+
mkdirSync4(dirname9(file), { recursive: true });
|
|
3895
|
+
writeFileSync3(file, content);
|
|
3537
3896
|
return `wrote ${a.path}`;
|
|
3538
3897
|
}
|
|
3539
3898
|
case "edit": {
|
|
3540
3899
|
let file = guardPath(ctx, String(a.path), "write");
|
|
3541
3900
|
const approvedTarget = file;
|
|
3542
|
-
let text =
|
|
3901
|
+
let text = readFileSync5(file, "utf8");
|
|
3543
3902
|
let count = text.split(String(a.old)).length - 1;
|
|
3544
3903
|
if (!a.old || count !== 1)
|
|
3545
3904
|
return `error: \`old\` must match exactly once, it matched ${count} times`;
|
|
@@ -3553,11 +3912,11 @@ ${preview(content)}`, "write", ctx.signal, file, undefined, String(a.path)), "er
|
|
|
3553
3912
|
file = guardPath(ctx, String(a.path), "write");
|
|
3554
3913
|
if (file !== approvedTarget)
|
|
3555
3914
|
return "error: path target changed during approval; inspect before retrying";
|
|
3556
|
-
text =
|
|
3915
|
+
text = readFileSync5(file, "utf8");
|
|
3557
3916
|
count = text.split(String(a.old)).length - 1;
|
|
3558
3917
|
if (count !== 1)
|
|
3559
3918
|
return `error: file changed during approval; \`old\` must match exactly once, it matched ${count} times`;
|
|
3560
|
-
|
|
3919
|
+
writeFileSync3(file, text.replace(String(a.old), () => String(a.new ?? "")));
|
|
3561
3920
|
return `edited ${a.path}`;
|
|
3562
3921
|
}
|
|
3563
3922
|
case "bash": {
|
|
@@ -3607,7 +3966,7 @@ ${preview(content)}`, "write", ctx.signal, file, undefined, String(a.path)), "er
|
|
|
3607
3966
|
}
|
|
3608
3967
|
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
3968
|
try {
|
|
3610
|
-
|
|
3969
|
+
lstatSync6(p);
|
|
3611
3970
|
return true;
|
|
3612
3971
|
} catch {
|
|
3613
3972
|
return false;
|
|
@@ -3661,9 +4020,9 @@ var PI_CEILING_KIND = "tool-step-ceiling", PI_CEILING_UNIT = "tool-step";
|
|
|
3661
4020
|
|
|
3662
4021
|
// src/adapters/pi.ts
|
|
3663
4022
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3664
|
-
import { existsSync as
|
|
3665
|
-
import { join as
|
|
3666
|
-
import { spawn as
|
|
4023
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5 } from "fs";
|
|
4024
|
+
import { join as join11, resolve as resolve9 } from "path";
|
|
4025
|
+
import { spawn as spawn8 } from "child_process";
|
|
3667
4026
|
function ownerStillAlive(pid, signature) {
|
|
3668
4027
|
const current = processSignature(pid);
|
|
3669
4028
|
if (current !== undefined)
|
|
@@ -3779,7 +4138,7 @@ var init_pi = __esm(() => {
|
|
|
3779
4138
|
get recoveryReady() {
|
|
3780
4139
|
if (this.state === "busy" || this.starting || this.activeTools)
|
|
3781
4140
|
return false;
|
|
3782
|
-
if (this.sessionFile && !
|
|
4141
|
+
if (this.sessionFile && !existsSync6(this.sessionFile) && (this.activityObserved || this.persistedOnce))
|
|
3783
4142
|
return false;
|
|
3784
4143
|
if (this.opts.mode === "headless")
|
|
3785
4144
|
return this.state === "idle" || !this.proc || this.proc.exitCode !== null || this.proc.signalCode !== null;
|
|
@@ -3798,7 +4157,7 @@ var init_pi = __esm(() => {
|
|
|
3798
4157
|
return this.ownerClaimed && this.ownerPid && this.ownerSignature ? `${this.ownerPid}:${this.ownerSignature}:${this.sessionId ?? ""}` : undefined;
|
|
3799
4158
|
}
|
|
3800
4159
|
recoveryMetadata() {
|
|
3801
|
-
const exists = !!this.sessionFile &&
|
|
4160
|
+
const exists = !!this.sessionFile && existsSync6(this.sessionFile);
|
|
3802
4161
|
if (exists) {
|
|
3803
4162
|
this.persistedOnce = true;
|
|
3804
4163
|
this.verifiedEmptyResume = undefined;
|
|
@@ -3811,14 +4170,14 @@ var init_pi = __esm(() => {
|
|
|
3811
4170
|
if (this.state === "offline") {
|
|
3812
4171
|
if (this.canReuseVerifiedEmptyResume())
|
|
3813
4172
|
return this.recoveryMetadata();
|
|
3814
|
-
if (this.recoveryReady && this.sessionFile &&
|
|
4173
|
+
if (this.recoveryReady && this.sessionFile && existsSync6(this.sessionFile))
|
|
3815
4174
|
return this.recoveryMetadata();
|
|
3816
4175
|
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
4176
|
}
|
|
3818
4177
|
this.emptyResumeVerified = false;
|
|
3819
4178
|
if (this.state !== "idle" || this.stopping || this.activeTools)
|
|
3820
4179
|
throw new Error("Pi must settle before session capture");
|
|
3821
|
-
if (this.sessionFile &&
|
|
4180
|
+
if (this.sessionFile && existsSync6(this.sessionFile))
|
|
3822
4181
|
return this.recoveryMetadata();
|
|
3823
4182
|
const snapshot = this.opts.mode === "headless" ? (await this.waitRpc("get_state", 15000)).data : await this.sendTui({ type: "get_session_state" });
|
|
3824
4183
|
if (this.state !== "idle" || this.activeTools || snapshot?.sessionId !== this.sessionId || snapshot?.sessionFile !== this.sessionFile)
|
|
@@ -3833,7 +4192,7 @@ var init_pi = __esm(() => {
|
|
|
3833
4192
|
return captured;
|
|
3834
4193
|
}
|
|
3835
4194
|
canReuseVerifiedEmptyResume() {
|
|
3836
|
-
return !!this.verifiedEmptyResume && this.recoveryReady && !this.activityObserved && !this.persistedOnce && this.sessionId === this.verifiedEmptyResume.sessionId && (!this.sessionFile || !
|
|
4195
|
+
return !!this.verifiedEmptyResume && this.recoveryReady && !this.activityObserved && !this.persistedOnce && this.sessionId === this.verifiedEmptyResume.sessionId && (!this.sessionFile || !existsSync6(this.sessionFile));
|
|
3837
4196
|
}
|
|
3838
4197
|
noteActivity() {
|
|
3839
4198
|
this.activityObserved = true;
|
|
@@ -4025,9 +4384,9 @@ var init_pi = __esm(() => {
|
|
|
4025
4384
|
this.approvalAnswerEpoch = undefined;
|
|
4026
4385
|
this.approvalStopReason = "";
|
|
4027
4386
|
this.approvalAbortSent = false;
|
|
4028
|
-
|
|
4029
|
-
const sessions =
|
|
4030
|
-
|
|
4387
|
+
mkdirSync5(this.opts.stateDir, { recursive: true });
|
|
4388
|
+
const sessions = join11(this.opts.stateDir, "pi-sessions");
|
|
4389
|
+
mkdirSync5(sessions, { recursive: true, mode: 448 });
|
|
4031
4390
|
if (this.opts.sessionFile) {
|
|
4032
4391
|
try {
|
|
4033
4392
|
validatePiSession(this.opts.stateDir, this.opts.cwd, this.opts.sessionFile, this.opts.sessionId);
|
|
@@ -4084,7 +4443,7 @@ var init_pi = __esm(() => {
|
|
|
4084
4443
|
return new Response("not found", { status: 404 });
|
|
4085
4444
|
} });
|
|
4086
4445
|
this.server = bridge;
|
|
4087
|
-
const extension =
|
|
4446
|
+
const extension = resolve9(join11(import.meta.dir, "../pi/extension.ts"));
|
|
4088
4447
|
const inherited = {};
|
|
4089
4448
|
for (const key of ["PATH", "HOME", "USER", "SHELL", "TMPDIR", "TERM", "TERM_PROGRAM", "LANG", "LC_ALL", "LC_CTYPE", "NO_COLOR", "CODEX_HOME"])
|
|
4090
4449
|
if (process.env[key])
|
|
@@ -4094,7 +4453,7 @@ var init_pi = __esm(() => {
|
|
|
4094
4453
|
if (this.opts.mode === "tui") {
|
|
4095
4454
|
return;
|
|
4096
4455
|
}
|
|
4097
|
-
this.proc =
|
|
4456
|
+
this.proc = spawn8(bin, args, { cwd: this.opts.cwd, env, stdio: ["pipe", "pipe", "pipe"], detached: true });
|
|
4098
4457
|
trackGroup(this.proc);
|
|
4099
4458
|
this.proc.stdout.on("data", (chunk) => this.onOutput(String(chunk)));
|
|
4100
4459
|
this.proc.stderr.on("data", (chunk) => this.opts.log?.(`[${this.id}] ${String(chunk).trimEnd()}`));
|
|
@@ -4126,7 +4485,7 @@ var init_pi = __esm(() => {
|
|
|
4126
4485
|
this.ownerToken = String(this._tuiLaunch?.env.AGENTHUB_PI_OWNER_TOKEN ?? "");
|
|
4127
4486
|
this.ownerClaimed = true;
|
|
4128
4487
|
}
|
|
4129
|
-
this.persistedOnce =
|
|
4488
|
+
this.persistedOnce = existsSync6(this.sessionFile);
|
|
4130
4489
|
if (this.opts.sessionId && this.sessionId !== this.opts.sessionId || this.opts.sessionFile && this.sessionFile !== this.opts.sessionFile)
|
|
4131
4490
|
throw new Error("Pi startup session identity mismatch");
|
|
4132
4491
|
this.started = true;
|
|
@@ -5865,7 +6224,7 @@ var init_signals = __esm(() => {
|
|
|
5865
6224
|
});
|
|
5866
6225
|
|
|
5867
6226
|
// src/models/route/labels.ts
|
|
5868
|
-
import { createHash as
|
|
6227
|
+
import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
|
|
5869
6228
|
|
|
5870
6229
|
class RouteLabelTracker {
|
|
5871
6230
|
sink;
|
|
@@ -6015,7 +6374,7 @@ function safeJson(value) {
|
|
|
6015
6374
|
return "";
|
|
6016
6375
|
}
|
|
6017
6376
|
}
|
|
6018
|
-
var discreteSeverity = (value) => value >= 1 ? 1 : value >= 0.7 ? 0.7 : value >= 0.3 ? 0.3 : 0, sha256 = (value) =>
|
|
6377
|
+
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
6378
|
var init_labels = __esm(() => {
|
|
6020
6379
|
init_signals();
|
|
6021
6380
|
});
|
|
@@ -6883,9 +7242,9 @@ var init_shipped_roles = __esm(() => {
|
|
|
6883
7242
|
});
|
|
6884
7243
|
|
|
6885
7244
|
// src/hub/roles.ts
|
|
6886
|
-
import { createHash as
|
|
6887
|
-
import { constants as constants2, closeSync as closeSync2, fstatSync, lstatSync as
|
|
6888
|
-
import { join as
|
|
7245
|
+
import { createHash as createHash5 } from "crypto";
|
|
7246
|
+
import { constants as constants2, closeSync as closeSync2, fstatSync, lstatSync as lstatSync7, openSync as openSync2, readSync, readdirSync as readdirSync2 } from "fs";
|
|
7247
|
+
import { join as join12 } from "path";
|
|
6889
7248
|
function parseRole(text, stem, layer) {
|
|
6890
7249
|
if (Buffer.byteLength(text) > 8192 || text.includes("\r") || !ID.test(stem))
|
|
6891
7250
|
throw new Error("invalid role size, encoding or file id");
|
|
@@ -6929,7 +7288,7 @@ function parseRole(text, stem, layer) {
|
|
|
6929
7288
|
}
|
|
6930
7289
|
const reviewStart = sections[4].index + sections[4][0].length;
|
|
6931
7290
|
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:
|
|
7291
|
+
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
7292
|
}
|
|
6934
7293
|
function projectRole(file) {
|
|
6935
7294
|
const fd = openSync2(file, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
|
|
@@ -6964,9 +7323,9 @@ function loadRoleCatalog(cwd) {
|
|
|
6964
7323
|
if (!cwd)
|
|
6965
7324
|
return { roles, errors };
|
|
6966
7325
|
try {
|
|
6967
|
-
const config =
|
|
7326
|
+
const config = join12(cwd, ".agenthub"), dir = join12(config, "roles");
|
|
6968
7327
|
for (const path of [config, dir]) {
|
|
6969
|
-
const stat =
|
|
7328
|
+
const stat = lstatSync7(path);
|
|
6970
7329
|
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
6971
7330
|
throw new Error("unsafe role directory");
|
|
6972
7331
|
}
|
|
@@ -6976,7 +7335,7 @@ function loadRoleCatalog(cwd) {
|
|
|
6976
7335
|
for (const file of files) {
|
|
6977
7336
|
const id = file.slice(0, -3);
|
|
6978
7337
|
try {
|
|
6979
|
-
roles.set(id, parseRole(projectRole(
|
|
7338
|
+
roles.set(id, parseRole(projectRole(join12(dir, file)), id, "project"));
|
|
6980
7339
|
} catch {
|
|
6981
7340
|
errors.push({ id: ID.test(id) ? id : "invalid-file-id", reason: "invalid or unsafe project role" });
|
|
6982
7341
|
}
|
|
@@ -7878,21 +8237,748 @@ function reviewerVariant(kind, displayName, reviewer, display) {
|
|
|
7878
8237
|
display
|
|
7879
8238
|
};
|
|
7880
8239
|
}
|
|
7881
|
-
var codexVariant, CODEX_REVIEWER_VARIANT, KIMI_REVIEWER_VARIANT,
|
|
8240
|
+
var codexVariant, CODEX_REVIEWER_VARIANT, KIMI_REVIEWER_VARIANT, AGY_REVIEWER_VARIANT;
|
|
7882
8241
|
var init_reviewer_variants = __esm(() => {
|
|
7883
8242
|
init_claude_print();
|
|
7884
8243
|
init_codex_review_isolation();
|
|
8244
|
+
init_kimi_review();
|
|
7885
8245
|
init_native_factories();
|
|
7886
8246
|
init_native_display();
|
|
7887
8247
|
codexVariant = reviewerVariant("codex", "Codex reviewer", { backend: codexReviewerBackend, defaultPool: true, declaration: "explicit" }, CODEX_DISPLAY);
|
|
7888
8248
|
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
|
|
8249
|
+
KIMI_REVIEWER_VARIANT = reviewerVariant("kimi", "Kimi reviewer", { backend: kimiReviewerBackend, defaultPool: true, declaration: "explicit" }, KIMI_DISPLAY);
|
|
8250
|
+
AGY_REVIEWER_VARIANT = {
|
|
8251
|
+
...reviewerVariant("agy", "agy reviewer", { defaultPool: false, declaration: "automatic", unavailable: "agy is an implementer peer (#485)" }, { palette: { fg: "#6558a6", bg: "#f1effa" } }),
|
|
7892
8252
|
optional: true
|
|
7893
8253
|
};
|
|
7894
8254
|
});
|
|
7895
8255
|
|
|
8256
|
+
// src/adapters/agy/hooks.ts
|
|
8257
|
+
import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync2, lstatSync as lstatSync8, openSync as openSync3, readSync as readSync2 } from "fs";
|
|
8258
|
+
import { homedir as homedir4 } from "os";
|
|
8259
|
+
import { isAbsolute as isAbsolute5, join as join13, relative as relative5 } from "path";
|
|
8260
|
+
import { createHash as createHash6 } from "crypto";
|
|
8261
|
+
function decodeAgyHook(event, value) {
|
|
8262
|
+
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)))
|
|
8263
|
+
return;
|
|
8264
|
+
if (event === "Stop" && typeof value.fullyIdle !== "boolean")
|
|
8265
|
+
return;
|
|
8266
|
+
const call = record3(value.toolCall) ? value.toolCall : undefined;
|
|
8267
|
+
if ((event === "PreToolUse" || event === "PostToolUse") && (!call || typeof call.name !== "string" || call.name.length > 128 || !record3(call.args)))
|
|
8268
|
+
return;
|
|
8269
|
+
const native = call?.args;
|
|
8270
|
+
const observedRead = call?.name === "view_file" && typeof native?.AbsolutePath === "string";
|
|
8271
|
+
const phase = { SessionStart: "session", PreInvocation: "start", PostInvocation: "post", PreToolUse: "pre", PostToolUse: "post", Stop: "stop" }[event];
|
|
8272
|
+
return {
|
|
8273
|
+
event,
|
|
8274
|
+
phase,
|
|
8275
|
+
sessionId: value.conversationId,
|
|
8276
|
+
transcriptPath: value.transcriptPath,
|
|
8277
|
+
workspacePaths: value.workspacePaths,
|
|
8278
|
+
tool: observedRead ? "Read" : typeof call?.name === "string" ? call.name : "",
|
|
8279
|
+
input: observedRead ? { file_path: native.AbsolutePath } : native ?? {},
|
|
8280
|
+
...Number.isSafeInteger(value.stepIdx) && value.stepIdx >= 0 ? { toolUseId: `${value.conversationId}:${value.stepIdx}` } : {},
|
|
8281
|
+
...Number.isSafeInteger(value.invocationNum) && value.invocationNum >= 0 ? { invocation: value.invocationNum } : {},
|
|
8282
|
+
...event === "Stop" ? { fullyIdle: value.fullyIdle, terminationReason: typeof value.terminationReason === "string" ? value.terminationReason : "" } : {}
|
|
8283
|
+
};
|
|
8284
|
+
}
|
|
8285
|
+
function encodeAgyHook(event, text) {
|
|
8286
|
+
if (text && (event === "SessionStart" || event === "PreInvocation"))
|
|
8287
|
+
return { injectSteps: [{ ephemeralMessage: text }] };
|
|
8288
|
+
return;
|
|
8289
|
+
}
|
|
8290
|
+
function readAgyTranscript(session, transcript) {
|
|
8291
|
+
const empty = { factIds: [] };
|
|
8292
|
+
if (!UUID.test(session))
|
|
8293
|
+
return empty;
|
|
8294
|
+
const home = process.env.HOME ?? homedir4();
|
|
8295
|
+
const candidates = ["antigravity-cli", "antigravity", "antigravity-ide"].map((surface) => join13(home, ".gemini", surface, "brain", session, ".system_generated/logs/transcript_full.jsonl"));
|
|
8296
|
+
if (!candidates.includes(transcript))
|
|
8297
|
+
return empty;
|
|
8298
|
+
let fd;
|
|
8299
|
+
try {
|
|
8300
|
+
let cursor = home;
|
|
8301
|
+
for (const name of relative5(home, transcript).split("/")) {
|
|
8302
|
+
cursor = join13(cursor, name);
|
|
8303
|
+
if (lstatSync8(cursor).isSymbolicLink())
|
|
8304
|
+
return empty;
|
|
8305
|
+
}
|
|
8306
|
+
fd = openSync3(transcript, constants3.O_RDONLY | constants3.O_NOFOLLOW);
|
|
8307
|
+
const stat = fstatSync2(fd);
|
|
8308
|
+
if (!stat.isFile() || stat.nlink !== 1)
|
|
8309
|
+
return empty;
|
|
8310
|
+
const cap = 4 * 1024 * 1024;
|
|
8311
|
+
for (let window = 64 * 1024;; window = Math.min(cap, window * 2)) {
|
|
8312
|
+
const start = Math.max(0, stat.size - window), from = start > 0 ? start - 1 : 0;
|
|
8313
|
+
const buffer = Buffer.alloc(stat.size - from);
|
|
8314
|
+
let bytes = 0;
|
|
8315
|
+
while (bytes < buffer.length) {
|
|
8316
|
+
const count = readSync2(fd, buffer, bytes, buffer.length - bytes, from + bytes);
|
|
8317
|
+
if (!count)
|
|
8318
|
+
break;
|
|
8319
|
+
bytes += count;
|
|
8320
|
+
}
|
|
8321
|
+
const lines = buffer.subarray(0, bytes).toString("utf8").split(`
|
|
8322
|
+
`);
|
|
8323
|
+
if (start)
|
|
8324
|
+
lines.shift();
|
|
8325
|
+
const out = { factIds: [] };
|
|
8326
|
+
for (const line of lines) {
|
|
8327
|
+
let row;
|
|
8328
|
+
try {
|
|
8329
|
+
row = JSON.parse(line);
|
|
8330
|
+
} catch {
|
|
8331
|
+
continue;
|
|
8332
|
+
}
|
|
8333
|
+
if (!record3(row) || row.status !== "DONE")
|
|
8334
|
+
continue;
|
|
8335
|
+
if (row.source === "SYSTEM_SDK" && row.type === "EPHEMERAL_MESSAGE" && typeof row.content === "string") {
|
|
8336
|
+
for (const match of row.content.matchAll(/agent-hub facts \[([a-zA-Z0-9-]{1,128})\]/g))
|
|
8337
|
+
out.factIds.push(match[1]);
|
|
8338
|
+
}
|
|
8339
|
+
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))
|
|
8340
|
+
out.completion = { id: createHash6("sha256").update(`${session}:${row.step_index}:${line}`).digest("hex"), index: row.step_index };
|
|
8341
|
+
}
|
|
8342
|
+
if (out.completion || start === 0 || window === cap)
|
|
8343
|
+
return out;
|
|
8344
|
+
}
|
|
8345
|
+
} catch {
|
|
8346
|
+
return empty;
|
|
8347
|
+
} finally {
|
|
8348
|
+
if (fd !== undefined)
|
|
8349
|
+
closeSync3(fd);
|
|
8350
|
+
}
|
|
8351
|
+
}
|
|
8352
|
+
var EVENTS, UUID, record3 = (value) => !!value && typeof value === "object" && !Array.isArray(value), AGY_FACTS_HOOK;
|
|
8353
|
+
var init_hooks = __esm(() => {
|
|
8354
|
+
EVENTS = ["SessionStart", "PreInvocation", "PostInvocation", "PreToolUse", "PostToolUse", "Stop"];
|
|
8355
|
+
UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
8356
|
+
AGY_FACTS_HOOK = { decode: decodeAgyHook, encode: encodeAgyHook, read: readAgyTranscript };
|
|
8357
|
+
});
|
|
8358
|
+
|
|
8359
|
+
// src/adapters/agy/definition.ts
|
|
8360
|
+
function agyLaunch(ctx) {
|
|
8361
|
+
const argv = ["agy", ...ctx.userArgs], command = argv.map(shellQuote).join(" ");
|
|
8362
|
+
const metadata = { packageEntrypoint: ctx.entrypoint, command, argv, env: {}, kind: "agy", peer: ctx.instance };
|
|
8363
|
+
return { cmd: "agy", args: ctx.userArgs, metadata };
|
|
8364
|
+
}
|
|
8365
|
+
var DISPLAY, shellQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`, AGY_DEFINITION;
|
|
8366
|
+
var init_definition = __esm(() => {
|
|
8367
|
+
init_reviewer_variants();
|
|
8368
|
+
init_hooks();
|
|
8369
|
+
DISPLAY = { palette: { fg: "#6558a6", bg: "#f1effa" } };
|
|
8370
|
+
AGY_DEFINITION = {
|
|
8371
|
+
api: 1,
|
|
8372
|
+
kind: "agy",
|
|
8373
|
+
displayName: "Antigravity",
|
|
8374
|
+
family: "agy",
|
|
8375
|
+
transport: "ws-pull",
|
|
8376
|
+
roles: ["implementer", "verifier"],
|
|
8377
|
+
optional: true,
|
|
8378
|
+
defaultOnRoster: false,
|
|
8379
|
+
trust: { onPrem: false, hubSandboxed: false },
|
|
8380
|
+
start: {
|
|
8381
|
+
forms: ["tui"],
|
|
8382
|
+
defaultForm: "tui",
|
|
8383
|
+
ownsTerminal: (form) => form === "tui",
|
|
8384
|
+
launch: agyLaunch,
|
|
8385
|
+
conductorStartable: false,
|
|
8386
|
+
hubSwitches: [],
|
|
8387
|
+
ownedFlags: ["--new", "--continue", "--conversation", "--resume"],
|
|
8388
|
+
conductorStartArgs: (args) => args.length ? new Error("conductor starts accept no launch overrides; use ahub agy") : {}
|
|
8389
|
+
},
|
|
8390
|
+
identity: { mcp: "channel-plugin" },
|
|
8391
|
+
attach: {
|
|
8392
|
+
pullOnlyForms: ["tui"],
|
|
8393
|
+
recoveryLaunch: (instance) => ({ kind: "agy", peer: instance }),
|
|
8394
|
+
pullOnlyNotice: "pull-only: messages wait for hub_inbox; permission modes and session recovery are not managed"
|
|
8395
|
+
},
|
|
8396
|
+
permission: {
|
|
8397
|
+
modes: {},
|
|
8398
|
+
unmanaged: "Antigravity selects its own permission mode in its TUI; no hub permission mode",
|
|
8399
|
+
unmanagedForms: ["tui"],
|
|
8400
|
+
unmanagedNote: "Antigravity approves tools in its own TUI; no hub permission mode",
|
|
8401
|
+
unmanagedStatusNote: "unmanaged native permission policy"
|
|
8402
|
+
},
|
|
8403
|
+
session: {
|
|
8404
|
+
idField: "sessionId",
|
|
8405
|
+
currentId: () => {
|
|
8406
|
+
return;
|
|
8407
|
+
},
|
|
8408
|
+
freshOnRestore: true,
|
|
8409
|
+
recoveryRefusal: () => "Antigravity session continuity and resume are unavailable until native behavior is verified"
|
|
8410
|
+
},
|
|
8411
|
+
turnEnd: "native-hook",
|
|
8412
|
+
cli: { command: "agy", authority: "person", startWords: () => ["agy"] },
|
|
8413
|
+
display: DISPLAY,
|
|
8414
|
+
routing: { allowedRoles: ["implementer", "verifier"] },
|
|
8415
|
+
reviewer: { defaultPool: false, declaration: "automatic", unavailable: "agy is an implementer peer (#485)" },
|
|
8416
|
+
reviewerVariant: AGY_REVIEWER_VARIANT,
|
|
8417
|
+
hooks: { factsHook: AGY_FACTS_HOOK }
|
|
8418
|
+
};
|
|
8419
|
+
});
|
|
8420
|
+
|
|
8421
|
+
// src/adapters/definitions/native-cli-preview-common.ts
|
|
8422
|
+
function publicArg(arg) {
|
|
8423
|
+
const flags = new Set(["--settings", "--model", "--mcp-config", "--session-id", "--session-file", "--resume", "--continue", "--print", "--safe", "--new"]);
|
|
8424
|
+
const key = arg.split("=", 1)[0];
|
|
8425
|
+
return flags.has(key) ? arg.includes("=") ? key + "=" + REDACTED : key : REDACTED;
|
|
8426
|
+
}
|
|
8427
|
+
function nativeCommand(command, trusted) {
|
|
8428
|
+
return command.map((value, i) => value === trusted[i] ? value : REDACTED);
|
|
8429
|
+
}
|
|
8430
|
+
function previewState(args, baseEnvNames) {
|
|
8431
|
+
const unresolved = [];
|
|
8432
|
+
const envNames = [...baseEnvNames];
|
|
8433
|
+
const value = (flag) => {
|
|
8434
|
+
const at = args.indexOf(flag);
|
|
8435
|
+
if (at < 0)
|
|
8436
|
+
return;
|
|
8437
|
+
const next = args[at + 1];
|
|
8438
|
+
if (!next || next.startsWith("--"))
|
|
8439
|
+
throw new Error("missing option value");
|
|
8440
|
+
return next;
|
|
8441
|
+
};
|
|
8442
|
+
return { unresolved, envNames, value };
|
|
8443
|
+
}
|
|
8444
|
+
function finishPreview(ctx, launch, envNames, unresolved, metadata, settings) {
|
|
8445
|
+
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."] };
|
|
8446
|
+
}
|
|
8447
|
+
var REDACTED = "[redacted]", UNRESOLVED = "[unresolved]";
|
|
8448
|
+
|
|
8449
|
+
// src/hub/status-roster.ts
|
|
8450
|
+
function rosterInStatus(status, peer) {
|
|
8451
|
+
const rows = status?.roster;
|
|
8452
|
+
if (!Array.isArray(rows))
|
|
8453
|
+
return;
|
|
8454
|
+
const row = rows.find((r) => !!r && typeof r === "object" && r.peer === peer);
|
|
8455
|
+
return typeof row?.on === "boolean" ? row.on : undefined;
|
|
8456
|
+
}
|
|
8457
|
+
function bundleInStatus(status) {
|
|
8458
|
+
const value = status?.pluginBundle;
|
|
8459
|
+
return typeof value === "string" && value.startsWith("/") && /[\\/]plugins[\\/]agent-hub[\\/]server\.js$/.test(value) ? value : undefined;
|
|
8460
|
+
}
|
|
8461
|
+
|
|
8462
|
+
// src/cli/agy-mcp.ts
|
|
8463
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
8464
|
+
import { closeSync as closeSync4, fstatSync as fstatSync3, lstatSync as lstatSync9, mkdirSync as mkdirSync6, openSync as openSync4, readFileSync as readFileSync6, renameSync, rmdirSync, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8465
|
+
import { isAbsolute as isAbsolute6, join as join14, resolve as resolve10 } from "path";
|
|
8466
|
+
import { isDeepStrictEqual } from "util";
|
|
8467
|
+
function object4(value) {
|
|
8468
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
8469
|
+
}
|
|
8470
|
+
function jsonFile(value, name) {
|
|
8471
|
+
if (!object4(value))
|
|
8472
|
+
throw new Error(`${name} must be a JSON object`);
|
|
8473
|
+
return value;
|
|
8474
|
+
}
|
|
8475
|
+
function parseJson(bytes, name) {
|
|
8476
|
+
try {
|
|
8477
|
+
return jsonFile(JSON.parse(bytes.toString("utf8")), name);
|
|
8478
|
+
} catch (error) {
|
|
8479
|
+
if (error instanceof SyntaxError)
|
|
8480
|
+
throw new Error(`${name} is invalid JSON`);
|
|
8481
|
+
throw error;
|
|
8482
|
+
}
|
|
8483
|
+
}
|
|
8484
|
+
function shellQuote2(value) {
|
|
8485
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
8486
|
+
}
|
|
8487
|
+
function shellUnquote(value) {
|
|
8488
|
+
if (!value.startsWith("'") || !value.endsWith("'"))
|
|
8489
|
+
return;
|
|
8490
|
+
let out = "";
|
|
8491
|
+
for (let index = 1;index < value.length - 1; ) {
|
|
8492
|
+
if (value.startsWith("'\\''", index)) {
|
|
8493
|
+
out += "'";
|
|
8494
|
+
index += 4;
|
|
8495
|
+
} else if (value[index] === "'")
|
|
8496
|
+
return;
|
|
8497
|
+
else
|
|
8498
|
+
out += value[index++];
|
|
8499
|
+
}
|
|
8500
|
+
return out;
|
|
8501
|
+
}
|
|
8502
|
+
function validateOptions(options) {
|
|
8503
|
+
const peer = options.peer ?? "agy";
|
|
8504
|
+
if (!/^[a-z][a-z0-9-]{0,31}$/.test(peer))
|
|
8505
|
+
throw new Error("agy peer id is invalid");
|
|
8506
|
+
for (const [name, value] of Object.entries({ projectRoot: options.projectRoot, stateDir: options.stateDir, serverJs: options.serverJs, hookScript: options.hookScript })) {
|
|
8507
|
+
if (typeof value !== "string" || !isAbsolute6(value) || value.length > 4096 || /[\u0000-\u001f\u007f]/.test(value))
|
|
8508
|
+
throw new Error(`${name} must be an absolute control-free path`);
|
|
8509
|
+
}
|
|
8510
|
+
for (const [name, value] of Object.entries({ instanceId: options.instanceId, launchId: options.launchId })) {
|
|
8511
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9._:-]{1,128}$/.test(value))
|
|
8512
|
+
throw new Error(`${name} is invalid`);
|
|
8513
|
+
}
|
|
8514
|
+
const root = resolve10(options.projectRoot);
|
|
8515
|
+
return { root, peer };
|
|
8516
|
+
}
|
|
8517
|
+
function safeDirectory(path) {
|
|
8518
|
+
const stat = lstatSync9(path, { throwIfNoEntry: false });
|
|
8519
|
+
if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
|
|
8520
|
+
throw new Error(`${path} must be a project directory, not a link`);
|
|
8521
|
+
return stat;
|
|
8522
|
+
}
|
|
8523
|
+
function safeFile(path) {
|
|
8524
|
+
const stat = lstatSync9(path, { throwIfNoEntry: false });
|
|
8525
|
+
if (!stat)
|
|
8526
|
+
return;
|
|
8527
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1 || stat.size > MAX_CONFIG_BYTES)
|
|
8528
|
+
throw new Error(`${path} must be a bounded regular file, not a link`);
|
|
8529
|
+
const bytes = readFileSync6(path);
|
|
8530
|
+
if (bytes.length > MAX_CONFIG_BYTES)
|
|
8531
|
+
throw new Error(`${path} exceeds the configuration size limit`);
|
|
8532
|
+
return { stat, bytes };
|
|
8533
|
+
}
|
|
8534
|
+
function pluginDirectories(root) {
|
|
8535
|
+
const directories = [];
|
|
8536
|
+
for (const relative of [".", ".agents", ".agents/plugins", PLUGIN_DIR]) {
|
|
8537
|
+
const path = relative === "." ? root : join14(root, relative);
|
|
8538
|
+
const stat = safeDirectory(path);
|
|
8539
|
+
if (!stat)
|
|
8540
|
+
throw new Error(`${path} must be an existing project directory`);
|
|
8541
|
+
directories.push({ path, stat });
|
|
8542
|
+
}
|
|
8543
|
+
return directories;
|
|
8544
|
+
}
|
|
8545
|
+
function verifyPluginDirectory(plan, target) {
|
|
8546
|
+
if (!plan.canonicalPluginDir || !plan.directories)
|
|
8547
|
+
throw new Error("managed agy plugin directory identity is unavailable");
|
|
8548
|
+
if (realPath(plan.root) !== plan.canonicalRoot || realPath(plan.pluginDir) !== plan.canonicalPluginDir || plan.canonicalPluginDir !== join14(plan.canonicalRoot, PLUGIN_DIR)) {
|
|
8549
|
+
throw new Error("managed agy plugin path changed; inspect it before writing");
|
|
8550
|
+
}
|
|
8551
|
+
for (const expected of plan.directories) {
|
|
8552
|
+
const current = safeDirectory(expected.path);
|
|
8553
|
+
if (!current || current.dev !== expected.stat.dev || current.ino !== expected.stat.ino)
|
|
8554
|
+
throw new Error("managed agy plugin directory changed; inspect it before writing");
|
|
8555
|
+
}
|
|
8556
|
+
if (!FILES.some((file) => join14(plan.root, file) === target)) {
|
|
8557
|
+
throw new Error("atomic write target is outside the managed agy plugin files");
|
|
8558
|
+
}
|
|
8559
|
+
}
|
|
8560
|
+
function hookCommand(phase, options, peer) {
|
|
8561
|
+
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)}`;
|
|
8562
|
+
}
|
|
8563
|
+
function desired(options, root, peer) {
|
|
8564
|
+
const events = {};
|
|
8565
|
+
for (const phase of EVENTS2) {
|
|
8566
|
+
if (phase === "PostInvocation") {
|
|
8567
|
+
events[phase] = [];
|
|
8568
|
+
continue;
|
|
8569
|
+
}
|
|
8570
|
+
const command = { type: "command", command: hookCommand(phase, options, peer), timeout: 5 };
|
|
8571
|
+
events[phase] = phase === "PreToolUse" || phase === "PostToolUse" ? [{ matcher: ".*", hooks: [command] }] : [command];
|
|
8572
|
+
}
|
|
8573
|
+
return {
|
|
8574
|
+
[MANIFEST_FILE]: { name: PLUGIN_KEY, description: MANAGED_DESCRIPTION },
|
|
8575
|
+
[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 } } } },
|
|
8576
|
+
[HOOKS_FILE]: { [PLUGIN_KEY]: events }
|
|
8577
|
+
};
|
|
8578
|
+
}
|
|
8579
|
+
function parseManagedHookCommand(command, phase, options, peer) {
|
|
8580
|
+
if (typeof command !== "string")
|
|
8581
|
+
return;
|
|
8582
|
+
const prefix = `AGENTHUB_HOOK_EVENT='${phase}' AGENTHUB_INSTANCE_ID='`;
|
|
8583
|
+
const middle = `' AGENTHUB_LAUNCH_ID='`;
|
|
8584
|
+
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 `;
|
|
8585
|
+
if (!command.startsWith(prefix))
|
|
8586
|
+
return;
|
|
8587
|
+
const rest = command.slice(prefix.length);
|
|
8588
|
+
const launchSplit = rest.indexOf(middle);
|
|
8589
|
+
if (launchSplit < 1)
|
|
8590
|
+
return;
|
|
8591
|
+
const instanceId = rest.slice(0, launchSplit);
|
|
8592
|
+
const afterInstance = rest.slice(launchSplit + middle.length);
|
|
8593
|
+
const pathSplit = afterInstance.indexOf(suffix);
|
|
8594
|
+
if (pathSplit < 1)
|
|
8595
|
+
return;
|
|
8596
|
+
const launchId = afterInstance.slice(0, pathSplit);
|
|
8597
|
+
const hookScript = shellUnquote(afterInstance.slice(pathSplit + suffix.length));
|
|
8598
|
+
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"))
|
|
8599
|
+
return;
|
|
8600
|
+
return { instanceId, launchId };
|
|
8601
|
+
}
|
|
8602
|
+
function managedHookLaunch(value, options, peer) {
|
|
8603
|
+
if (Object.keys(value).length !== 1 || !Object.hasOwn(value, PLUGIN_KEY))
|
|
8604
|
+
return;
|
|
8605
|
+
const events = value[PLUGIN_KEY];
|
|
8606
|
+
if (!object4(events) || Object.keys(events).length !== EVENTS2.length || EVENTS2.some((phase) => !Object.hasOwn(events, phase)))
|
|
8607
|
+
return;
|
|
8608
|
+
let identity;
|
|
8609
|
+
for (const phase of EVENTS2) {
|
|
8610
|
+
const rows = events[phase];
|
|
8611
|
+
if (phase === "PostInvocation") {
|
|
8612
|
+
if (!Array.isArray(rows) || rows.length !== 0)
|
|
8613
|
+
return;
|
|
8614
|
+
continue;
|
|
8615
|
+
}
|
|
8616
|
+
if (!Array.isArray(rows) || rows.length !== 1 || !object4(rows[0]))
|
|
8617
|
+
return;
|
|
8618
|
+
let command;
|
|
8619
|
+
if (phase === "PreToolUse" || phase === "PostToolUse") {
|
|
8620
|
+
const row = rows[0];
|
|
8621
|
+
if (Object.keys(row).sort().join(",") !== "hooks,matcher" || row.matcher !== ".*" || !Array.isArray(row.hooks) || row.hooks.length !== 1 || !object4(row.hooks[0]))
|
|
8622
|
+
return;
|
|
8623
|
+
command = row.hooks[0];
|
|
8624
|
+
} else
|
|
8625
|
+
command = rows[0];
|
|
8626
|
+
if (!object4(command) || Object.keys(command).sort().join(",") !== "command,timeout,type" || command.type !== "command" || command.timeout !== 5)
|
|
8627
|
+
return;
|
|
8628
|
+
const found = parseManagedHookCommand(command.command, phase, options, peer);
|
|
8629
|
+
if (!found || identity && (identity.instanceId !== found.instanceId || identity.launchId !== found.launchId))
|
|
8630
|
+
return;
|
|
8631
|
+
identity = found;
|
|
8632
|
+
}
|
|
8633
|
+
return identity;
|
|
8634
|
+
}
|
|
8635
|
+
function nextDocuments(current, wanted) {
|
|
8636
|
+
const nextMcp = current[MCP_FILE] ? { ...current[MCP_FILE] } : {};
|
|
8637
|
+
const currentServers = nextMcp.mcpServers;
|
|
8638
|
+
const servers = currentServers === undefined ? {} : currentServers;
|
|
8639
|
+
if (!object4(servers))
|
|
8640
|
+
throw new Error(`${MCP_FILE} mcpServers must be an object`);
|
|
8641
|
+
nextMcp.mcpServers = { ...servers, [PLUGIN_KEY]: wanted[MCP_FILE].mcpServers[PLUGIN_KEY] };
|
|
8642
|
+
return { [MANIFEST_FILE]: wanted[MANIFEST_FILE], [MCP_FILE]: nextMcp, [HOOKS_FILE]: wanted[HOOKS_FILE] };
|
|
8643
|
+
}
|
|
8644
|
+
function internalPlan(options) {
|
|
8645
|
+
const { root, peer } = validateOptions(options);
|
|
8646
|
+
const rootStat = safeDirectory(root);
|
|
8647
|
+
if (!rootStat)
|
|
8648
|
+
throw new Error("project root must be an existing directory");
|
|
8649
|
+
const canonicalRoot = realPath(root);
|
|
8650
|
+
const pluginDir = join14(root, PLUGIN_DIR);
|
|
8651
|
+
let parentsPresent = true;
|
|
8652
|
+
for (const relative of [".agents", ".agents/plugins", PLUGIN_DIR]) {
|
|
8653
|
+
if (!parentsPresent)
|
|
8654
|
+
break;
|
|
8655
|
+
parentsPresent = !!safeDirectory(join14(root, relative));
|
|
8656
|
+
}
|
|
8657
|
+
let directories;
|
|
8658
|
+
let canonicalPluginDir;
|
|
8659
|
+
if (parentsPresent) {
|
|
8660
|
+
directories = pluginDirectories(root);
|
|
8661
|
+
canonicalPluginDir = realPath(pluginDir);
|
|
8662
|
+
if (canonicalPluginDir !== join14(canonicalRoot, PLUGIN_DIR))
|
|
8663
|
+
throw new Error("managed agy plugin path traverses a symlink");
|
|
8664
|
+
}
|
|
8665
|
+
const snapshots = Object.fromEntries(FILES.map((relative) => {
|
|
8666
|
+
const path = join14(root, relative);
|
|
8667
|
+
const found = parentsPresent ? safeFile(path) : undefined;
|
|
8668
|
+
return [relative, found];
|
|
8669
|
+
}));
|
|
8670
|
+
const current = {};
|
|
8671
|
+
for (const relative of FILES) {
|
|
8672
|
+
const found = snapshots[relative];
|
|
8673
|
+
current[relative] = found ? parseJson(found.bytes, relative) : undefined;
|
|
8674
|
+
}
|
|
8675
|
+
const manifest = current[MANIFEST_FILE];
|
|
8676
|
+
if (manifest && (Object.keys(manifest).sort().join(",") !== "description,name" || manifest.name !== PLUGIN_KEY || manifest.description !== MANAGED_DESCRIPTION)) {
|
|
8677
|
+
throw new Error(`${MANIFEST_FILE} is a foreign agent-hub plugin manifest; it was not changed`);
|
|
8678
|
+
}
|
|
8679
|
+
const mcp = current[MCP_FILE];
|
|
8680
|
+
if (mcp) {
|
|
8681
|
+
if (Object.hasOwn(mcp, "mcpServers") && !object4(mcp.mcpServers))
|
|
8682
|
+
throw new Error(`${MCP_FILE} mcpServers must be an object`);
|
|
8683
|
+
const entry = mcp.mcpServers?.[PLUGIN_KEY];
|
|
8684
|
+
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)) {
|
|
8685
|
+
throw new Error(`${MCP_FILE} agent-hub is a foreign MCP entry; it was not changed`);
|
|
8686
|
+
}
|
|
8687
|
+
}
|
|
8688
|
+
const hooks = current[HOOKS_FILE];
|
|
8689
|
+
const oldLaunch = hooks ? managedHookLaunch(hooks, options, peer) : undefined;
|
|
8690
|
+
if (hooks && !oldLaunch)
|
|
8691
|
+
throw new Error(`${HOOKS_FILE} agent-hub is a foreign hooks entry; it was not changed`);
|
|
8692
|
+
const wanted = desired(options, root, peer);
|
|
8693
|
+
const docs = nextDocuments(current, wanted);
|
|
8694
|
+
const targets = FILES.map((relative) => {
|
|
8695
|
+
const old = snapshots[relative];
|
|
8696
|
+
const next = Buffer.from(`${JSON.stringify(docs[relative], null, 2)}
|
|
8697
|
+
`);
|
|
8698
|
+
return { path: join14(root, relative), relative, ...old ? { current: old.bytes, stat: old.stat } : {}, next, changed: !old || !isDeepStrictEqual(current[relative], docs[relative]) };
|
|
8699
|
+
});
|
|
8700
|
+
const git = memoryGitSync(["ls-files", "--", ...FILES], { cwd: root, encoding: "utf8", timeout: 3000 });
|
|
8701
|
+
const tracked = git.status === 0 && !!git.stdout.trim();
|
|
8702
|
+
const changed = targets.some((target) => target.changed);
|
|
8703
|
+
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 };
|
|
8704
|
+
}
|
|
8705
|
+
function identityMatches(path, before) {
|
|
8706
|
+
const now = lstatSync9(path, { throwIfNoEntry: false });
|
|
8707
|
+
return before ? !!now && now.dev === before.dev && now.ino === before.ino : !now;
|
|
8708
|
+
}
|
|
8709
|
+
function atomicWrite(path, bytes, expected, plan) {
|
|
8710
|
+
const temporary = `${path}.${randomUUID5()}.tmp`;
|
|
8711
|
+
let fd;
|
|
8712
|
+
let tempIdentity;
|
|
8713
|
+
try {
|
|
8714
|
+
verifyPluginDirectory(plan, path);
|
|
8715
|
+
fd = openSync4(temporary, "wx", 384);
|
|
8716
|
+
const tempStat = fstatSync3(fd);
|
|
8717
|
+
tempIdentity = { dev: tempStat.dev, ino: tempStat.ino };
|
|
8718
|
+
writeFileSync4(fd, bytes);
|
|
8719
|
+
closeSync4(fd);
|
|
8720
|
+
fd = undefined;
|
|
8721
|
+
verifyPluginDirectory(plan, path);
|
|
8722
|
+
if (!identityMatches(path, expected))
|
|
8723
|
+
throw new Error(`${path} changed while the managed plugin was being written`);
|
|
8724
|
+
renameSync(temporary, path);
|
|
8725
|
+
const stat = lstatSync9(path);
|
|
8726
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1)
|
|
8727
|
+
throw new Error(`${path} changed while the managed plugin was written`);
|
|
8728
|
+
return { dev: stat.dev, ino: stat.ino };
|
|
8729
|
+
} finally {
|
|
8730
|
+
if (fd !== undefined)
|
|
8731
|
+
closeSync4(fd);
|
|
8732
|
+
try {
|
|
8733
|
+
const current = lstatSync9(temporary, { throwIfNoEntry: false });
|
|
8734
|
+
if (current && tempIdentity && current.dev === tempIdentity.dev && current.ino === tempIdentity.ino)
|
|
8735
|
+
unlinkSync(temporary);
|
|
8736
|
+
} catch {}
|
|
8737
|
+
}
|
|
8738
|
+
}
|
|
8739
|
+
function rollback(plan, written, createdDirs) {
|
|
8740
|
+
for (const { target, own } of written.reverse()) {
|
|
8741
|
+
try {
|
|
8742
|
+
const now = lstatSync9(target.path, { throwIfNoEntry: false });
|
|
8743
|
+
if (!now || now.dev !== own.dev || now.ino !== own.ino)
|
|
8744
|
+
continue;
|
|
8745
|
+
if (target.current)
|
|
8746
|
+
atomicWrite(target.path, target.current, now, plan);
|
|
8747
|
+
else
|
|
8748
|
+
unlinkSync(target.path);
|
|
8749
|
+
} catch {}
|
|
8750
|
+
}
|
|
8751
|
+
for (const { path, dev, ino } of createdDirs.reverse()) {
|
|
8752
|
+
try {
|
|
8753
|
+
const now = lstatSync9(path);
|
|
8754
|
+
if (now.isDirectory() && !now.isSymbolicLink() && now.dev === dev && now.ino === ino)
|
|
8755
|
+
rmdirSync(path);
|
|
8756
|
+
} catch {}
|
|
8757
|
+
}
|
|
8758
|
+
}
|
|
8759
|
+
function ensureAgyMcp(options) {
|
|
8760
|
+
const plan = internalPlan(options);
|
|
8761
|
+
if (!plan.changed)
|
|
8762
|
+
return { changed: false, tracked: plan.tracked };
|
|
8763
|
+
const createdDirs = [];
|
|
8764
|
+
const written = [];
|
|
8765
|
+
try {
|
|
8766
|
+
for (const relative of [".agents", ".agents/plugins", PLUGIN_DIR]) {
|
|
8767
|
+
const path = join14(plan.root, relative);
|
|
8768
|
+
const prior = safeDirectory(path);
|
|
8769
|
+
if (prior)
|
|
8770
|
+
continue;
|
|
8771
|
+
mkdirSync6(path, { mode: 448 });
|
|
8772
|
+
const stat = safeDirectory(path);
|
|
8773
|
+
createdDirs.push({ path, dev: stat.dev, ino: stat.ino });
|
|
8774
|
+
}
|
|
8775
|
+
plan.directories = pluginDirectories(plan.root);
|
|
8776
|
+
plan.canonicalPluginDir = realPath(plan.pluginDir);
|
|
8777
|
+
if (plan.canonicalRoot !== realPath(plan.root) || plan.canonicalPluginDir !== join14(plan.canonicalRoot, PLUGIN_DIR))
|
|
8778
|
+
throw new Error("managed agy plugin path changed; inspect it before writing");
|
|
8779
|
+
for (const target of plan.targets) {
|
|
8780
|
+
if (!target.changed)
|
|
8781
|
+
continue;
|
|
8782
|
+
if (!identityMatches(target.path, target.stat))
|
|
8783
|
+
throw new Error(`${target.path} changed while the managed plugin was being prepared`);
|
|
8784
|
+
const own = atomicWrite(target.path, target.next, target.stat, plan);
|
|
8785
|
+
written.push({ target, own });
|
|
8786
|
+
}
|
|
8787
|
+
} catch (error) {
|
|
8788
|
+
rollback(plan, written, createdDirs);
|
|
8789
|
+
throw error;
|
|
8790
|
+
}
|
|
8791
|
+
return { changed: true, tracked: plan.tracked };
|
|
8792
|
+
}
|
|
8793
|
+
function cleanupAgyMcp(options) {
|
|
8794
|
+
const plan = internalPlan(options);
|
|
8795
|
+
const present = plan.targets.filter((target) => target.current !== undefined);
|
|
8796
|
+
if (!present.length)
|
|
8797
|
+
return;
|
|
8798
|
+
if (present.length !== FILES.length)
|
|
8799
|
+
throw new Error("managed agy plugin is incomplete; inspect it before cleanup");
|
|
8800
|
+
const currentHooks = parseJson(readFileSync6(join14(plan.root, HOOKS_FILE)), HOOKS_FILE);
|
|
8801
|
+
const launch = managedHookLaunch(currentHooks, options, options.peer ?? "agy");
|
|
8802
|
+
if (!launch || launch.instanceId !== options.instanceId || launch.launchId !== options.launchId)
|
|
8803
|
+
return;
|
|
8804
|
+
if (plan.changed)
|
|
8805
|
+
return;
|
|
8806
|
+
const deleted = [];
|
|
8807
|
+
try {
|
|
8808
|
+
for (const target of plan.targets) {
|
|
8809
|
+
const stat = lstatSync9(target.path);
|
|
8810
|
+
if (!target.stat || stat.dev !== target.stat.dev || stat.ino !== target.stat.ino)
|
|
8811
|
+
throw new Error(`${target.path} changed before agy plugin cleanup`);
|
|
8812
|
+
}
|
|
8813
|
+
for (const target of plan.targets) {
|
|
8814
|
+
if (!identityMatches(target.path, target.stat))
|
|
8815
|
+
throw new Error(`${target.path} changed before agy plugin cleanup`);
|
|
8816
|
+
if (target.relative === MCP_FILE) {
|
|
8817
|
+
const currentMcp = parseJson(target.current, MCP_FILE);
|
|
8818
|
+
const servers = { ...currentMcp.mcpServers };
|
|
8819
|
+
delete servers[PLUGIN_KEY];
|
|
8820
|
+
const nextMcp = { ...currentMcp, mcpServers: servers };
|
|
8821
|
+
const retain = Object.keys(servers).length > 0 || Object.keys(currentMcp).some((key) => key !== "mcpServers");
|
|
8822
|
+
if (retain) {
|
|
8823
|
+
const own = atomicWrite(target.path, Buffer.from(`${JSON.stringify(nextMcp, null, 2)}
|
|
8824
|
+
`), target.stat, plan);
|
|
8825
|
+
deleted.push({ path: target.path, bytes: target.current, own });
|
|
8826
|
+
} else {
|
|
8827
|
+
unlinkSync(target.path);
|
|
8828
|
+
deleted.push({ path: target.path, bytes: target.current });
|
|
8829
|
+
}
|
|
8830
|
+
} else {
|
|
8831
|
+
unlinkSync(target.path);
|
|
8832
|
+
deleted.push({ path: target.path, bytes: target.current });
|
|
8833
|
+
}
|
|
8834
|
+
}
|
|
8835
|
+
} catch (error) {
|
|
8836
|
+
for (const item of deleted.reverse()) {
|
|
8837
|
+
try {
|
|
8838
|
+
const current = lstatSync9(item.path, { throwIfNoEntry: false });
|
|
8839
|
+
if (item.own) {
|
|
8840
|
+
if (current?.dev === item.own.dev && current.ino === item.own.ino)
|
|
8841
|
+
atomicWrite(item.path, item.bytes, current, plan);
|
|
8842
|
+
} else if (!current)
|
|
8843
|
+
atomicWrite(item.path, item.bytes, undefined, plan);
|
|
8844
|
+
} catch {}
|
|
8845
|
+
}
|
|
8846
|
+
throw error;
|
|
8847
|
+
}
|
|
8848
|
+
}
|
|
8849
|
+
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;
|
|
8850
|
+
var init_agy_mcp = __esm(() => {
|
|
8851
|
+
init_git();
|
|
8852
|
+
init_project();
|
|
8853
|
+
MANIFEST_FILE = `${PLUGIN_DIR}/plugin.json`;
|
|
8854
|
+
MCP_FILE = `${PLUGIN_DIR}/mcp_config.json`;
|
|
8855
|
+
HOOKS_FILE = `${PLUGIN_DIR}/hooks.json`;
|
|
8856
|
+
FILES = [MANIFEST_FILE, MCP_FILE, HOOKS_FILE];
|
|
8857
|
+
EVENTS2 = ["SessionStart", "PreInvocation", "PostInvocation", "PreToolUse", "PostToolUse", "Stop"];
|
|
8858
|
+
MAX_CONFIG_BYTES = 256 * 1024;
|
|
8859
|
+
});
|
|
8860
|
+
|
|
8861
|
+
// src/adapters/agy/entry.ts
|
|
8862
|
+
import { spawn as spawn9, spawnSync as spawnSync4 } from "child_process";
|
|
8863
|
+
import { resolve as resolve11 } from "path";
|
|
8864
|
+
function agyTuiSpawnOptions(cwd, env) {
|
|
8865
|
+
return { cwd, env, stdio: "inherit" };
|
|
8866
|
+
}
|
|
8867
|
+
function runAgyTui(argv, cwd, env) {
|
|
8868
|
+
const child = spawn9("agy", argv, agyTuiSpawnOptions(cwd, env));
|
|
8869
|
+
return new Promise((resolveExit, reject) => {
|
|
8870
|
+
const forward = (signal) => {
|
|
8871
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
8872
|
+
return;
|
|
8873
|
+
try {
|
|
8874
|
+
child.kill(signal);
|
|
8875
|
+
} catch {}
|
|
8876
|
+
};
|
|
8877
|
+
const onInterrupt = () => forward("SIGINT");
|
|
8878
|
+
const onTerminate = () => forward("SIGTERM");
|
|
8879
|
+
const onHangup = () => forward("SIGHUP");
|
|
8880
|
+
const cleanup = () => {
|
|
8881
|
+
process.off("SIGINT", onInterrupt);
|
|
8882
|
+
process.off("SIGTERM", onTerminate);
|
|
8883
|
+
process.off("SIGHUP", onHangup);
|
|
8884
|
+
};
|
|
8885
|
+
process.on("SIGINT", onInterrupt);
|
|
8886
|
+
process.on("SIGTERM", onTerminate);
|
|
8887
|
+
process.on("SIGHUP", onHangup);
|
|
8888
|
+
child.once("error", (error) => {
|
|
8889
|
+
cleanup();
|
|
8890
|
+
reject(error);
|
|
8891
|
+
});
|
|
8892
|
+
child.once("close", (code) => {
|
|
8893
|
+
cleanup();
|
|
8894
|
+
resolveExit(code ?? 1);
|
|
8895
|
+
});
|
|
8896
|
+
});
|
|
8897
|
+
}
|
|
8898
|
+
async function runAgyEntry(ctx) {
|
|
8899
|
+
const { args, cwd, stateDir, cliDir, connect, fail } = ctx;
|
|
8900
|
+
if (ctx.instance)
|
|
8901
|
+
fail("extra Antigravity TUI instances are not verified in P1");
|
|
8902
|
+
const preview = args.includes("--print-command") || args.includes("--dry-run");
|
|
8903
|
+
const unattended = ctx.unattendedEnv || args.includes("--unattended");
|
|
8904
|
+
const nativeArgs = args.filter((arg) => !["--print-command", "--dry-run", "--unattended"].includes(arg));
|
|
8905
|
+
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}=`))))
|
|
8906
|
+
fail("agy P1 supports a fresh TUI only; headless, resume and permission flags need a verified mapping");
|
|
8907
|
+
if (unattended)
|
|
8908
|
+
fail("agy P1 native permissions are unmanaged; unattended mode has no verified prompt mapping");
|
|
8909
|
+
const argv = nativeArgs;
|
|
8910
|
+
if (preview) {
|
|
8911
|
+
console.log(JSON.stringify({ command: "agy", args: argv.map(publicArg), mode: "tui", permission: "unmanaged", mcp: ".agents/plugins/agent-hub/", writes: false }));
|
|
8912
|
+
return;
|
|
8913
|
+
}
|
|
8914
|
+
const version = spawnSync4("agy", ["--version"], { encoding: "utf8", timeout: 5000, maxBuffer: 4096, env: peerChildEnv("agy") });
|
|
8915
|
+
if (version.status !== 0 || version.stdout.trim() !== "1.4.2")
|
|
8916
|
+
fail("agy P1 requires the verified Antigravity CLI 1.4.2");
|
|
8917
|
+
const hub = await connect();
|
|
8918
|
+
let options;
|
|
8919
|
+
let pluginWritten = false;
|
|
8920
|
+
try {
|
|
8921
|
+
if (!hub.features.includes("native_hook_launch"))
|
|
8922
|
+
fail("running hub lacks the verified native hook launcher; upgrade or restart it before launching agy");
|
|
8923
|
+
const status = (await hub.request({ t: "status" })).status;
|
|
8924
|
+
if (rosterInStatus(status, "agy") !== true)
|
|
8925
|
+
fail("agy is off roster; add it with ahub roster add agy");
|
|
8926
|
+
const admitted = await hub.request({ t: "native_hook_launch", op: "begin", peer: "agy" });
|
|
8927
|
+
if (!admitted.ok || typeof admitted.launchId !== "string" || typeof admitted.instanceId !== "string")
|
|
8928
|
+
fail(admitted.error ?? "native hook launch was not admitted");
|
|
8929
|
+
options = {
|
|
8930
|
+
projectRoot: cwd,
|
|
8931
|
+
stateDir,
|
|
8932
|
+
serverJs: bundleInStatus(status) ?? resolve11(cliDir, "../../plugins/agent-hub/server.js"),
|
|
8933
|
+
hookScript: resolve11(cliDir, "facts-hook.ts"),
|
|
8934
|
+
peer: "agy",
|
|
8935
|
+
launchId: admitted.launchId,
|
|
8936
|
+
instanceId: admitted.instanceId
|
|
8937
|
+
};
|
|
8938
|
+
const written = ensureAgyMcp(options);
|
|
8939
|
+
pluginWritten = written.changed;
|
|
8940
|
+
if (written.tracked)
|
|
8941
|
+
console.error("The managed agy plugin is tracked; review its local machine paths before committing");
|
|
8942
|
+
console.error("Antigravity: pull-only TUI; native permissions unmanaged, no hub sandbox");
|
|
8943
|
+
const env = { ...ctx.helpers.nativeLaunchEnv("agy", ctx.helpers.childEnv()), AGENTHUB_STATE_DIR: stateDir, AGENTHUB_PROJECT_DIR: cwd };
|
|
8944
|
+
delete env.AGENTHUB_CHANNEL;
|
|
8945
|
+
const code = await runAgyTui(argv, cwd, env);
|
|
8946
|
+
if (code !== 0)
|
|
8947
|
+
process.exitCode = code;
|
|
8948
|
+
} finally {
|
|
8949
|
+
if (options) {
|
|
8950
|
+
await hub.request({ t: "native_hook_launch", op: "end", peer: "agy", launchId: options.launchId }).catch(() => {});
|
|
8951
|
+
if (pluginWritten)
|
|
8952
|
+
try {
|
|
8953
|
+
cleanupAgyMcp(options);
|
|
8954
|
+
} catch {
|
|
8955
|
+
console.error("Managed agy plugin cleanup refused: files changed or ownership could not be verified");
|
|
8956
|
+
}
|
|
8957
|
+
}
|
|
8958
|
+
hub.close();
|
|
8959
|
+
}
|
|
8960
|
+
}
|
|
8961
|
+
var init_entry = __esm(() => {
|
|
8962
|
+
init_agy_mcp();
|
|
8963
|
+
init_child_process();
|
|
8964
|
+
});
|
|
8965
|
+
|
|
8966
|
+
// src/adapters/definitions/native-cli-retained.ts
|
|
8967
|
+
function retainedLaunchWarning(status, peer, definition) {
|
|
8968
|
+
const keys = definition.cli.launchConfigKeys;
|
|
8969
|
+
const retained = status?.retainedConfig;
|
|
8970
|
+
if (!keys?.length || !Array.isArray(retained))
|
|
8971
|
+
return;
|
|
8972
|
+
const pending = retained.flatMap((row) => {
|
|
8973
|
+
if (!row || typeof row !== "object")
|
|
8974
|
+
return [];
|
|
8975
|
+
const key = row.key;
|
|
8976
|
+
return typeof key === "string" && keys.some((candidate) => key === candidate || key.startsWith(`${candidate}.`)) ? [key] : [];
|
|
8977
|
+
});
|
|
8978
|
+
const unique = [...new Set(pending)];
|
|
8979
|
+
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;
|
|
8980
|
+
}
|
|
8981
|
+
|
|
7896
8982
|
// src/hub/claude-state.ts
|
|
7897
8983
|
function claudeStateFile(base, peer) {
|
|
7898
8984
|
return peer.includes("-") && PEER2.test(peer) ? `${base}.${peer}.json` : `${base}.json`;
|
|
@@ -7903,9 +8989,9 @@ var init_claude_state = __esm(() => {
|
|
|
7903
8989
|
});
|
|
7904
8990
|
|
|
7905
8991
|
// src/adapters/definitions/native-cli-entry-claude.ts
|
|
7906
|
-
import { readFileSync as
|
|
7907
|
-
import { join as
|
|
7908
|
-
import { randomUUID as
|
|
8992
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
8993
|
+
import { join as join15 } from "path";
|
|
8994
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
7909
8995
|
async function runClaudeEntry(ctx) {
|
|
7910
8996
|
const { args, cwd, stateDir, cliDir, unattendedEnv, projectConfig, fail } = ctx;
|
|
7911
8997
|
const peer = ctx.instance ?? "claude";
|
|
@@ -7914,6 +9000,10 @@ async function runClaudeEntry(ctx) {
|
|
|
7914
9000
|
return console.log(JSON.stringify({ peer, start: "hub", note: "a headless reviewer the hub runs per review" }, null, 2));
|
|
7915
9001
|
const hub = await ctx.connect();
|
|
7916
9002
|
try {
|
|
9003
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9004
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9005
|
+
if (warning)
|
|
9006
|
+
console.error(warning);
|
|
7917
9007
|
const res = await hub.request({ t: "start", peer, operationId: process.env.AGENTHUB_RECOVERY_OPERATION });
|
|
7918
9008
|
if (res.ok !== true)
|
|
7919
9009
|
fail(String(res.error ?? `${peer} was not started`));
|
|
@@ -7935,20 +9025,20 @@ async function runClaudeEntry(ctx) {
|
|
|
7935
9025
|
process.env.AGENTHUB_INSTANCE_ID = control.instanceId;
|
|
7936
9026
|
const terminal = await recordTerminalLaunch(peer, cwd, stateDir, control.instanceId, ctx.instance ? { executionCwd: cwd } : undefined);
|
|
7937
9027
|
if (!terminal)
|
|
7938
|
-
process.env.AGENTHUB_LAUNCH_ID =
|
|
9028
|
+
process.env.AGENTHUB_LAUNCH_ID = randomUUID6();
|
|
7939
9029
|
}
|
|
7940
9030
|
let original;
|
|
7941
|
-
for (const file of [
|
|
9031
|
+
for (const file of [join15(cwd, ".claude", "settings.local.json"), join15(cwd, ".claude", "settings.json"), join15(process.env.HOME ?? "", ".claude", "settings.json")]) {
|
|
7942
9032
|
try {
|
|
7943
|
-
original ??= JSON.parse(
|
|
9033
|
+
original ??= JSON.parse(readFileSync7(file, "utf8")).statusLine;
|
|
7944
9034
|
} catch {}
|
|
7945
9035
|
}
|
|
7946
|
-
const facts = claudeObservationHooks(projectConfig(), { script:
|
|
9036
|
+
const facts = claudeObservationHooks(projectConfig(), { script: join15(cliDir, "facts-hook.ts"), stateDir });
|
|
7947
9037
|
let previousLaunch;
|
|
7948
9038
|
try {
|
|
7949
|
-
previousLaunch = JSON.parse(
|
|
9039
|
+
previousLaunch = JSON.parse(readFileSync7(join15(stateDir, claudeStateFile("claude-launch", peer)), "utf8"));
|
|
7950
9040
|
} catch {}
|
|
7951
|
-
const launch = buildLaunch("claude", args, { unattended: unattendedEnv, statusLine: { script:
|
|
9041
|
+
const launch = buildLaunch("claude", args, { unattended: unattendedEnv, statusLine: { script: join15(cliDir, "statusline-tee.ts"), stateDir, ...original ? { original } : {} }, ...facts ? { facts } : {} });
|
|
7952
9042
|
let result;
|
|
7953
9043
|
try {
|
|
7954
9044
|
let launchRecord;
|
|
@@ -7992,6 +9082,10 @@ async function runCodexEntry(ctx) {
|
|
|
7992
9082
|
if (recovering && !ctx.instance && before?.instanceId)
|
|
7993
9083
|
await recordTerminalLaunch(peer, cwd, stateDir, before.instanceId);
|
|
7994
9084
|
const hub = await connect();
|
|
9085
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9086
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9087
|
+
if (warning)
|
|
9088
|
+
console.error(warning);
|
|
7995
9089
|
const res = await hub.request({ t: "start", peer, args: { unattended: launch0.unattended === true }, operationId: process.env.AGENTHUB_RECOVERY_OPERATION });
|
|
7996
9090
|
hub.close();
|
|
7997
9091
|
if (!res.ok)
|
|
@@ -8004,27 +9098,16 @@ async function runCodexEntry(ctx) {
|
|
|
8004
9098
|
const control = readControl(stateDir);
|
|
8005
9099
|
if ((!recovering || ctx.instance) && control?.instanceId)
|
|
8006
9100
|
await recordTerminalLaunch(peer, cwd, stateDir, control.instanceId, ctx.instance ? { executionCwd: ctx.executionCwd } : undefined);
|
|
8007
|
-
const
|
|
9101
|
+
const runningCodexBin = res.retainedLaunchConfig?.codex_bin;
|
|
9102
|
+
const launch = buildLaunch("codex", args, { unattended: unattendedEnv, instance: peer, proxyUrl: res.proxyUrl, codexBin: typeof runningCodexBin === "string" ? runningCodexBin : projectConfig().codex_bin });
|
|
8008
9103
|
if (launch0.warning)
|
|
8009
9104
|
console.error(launch0.warning);
|
|
8010
9105
|
exec(launch.cmd, launch.args, "codex");
|
|
8011
9106
|
}
|
|
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
|
-
}
|
|
9107
|
+
var init_native_cli_entry_codex = () => {};
|
|
8025
9108
|
|
|
8026
9109
|
// src/adapters/definitions/native-cli-entry-jcode.ts
|
|
8027
|
-
import { resolve as
|
|
9110
|
+
import { resolve as resolve12 } from "path";
|
|
8028
9111
|
async function runJcodeEntry(ctx) {
|
|
8029
9112
|
const { args, cwd, stateDir, cliDir, unattendedEnv, connect, fail, takeFlags } = ctx;
|
|
8030
9113
|
const { launcherPreview, qwenSessionId, configureJcodeMcp } = ctx.helpers;
|
|
@@ -8048,7 +9131,10 @@ async function runJcodeEntry(ctx) {
|
|
|
8048
9131
|
fail("cannot read jcode's roster membership from the running hub; upgrade or restart it, then retry (nothing was started)");
|
|
8049
9132
|
if (!rostered)
|
|
8050
9133
|
fail("jcode is off roster; add it with ahub roster add jcode");
|
|
8051
|
-
const
|
|
9134
|
+
const warning = retainedLaunchWarning(snapshot, "jcode", ctx.definition);
|
|
9135
|
+
if (warning)
|
|
9136
|
+
console.error(warning);
|
|
9137
|
+
const written = configureJcodeMcp(cwd, stateDir, bundleInStatus(snapshot) ?? resolve12(cliDir, "../../plugins/agent-hub/server.js"));
|
|
8052
9138
|
if (written.tracked)
|
|
8053
9139
|
console.error(".jcode/mcp.json is tracked; review this local machine-path change before committing it");
|
|
8054
9140
|
const reply = await hub.request({ t: "start", peer: "jcode", args: { mode: "headless", model: one["--model"], providerProfile: one["--provider-profile"], sessionId: one["--session-id"] }, operationId: process.env.AGENTHUB_RECOVERY_OPERATION }, 65000);
|
|
@@ -8081,6 +9167,10 @@ async function runQwenEntry(ctx) {
|
|
|
8081
9167
|
fail("Qwen TUI is not offered before verification; usage: ahub qwen [--mode headless] [--model <alias>] [--session-id <id>]");
|
|
8082
9168
|
const hub = await connect();
|
|
8083
9169
|
try {
|
|
9170
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9171
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9172
|
+
if (warning)
|
|
9173
|
+
console.error(warning);
|
|
8084
9174
|
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
9175
|
if (!reply.ok)
|
|
8086
9176
|
fail(reply.error);
|
|
@@ -8089,14 +9179,15 @@ async function runQwenEntry(ctx) {
|
|
|
8089
9179
|
hub.close();
|
|
8090
9180
|
}
|
|
8091
9181
|
}
|
|
9182
|
+
var init_native_cli_entry_qwen = () => {};
|
|
8092
9183
|
|
|
8093
9184
|
// src/adapters/definitions/native-cli-entry-kimi.ts
|
|
8094
|
-
import { resolve as
|
|
8095
|
-
import { spawnSync as
|
|
9185
|
+
import { resolve as resolve13 } from "path";
|
|
9186
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
8096
9187
|
async function runKimiEntry(ctx) {
|
|
8097
9188
|
const peer = ctx.instance ?? "kimi";
|
|
8098
9189
|
const { args, cwd, stateDir, cliDir, unattendedEnv, projectConfig, connect, fail, takeFlags } = ctx;
|
|
8099
|
-
const { launcherPreview, startModeOf, buildKimiTuiLaunch, configureKimiMcp, nativeLaunchEnv, childEnv } = ctx.helpers;
|
|
9190
|
+
const { launcherPreview, readControl, startModeOf, buildKimiTuiLaunch, configureKimiMcp, nativeLaunchEnv, childEnv } = ctx.helpers;
|
|
8100
9191
|
if (args.includes("--print-command") || args.includes("--dry-run")) {
|
|
8101
9192
|
try {
|
|
8102
9193
|
return console.log(JSON.stringify(launcherPreview("kimi", args, cwd, stateDir, unattendedEnv), null, 2));
|
|
@@ -8105,7 +9196,8 @@ async function runKimiEntry(ctx) {
|
|
|
8105
9196
|
}
|
|
8106
9197
|
}
|
|
8107
9198
|
const { one, rest } = takeFlags(args, ["--mode", "--model"], []);
|
|
8108
|
-
const
|
|
9199
|
+
const acceptedMode = readControl(stateDir)?.startModes?.[peer];
|
|
9200
|
+
const mode = one["--mode"] ?? (ctx.instance ? "headless" : acceptedMode ?? startModeOf(projectConfig().peers, "kimi")), model = one["--model"];
|
|
8109
9201
|
if (ctx.instance && mode !== "headless")
|
|
8110
9202
|
fail("extra Kimi instances require headless mode");
|
|
8111
9203
|
if (rest.length || !["tui", "headless"].includes(mode))
|
|
@@ -8115,6 +9207,10 @@ async function runKimiEntry(ctx) {
|
|
|
8115
9207
|
fail("Kimi's TUI needs a terminal for these options; run in a terminal or add --mode headless");
|
|
8116
9208
|
const hub = await connect();
|
|
8117
9209
|
try {
|
|
9210
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9211
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9212
|
+
if (warning)
|
|
9213
|
+
console.error(warning);
|
|
8118
9214
|
const reply = await hub.request({ t: "peer_start", peer }, 35000);
|
|
8119
9215
|
if (!reply.ok)
|
|
8120
9216
|
fail(reply.error);
|
|
@@ -8126,25 +9222,29 @@ async function runKimiEntry(ctx) {
|
|
|
8126
9222
|
const hub = await connect();
|
|
8127
9223
|
try {
|
|
8128
9224
|
if (mode === "tui") {
|
|
8129
|
-
const launch = buildKimiTuiLaunch(projectConfig().kimi_cmd, model);
|
|
8130
9225
|
const snapshot = (await hub.request({ t: "status" })).status;
|
|
8131
9226
|
const rostered = rosterInStatus(snapshot, peer);
|
|
8132
9227
|
if (rostered === undefined)
|
|
8133
9228
|
fail("cannot read kimi's roster membership from the running hub; upgrade or restart it, then retry (nothing was started)");
|
|
8134
9229
|
if (!rostered)
|
|
8135
9230
|
fail("kimi is off roster; add it with ahub roster add kimi");
|
|
9231
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9232
|
+
if (warning)
|
|
9233
|
+
console.error(warning);
|
|
8136
9234
|
const owner = snapshot?.peers?.[peer];
|
|
8137
9235
|
if (owner && !["offline"].includes(owner.state) && !owner.pullOnly)
|
|
8138
9236
|
fail("Kimi is running headless; run ahub stop kimi before starting its TUI");
|
|
8139
9237
|
const admission = await hub.request({ t: "start", peer, args: { mode: "tui" }, operationId: process.env.AGENTHUB_RECOVERY_OPERATION });
|
|
8140
9238
|
if (!admission.ok)
|
|
8141
9239
|
fail(admission.error);
|
|
9240
|
+
const runningCommand = admission.retainedLaunchConfig?.kimi_cmd;
|
|
9241
|
+
const launch = buildKimiTuiLaunch(Array.isArray(runningCommand) && runningCommand.every((arg) => typeof arg === "string") ? runningCommand : projectConfig().kimi_cmd, model);
|
|
8142
9242
|
let result;
|
|
8143
9243
|
try {
|
|
8144
|
-
const written = configureKimiMcp(cwd, stateDir, bundleInStatus(snapshot) ??
|
|
9244
|
+
const written = configureKimiMcp(cwd, stateDir, bundleInStatus(snapshot) ?? resolve13(cliDir, "../../plugins/agent-hub/server.js"), "tui");
|
|
8145
9245
|
if (written.tracked)
|
|
8146
9246
|
console.error(".kimi-code/mcp.json is tracked; the agent-hub entry contains machine paths. Review this local change before committing it.");
|
|
8147
|
-
result =
|
|
9247
|
+
result = spawnSync5(launch.cmd, launch.args, { cwd, stdio: "inherit", env: { ...nativeLaunchEnv("kimi", childEnv()), AGENTHUB_STATE_DIR: stateDir, AGENTHUB_PROJECT_DIR: cwd } });
|
|
8148
9248
|
} finally {
|
|
8149
9249
|
if (admission.admissionId)
|
|
8150
9250
|
await hub.request({ t: "kimi_launch_end", admissionId: admission.admissionId }).catch(() => {});
|
|
@@ -8153,11 +9253,15 @@ async function runKimiEntry(ctx) {
|
|
|
8153
9253
|
fail(`cannot run ${launch.cmd}: ${result.error.message}`);
|
|
8154
9254
|
process.exit(result.status ?? 1);
|
|
8155
9255
|
}
|
|
8156
|
-
const
|
|
9256
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9257
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9258
|
+
if (warning)
|
|
9259
|
+
console.error(warning);
|
|
9260
|
+
const owner = snapshot?.peers?.[peer];
|
|
8157
9261
|
if (owner?.pullOnly && owner.state !== "offline")
|
|
8158
9262
|
fail("Kimi runs in a terminal; end it there before starting a headless one");
|
|
8159
9263
|
if (!ctx.instance)
|
|
8160
|
-
configureKimiMcp(cwd, stateDir,
|
|
9264
|
+
configureKimiMcp(cwd, stateDir, resolve13(cliDir, "../../plugins/agent-hub/server.js"), "headless");
|
|
8161
9265
|
const res = await hub.request({ t: "start", peer, args: { mode: "headless", model }, operationId: process.env.AGENTHUB_RECOVERY_OPERATION });
|
|
8162
9266
|
if (!res.ok)
|
|
8163
9267
|
fail(res.error);
|
|
@@ -8224,7 +9328,8 @@ async function runPiEntry(ctx) {
|
|
|
8224
9328
|
}
|
|
8225
9329
|
}
|
|
8226
9330
|
const flags = parsePiFlags(args, fail);
|
|
8227
|
-
const
|
|
9331
|
+
const acceptedMode = ctx.helpers.readControl(stateDir)?.startModes?.[peer];
|
|
9332
|
+
const options = { ...flags, mode: flags.mode ?? (ctx.instance ? "headless" : acceptedMode ?? startModeOf(projectConfig().peers, "pi")) };
|
|
8228
9333
|
if (ctx.instance && options.mode !== "headless")
|
|
8229
9334
|
fail("extra Pi instances require headless mode");
|
|
8230
9335
|
if (flags.mode === undefined && options.mode === "tui" && !(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
@@ -8232,6 +9337,10 @@ async function runPiEntry(ctx) {
|
|
|
8232
9337
|
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
9338
|
const hub = await connect();
|
|
8234
9339
|
try {
|
|
9340
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9341
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9342
|
+
if (warning)
|
|
9343
|
+
console.error(warning);
|
|
8235
9344
|
const reply = await hub.request({ t: "peer_start", peer }, 35000);
|
|
8236
9345
|
if (reply.ok !== true) {
|
|
8237
9346
|
const error = String(reply.error ?? "pi start refused");
|
|
@@ -8247,6 +9356,10 @@ async function runPiEntry(ctx) {
|
|
|
8247
9356
|
if (recovering && before?.instanceId)
|
|
8248
9357
|
await recordTerminalLaunch(peer, cwd, stateDir, before.instanceId);
|
|
8249
9358
|
const hub = await connect();
|
|
9359
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9360
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9361
|
+
if (warning)
|
|
9362
|
+
console.error(warning);
|
|
8250
9363
|
const res = await hub.request({ t: "start", peer, args: options, operationId: process.env.AGENTHUB_RECOVERY_OPERATION });
|
|
8251
9364
|
hub.close();
|
|
8252
9365
|
if (!res.ok)
|
|
@@ -8280,21 +9393,31 @@ async function runLocalEntry(ctx) {
|
|
|
8280
9393
|
if (args.some((a, i) => i % 2 === 0 && !["--route", "--model"].includes(a)))
|
|
8281
9394
|
fail("usage: ahub local [--route <id> | --model <id>]");
|
|
8282
9395
|
const hub = await connect();
|
|
9396
|
+
const snapshot = (await hub.request({ t: "status" })).status;
|
|
9397
|
+
const warning = retainedLaunchWarning(snapshot, peer, ctx.definition);
|
|
9398
|
+
if (warning)
|
|
9399
|
+
console.error(warning);
|
|
8283
9400
|
const res = await hub.request({ t: "start", peer, args: { route: opt("--route"), model: opt("--model") }, operationId: process.env.AGENTHUB_RECOVERY_OPERATION });
|
|
8284
9401
|
hub.close();
|
|
8285
9402
|
if (!res.ok)
|
|
8286
9403
|
fail(res.error);
|
|
8287
9404
|
console.log(res.already ? `${peer} is already attached` : `${peer} attached on ${res.model}. Give it work with: ahub say @${peer} "..."`);
|
|
8288
9405
|
}
|
|
9406
|
+
var init_native_cli_entry_local = () => {};
|
|
8289
9407
|
|
|
8290
9408
|
// src/adapters/definitions/native-cli-entry.ts
|
|
8291
9409
|
var builtinCliEntries;
|
|
8292
9410
|
var init_native_cli_entry = __esm(() => {
|
|
9411
|
+
init_entry();
|
|
8293
9412
|
init_native_cli_entry_claude();
|
|
9413
|
+
init_native_cli_entry_codex();
|
|
8294
9414
|
init_native_cli_entry_jcode();
|
|
9415
|
+
init_native_cli_entry_qwen();
|
|
8295
9416
|
init_native_cli_entry_kimi();
|
|
8296
9417
|
init_native_cli_entry_pi();
|
|
9418
|
+
init_native_cli_entry_local();
|
|
8297
9419
|
builtinCliEntries = Object.freeze([
|
|
9420
|
+
{ command: "agy", lifecycleAdmission: true, execute: runAgyEntry },
|
|
8298
9421
|
{ command: "claude", lifecycleAdmission: true, execute: runClaudeEntry },
|
|
8299
9422
|
{ command: "codex", lifecycleAdmission: true, execute: runCodexEntry },
|
|
8300
9423
|
{ command: "jcode", lifecycleAdmission: true, execute: runJcodeEntry },
|
|
@@ -8305,37 +9428,9 @@ var init_native_cli_entry = __esm(() => {
|
|
|
8305
9428
|
]);
|
|
8306
9429
|
});
|
|
8307
9430
|
|
|
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
9431
|
// src/adapters/definitions/native-cli-preview-claude.ts
|
|
8337
|
-
import { readFileSync as
|
|
8338
|
-
import { join as
|
|
9432
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
9433
|
+
import { join as join16 } from "path";
|
|
8339
9434
|
function previewClaude(ctx) {
|
|
8340
9435
|
const { args, cwd, stateDir, cliDir, unattended, config } = ctx;
|
|
8341
9436
|
const { buildLaunch, claudeObservationHooks } = ctx.helpers;
|
|
@@ -8343,13 +9438,13 @@ function previewClaude(ctx) {
|
|
|
8343
9438
|
let envNames = state.envNames;
|
|
8344
9439
|
let launch;
|
|
8345
9440
|
let original;
|
|
8346
|
-
for (const file of [
|
|
9441
|
+
for (const file of [join16(cwd, ".claude/settings.local.json"), join16(cwd, ".claude/settings.json"), join16(process.env.HOME ?? "", ".claude/settings.json")]) {
|
|
8347
9442
|
try {
|
|
8348
|
-
original ??= JSON.parse(
|
|
9443
|
+
original ??= JSON.parse(readFileSync8(file, "utf8")).statusLine;
|
|
8349
9444
|
} catch {}
|
|
8350
9445
|
}
|
|
8351
|
-
const facts = claudeObservationHooks(config, { script:
|
|
8352
|
-
launch = buildLaunch("claude", args, { preview: true, unattended, statusLine: { script:
|
|
9446
|
+
const facts = claudeObservationHooks(config, { script: join16(cliDir, "facts-hook.ts"), stateDir });
|
|
9447
|
+
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
9448
|
const safeArgs = args.map((arg, i) => {
|
|
8354
9449
|
if (args[i - 1] === "--settings")
|
|
8355
9450
|
return "{}";
|
|
@@ -8360,7 +9455,7 @@ function previewClaude(ctx) {
|
|
|
8360
9455
|
return publicArg(arg);
|
|
8361
9456
|
});
|
|
8362
9457
|
const publicLaunch = buildLaunch("claude", safeArgs, { preview: true, unattended, statusLine: {
|
|
8363
|
-
script:
|
|
9458
|
+
script: join16(cliDir, "statusline-tee.ts"),
|
|
8364
9459
|
stateDir,
|
|
8365
9460
|
...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
9461
|
}, facts });
|
|
@@ -8393,7 +9488,7 @@ function previewCodex(ctx) {
|
|
|
8393
9488
|
var init_native_cli_preview_codex = () => {};
|
|
8394
9489
|
|
|
8395
9490
|
// src/adapters/definitions/native-cli-preview-kimi.ts
|
|
8396
|
-
import { join as
|
|
9491
|
+
import { join as join17 } from "path";
|
|
8397
9492
|
function previewKimi(ctx) {
|
|
8398
9493
|
const { args, cwd, stateDir, cliDir, config } = ctx;
|
|
8399
9494
|
const { childEnv, startModeOf, buildKimiLaunch, buildKimiTuiLaunch, nativeLaunchEnv, kimiMcpPlan } = ctx.helpers;
|
|
@@ -8416,14 +9511,14 @@ function previewKimi(ctx) {
|
|
|
8416
9511
|
launch = { ...native, cmd: native.cmd === "kimi" ? "kimi" : REDACTED, args: native.args.map((arg) => arg === "--model" || arg === REDACTED ? arg : REDACTED) };
|
|
8417
9512
|
envNames = Object.keys(nativeLaunchEnv("kimi", childEnv())).concat(["AGENTHUB_STATE_DIR", "AGENTHUB_PROJECT_DIR"]);
|
|
8418
9513
|
}
|
|
8419
|
-
const { file, action, changed, tracked } = kimiMcpPlan(cwd, stateDir,
|
|
9514
|
+
const { file, action, changed, tracked } = kimiMcpPlan(cwd, stateDir, join17(cliDir, "../../plugins/agent-hub/server.js"), mode);
|
|
8420
9515
|
kimiMetadata = { mode, mcp: { file, action, changed, tracked } };
|
|
8421
9516
|
return finishPreview(ctx, launch, envNames, unresolved, kimiMetadata);
|
|
8422
9517
|
}
|
|
8423
9518
|
var init_native_cli_preview_kimi = () => {};
|
|
8424
9519
|
|
|
8425
9520
|
// src/adapters/definitions/native-cli-preview-pi.ts
|
|
8426
|
-
import { join as
|
|
9521
|
+
import { join as join18, resolve as resolve14 } from "path";
|
|
8427
9522
|
function previewPi(ctx) {
|
|
8428
9523
|
const { args, stateDir, cliDir, config } = ctx;
|
|
8429
9524
|
const { relayModelIds, buildPiLaunch, startModeOf } = ctx.helpers;
|
|
@@ -8458,7 +9553,7 @@ function previewPi(ctx) {
|
|
|
8458
9553
|
tools: [],
|
|
8459
9554
|
preamble: REDACTED,
|
|
8460
9555
|
maxSteps: config.pi.max_steps
|
|
8461
|
-
}, {},
|
|
9556
|
+
}, {}, resolve14(join18(cliDir, "../pi/extension.ts")), UNRESOLVED, UNRESOLVED);
|
|
8462
9557
|
launch = pi;
|
|
8463
9558
|
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
9559
|
if (mode === "tui")
|
|
@@ -8471,7 +9566,7 @@ function previewPi(ctx) {
|
|
|
8471
9566
|
var init_native_cli_preview_pi = () => {};
|
|
8472
9567
|
|
|
8473
9568
|
// src/adapters/definitions/native-cli-preview-qwen.ts
|
|
8474
|
-
import { join as
|
|
9569
|
+
import { join as join19 } from "path";
|
|
8475
9570
|
function previewQwen(ctx) {
|
|
8476
9571
|
const { tool, args, cwd, stateDir, cliDir, config } = ctx;
|
|
8477
9572
|
const state = previewState(args, ctx.baseEnvNames), { unresolved, value } = state;
|
|
@@ -8494,7 +9589,7 @@ function previewQwen(ctx) {
|
|
|
8494
9589
|
form: "headless",
|
|
8495
9590
|
projectRoot: cwd,
|
|
8496
9591
|
stateDir,
|
|
8497
|
-
entrypoint:
|
|
9592
|
+
entrypoint: join19(cliDir, "main.js"),
|
|
8498
9593
|
userArgs: [],
|
|
8499
9594
|
unattended: false,
|
|
8500
9595
|
env: {},
|
|
@@ -8602,8 +9697,8 @@ var init_native_config_policy = __esm(() => {
|
|
|
8602
9697
|
});
|
|
8603
9698
|
|
|
8604
9699
|
// src/adapters/definitions/native-policy-aux.ts
|
|
8605
|
-
import { closeSync as
|
|
8606
|
-
import { isAbsolute as
|
|
9700
|
+
import { closeSync as closeSync5, constants as constants4, fstatSync as fstatSync4, openSync as openSync5, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
|
|
9701
|
+
import { isAbsolute as isAbsolute7, join as join20, normalize } from "path";
|
|
8607
9702
|
function claudeCrashPlan(_meta) {
|
|
8608
9703
|
return { how: "claude: the Claude Code plugin reconnects by itself while that session is still open" };
|
|
8609
9704
|
}
|
|
@@ -8648,13 +9743,13 @@ function localCrashPlan(meta) {
|
|
|
8648
9743
|
}
|
|
8649
9744
|
function claudePlugins(root, cwd) {
|
|
8650
9745
|
const enabled = new Map;
|
|
8651
|
-
for (const file of [
|
|
9746
|
+
for (const file of [join20(root, "settings.json"), ...cwd ? [join20(cwd, ".claude", "settings.json"), join20(cwd, ".claude", "settings.local.json")] : []]) {
|
|
8652
9747
|
const map = json(file)?.enabledPlugins;
|
|
8653
9748
|
if (map && typeof map === "object")
|
|
8654
9749
|
for (const [key, on] of Object.entries(map))
|
|
8655
9750
|
enabled.set(key, on === true);
|
|
8656
9751
|
}
|
|
8657
|
-
const installed = json(
|
|
9752
|
+
const installed = json(join20(root, "plugins", "installed_plugins.json"))?.plugins ?? {};
|
|
8658
9753
|
const out = [];
|
|
8659
9754
|
for (const [key, on] of enabled) {
|
|
8660
9755
|
if (!on)
|
|
@@ -8663,12 +9758,12 @@ function claudePlugins(root, cwd) {
|
|
|
8663
9758
|
const entries = Array.isArray(installed[key]) ? installed[key] : [];
|
|
8664
9759
|
const entry = entries.find((e) => e?.scope === "user") ?? entries.find((e) => e?.projectPath === cwd);
|
|
8665
9760
|
if (name && entry)
|
|
8666
|
-
out.push({ name, version:
|
|
9761
|
+
out.push({ name, version: version2(entry.version) });
|
|
8667
9762
|
}
|
|
8668
9763
|
return out;
|
|
8669
9764
|
}
|
|
8670
9765
|
function codexPlugins(root) {
|
|
8671
|
-
const config = text(
|
|
9766
|
+
const config = text(join20(root, "config.toml"));
|
|
8672
9767
|
if (config === undefined)
|
|
8673
9768
|
return [];
|
|
8674
9769
|
const found = new Map, disabled = new Set;
|
|
@@ -8687,9 +9782,9 @@ function codexPlugins(root) {
|
|
|
8687
9782
|
continue;
|
|
8688
9783
|
let versions = [];
|
|
8689
9784
|
try {
|
|
8690
|
-
versions = readdirSync3(
|
|
9785
|
+
versions = readdirSync3(join20(root, "plugins", "cache", market, plugin));
|
|
8691
9786
|
} catch {}
|
|
8692
|
-
out.push({ name, version: versions.length === 1 ?
|
|
9787
|
+
out.push({ name, version: versions.length === 1 ? version2(versions[0]) : null });
|
|
8693
9788
|
}
|
|
8694
9789
|
return out;
|
|
8695
9790
|
}
|
|
@@ -8702,21 +9797,21 @@ function parsePiContinuity(value) {
|
|
|
8702
9797
|
return { sessionId: value.sessionId, ...value.sessionFile === undefined ? {} : { sessionFile: value.sessionFile }, ...value.backend === undefined ? {} : { backend: value.backend }, ...value.model === undefined ? {} : { model: value.model } };
|
|
8703
9798
|
}
|
|
8704
9799
|
function absoluteContinuityPath(value) {
|
|
8705
|
-
return typeof value === "string" && value.length > 0 && value.length <= 4096 && !/[\x00-\x1f\x7f]/.test(value) &&
|
|
9800
|
+
return typeof value === "string" && value.length > 0 && value.length <= 4096 && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute7(value) && normalize(value) === value;
|
|
8706
9801
|
}
|
|
8707
9802
|
var str3 = (value) => typeof value === "string" && value ? value : undefined, PLUGINS, CLAUDE_ONLY, text = (file) => {
|
|
8708
9803
|
let fd;
|
|
8709
9804
|
try {
|
|
8710
|
-
fd =
|
|
8711
|
-
const st =
|
|
9805
|
+
fd = openSync5(file, constants4.O_RDONLY | constants4.O_NONBLOCK);
|
|
9806
|
+
const st = fstatSync4(fd);
|
|
8712
9807
|
if (!st.isFile() || st.size > 1024 * 1024)
|
|
8713
9808
|
return;
|
|
8714
|
-
return
|
|
9809
|
+
return readFileSync9(fd, "utf8");
|
|
8715
9810
|
} catch {
|
|
8716
9811
|
return;
|
|
8717
9812
|
} finally {
|
|
8718
9813
|
if (fd !== undefined)
|
|
8719
|
-
|
|
9814
|
+
closeSync5(fd);
|
|
8720
9815
|
}
|
|
8721
9816
|
}, json = (file) => {
|
|
8722
9817
|
try {
|
|
@@ -8725,7 +9820,7 @@ var str3 = (value) => typeof value === "string" && value ? value : undefined, PL
|
|
|
8725
9820
|
} catch {
|
|
8726
9821
|
return;
|
|
8727
9822
|
}
|
|
8728
|
-
},
|
|
9823
|
+
}, 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
9824
|
var init_native_policy_aux = __esm(() => {
|
|
8730
9825
|
PLUGINS = new Map([["claude-mem", "claude-mem"], ["ponytail", "ponytail"], ["open-code-review", "open-code-review"]]);
|
|
8731
9826
|
CLAUDE_ONLY = new Map([["codex@openai-codex", "codex-plugin"]]);
|
|
@@ -8771,7 +9866,7 @@ var init_launch2 = __esm(() => {
|
|
|
8771
9866
|
});
|
|
8772
9867
|
|
|
8773
9868
|
// src/cli/shell-quote.ts
|
|
8774
|
-
function
|
|
9869
|
+
function shellQuote3(value) {
|
|
8775
9870
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
8776
9871
|
}
|
|
8777
9872
|
|
|
@@ -8786,26 +9881,26 @@ function nativeLaunchArguments(tool, ctx, policy) {
|
|
|
8786
9881
|
}
|
|
8787
9882
|
function nativeLaunchPlan(launch, ctx) {
|
|
8788
9883
|
const argv = [launch.cmd, ...launch.args];
|
|
8789
|
-
return { ...launch, metadata: { packageEntrypoint: ctx.entrypoint, argv, env: {}, command: argv.map(
|
|
9884
|
+
return { ...launch, metadata: { packageEntrypoint: ctx.entrypoint, argv, env: {}, command: argv.map(shellQuote3).join(" ") } };
|
|
8790
9885
|
}
|
|
8791
9886
|
var init_native_cli_launch_common = __esm(() => {
|
|
8792
9887
|
init_launch2();
|
|
8793
9888
|
});
|
|
8794
9889
|
|
|
8795
9890
|
// src/adapters/definitions/native-cli-claude-launch.ts
|
|
8796
|
-
import { mkdirSync as
|
|
8797
|
-
import { randomUUID as
|
|
8798
|
-
import { resolve as
|
|
9891
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync5, renameSync as renameSync2, unlinkSync as unlinkSync2, readFileSync as readFileSync10 } from "fs";
|
|
9892
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
9893
|
+
import { resolve as resolve15, join as join21 } from "path";
|
|
8799
9894
|
function claudeChannel(userArgs) {
|
|
8800
|
-
const bundle =
|
|
9895
|
+
const bundle = resolve15(import.meta.dir, "../../../plugins/agent-hub/server.js");
|
|
8801
9896
|
for (let i = 0;i < userArgs.length; i++) {
|
|
8802
9897
|
const arg = userArgs[i];
|
|
8803
9898
|
const path = arg === "--mcp-config" ? userArgs[i + 1] : arg.startsWith("--mcp-config=") ? arg.slice("--mcp-config=".length) : undefined;
|
|
8804
9899
|
if (!path)
|
|
8805
9900
|
continue;
|
|
8806
9901
|
try {
|
|
8807
|
-
const server = JSON.parse(
|
|
8808
|
-
if (server?.command === "bun" && Array.isArray(server.args) && server.args.some((value) => typeof value === "string" &&
|
|
9902
|
+
const server = JSON.parse(readFileSync10(resolve15(path), "utf8")).mcpServers?.["agent-hub"];
|
|
9903
|
+
if (server?.command === "bun" && Array.isArray(server.args) && server.args.some((value) => typeof value === "string" && resolve15(value) === bundle))
|
|
8809
9904
|
return "server:agent-hub";
|
|
8810
9905
|
} catch {}
|
|
8811
9906
|
}
|
|
@@ -8828,7 +9923,7 @@ function claudeNativeLaunch(ctx) {
|
|
|
8828
9923
|
throw new Error("--settings requires a JSON object or readable file so the hub permission hook can be installed");
|
|
8829
9924
|
let caller;
|
|
8830
9925
|
try {
|
|
8831
|
-
caller = JSON.parse(value.trim().startsWith("{") ? value :
|
|
9926
|
+
caller = JSON.parse(value.trim().startsWith("{") ? value : readFileSync10(resolve15(value), "utf8"));
|
|
8832
9927
|
if (!caller || typeof caller !== "object" || Array.isArray(caller))
|
|
8833
9928
|
throw new Error("not an object");
|
|
8834
9929
|
} catch {
|
|
@@ -8857,14 +9952,14 @@ function claudeNativeLaunch(ctx) {
|
|
|
8857
9952
|
const stateDir = ctx.facts?.stateDir ?? ctx.statusLine?.stateDir;
|
|
8858
9953
|
if (!stateDir)
|
|
8859
9954
|
throw new Error("Claude session settings require the hub state directory");
|
|
8860
|
-
const file =
|
|
9955
|
+
const file = join21(stateDir, `claude-settings-${randomUUID7()}.json`);
|
|
8861
9956
|
try {
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
9957
|
+
mkdirSync7(stateDir, { recursive: true, mode: 448 });
|
|
9958
|
+
writeFileSync5(`${file}.tmp`, settingsArgs[1], { mode: 384, flag: "wx" });
|
|
9959
|
+
renameSync2(`${file}.tmp`, file);
|
|
8865
9960
|
} catch {
|
|
8866
9961
|
try {
|
|
8867
|
-
|
|
9962
|
+
unlinkSync2(`${file}.tmp`);
|
|
8868
9963
|
} catch {}
|
|
8869
9964
|
throw new Error("cannot write private Claude session settings in the hub state directory");
|
|
8870
9965
|
}
|
|
@@ -8914,11 +10009,11 @@ var init_native_cli_codex_launch = __esm(() => {
|
|
|
8914
10009
|
});
|
|
8915
10010
|
|
|
8916
10011
|
// src/hub/events.ts
|
|
8917
|
-
import { appendFileSync, closeSync as
|
|
10012
|
+
import { appendFileSync, closeSync as closeSync6, existsSync as existsSync7, fstatSync as fstatSync5, openSync as openSync6, readFileSync as readFileSync11, readSync as readSync3 } from "fs";
|
|
8918
10013
|
function readEvents(file, since = 0) {
|
|
8919
|
-
if (!
|
|
10014
|
+
if (!existsSync7(file))
|
|
8920
10015
|
return [];
|
|
8921
|
-
return
|
|
10016
|
+
return readFileSync11(file, "utf8").split(`
|
|
8922
10017
|
`).flatMap((line) => {
|
|
8923
10018
|
if (!line.trim())
|
|
8924
10019
|
return [];
|
|
@@ -8935,14 +10030,14 @@ var init_events = () => {};
|
|
|
8935
10030
|
|
|
8936
10031
|
// src/cli/recovery-session-file.ts
|
|
8937
10032
|
import { statSync as statSync4 } from "fs";
|
|
8938
|
-
import { homedir as
|
|
8939
|
-
import { join as
|
|
10033
|
+
import { homedir as homedir5 } from "os";
|
|
10034
|
+
import { join as join22 } from "path";
|
|
8940
10035
|
function checkedSessionPath(binding, kind) {
|
|
8941
10036
|
if (kind === "rollout")
|
|
8942
|
-
return
|
|
8943
|
-
const config = binding.launch.env.CLAUDE_CONFIG_DIR ??
|
|
10037
|
+
return join22(binding.launch.env.CODEX_HOME ?? join22(homedir5(), ".codex"), "sessions");
|
|
10038
|
+
const config = binding.launch.env.CLAUDE_CONFIG_DIR ?? join22(homedir5(), ".claude");
|
|
8944
10039
|
const slug = binding.projectRoot.replace(/[^a-zA-Z0-9]/g, "-");
|
|
8945
|
-
return
|
|
10040
|
+
return join22(config, "projects", slug, `${binding.sessionId}.jsonl`);
|
|
8946
10041
|
}
|
|
8947
10042
|
function recordedFileState(file) {
|
|
8948
10043
|
try {
|
|
@@ -8955,17 +10050,17 @@ var init_recovery_session_file = () => {};
|
|
|
8955
10050
|
|
|
8956
10051
|
// src/adapters/definitions/native-recovery.ts
|
|
8957
10052
|
import { readdirSync as readdirSync4 } from "fs";
|
|
8958
|
-
import { basename as
|
|
10053
|
+
import { basename as basename5, join as join23 } from "path";
|
|
8959
10054
|
var rolloutState = (binding) => {
|
|
8960
10055
|
try {
|
|
8961
|
-
return readdirSync4(checkedSessionPath(binding, "rollout"), { recursive: true }).some((name) => String(name).endsWith(".jsonl") &&
|
|
10056
|
+
return readdirSync4(checkedSessionPath(binding, "rollout"), { recursive: true }).some((name) => String(name).endsWith(".jsonl") && basename5(String(name)).includes(binding.sessionId));
|
|
8962
10057
|
} catch (error) {
|
|
8963
10058
|
return error.code === "ENOENT" ? false : "unknown";
|
|
8964
10059
|
}
|
|
8965
10060
|
}, transcriptState = (binding) => {
|
|
8966
10061
|
const state = recordedFileState(checkedSessionPath(binding, "transcript"));
|
|
8967
10062
|
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,
|
|
10063
|
+
}, 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
10064
|
var init_native_recovery = __esm(() => {
|
|
8970
10065
|
init_events();
|
|
8971
10066
|
init_recovery_session_file();
|
|
@@ -8979,7 +10074,7 @@ var init_native_recovery = __esm(() => {
|
|
|
8979
10074
|
if (phase !== "plan")
|
|
8980
10075
|
return false;
|
|
8981
10076
|
let current, started = false, turned = false;
|
|
8982
|
-
for (const e of readEvents(
|
|
10077
|
+
for (const e of readEvents(join23(stateDir, "events.jsonl"))) {
|
|
8983
10078
|
if (e.type === "native_thread" && e.peer === binding.peer) {
|
|
8984
10079
|
current = e.thread;
|
|
8985
10080
|
if (e.thread === binding.sessionId && e.fresh)
|
|
@@ -9020,7 +10115,7 @@ var init_native_recovery = __esm(() => {
|
|
|
9020
10115
|
return args;
|
|
9021
10116
|
},
|
|
9022
10117
|
crashIdentity: (row) => {
|
|
9023
|
-
const value =
|
|
10118
|
+
const value = object5(row), args = object5(value.args), session = object5(value.session);
|
|
9024
10119
|
const sessionId = [value.sessionId, value.agentSessionId, value.resumeSessionId, session.id, session.sessionId].find(text2);
|
|
9025
10120
|
const sessionFile = [value.sessionFile, value.sessionPath, args.sessionFile, session.file, session.path].find(text2);
|
|
9026
10121
|
return { ...sessionId ? { sessionId } : {}, ...sessionFile ? { sessionFile } : {} };
|
|
@@ -9094,12 +10189,12 @@ function compatibilityLaunch(kind, ctx) {
|
|
|
9094
10189
|
...ctx.env.CODEX_HOME !== undefined ? { CODEX_HOME: ctx.env.CODEX_HOME } : {},
|
|
9095
10190
|
...ctx.env.CLAUDE_CONFIG_DIR !== undefined ? { CLAUDE_CONFIG_DIR: ctx.env.CLAUDE_CONFIG_DIR } : {}
|
|
9096
10191
|
};
|
|
9097
|
-
const prefix = Object.entries(env).map(([name, value]) => `${name}=${
|
|
10192
|
+
const prefix = Object.entries(env).map(([name, value]) => `${name}=${shellQuote3(value)}`);
|
|
9098
10193
|
return {
|
|
9099
10194
|
cmd: process.execPath,
|
|
9100
10195
|
args,
|
|
9101
10196
|
...unattended.length ? { unattended: true } : {},
|
|
9102
|
-
metadata: { packageEntrypoint: ctx.entrypoint, argv, env, command: [...prefix, ...argv.map(
|
|
10197
|
+
metadata: { packageEntrypoint: ctx.entrypoint, argv, env, command: [...prefix, ...argv.map(shellQuote3)].join(" ") }
|
|
9103
10198
|
};
|
|
9104
10199
|
}
|
|
9105
10200
|
function sessionId(field) {
|
|
@@ -9223,7 +10318,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
9223
10318
|
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
10319
|
turnEnd: "adapter",
|
|
9225
10320
|
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"] },
|
|
10321
|
+
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
10322
|
display: CODEX_DISPLAY,
|
|
9228
10323
|
hooks: { recallPlatform: "codex", recallSource: "codex", recallInjected: true, pluginInventory: codexPluginInventory, classifyTool: codexToolSemantic }
|
|
9229
10324
|
},
|
|
@@ -9258,7 +10353,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
9258
10353
|
capabilities: { watch: true },
|
|
9259
10354
|
init: { mcpFile: ".kimi-code/mcp.json" },
|
|
9260
10355
|
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] },
|
|
10356
|
+
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
10357
|
display: KIMI_DISPLAY
|
|
9263
10358
|
},
|
|
9264
10359
|
{
|
|
@@ -9280,7 +10375,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
9280
10375
|
reporting: { tokensNotReported: true },
|
|
9281
10376
|
configuration: { schema: piConfigSchema, routingSchema: piRoutingSchema, settings: PI_SETTINGS, restartReasons: PI_RESTART_REASONS, validateEffective: piEffectiveConfig },
|
|
9282
10377
|
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] },
|
|
10378
|
+
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
10379
|
display: PI_DISPLAY
|
|
9285
10380
|
},
|
|
9286
10381
|
{
|
|
@@ -9300,7 +10395,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
9300
10395
|
budget: { account: "local", quotaExempt: true, instrumented: true },
|
|
9301
10396
|
configuration: { schema: localConfigSchema, routingSchema: localRoutingSchema, restartReasons: LOCAL_RESTART_REASONS },
|
|
9302
10397
|
routing: { localAllowedRefusal: "local_allowed = false for this class", longContext: "skip-owner", preferredTier: true, handoffPriority: 1, trace: "route" },
|
|
9303
|
-
cli: { command: "local", startWords: () => ["local"] },
|
|
10398
|
+
cli: { command: "local", launchConfigKeys: ["local.deny", "local.bash_network", "local.network_allow", "local.max_steps", "local.read_allow", "routing.local"], startWords: () => ["local"] },
|
|
9304
10399
|
display: LOCAL_DISPLAY
|
|
9305
10400
|
},
|
|
9306
10401
|
{
|
|
@@ -9323,7 +10418,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
9323
10418
|
session: { idField: "sessionId", currentId: sessionId("sessionId"), launchKind: "jcode", ...JCODE_RECOVERY_SESSION, crashPlan: jcodeCrashPlan },
|
|
9324
10419
|
turnEnd: "adapter",
|
|
9325
10420
|
budget: { account: "jcode" },
|
|
9326
|
-
cli: { command: "jcode", authority: "person", startWords: () => ["jcode"] },
|
|
10421
|
+
cli: { command: "jcode", authority: "person", launchConfigKeys: ["jcode_cmd", "jcode_home", "jcode_provider_profile"], startWords: () => ["jcode"] },
|
|
9327
10422
|
display: JCODE_DISPLAY,
|
|
9328
10423
|
hooks: { recallPlatform: "jcode" }
|
|
9329
10424
|
}
|
|
@@ -9514,7 +10609,7 @@ var init_qwen = __esm(() => {
|
|
|
9514
10609
|
throw new Error("Qwen TUI is not offered before its native verification; use ahub qwen --mode headless");
|
|
9515
10610
|
const launch = buildQwenLaunch(context.nativeCommand ?? ["qwen", "--acp"], context.model);
|
|
9516
10611
|
const argv = [launch.cmd, ...launch.args];
|
|
9517
|
-
return { ...launch, metadata: { packageEntrypoint: context.entrypoint, argv, command: argv.map(
|
|
10612
|
+
return { ...launch, metadata: { packageEntrypoint: context.entrypoint, argv, command: argv.map(shellQuote3).join(" "), env: {} } };
|
|
9518
10613
|
},
|
|
9519
10614
|
create: (context) => {
|
|
9520
10615
|
if (!("acp" in context.daemon))
|
|
@@ -9555,7 +10650,7 @@ var init_qwen = __esm(() => {
|
|
|
9555
10650
|
turnEnd: "adapter",
|
|
9556
10651
|
budget: { account: "qwen" },
|
|
9557
10652
|
capabilities: { watch: true },
|
|
9558
|
-
cli: { command: "qwen", startWords: () => ["qwen"] },
|
|
10653
|
+
cli: { command: "qwen", launchConfigKeys: ["qwen_cmd"], startWords: () => ["qwen"] },
|
|
9559
10654
|
display: QWEN_DISPLAY,
|
|
9560
10655
|
hooks: { agentIdentity: (init) => {
|
|
9561
10656
|
qwenNativeIdentity("qwen", true, init);
|
|
@@ -9644,7 +10739,8 @@ function assertNativeCapabilityScope(definition) {
|
|
|
9644
10739
|
["start.daemonPort pi-rpc", definition.start.daemonPort === "pi-rpc"],
|
|
9645
10740
|
["routing", definition.routing !== undefined],
|
|
9646
10741
|
["reviewerVariant", definition.reviewerVariant !== undefined],
|
|
9647
|
-
["reviewer", definition.reviewer !== undefined]
|
|
10742
|
+
["reviewer", definition.reviewer !== undefined],
|
|
10743
|
+
["hooks.factsHook", declared(definition.hooks, "factsHook")]
|
|
9648
10744
|
];
|
|
9649
10745
|
const field = restricted.find(([, present]) => present)?.[0];
|
|
9650
10746
|
if (field)
|
|
@@ -9652,6 +10748,7 @@ function assertNativeCapabilityScope(definition) {
|
|
|
9652
10748
|
}
|
|
9653
10749
|
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
10750
|
var init_registry = __esm(() => {
|
|
10751
|
+
init_definition();
|
|
9655
10752
|
init_reviewer_variants();
|
|
9656
10753
|
init_native_cli_entry();
|
|
9657
10754
|
init_native_cli_preview();
|
|
@@ -9662,18 +10759,19 @@ var init_registry = __esm(() => {
|
|
|
9662
10759
|
init_native_cli_doctor();
|
|
9663
10760
|
init_native_display();
|
|
9664
10761
|
KNOWN_PEERS = ["claude", "codex", "kimi", "pi", "local"];
|
|
9665
|
-
OPTIONAL_PEERS = ["qwen", "jcode"];
|
|
10762
|
+
OPTIONAL_PEERS = ["qwen", "jcode", "agy"];
|
|
9666
10763
|
SUPPORTED_PEERS = [...KNOWN_PEERS, ...OPTIONAL_PEERS];
|
|
9667
|
-
fixedBuiltinDefinitions = new Set([...NATIVE_BUILTIN_DEFINITIONS_WITHOUT_QWEN, QWEN_DEFINITION, AGY_DEFINITION]);
|
|
10764
|
+
fixedBuiltinDefinitions = new Set([...NATIVE_BUILTIN_DEFINITIONS_WITHOUT_QWEN, QWEN_DEFINITION, AGY_DEFINITION, AGY_REVIEWER_VARIANT]);
|
|
9668
10765
|
for (const definition of [...fixedBuiltinDefinitions])
|
|
9669
10766
|
if (definition.reviewerVariant)
|
|
9670
10767
|
fixedBuiltinDefinitions.add(definition.reviewerVariant);
|
|
9671
10768
|
ADAPTERS = immutable([
|
|
9672
10769
|
...NATIVE_BUILTIN_DEFINITIONS_WITHOUT_QWEN.filter((definition) => definition.kind !== "jcode"),
|
|
9673
10770
|
QWEN_DEFINITION,
|
|
9674
|
-
...NATIVE_BUILTIN_DEFINITIONS_WITHOUT_QWEN.filter((definition) => definition.kind === "jcode")
|
|
10771
|
+
...NATIVE_BUILTIN_DEFINITIONS_WITHOUT_QWEN.filter((definition) => definition.kind === "jcode"),
|
|
10772
|
+
AGY_DEFINITION
|
|
9675
10773
|
]);
|
|
9676
|
-
declarationOnlyDefinitions = immutable([
|
|
10774
|
+
declarationOnlyDefinitions = immutable([]);
|
|
9677
10775
|
definitions = new Map;
|
|
9678
10776
|
for (const definition of [...ADAPTERS, ...declarationOnlyDefinitions]) {
|
|
9679
10777
|
assertDaemonEngine(definition);
|
|
@@ -9689,7 +10787,7 @@ var init_registry = __esm(() => {
|
|
|
9689
10787
|
REVIEW_POOL_KINDS = Object.freeze([...definitions.values()].filter((definition) => !!(definition.reviewerVariant ?? definition).reviewer).map((definition) => definition.kind));
|
|
9690
10788
|
DEFAULT_REVIEW_POOL = Object.freeze(ADAPTERS.filter((definition) => (definition.reviewerVariant ?? definition).reviewer?.defaultPool === true).map((definition) => definition.kind));
|
|
9691
10789
|
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"];
|
|
10790
|
+
ROLE_ORDER = ["claude", "codex", "kimi", "local", "pi", "qwen", "jcode", "agy"];
|
|
9693
10791
|
CONDUCTOR_START_PRIORITY = ["local", "kimi", "pi"];
|
|
9694
10792
|
CONDUCTOR_START_PEERS = [
|
|
9695
10793
|
...CONDUCTOR_START_PRIORITY,
|
|
@@ -16873,8 +17971,8 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
16873
17971
|
|
|
16874
17972
|
// src/hub/instance-config.ts
|
|
16875
17973
|
init_registry();
|
|
16876
|
-
import { readFileSync as
|
|
16877
|
-
import { join as
|
|
17974
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
17975
|
+
import { join as join26 } from "path";
|
|
16878
17976
|
|
|
16879
17977
|
// src/hub/conductor.ts
|
|
16880
17978
|
init_registry();
|
|
@@ -16895,7 +17993,7 @@ var STDOUT_LIMIT = 512 * 1024;
|
|
|
16895
17993
|
// package.json
|
|
16896
17994
|
var package_default = {
|
|
16897
17995
|
name: "@staix/agent-hub",
|
|
16898
|
-
version: "0.12.
|
|
17996
|
+
version: "0.12.38",
|
|
16899
17997
|
description: "Native multi-agent hub: Claude Code, Codex, Kimi Code, Pi and local inference as peers in one project",
|
|
16900
17998
|
license: "MIT",
|
|
16901
17999
|
type: "module",
|
|
@@ -16945,7 +18043,7 @@ var package_default = {
|
|
|
16945
18043
|
};
|
|
16946
18044
|
|
|
16947
18045
|
// src/version.ts
|
|
16948
|
-
var
|
|
18046
|
+
var VERSION3 = package_default.version;
|
|
16949
18047
|
|
|
16950
18048
|
// src/hub/github-import.ts
|
|
16951
18049
|
init_sdlc();
|
|
@@ -17036,7 +18134,7 @@ var USER2 = Symbol("shared-user-shard");
|
|
|
17036
18134
|
// node_modules/@staix/agent-mem/src/mcp.ts
|
|
17037
18135
|
var string = (maxLength, minLength = 1) => ({ type: "string", minLength, maxLength });
|
|
17038
18136
|
var limit = { type: "integer", minimum: 1, maximum: 100 };
|
|
17039
|
-
var
|
|
18137
|
+
var object6 = (properties, required = []) => ({ type: "object", properties, required, additionalProperties: false });
|
|
17040
18138
|
var tool2 = (name, description, inputSchema, readOnlyHint = true) => ({
|
|
17041
18139
|
name,
|
|
17042
18140
|
description,
|
|
@@ -17044,20 +18142,20 @@ var tool2 = (name, description, inputSchema, readOnlyHint = true) => ({
|
|
|
17044
18142
|
annotations: { readOnlyHint, destructiveHint: false, openWorldHint: false }
|
|
17045
18143
|
});
|
|
17046
18144
|
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.",
|
|
18145
|
+
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"])),
|
|
18146
|
+
tool2("amem_show", "Show a screened memory entry in this project by its exact id or unique prefix.", object6({ id: string(80) }, ["id"])),
|
|
18147
|
+
tool2("amem_timeline", "Read screened memory near an entry or time in this project.", object6({ id: string(80), since: string(40), limit })),
|
|
18148
|
+
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"])),
|
|
18149
|
+
tool2("amem_work_state_read", "Read project work lists; closed lists/items can be included explicitly.", object6({ list: string(200), includeClosed: { type: "boolean" } })),
|
|
18150
|
+
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
18151
|
type: "object",
|
|
17054
18152
|
minProperties: 1,
|
|
17055
18153
|
maxProperties: 64,
|
|
17056
18154
|
additionalProperties: { type: ["string", "number", "boolean", "null"], maxLength: 4000 }
|
|
17057
18155
|
} }, ["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.",
|
|
18156
|
+
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),
|
|
18157
|
+
tool2("amem_vault_candidates", "List screened durable curated notes eligible for a person to move to the vault. Read only.", object6({ limit })),
|
|
18158
|
+
tool2("amem_capacity", "Read this machine's bounded capture capacity proposal; applying it is person-only.", object6({}))
|
|
17061
18159
|
];
|
|
17062
18160
|
// node_modules/@staix/agent-mem/src/mcp-memory.ts
|
|
17063
18161
|
init_capture();
|
|
@@ -17084,14 +18182,14 @@ init_generation();
|
|
|
17084
18182
|
// src/hub/routing.ts
|
|
17085
18183
|
init_registry();
|
|
17086
18184
|
init_board();
|
|
17087
|
-
import { join as
|
|
18185
|
+
import { join as join24, resolve as resolve16 } from "path";
|
|
17088
18186
|
|
|
17089
18187
|
// src/models/route/config.ts
|
|
17090
18188
|
var TYPES = new Set(["stage", "plan_execute", "advisor", "escalation"]);
|
|
17091
18189
|
|
|
17092
18190
|
// src/hub/routing.ts
|
|
17093
18191
|
init_stage();
|
|
17094
|
-
var TEMPLATE =
|
|
18192
|
+
var TEMPLATE = join24(import.meta.dir, "..", "..", "templates", "routing.toml");
|
|
17095
18193
|
var cache = new Map;
|
|
17096
18194
|
|
|
17097
18195
|
// src/hub/memory-policy.ts
|
|
@@ -17118,8 +18216,8 @@ var OWNERSHIP_EVENTS = new Set(["assigned", "escalated", "reassigned", "unassign
|
|
|
17118
18216
|
// src/hub/config-trust.ts
|
|
17119
18217
|
init_child_process();
|
|
17120
18218
|
init_git();
|
|
17121
|
-
import { lstatSync as
|
|
17122
|
-
import { dirname as
|
|
18219
|
+
import { lstatSync as lstatSync10, statSync as statSync5 } from "fs";
|
|
18220
|
+
import { dirname as dirname10, join as join25 } from "path";
|
|
17123
18221
|
var MACHINE_LOCAL = [
|
|
17124
18222
|
"kimi_cmd",
|
|
17125
18223
|
"permission_modes",
|
|
@@ -17151,7 +18249,7 @@ function configTracked(cwd, name) {
|
|
|
17151
18249
|
return;
|
|
17152
18250
|
let opened;
|
|
17153
18251
|
try {
|
|
17154
|
-
opened = statSync5(
|
|
18252
|
+
opened = statSync5(join25(cwd, ".agenthub", name));
|
|
17155
18253
|
} catch {}
|
|
17156
18254
|
return r.stdout.split("\x00").filter(Boolean).some((row) => {
|
|
17157
18255
|
const path = row.slice(row.indexOf("\t") + 1);
|
|
@@ -17160,7 +18258,7 @@ function configTracked(cwd, name) {
|
|
|
17160
18258
|
if (!opened)
|
|
17161
18259
|
return path === `.agenthub/${name}`;
|
|
17162
18260
|
try {
|
|
17163
|
-
const s = statSync5(
|
|
18261
|
+
const s = statSync5(join25(cwd, path));
|
|
17164
18262
|
return !!opened && s.dev === opened.dev && s.ino === opened.ino;
|
|
17165
18263
|
} catch {
|
|
17166
18264
|
return false;
|
|
@@ -17293,7 +18391,7 @@ var INSTANCE_CONFIG_FILES = ["config.json", "config.local.json"];
|
|
|
17293
18391
|
function readInstanceConfigLayers(cwd) {
|
|
17294
18392
|
return INSTANCE_CONFIG_FILES.flatMap((name) => {
|
|
17295
18393
|
try {
|
|
17296
|
-
return [{ name, raw: JSON.parse(
|
|
18394
|
+
return [{ name, raw: JSON.parse(readFileSync12(join26(cwd, ".agenthub", name), "utf8")) }];
|
|
17297
18395
|
} catch (error) {
|
|
17298
18396
|
if (error.code === "ENOENT")
|
|
17299
18397
|
return [];
|
|
@@ -17323,8 +18421,8 @@ function readInstanceConfig(cwd, layers = readInstanceConfigLayers(cwd)) {
|
|
|
17323
18421
|
|
|
17324
18422
|
// src/hub/control-client.ts
|
|
17325
18423
|
init_project();
|
|
17326
|
-
import { readFileSync as
|
|
17327
|
-
import { join as
|
|
18424
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
18425
|
+
import { join as join27 } from "path";
|
|
17328
18426
|
function stateDirFor(cwd) {
|
|
17329
18427
|
return projectContext(cwd).stateDir;
|
|
17330
18428
|
}
|
|
@@ -17332,10 +18430,11 @@ var PROTOCOL = 16;
|
|
|
17332
18430
|
var RECOVERY_SOURCE_PROTOCOLS = [9, 10, 11, 12, 13, 14, 15, PROTOCOL];
|
|
17333
18431
|
function readControl(stateDir) {
|
|
17334
18432
|
try {
|
|
17335
|
-
const status = JSON.parse(
|
|
17336
|
-
const token =
|
|
18433
|
+
const status = JSON.parse(readFileSync13(join27(stateDir, "status.json"), "utf8"));
|
|
18434
|
+
const token = readFileSync13(join27(stateDir, "control-token"), "utf8").trim();
|
|
17337
18435
|
if (!Number.isInteger(status.controlPort) || status.controlPort < 1 || status.controlPort > 65535 || !token)
|
|
17338
18436
|
return;
|
|
18437
|
+
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
18438
|
return {
|
|
17340
18439
|
url: `ws://127.0.0.1:${status.controlPort}`,
|
|
17341
18440
|
token,
|
|
@@ -17345,6 +18444,7 @@ function readControl(stateDir) {
|
|
|
17345
18444
|
protocol: status.protocol,
|
|
17346
18445
|
pid: status.pid,
|
|
17347
18446
|
features: Array.isArray(status.features) ? status.features.filter((feature) => typeof feature === "string") : [],
|
|
18447
|
+
...startModes ? { startModes } : {},
|
|
17348
18448
|
...typeof status.pidSignature === "string" && status.pidSignature ? { pidSignature: status.pidSignature } : {}
|
|
17349
18449
|
};
|
|
17350
18450
|
} catch {
|
|
@@ -18811,7 +19911,7 @@ ${content.join(`
|
|
|
18811
19911
|
}
|
|
18812
19912
|
|
|
18813
19913
|
// node_modules/zod/v4/core/versions.js
|
|
18814
|
-
var
|
|
19914
|
+
var version3 = {
|
|
18815
19915
|
major: 4,
|
|
18816
19916
|
minor: 6,
|
|
18817
19917
|
patch: 5
|
|
@@ -18823,7 +19923,7 @@ var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => {
|
|
|
18823
19923
|
inst ?? (inst = {});
|
|
18824
19924
|
inst._zod.def = def;
|
|
18825
19925
|
inst._zod.bag = inst._zod.bag || {};
|
|
18826
|
-
inst._zod.version =
|
|
19926
|
+
inst._zod.version = version3;
|
|
18827
19927
|
const defChecks = inst._zod.def.checks;
|
|
18828
19928
|
const checks = inst._zod.traits.has("$ZodCheck") ? [inst, ...defChecks ?? []] : defChecks?.length ? [...defChecks] : [];
|
|
18829
19929
|
for (const ch of checks) {
|
|
@@ -23131,7 +24231,7 @@ var ZodObject = /* @__PURE__ */ $constructor("ZodObject", (inst, def) => {
|
|
|
23131
24231
|
return required(ZodNonOptional, this, args[0]);
|
|
23132
24232
|
}
|
|
23133
24233
|
});
|
|
23134
|
-
function
|
|
24234
|
+
function object8(shape, params) {
|
|
23135
24235
|
const def = {
|
|
23136
24236
|
type: "object",
|
|
23137
24237
|
shape: shape ?? {},
|
|
@@ -23192,7 +24292,7 @@ var ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
|
|
|
23192
24292
|
inst.keyType = def.keyType;
|
|
23193
24293
|
inst.valueType = def.valueType;
|
|
23194
24294
|
});
|
|
23195
|
-
function
|
|
24295
|
+
function record4(keyType, valueType, params) {
|
|
23196
24296
|
if (!valueType || !valueType._zod) {
|
|
23197
24297
|
return new ZodRecord({
|
|
23198
24298
|
type: "record",
|
|
@@ -23476,31 +24576,31 @@ var TaskCreationParamsSchema = looseObject({
|
|
|
23476
24576
|
ttl: number2().optional(),
|
|
23477
24577
|
pollInterval: number2().optional()
|
|
23478
24578
|
});
|
|
23479
|
-
var TaskMetadataSchema =
|
|
24579
|
+
var TaskMetadataSchema = object8({
|
|
23480
24580
|
ttl: number2().optional()
|
|
23481
24581
|
});
|
|
23482
|
-
var RelatedTaskMetadataSchema =
|
|
24582
|
+
var RelatedTaskMetadataSchema = object8({
|
|
23483
24583
|
taskId: string3()
|
|
23484
24584
|
});
|
|
23485
24585
|
var RequestMetaSchema = looseObject({
|
|
23486
24586
|
progressToken: ProgressTokenSchema.optional(),
|
|
23487
24587
|
[RELATED_TASK_META_KEY]: RelatedTaskMetadataSchema.optional()
|
|
23488
24588
|
});
|
|
23489
|
-
var BaseRequestParamsSchema =
|
|
24589
|
+
var BaseRequestParamsSchema = object8({
|
|
23490
24590
|
_meta: RequestMetaSchema.optional()
|
|
23491
24591
|
});
|
|
23492
24592
|
var TaskAugmentedRequestParamsSchema = BaseRequestParamsSchema.extend({
|
|
23493
24593
|
task: TaskMetadataSchema.optional()
|
|
23494
24594
|
});
|
|
23495
24595
|
var isTaskAugmentedRequestParams = (value) => TaskAugmentedRequestParamsSchema.safeParse(value).success;
|
|
23496
|
-
var RequestSchema =
|
|
24596
|
+
var RequestSchema = object8({
|
|
23497
24597
|
method: string3(),
|
|
23498
24598
|
params: BaseRequestParamsSchema.loose().optional()
|
|
23499
24599
|
});
|
|
23500
|
-
var NotificationsParamsSchema =
|
|
24600
|
+
var NotificationsParamsSchema = object8({
|
|
23501
24601
|
_meta: RequestMetaSchema.optional()
|
|
23502
24602
|
});
|
|
23503
|
-
var NotificationSchema =
|
|
24603
|
+
var NotificationSchema = object8({
|
|
23504
24604
|
method: string3(),
|
|
23505
24605
|
params: NotificationsParamsSchema.loose().optional()
|
|
23506
24606
|
});
|
|
@@ -23508,18 +24608,18 @@ var ResultSchema = looseObject({
|
|
|
23508
24608
|
_meta: RequestMetaSchema.optional()
|
|
23509
24609
|
});
|
|
23510
24610
|
var RequestIdSchema = union([string3(), number2().int()]);
|
|
23511
|
-
var JSONRPCRequestSchema =
|
|
24611
|
+
var JSONRPCRequestSchema = object8({
|
|
23512
24612
|
jsonrpc: literal(JSONRPC_VERSION),
|
|
23513
24613
|
id: RequestIdSchema,
|
|
23514
24614
|
...RequestSchema.shape
|
|
23515
24615
|
}).strict();
|
|
23516
24616
|
var isJSONRPCRequest = (value) => JSONRPCRequestSchema.safeParse(value).success;
|
|
23517
|
-
var JSONRPCNotificationSchema =
|
|
24617
|
+
var JSONRPCNotificationSchema = object8({
|
|
23518
24618
|
jsonrpc: literal(JSONRPC_VERSION),
|
|
23519
24619
|
...NotificationSchema.shape
|
|
23520
24620
|
}).strict();
|
|
23521
24621
|
var isJSONRPCNotification = (value) => JSONRPCNotificationSchema.safeParse(value).success;
|
|
23522
|
-
var JSONRPCResultResponseSchema =
|
|
24622
|
+
var JSONRPCResultResponseSchema = object8({
|
|
23523
24623
|
jsonrpc: literal(JSONRPC_VERSION),
|
|
23524
24624
|
id: RequestIdSchema,
|
|
23525
24625
|
result: ResultSchema
|
|
@@ -23536,10 +24636,10 @@ var ErrorCode;
|
|
|
23536
24636
|
ErrorCode[ErrorCode["InternalError"] = -32603] = "InternalError";
|
|
23537
24637
|
ErrorCode[ErrorCode["UrlElicitationRequired"] = -32042] = "UrlElicitationRequired";
|
|
23538
24638
|
})(ErrorCode || (ErrorCode = {}));
|
|
23539
|
-
var JSONRPCErrorResponseSchema =
|
|
24639
|
+
var JSONRPCErrorResponseSchema = object8({
|
|
23540
24640
|
jsonrpc: literal(JSONRPC_VERSION),
|
|
23541
24641
|
id: RequestIdSchema.optional(),
|
|
23542
|
-
error:
|
|
24642
|
+
error: object8({
|
|
23543
24643
|
code: number2().int(),
|
|
23544
24644
|
message: string3(),
|
|
23545
24645
|
data: unknown().optional()
|
|
@@ -23562,16 +24662,16 @@ var CancelledNotificationSchema = NotificationSchema.extend({
|
|
|
23562
24662
|
method: literal("notifications/cancelled"),
|
|
23563
24663
|
params: CancelledNotificationParamsSchema
|
|
23564
24664
|
});
|
|
23565
|
-
var IconSchema =
|
|
24665
|
+
var IconSchema = object8({
|
|
23566
24666
|
src: string3(),
|
|
23567
24667
|
mimeType: string3().optional(),
|
|
23568
24668
|
sizes: array(string3()).optional(),
|
|
23569
24669
|
theme: _enum(["light", "dark"]).optional()
|
|
23570
24670
|
});
|
|
23571
|
-
var IconsSchema =
|
|
24671
|
+
var IconsSchema = object8({
|
|
23572
24672
|
icons: array(IconSchema).optional()
|
|
23573
24673
|
});
|
|
23574
|
-
var BaseMetadataSchema =
|
|
24674
|
+
var BaseMetadataSchema = object8({
|
|
23575
24675
|
name: string3(),
|
|
23576
24676
|
title: string3().optional()
|
|
23577
24677
|
});
|
|
@@ -23582,9 +24682,9 @@ var ImplementationSchema = BaseMetadataSchema.extend({
|
|
|
23582
24682
|
websiteUrl: string3().optional(),
|
|
23583
24683
|
description: string3().optional()
|
|
23584
24684
|
});
|
|
23585
|
-
var FormElicitationCapabilitySchema = intersection(
|
|
24685
|
+
var FormElicitationCapabilitySchema = intersection(object8({
|
|
23586
24686
|
applyDefaults: boolean2().optional()
|
|
23587
|
-
}),
|
|
24687
|
+
}), record4(string3(), unknown()));
|
|
23588
24688
|
var ElicitationCapabilitySchema = preprocess((value) => {
|
|
23589
24689
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
23590
24690
|
if (Object.keys(value).length === 0) {
|
|
@@ -23592,10 +24692,10 @@ var ElicitationCapabilitySchema = preprocess((value) => {
|
|
|
23592
24692
|
}
|
|
23593
24693
|
}
|
|
23594
24694
|
return value;
|
|
23595
|
-
}, intersection(
|
|
24695
|
+
}, intersection(object8({
|
|
23596
24696
|
form: FormElicitationCapabilitySchema.optional(),
|
|
23597
24697
|
url: AssertObjectSchema.optional()
|
|
23598
|
-
}),
|
|
24698
|
+
}), record4(string3(), unknown()).optional()));
|
|
23599
24699
|
var ClientTasksCapabilitySchema = looseObject({
|
|
23600
24700
|
list: AssertObjectSchema.optional(),
|
|
23601
24701
|
cancel: AssertObjectSchema.optional(),
|
|
@@ -23617,18 +24717,18 @@ var ServerTasksCapabilitySchema = looseObject({
|
|
|
23617
24717
|
}).optional()
|
|
23618
24718
|
}).optional()
|
|
23619
24719
|
});
|
|
23620
|
-
var ClientCapabilitiesSchema =
|
|
23621
|
-
experimental:
|
|
23622
|
-
sampling:
|
|
24720
|
+
var ClientCapabilitiesSchema = object8({
|
|
24721
|
+
experimental: record4(string3(), AssertObjectSchema).optional(),
|
|
24722
|
+
sampling: object8({
|
|
23623
24723
|
context: AssertObjectSchema.optional(),
|
|
23624
24724
|
tools: AssertObjectSchema.optional()
|
|
23625
24725
|
}).optional(),
|
|
23626
24726
|
elicitation: ElicitationCapabilitySchema.optional(),
|
|
23627
|
-
roots:
|
|
24727
|
+
roots: object8({
|
|
23628
24728
|
listChanged: boolean2().optional()
|
|
23629
24729
|
}).optional(),
|
|
23630
24730
|
tasks: ClientTasksCapabilitySchema.optional(),
|
|
23631
|
-
extensions:
|
|
24731
|
+
extensions: record4(string3(), AssertObjectSchema).optional()
|
|
23632
24732
|
});
|
|
23633
24733
|
var InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
|
|
23634
24734
|
protocolVersion: string3(),
|
|
@@ -23639,22 +24739,22 @@ var InitializeRequestSchema = RequestSchema.extend({
|
|
|
23639
24739
|
method: literal("initialize"),
|
|
23640
24740
|
params: InitializeRequestParamsSchema
|
|
23641
24741
|
});
|
|
23642
|
-
var ServerCapabilitiesSchema =
|
|
23643
|
-
experimental:
|
|
24742
|
+
var ServerCapabilitiesSchema = object8({
|
|
24743
|
+
experimental: record4(string3(), AssertObjectSchema).optional(),
|
|
23644
24744
|
logging: AssertObjectSchema.optional(),
|
|
23645
24745
|
completions: AssertObjectSchema.optional(),
|
|
23646
|
-
prompts:
|
|
24746
|
+
prompts: object8({
|
|
23647
24747
|
listChanged: boolean2().optional()
|
|
23648
24748
|
}).optional(),
|
|
23649
|
-
resources:
|
|
24749
|
+
resources: object8({
|
|
23650
24750
|
subscribe: boolean2().optional(),
|
|
23651
24751
|
listChanged: boolean2().optional()
|
|
23652
24752
|
}).optional(),
|
|
23653
|
-
tools:
|
|
24753
|
+
tools: object8({
|
|
23654
24754
|
listChanged: boolean2().optional()
|
|
23655
24755
|
}).optional(),
|
|
23656
24756
|
tasks: ServerTasksCapabilitySchema.optional(),
|
|
23657
|
-
extensions:
|
|
24757
|
+
extensions: record4(string3(), AssertObjectSchema).optional()
|
|
23658
24758
|
});
|
|
23659
24759
|
var InitializeResultSchema = ResultSchema.extend({
|
|
23660
24760
|
protocolVersion: string3(),
|
|
@@ -23670,12 +24770,12 @@ var PingRequestSchema = RequestSchema.extend({
|
|
|
23670
24770
|
method: literal("ping"),
|
|
23671
24771
|
params: BaseRequestParamsSchema.optional()
|
|
23672
24772
|
});
|
|
23673
|
-
var ProgressSchema =
|
|
24773
|
+
var ProgressSchema = object8({
|
|
23674
24774
|
progress: number2(),
|
|
23675
24775
|
total: optional(number2()),
|
|
23676
24776
|
message: optional(string3())
|
|
23677
24777
|
});
|
|
23678
|
-
var ProgressNotificationParamsSchema =
|
|
24778
|
+
var ProgressNotificationParamsSchema = object8({
|
|
23679
24779
|
...NotificationsParamsSchema.shape,
|
|
23680
24780
|
...ProgressSchema.shape,
|
|
23681
24781
|
progressToken: ProgressTokenSchema
|
|
@@ -23694,7 +24794,7 @@ var PaginatedResultSchema = ResultSchema.extend({
|
|
|
23694
24794
|
nextCursor: CursorSchema.optional()
|
|
23695
24795
|
});
|
|
23696
24796
|
var TaskStatusSchema = _enum(["working", "input_required", "completed", "failed", "cancelled"]);
|
|
23697
|
-
var TaskSchema =
|
|
24797
|
+
var TaskSchema = object8({
|
|
23698
24798
|
taskId: string3(),
|
|
23699
24799
|
status: TaskStatusSchema,
|
|
23700
24800
|
ttl: union([number2(), _null3()]),
|
|
@@ -23738,10 +24838,10 @@ var CancelTaskRequestSchema = RequestSchema.extend({
|
|
|
23738
24838
|
})
|
|
23739
24839
|
});
|
|
23740
24840
|
var CancelTaskResultSchema = ResultSchema.merge(TaskSchema);
|
|
23741
|
-
var ResourceContentsSchema =
|
|
24841
|
+
var ResourceContentsSchema = object8({
|
|
23742
24842
|
uri: string3(),
|
|
23743
24843
|
mimeType: optional(string3()),
|
|
23744
|
-
_meta:
|
|
24844
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23745
24845
|
});
|
|
23746
24846
|
var TextResourceContentsSchema = ResourceContentsSchema.extend({
|
|
23747
24847
|
text: string3()
|
|
@@ -23758,12 +24858,12 @@ var BlobResourceContentsSchema = ResourceContentsSchema.extend({
|
|
|
23758
24858
|
blob: Base64Schema
|
|
23759
24859
|
});
|
|
23760
24860
|
var RoleSchema = _enum(["user", "assistant"]);
|
|
23761
|
-
var AnnotationsSchema =
|
|
24861
|
+
var AnnotationsSchema = object8({
|
|
23762
24862
|
audience: array(RoleSchema).optional(),
|
|
23763
24863
|
priority: number2().min(0).max(1).optional(),
|
|
23764
24864
|
lastModified: datetime2({ offset: true }).optional()
|
|
23765
24865
|
});
|
|
23766
|
-
var ResourceSchema =
|
|
24866
|
+
var ResourceSchema = object8({
|
|
23767
24867
|
...BaseMetadataSchema.shape,
|
|
23768
24868
|
...IconsSchema.shape,
|
|
23769
24869
|
uri: string3(),
|
|
@@ -23773,7 +24873,7 @@ var ResourceSchema = object6({
|
|
|
23773
24873
|
annotations: AnnotationsSchema.optional(),
|
|
23774
24874
|
_meta: optional(looseObject({}))
|
|
23775
24875
|
});
|
|
23776
|
-
var ResourceTemplateSchema =
|
|
24876
|
+
var ResourceTemplateSchema = object8({
|
|
23777
24877
|
...BaseMetadataSchema.shape,
|
|
23778
24878
|
...IconsSchema.shape,
|
|
23779
24879
|
uriTemplate: string3(),
|
|
@@ -23826,12 +24926,12 @@ var ResourceUpdatedNotificationSchema = NotificationSchema.extend({
|
|
|
23826
24926
|
method: literal("notifications/resources/updated"),
|
|
23827
24927
|
params: ResourceUpdatedNotificationParamsSchema
|
|
23828
24928
|
});
|
|
23829
|
-
var PromptArgumentSchema =
|
|
24929
|
+
var PromptArgumentSchema = object8({
|
|
23830
24930
|
name: string3(),
|
|
23831
24931
|
description: optional(string3()),
|
|
23832
24932
|
required: optional(boolean2())
|
|
23833
24933
|
});
|
|
23834
|
-
var PromptSchema =
|
|
24934
|
+
var PromptSchema = object8({
|
|
23835
24935
|
...BaseMetadataSchema.shape,
|
|
23836
24936
|
...IconsSchema.shape,
|
|
23837
24937
|
description: optional(string3()),
|
|
@@ -23846,44 +24946,44 @@ var ListPromptsResultSchema = PaginatedResultSchema.extend({
|
|
|
23846
24946
|
});
|
|
23847
24947
|
var GetPromptRequestParamsSchema = BaseRequestParamsSchema.extend({
|
|
23848
24948
|
name: string3(),
|
|
23849
|
-
arguments:
|
|
24949
|
+
arguments: record4(string3(), string3()).optional()
|
|
23850
24950
|
});
|
|
23851
24951
|
var GetPromptRequestSchema = RequestSchema.extend({
|
|
23852
24952
|
method: literal("prompts/get"),
|
|
23853
24953
|
params: GetPromptRequestParamsSchema
|
|
23854
24954
|
});
|
|
23855
|
-
var TextContentSchema =
|
|
24955
|
+
var TextContentSchema = object8({
|
|
23856
24956
|
type: literal("text"),
|
|
23857
24957
|
text: string3(),
|
|
23858
24958
|
annotations: AnnotationsSchema.optional(),
|
|
23859
|
-
_meta:
|
|
24959
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23860
24960
|
});
|
|
23861
|
-
var ImageContentSchema =
|
|
24961
|
+
var ImageContentSchema = object8({
|
|
23862
24962
|
type: literal("image"),
|
|
23863
24963
|
data: Base64Schema,
|
|
23864
24964
|
mimeType: string3(),
|
|
23865
24965
|
annotations: AnnotationsSchema.optional(),
|
|
23866
|
-
_meta:
|
|
24966
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23867
24967
|
});
|
|
23868
|
-
var AudioContentSchema =
|
|
24968
|
+
var AudioContentSchema = object8({
|
|
23869
24969
|
type: literal("audio"),
|
|
23870
24970
|
data: Base64Schema,
|
|
23871
24971
|
mimeType: string3(),
|
|
23872
24972
|
annotations: AnnotationsSchema.optional(),
|
|
23873
|
-
_meta:
|
|
24973
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23874
24974
|
});
|
|
23875
|
-
var ToolUseContentSchema =
|
|
24975
|
+
var ToolUseContentSchema = object8({
|
|
23876
24976
|
type: literal("tool_use"),
|
|
23877
24977
|
name: string3(),
|
|
23878
24978
|
id: string3(),
|
|
23879
|
-
input:
|
|
23880
|
-
_meta:
|
|
24979
|
+
input: record4(string3(), unknown()),
|
|
24980
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23881
24981
|
});
|
|
23882
|
-
var EmbeddedResourceSchema =
|
|
24982
|
+
var EmbeddedResourceSchema = object8({
|
|
23883
24983
|
type: literal("resource"),
|
|
23884
24984
|
resource: union([TextResourceContentsSchema, BlobResourceContentsSchema]),
|
|
23885
24985
|
annotations: AnnotationsSchema.optional(),
|
|
23886
|
-
_meta:
|
|
24986
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23887
24987
|
});
|
|
23888
24988
|
var ResourceLinkSchema = ResourceSchema.extend({
|
|
23889
24989
|
type: literal("resource_link")
|
|
@@ -23895,7 +24995,7 @@ var ContentBlockSchema = union([
|
|
|
23895
24995
|
ResourceLinkSchema,
|
|
23896
24996
|
EmbeddedResourceSchema
|
|
23897
24997
|
]);
|
|
23898
|
-
var PromptMessageSchema =
|
|
24998
|
+
var PromptMessageSchema = object8({
|
|
23899
24999
|
role: RoleSchema,
|
|
23900
25000
|
content: ContentBlockSchema
|
|
23901
25001
|
});
|
|
@@ -23907,33 +25007,33 @@ var PromptListChangedNotificationSchema = NotificationSchema.extend({
|
|
|
23907
25007
|
method: literal("notifications/prompts/list_changed"),
|
|
23908
25008
|
params: NotificationsParamsSchema.optional()
|
|
23909
25009
|
});
|
|
23910
|
-
var ToolAnnotationsSchema =
|
|
25010
|
+
var ToolAnnotationsSchema = object8({
|
|
23911
25011
|
title: string3().optional(),
|
|
23912
25012
|
readOnlyHint: boolean2().optional(),
|
|
23913
25013
|
destructiveHint: boolean2().optional(),
|
|
23914
25014
|
idempotentHint: boolean2().optional(),
|
|
23915
25015
|
openWorldHint: boolean2().optional()
|
|
23916
25016
|
});
|
|
23917
|
-
var ToolExecutionSchema =
|
|
25017
|
+
var ToolExecutionSchema = object8({
|
|
23918
25018
|
taskSupport: _enum(["required", "optional", "forbidden"]).optional()
|
|
23919
25019
|
});
|
|
23920
|
-
var ToolSchema =
|
|
25020
|
+
var ToolSchema = object8({
|
|
23921
25021
|
...BaseMetadataSchema.shape,
|
|
23922
25022
|
...IconsSchema.shape,
|
|
23923
25023
|
description: string3().optional(),
|
|
23924
|
-
inputSchema:
|
|
25024
|
+
inputSchema: object8({
|
|
23925
25025
|
type: literal("object"),
|
|
23926
|
-
properties:
|
|
25026
|
+
properties: record4(string3(), AssertObjectSchema).optional(),
|
|
23927
25027
|
required: array(string3()).optional()
|
|
23928
25028
|
}).catchall(unknown()),
|
|
23929
|
-
outputSchema:
|
|
25029
|
+
outputSchema: object8({
|
|
23930
25030
|
type: literal("object"),
|
|
23931
|
-
properties:
|
|
25031
|
+
properties: record4(string3(), AssertObjectSchema).optional(),
|
|
23932
25032
|
required: array(string3()).optional()
|
|
23933
25033
|
}).catchall(unknown()).optional(),
|
|
23934
25034
|
annotations: ToolAnnotationsSchema.optional(),
|
|
23935
25035
|
execution: ToolExecutionSchema.optional(),
|
|
23936
|
-
_meta:
|
|
25036
|
+
_meta: record4(string3(), unknown()).optional()
|
|
23937
25037
|
});
|
|
23938
25038
|
var ListToolsRequestSchema = PaginatedRequestSchema.extend({
|
|
23939
25039
|
method: literal("tools/list")
|
|
@@ -23943,7 +25043,7 @@ var ListToolsResultSchema = PaginatedResultSchema.extend({
|
|
|
23943
25043
|
});
|
|
23944
25044
|
var CallToolResultSchema = ResultSchema.extend({
|
|
23945
25045
|
content: array(ContentBlockSchema).default([]),
|
|
23946
|
-
structuredContent:
|
|
25046
|
+
structuredContent: record4(string3(), unknown()).optional(),
|
|
23947
25047
|
isError: boolean2().optional()
|
|
23948
25048
|
});
|
|
23949
25049
|
var CompatibilityCallToolResultSchema = CallToolResultSchema.or(ResultSchema.extend({
|
|
@@ -23951,7 +25051,7 @@ var CompatibilityCallToolResultSchema = CallToolResultSchema.or(ResultSchema.ext
|
|
|
23951
25051
|
}));
|
|
23952
25052
|
var CallToolRequestParamsSchema = TaskAugmentedRequestParamsSchema.extend({
|
|
23953
25053
|
name: string3(),
|
|
23954
|
-
arguments:
|
|
25054
|
+
arguments: record4(string3(), unknown()).optional()
|
|
23955
25055
|
});
|
|
23956
25056
|
var CallToolRequestSchema = RequestSchema.extend({
|
|
23957
25057
|
method: literal("tools/call"),
|
|
@@ -23961,7 +25061,7 @@ var ToolListChangedNotificationSchema = NotificationSchema.extend({
|
|
|
23961
25061
|
method: literal("notifications/tools/list_changed"),
|
|
23962
25062
|
params: NotificationsParamsSchema.optional()
|
|
23963
25063
|
});
|
|
23964
|
-
var ListChangedOptionsBaseSchema =
|
|
25064
|
+
var ListChangedOptionsBaseSchema = object8({
|
|
23965
25065
|
autoRefresh: boolean2().default(true),
|
|
23966
25066
|
debounceMs: number2().int().nonnegative().default(300)
|
|
23967
25067
|
});
|
|
@@ -23982,25 +25082,25 @@ var LoggingMessageNotificationSchema = NotificationSchema.extend({
|
|
|
23982
25082
|
method: literal("notifications/message"),
|
|
23983
25083
|
params: LoggingMessageNotificationParamsSchema
|
|
23984
25084
|
});
|
|
23985
|
-
var ModelHintSchema =
|
|
25085
|
+
var ModelHintSchema = object8({
|
|
23986
25086
|
name: string3().optional()
|
|
23987
25087
|
});
|
|
23988
|
-
var ModelPreferencesSchema =
|
|
25088
|
+
var ModelPreferencesSchema = object8({
|
|
23989
25089
|
hints: array(ModelHintSchema).optional(),
|
|
23990
25090
|
costPriority: number2().min(0).max(1).optional(),
|
|
23991
25091
|
speedPriority: number2().min(0).max(1).optional(),
|
|
23992
25092
|
intelligencePriority: number2().min(0).max(1).optional()
|
|
23993
25093
|
});
|
|
23994
|
-
var ToolChoiceSchema =
|
|
25094
|
+
var ToolChoiceSchema = object8({
|
|
23995
25095
|
mode: _enum(["auto", "required", "none"]).optional()
|
|
23996
25096
|
});
|
|
23997
|
-
var ToolResultContentSchema =
|
|
25097
|
+
var ToolResultContentSchema = object8({
|
|
23998
25098
|
type: literal("tool_result"),
|
|
23999
25099
|
toolUseId: string3().describe("The unique identifier for the corresponding tool call."),
|
|
24000
25100
|
content: array(ContentBlockSchema).default([]),
|
|
24001
|
-
structuredContent:
|
|
25101
|
+
structuredContent: object8({}).loose().optional(),
|
|
24002
25102
|
isError: boolean2().optional(),
|
|
24003
|
-
_meta:
|
|
25103
|
+
_meta: record4(string3(), unknown()).optional()
|
|
24004
25104
|
});
|
|
24005
25105
|
var SamplingContentSchema = discriminatedUnion("type", [TextContentSchema, ImageContentSchema, AudioContentSchema]);
|
|
24006
25106
|
var SamplingMessageContentBlockSchema = discriminatedUnion("type", [
|
|
@@ -24010,10 +25110,10 @@ var SamplingMessageContentBlockSchema = discriminatedUnion("type", [
|
|
|
24010
25110
|
ToolUseContentSchema,
|
|
24011
25111
|
ToolResultContentSchema
|
|
24012
25112
|
]);
|
|
24013
|
-
var SamplingMessageSchema =
|
|
25113
|
+
var SamplingMessageSchema = object8({
|
|
24014
25114
|
role: RoleSchema,
|
|
24015
25115
|
content: union([SamplingMessageContentBlockSchema, array(SamplingMessageContentBlockSchema)]),
|
|
24016
|
-
_meta:
|
|
25116
|
+
_meta: record4(string3(), unknown()).optional()
|
|
24017
25117
|
});
|
|
24018
25118
|
var CreateMessageRequestParamsSchema = TaskAugmentedRequestParamsSchema.extend({
|
|
24019
25119
|
messages: array(SamplingMessageSchema),
|
|
@@ -24043,13 +25143,13 @@ var CreateMessageResultWithToolsSchema = ResultSchema.extend({
|
|
|
24043
25143
|
role: RoleSchema,
|
|
24044
25144
|
content: union([SamplingMessageContentBlockSchema, array(SamplingMessageContentBlockSchema)])
|
|
24045
25145
|
});
|
|
24046
|
-
var BooleanSchemaSchema =
|
|
25146
|
+
var BooleanSchemaSchema = object8({
|
|
24047
25147
|
type: literal("boolean"),
|
|
24048
25148
|
title: string3().optional(),
|
|
24049
25149
|
description: string3().optional(),
|
|
24050
25150
|
default: boolean2().optional()
|
|
24051
25151
|
});
|
|
24052
|
-
var StringSchemaSchema =
|
|
25152
|
+
var StringSchemaSchema = object8({
|
|
24053
25153
|
type: literal("string"),
|
|
24054
25154
|
title: string3().optional(),
|
|
24055
25155
|
description: string3().optional(),
|
|
@@ -24058,7 +25158,7 @@ var StringSchemaSchema = object6({
|
|
|
24058
25158
|
format: _enum(["email", "uri", "date", "date-time"]).optional(),
|
|
24059
25159
|
default: string3().optional()
|
|
24060
25160
|
});
|
|
24061
|
-
var NumberSchemaSchema =
|
|
25161
|
+
var NumberSchemaSchema = object8({
|
|
24062
25162
|
type: _enum(["number", "integer"]),
|
|
24063
25163
|
title: string3().optional(),
|
|
24064
25164
|
description: string3().optional(),
|
|
@@ -24066,24 +25166,24 @@ var NumberSchemaSchema = object6({
|
|
|
24066
25166
|
maximum: number2().optional(),
|
|
24067
25167
|
default: number2().optional()
|
|
24068
25168
|
});
|
|
24069
|
-
var UntitledSingleSelectEnumSchemaSchema =
|
|
25169
|
+
var UntitledSingleSelectEnumSchemaSchema = object8({
|
|
24070
25170
|
type: literal("string"),
|
|
24071
25171
|
title: string3().optional(),
|
|
24072
25172
|
description: string3().optional(),
|
|
24073
25173
|
enum: array(string3()),
|
|
24074
25174
|
default: string3().optional()
|
|
24075
25175
|
});
|
|
24076
|
-
var TitledSingleSelectEnumSchemaSchema =
|
|
25176
|
+
var TitledSingleSelectEnumSchemaSchema = object8({
|
|
24077
25177
|
type: literal("string"),
|
|
24078
25178
|
title: string3().optional(),
|
|
24079
25179
|
description: string3().optional(),
|
|
24080
|
-
oneOf: array(
|
|
25180
|
+
oneOf: array(object8({
|
|
24081
25181
|
const: string3(),
|
|
24082
25182
|
title: string3()
|
|
24083
25183
|
})),
|
|
24084
25184
|
default: string3().optional()
|
|
24085
25185
|
});
|
|
24086
|
-
var LegacyTitledEnumSchemaSchema =
|
|
25186
|
+
var LegacyTitledEnumSchemaSchema = object8({
|
|
24087
25187
|
type: literal("string"),
|
|
24088
25188
|
title: string3().optional(),
|
|
24089
25189
|
description: string3().optional(),
|
|
@@ -24092,26 +25192,26 @@ var LegacyTitledEnumSchemaSchema = object6({
|
|
|
24092
25192
|
default: string3().optional()
|
|
24093
25193
|
});
|
|
24094
25194
|
var SingleSelectEnumSchemaSchema = union([UntitledSingleSelectEnumSchemaSchema, TitledSingleSelectEnumSchemaSchema]);
|
|
24095
|
-
var UntitledMultiSelectEnumSchemaSchema =
|
|
25195
|
+
var UntitledMultiSelectEnumSchemaSchema = object8({
|
|
24096
25196
|
type: literal("array"),
|
|
24097
25197
|
title: string3().optional(),
|
|
24098
25198
|
description: string3().optional(),
|
|
24099
25199
|
minItems: number2().optional(),
|
|
24100
25200
|
maxItems: number2().optional(),
|
|
24101
|
-
items:
|
|
25201
|
+
items: object8({
|
|
24102
25202
|
type: literal("string"),
|
|
24103
25203
|
enum: array(string3())
|
|
24104
25204
|
}),
|
|
24105
25205
|
default: array(string3()).optional()
|
|
24106
25206
|
});
|
|
24107
|
-
var TitledMultiSelectEnumSchemaSchema =
|
|
25207
|
+
var TitledMultiSelectEnumSchemaSchema = object8({
|
|
24108
25208
|
type: literal("array"),
|
|
24109
25209
|
title: string3().optional(),
|
|
24110
25210
|
description: string3().optional(),
|
|
24111
25211
|
minItems: number2().optional(),
|
|
24112
25212
|
maxItems: number2().optional(),
|
|
24113
|
-
items:
|
|
24114
|
-
anyOf: array(
|
|
25213
|
+
items: object8({
|
|
25214
|
+
anyOf: array(object8({
|
|
24115
25215
|
const: string3(),
|
|
24116
25216
|
title: string3()
|
|
24117
25217
|
}))
|
|
@@ -24124,9 +25224,9 @@ var PrimitiveSchemaDefinitionSchema = union([EnumSchemaSchema, BooleanSchemaSche
|
|
|
24124
25224
|
var ElicitRequestFormParamsSchema = TaskAugmentedRequestParamsSchema.extend({
|
|
24125
25225
|
mode: literal("form").optional(),
|
|
24126
25226
|
message: string3(),
|
|
24127
|
-
requestedSchema:
|
|
25227
|
+
requestedSchema: object8({
|
|
24128
25228
|
type: literal("object"),
|
|
24129
|
-
properties:
|
|
25229
|
+
properties: record4(string3(), PrimitiveSchemaDefinitionSchema),
|
|
24130
25230
|
required: array(string3()).optional()
|
|
24131
25231
|
})
|
|
24132
25232
|
});
|
|
@@ -24150,24 +25250,24 @@ var ElicitationCompleteNotificationSchema = NotificationSchema.extend({
|
|
|
24150
25250
|
});
|
|
24151
25251
|
var ElicitResultSchema = ResultSchema.extend({
|
|
24152
25252
|
action: _enum(["accept", "decline", "cancel"]),
|
|
24153
|
-
content: preprocess((val) => val === null ? undefined : val,
|
|
25253
|
+
content: preprocess((val) => val === null ? undefined : val, record4(string3(), union([string3(), number2(), boolean2(), array(string3())])).optional())
|
|
24154
25254
|
});
|
|
24155
|
-
var ResourceTemplateReferenceSchema =
|
|
25255
|
+
var ResourceTemplateReferenceSchema = object8({
|
|
24156
25256
|
type: literal("ref/resource"),
|
|
24157
25257
|
uri: string3()
|
|
24158
25258
|
});
|
|
24159
|
-
var PromptReferenceSchema =
|
|
25259
|
+
var PromptReferenceSchema = object8({
|
|
24160
25260
|
type: literal("ref/prompt"),
|
|
24161
25261
|
name: string3()
|
|
24162
25262
|
});
|
|
24163
25263
|
var CompleteRequestParamsSchema = BaseRequestParamsSchema.extend({
|
|
24164
25264
|
ref: union([PromptReferenceSchema, ResourceTemplateReferenceSchema]),
|
|
24165
|
-
argument:
|
|
25265
|
+
argument: object8({
|
|
24166
25266
|
name: string3(),
|
|
24167
25267
|
value: string3()
|
|
24168
25268
|
}),
|
|
24169
|
-
context:
|
|
24170
|
-
arguments:
|
|
25269
|
+
context: object8({
|
|
25270
|
+
arguments: record4(string3(), string3()).optional()
|
|
24171
25271
|
}).optional()
|
|
24172
25272
|
});
|
|
24173
25273
|
var CompleteRequestSchema = RequestSchema.extend({
|
|
@@ -24181,10 +25281,10 @@ var CompleteResultSchema = ResultSchema.extend({
|
|
|
24181
25281
|
hasMore: optional(boolean2())
|
|
24182
25282
|
})
|
|
24183
25283
|
});
|
|
24184
|
-
var RootSchema =
|
|
25284
|
+
var RootSchema = object8({
|
|
24185
25285
|
uri: string3().startsWith("file://"),
|
|
24186
25286
|
name: string3().optional(),
|
|
24187
|
-
_meta:
|
|
25287
|
+
_meta: record4(string3(), unknown()).optional()
|
|
24188
25288
|
});
|
|
24189
25289
|
var ListRootsRequestSchema = RequestSchema.extend({
|
|
24190
25290
|
method: literal("roots/list"),
|
|
@@ -25754,8 +26854,8 @@ class StdioServerTransport {
|
|
|
25754
26854
|
}
|
|
25755
26855
|
|
|
25756
26856
|
// src/adapters/claude-channel.ts
|
|
25757
|
-
import { readFileSync as
|
|
25758
|
-
import { join as
|
|
26857
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
26858
|
+
import { join as join28 } from "path";
|
|
25759
26859
|
init_hub_tools();
|
|
25760
26860
|
init_envelope();
|
|
25761
26861
|
|
|
@@ -25794,7 +26894,7 @@ var channel = !toolsMode && process.env.AGENTHUB_CHANNEL === "1";
|
|
|
25794
26894
|
function roles2() {
|
|
25795
26895
|
const read = (name) => {
|
|
25796
26896
|
try {
|
|
25797
|
-
return JSON.parse(
|
|
26897
|
+
return JSON.parse(readFileSync14(join28(projectRoot2, ".agenthub", name), "utf8")).roles ?? {};
|
|
25798
26898
|
} catch {
|
|
25799
26899
|
return {};
|
|
25800
26900
|
}
|
|
@@ -25813,7 +26913,7 @@ function sessionProfile() {
|
|
|
25813
26913
|
for (const name of ["config.json", "config.local.json"]) {
|
|
25814
26914
|
let data;
|
|
25815
26915
|
try {
|
|
25816
|
-
data = JSON.parse(
|
|
26916
|
+
data = JSON.parse(readFileSync14(join28(projectRoot2, ".agenthub", name), "utf8"));
|
|
25817
26917
|
} catch (error) {
|
|
25818
26918
|
if (error.code === "ENOENT")
|
|
25819
26919
|
continue;
|
|
@@ -25840,7 +26940,7 @@ function sessionRoles() {
|
|
|
25840
26940
|
const read = (name) => {
|
|
25841
26941
|
let raw;
|
|
25842
26942
|
try {
|
|
25843
|
-
raw = JSON.parse(
|
|
26943
|
+
raw = JSON.parse(readFileSync14(join28(projectRoot2, ".agenthub", name), "utf8")).catalog_roles;
|
|
25844
26944
|
} catch {
|
|
25845
26945
|
return {};
|
|
25846
26946
|
}
|
|
@@ -25869,7 +26969,7 @@ var HELD = `another session is attached to the hub as "${peerId}"; this one is s
|
|
|
25869
26969
|
var INBOX_CAP = 200;
|
|
25870
26970
|
function peerHeld() {
|
|
25871
26971
|
try {
|
|
25872
|
-
const status = JSON.parse(
|
|
26972
|
+
const status = JSON.parse(readFileSync14(join28(stateDir, "status.json"), "utf8"));
|
|
25873
26973
|
const peer = status.peers?.[peerId];
|
|
25874
26974
|
if (channel && peer?.toolsOnly && !peer.claiming)
|
|
25875
26975
|
return false;
|
|
@@ -25904,7 +27004,7 @@ var TOOLS_INSTRUCTIONS = ["agent-hub task tools for this project. Messages from
|
|
|
25904
27004
|
`);
|
|
25905
27005
|
var log = (line) => console.error(`[agent-hub] ${line}`);
|
|
25906
27006
|
var text4 = (s) => ({ content: [{ type: "text", text: s }] });
|
|
25907
|
-
var server = new Server({ name: "agent-hub", version:
|
|
27007
|
+
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
27008
|
var inbox = [];
|
|
25909
27009
|
var hub;
|
|
25910
27010
|
var detached;
|
|
@@ -26144,6 +27244,8 @@ async function callTool(req) {
|
|
|
26144
27244
|
return text4(`not sent: ${res.error}`);
|
|
26145
27245
|
if (res.recorded)
|
|
26146
27246
|
return text4("recorded only ([FYI]): it is on the hub console and log, and no peer spent a turn on it");
|
|
27247
|
+
if (res.dropped === "hop")
|
|
27248
|
+
return text4(res.notice);
|
|
26147
27249
|
const sent = `sent to: ${res.targets.join(", ") || "(no other peers attached)"}`;
|
|
26148
27250
|
return text4(typeof res.notice === "string" ? `${sent}; ${res.notice}` : sent);
|
|
26149
27251
|
}
|