@bivy/bivy 0.21.2-staging.1 → 0.21.2-staging.2
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/bin/cli-commands.mjs +1 -1
- package/bin/guides/add-an-agent.md +39 -0
- package/dist/agent-cli.js +91 -63
- package/dist/node-config.js +18 -0
- package/dist/plugin-cli.js +3 -4
- package/dist/plugin-sdk/manifest.js +19 -11
- package/dist/runtime/cli-parsers.js +18 -1
- package/dist/runtime/index.js +71 -41
- package/dist/server.js +0 -3
- package/package.json +1 -1
- package/web/assets/{AutomationsView-chlf1Cpr.js → AutomationsView-DJchxQrZ.js} +1 -1
- package/web/assets/{ChangesCard-BQ3lwCYn.js → ChangesCard-BUvAmeeQ.js} +1 -1
- package/web/assets/{LibraryView-CqY8xTAh.js → LibraryView-Dn47NNbH.js} +1 -1
- package/web/assets/{ProviderConnect-BC-EWUjN.js → ProviderConnect-DUW1H3tC.js} +1 -1
- package/web/assets/{SessionList-BG0cvQfe.js → SessionList-Cah3Kqlx.js} +1 -1
- package/web/assets/{Settings-Bpd2LYcT.js → Settings-ZdpcnN1P.js} +3 -3
- package/web/assets/{Sheet-B0HjJdGp.js → Sheet-dglO4sKv.js} +1 -1
- package/web/assets/{Terminal-DTbs_kzR.js → Terminal-B-xnPkKI.js} +1 -1
- package/web/assets/{VoiceSettings-5stXm8Rh.js → VoiceSettings-B1bu2yqG.js} +1 -1
- package/web/assets/{abnfDiagram-VCTEODGH-BzxGthCK.js → abnfDiagram-VCTEODGH-Cd5wpFQS.js} +1 -1
- package/web/assets/architecture-7GRP2DOG-DOsFSfqx.js +1 -0
- package/web/assets/{architectureDiagram-5GKGNRK7-BUKaHpyZ.js → architectureDiagram-5GKGNRK7-C79vg2c-.js} +1 -1
- package/web/assets/{blockDiagram-I7D4REHJ-BBCtk8Uh.js → blockDiagram-I7D4REHJ-XpPgl9gi.js} +1 -1
- package/web/assets/{c4Diagram-7LVT6UL2-DY29eJER.js → c4Diagram-7LVT6UL2-scuOhpsJ.js} +1 -1
- package/web/assets/channel-fjfLJqRt.js +1 -0
- package/web/assets/{chunk-4HAMMTFA-Dwwu-TeK.js → chunk-4HAMMTFA-CX2RCwgc.js} +1 -1
- package/web/assets/{chunk-75Z2AOVW-CIRWwn6Y.js → chunk-75Z2AOVW-Ckr8PvDp.js} +1 -1
- package/web/assets/{chunk-DU6HZSFF-BjC0SliR.js → chunk-DU6HZSFF-D4TOshjE.js} +1 -1
- package/web/assets/{chunk-F27PBJKO-wa_cUFjb.js → chunk-F27PBJKO-D1eChWME.js} +1 -1
- package/web/assets/{chunk-GMAD6QVW-ldP8bvnz.js → chunk-GMAD6QVW-BP1uiPYn.js} +1 -1
- package/web/assets/{chunk-GVQU2GXP-BiIt8KQk.js → chunk-GVQU2GXP-BXV3zU07.js} +1 -1
- package/web/assets/{chunk-IMKFNOWR-Dy7EkDid.js → chunk-IMKFNOWR-BCXDHEI9.js} +1 -1
- package/web/assets/{chunk-L3NEJ4N5-DrtR_CiV.js → chunk-L3NEJ4N5-BwzXV4PE.js} +1 -1
- package/web/assets/{chunk-OSK3NFVY-Dnvo5XKB.js → chunk-OSK3NFVY-Di-i3JM-.js} +1 -1
- package/web/assets/{chunk-P2QGCYS3-CdZdfs9H.js → chunk-P2QGCYS3-DbRCV-VU.js} +1 -1
- package/web/assets/{chunk-POPQ4Y6H-DUdouJMc.js → chunk-POPQ4Y6H-YV54bsCb.js} +1 -1
- package/web/assets/{chunk-PWAF6VOD-BMGGIPTG.js → chunk-PWAF6VOD-i0X9M6al.js} +1 -1
- package/web/assets/{chunk-SHT3W25Y-Mb0aPPgh.js → chunk-SHT3W25Y-CwBN0rQ7.js} +1 -1
- package/web/assets/{chunk-SVP7TREG-C5eicczL.js → chunk-SVP7TREG-CbrcXLZD.js} +1 -1
- package/web/assets/{chunk-TICWLB2K-CoKD47HN.js → chunk-TICWLB2K-Du-1KT-T.js} +1 -1
- package/web/assets/classDiagram-ZZMXUADV-BmRGg1Fc.js +1 -0
- package/web/assets/classDiagram-v2-VYDZK3BY-BmRGg1Fc.js +1 -0
- package/web/assets/{cynefin-OW5HDTMX-BTzJNWu1.js → cynefin-OW5HDTMX-FCt1UWBG.js} +1 -1
- package/web/assets/{cynefinDiagram-5FMLGOSQ-Dd7g6wFx.js → cynefinDiagram-5FMLGOSQ-B5ZVXcUd.js} +1 -1
- package/web/assets/{dagre-GXQ25YYZ-D3zVJkwr.js → dagre-GXQ25YYZ-BGx2JJsW.js} +1 -1
- package/web/assets/{diagram-S7CK7UJ4-S9vzG5Iu.js → diagram-S7CK7UJ4-Du9j6AuS.js} +1 -1
- package/web/assets/{diagram-UQ7AKVKN-Csc1xBzG.js → diagram-UQ7AKVKN-DqIqZboL.js} +1 -1
- package/web/assets/{diagram-VSXAHHWV-Bigb1Thn.js → diagram-VSXAHHWV-Ct0z-PV8.js} +1 -1
- package/web/assets/{diagram-VX7I27RA-D-FXM6TU.js → diagram-VX7I27RA-yUxc_nwu.js} +1 -1
- package/web/assets/{diagram-Z3DM3KII-D8hJhbd2.js → diagram-Z3DM3KII-CfG4Zaxz.js} +1 -1
- package/web/assets/{ebnfDiagram-PWID7BFC-DXvgoAAx.js → ebnfDiagram-PWID7BFC-BRxR7WEJ.js} +1 -1
- package/web/assets/{erDiagram-RLTQ6QDP-ZBHJikuC.js → erDiagram-RLTQ6QDP-RGO9wWT5.js} +1 -1
- package/web/assets/eventmodeling-NTZA5JFV-DGBy5vLs.js +1 -0
- package/web/assets/flowDiagram-HODETNUW-DT56J3Wm.js +1 -0
- package/web/assets/{ganttDiagram-EL5Y4UJY-CouBpzDS.js → ganttDiagram-EL5Y4UJY-B2yk0sFl.js} +1 -1
- package/web/assets/{gitGraph-4MIJSDKK-D_EH-2tK.js → gitGraph-4MIJSDKK-CyaqczMH.js} +1 -1
- package/web/assets/{gitGraphDiagram-WWUBYQGX-CiYi0gV_.js → gitGraphDiagram-WWUBYQGX-1OthYEqM.js} +1 -1
- package/web/assets/{index-BqTUAMUo.js → index-Dh8pIWYA.js} +2 -2
- package/web/assets/{info-A6RAGUB7-CqxR-vqg.js → info-A6RAGUB7-Cp-eLwmy.js} +1 -1
- package/web/assets/{infoDiagram-27XIBGKW-CKjwiYqh.js → infoDiagram-27XIBGKW-DSCawyMm.js} +1 -1
- package/web/assets/{ishikawaDiagram-5VMMS53U-OKhyRTlP.js → ishikawaDiagram-5VMMS53U-D_qrAFV5.js} +1 -1
- package/web/assets/{journeyDiagram-3NMN7TZE-DjpB5OfX.js → journeyDiagram-3NMN7TZE-kxdmuOty.js} +1 -1
- package/web/assets/{kanban-definition-UXKFOSKX-B8Xzxar1.js → kanban-definition-UXKFOSKX-BPw3S_oT.js} +1 -1
- package/web/assets/{mermaid-parser.core-DiDXDDZp.js → mermaid-parser.core-3WabdgX5.js} +3 -3
- package/web/assets/{mermaid.core-BcCoKrIv.js → mermaid.core-BVKHuEid.js} +4 -4
- package/web/assets/{mindmap-definition-YA3MSWOX-B37CHXfT.js → mindmap-definition-YA3MSWOX-DHR4jf7B.js} +1 -1
- package/web/assets/{mount-CyPeuIDQ.js → mount-fT0kwnSn.js} +5 -5
- package/web/assets/{packet-AYTQ26CC-DADMJ2Yt.js → packet-AYTQ26CC-_jVMhg1g.js} +1 -1
- package/web/assets/{pegDiagram-XKGWAZYB-BwXOeSnI.js → pegDiagram-XKGWAZYB-DHuv9Xcw.js} +1 -1
- package/web/assets/{pie-WAS4IAKB-D9hNG31t.js → pie-WAS4IAKB-oplTN_Mv.js} +1 -1
- package/web/assets/{pieDiagram-E7YTZNPT-BjQhU1Mo.js → pieDiagram-E7YTZNPT-B-9wMWV2.js} +1 -1
- package/web/assets/{quadrantDiagram-AXDQQJYC-BUmmIEMt.js → quadrantDiagram-AXDQQJYC-Cx6WBCYY.js} +1 -1
- package/web/assets/{radar-RG4KPBEZ-DW58Z5Mj.js → radar-RG4KPBEZ-BqhPkeN1.js} +1 -1
- package/web/assets/{railroad-74A4TZTK-Bxb0GBL6.js → railroad-74A4TZTK-B4os3txt.js} +1 -1
- package/web/assets/railroad-abnf-HS5TGJTU-rXGn5ha3.js +1 -0
- package/web/assets/railroad-ebnf-LZEXJU2U-Bq08RGuu.js +1 -0
- package/web/assets/railroad-peg-WCYAUIDC-BVPQA8YX.js +1 -0
- package/web/assets/{railroadDiagram-O6MQD6OU-CqOaqCxM.js → railroadDiagram-O6MQD6OU-Dj9FyDlx.js} +1 -1
- package/web/assets/{requirementDiagram-BXWQKSXE-B2Mihs8T.js → requirementDiagram-BXWQKSXE-B0shQEHN.js} +1 -1
- package/web/assets/{sankeyDiagram-P5KCCOFB-DI2cV18A.js → sankeyDiagram-P5KCCOFB-BNLjEK-Q.js} +1 -1
- package/web/assets/{sequenceDiagram-WJ2MYXX4-Ct6KM5nc.js → sequenceDiagram-WJ2MYXX4-ChvenM8n.js} +1 -1
- package/web/assets/{settingsRoute-DiYTWwf-.js → settingsRoute-CyCy5_yM.js} +1 -1
- package/web/assets/{stateDiagram-D77RDMKH-0z6WQwlg.js → stateDiagram-D77RDMKH-DuvGkTXC.js} +1 -1
- package/web/assets/stateDiagram-v2-MP3YSRHH-BvkWgIF0.js +1 -0
- package/web/assets/{swimlanes-42K2YHIH-mLnDFml4.js → swimlanes-42K2YHIH-Bz_rQJMo.js} +1 -1
- package/web/assets/swimlanesDiagram-VR7AAH4N-DoOeMiMx.js +8 -0
- package/web/assets/{timeline-definition-24CTP7MA-CSZZvZUD.js → timeline-definition-24CTP7MA-Tm4CUVVT.js} +1 -1
- package/web/assets/{treeView-Q6P3EWNA-nkM4Mhz8.js → treeView-Q6P3EWNA-Cot0o4Uq.js} +1 -1
- package/web/assets/{treemap-WGGIJYW6-D0q8GQM0.js → treemap-WGGIJYW6-n9IHmz7X.js} +1 -1
- package/web/assets/{useStore-0Cz83n1l.js → useStore-DJNGFENr.js} +1 -1
- package/web/assets/{vennDiagram-4TSXK5OY-DMh3_TDy.js → vennDiagram-4TSXK5OY-BsAdBLlU.js} +1 -1
- package/web/assets/{wardley-WFR3VGLG-C1n_QCAB.js → wardley-WFR3VGLG-DTUN6iAo.js} +1 -1
- package/web/assets/{wardleyDiagram-VM6X3IG4-C6ap89Zw.js → wardleyDiagram-VM6X3IG4-C_RmJiF8.js} +1 -1
- package/web/assets/{xychartDiagram-S5SC5T6Z-B27MAKKy.js → xychartDiagram-S5SC5T6Z-duOUYl3t.js} +1 -1
- package/web/index.html +1 -1
- package/web/sw.js +1 -1
- package/web/assets/architecture-7GRP2DOG-jzSX7bLG.js +0 -1
- package/web/assets/channel-C1pvpPyo.js +0 -1
- package/web/assets/classDiagram-ZZMXUADV-LoptuGLH.js +0 -1
- package/web/assets/classDiagram-v2-VYDZK3BY-LoptuGLH.js +0 -1
- package/web/assets/eventmodeling-NTZA5JFV-QziTZg_i.js +0 -1
- package/web/assets/flowDiagram-HODETNUW-hMB4P6zf.js +0 -1
- package/web/assets/railroad-abnf-HS5TGJTU-D71MxTg_.js +0 -1
- package/web/assets/railroad-ebnf-LZEXJU2U-DuU1fM8N.js +0 -1
- package/web/assets/railroad-peg-WCYAUIDC-B0-xZffZ.js +0 -1
- package/web/assets/stateDiagram-v2-MP3YSRHH-CrCsCpWo.js +0 -1
- package/web/assets/swimlanesDiagram-VR7AAH4N-CRl9QwhL.js +0 -8
package/bin/cli-commands.mjs
CHANGED
|
@@ -129,7 +129,7 @@ export const COMMANDS = [
|
|
|
129
129
|
|
|
130
130
|
// Help and discovery
|
|
131
131
|
{ name: "guide", group: "help", scope: "node", json: true, usage: "guide [topic] [--json]", summary: "Short playbooks for agents: show the user, talk to them, long work, more hands, automate",
|
|
132
|
-
tools: [{ name: "bivy_guide", description: "Short playbooks for working in Bivy. Without a topic, lists them.", input: { topic: { type: "string", description: "show-the-user, talk-to-the-user, long-work, more-hands or
|
|
132
|
+
tools: [{ name: "bivy_guide", description: "Short playbooks for working in Bivy. Without a topic, lists them.", input: { topic: { type: "string", description: "show-the-user, talk-to-the-user, long-work, more-hands, automate or add-an-agent." } }, argv: ["guide", { arg: "topic" }] }] },
|
|
133
133
|
{ name: "help", aliases: ["-h", "--help"], group: "help", scope: "node", json: true, usage: "help [command] [--json]", summary: "Show this help, one command's row, or every command as JSON" },
|
|
134
134
|
{ name: "version", aliases: ["--version", "-v"], group: "help", scope: "node", usage: "version", summary: "Print the installed Bivy version" },
|
|
135
135
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Add an agent: any ACP agent or CLI
|
|
2
|
+
|
|
3
|
+
Summary: Make another coding agent available in Bivy, writing a small adapter when its CLI needs one.
|
|
4
|
+
|
|
5
|
+
An agent with an ACP mode needs only its command:
|
|
6
|
+
|
|
7
|
+
bivy agent add gemini-acp --command gemini --args '["--experimental-acp"]'
|
|
8
|
+
|
|
9
|
+
Any other CLI that takes a prompt on stdin (or as its last argument with
|
|
10
|
+
`--prompt-mode argv`):
|
|
11
|
+
|
|
12
|
+
bivy agent add review-bot --transport process --command review-bot --args '["run"]'
|
|
13
|
+
|
|
14
|
+
It is in the picker at once. Check it with one real turn, which prints the answer:
|
|
15
|
+
|
|
16
|
+
bivy exec -a gemini-acp "Say hello"
|
|
17
|
+
|
|
18
|
+
When the CLI's output needs translating, write an adapter: a program Bivy runs
|
|
19
|
+
once per turn with the prompt on stdin. It prints one JSON event per line:
|
|
20
|
+
|
|
21
|
+
{"type":"session.started","sessionId":"abc"} # passed back as {id} to resume
|
|
22
|
+
{"type":"message.reasoning","text":"…"}
|
|
23
|
+
{"type":"tool.call","toolCallId":"t1","name":"bash","input":{"command":"ls"}}
|
|
24
|
+
{"type":"tool.result","toolCallId":"t1","result":"…"}
|
|
25
|
+
{"type":"message.delta","text":"The answer…"}
|
|
26
|
+
{"type":"usage","tokens":{"input_tokens":10,"output_tokens":20}}
|
|
27
|
+
{"type":"session.done"} # or {"type":"session.error","error":"…"}
|
|
28
|
+
|
|
29
|
+
Start from examples/agents/jsonl-adapter/adapter.mjs in the Bivy repo, then:
|
|
30
|
+
|
|
31
|
+
bivy agent add my-agent --transport process --parser bivy-protocol \
|
|
32
|
+
--command "$PWD/adapter.mjs" --resume-args '["--resume","{id}"]'
|
|
33
|
+
bivy exec -a my-agent "List the files here"
|
|
34
|
+
|
|
35
|
+
These events report what the agent did; they cannot ask before a tool runs. For
|
|
36
|
+
approvals, the adapter must speak ACP (`--transport acp`).
|
|
37
|
+
|
|
38
|
+
The entry lives under `agents:` in the node's config.yaml (`bivy agent list`,
|
|
39
|
+
`bivy agent remove <id>`). The full format is in docs/add-an-agent.md.
|
package/dist/agent-cli.js
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
3
|
// Copyright (c) 2026 Petter André Sjulstad
|
|
4
|
-
/** First-class
|
|
4
|
+
/** First-class bring-your-own-agent CLI: writes `agents:` in the node config.yaml. */
|
|
5
5
|
import fs from "node:fs";
|
|
6
|
-
import os from "node:os";
|
|
7
6
|
import path from "node:path";
|
|
8
|
-
import { stringify as stringifyYaml } from "yaml";
|
|
9
|
-
import { currentBivyVersion } from "./app-version.js";
|
|
10
7
|
import { defaultDataDir } from "./data-dir.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
8
|
+
import { mergeLegacyIntoNodeConfig, nodeConfigPath, readNodeConfig, writeNodeConfig } from "./node-config.js";
|
|
9
|
+
import { listInstalledPlugins, removePlugin } from "./plugins/store.js";
|
|
13
10
|
function option(args, name) {
|
|
14
11
|
const index = args.indexOf(name);
|
|
15
12
|
return index >= 0 ? args[index + 1] : undefined;
|
|
16
13
|
}
|
|
17
14
|
function positional(args) {
|
|
18
|
-
const valued = new Set(["--command", "--transport", "--adapter", "--args", "--name", "--prompt-mode", "--data-dir"]);
|
|
15
|
+
const valued = new Set(["--command", "--transport", "--adapter", "--args", "--name", "--prompt-mode", "--parser", "--resume-args", "--data-dir"]);
|
|
19
16
|
const out = [];
|
|
20
17
|
for (let index = 0; index < args.length; index += 1) {
|
|
21
18
|
if (valued.has(args[index])) {
|
|
@@ -42,17 +39,35 @@ function parseArgs(value) {
|
|
|
42
39
|
parsed = JSON.parse(value);
|
|
43
40
|
}
|
|
44
41
|
catch {
|
|
45
|
-
throw new Error("--args must be
|
|
42
|
+
throw new Error("--args and --resume-args must be JSON string arrays");
|
|
46
43
|
}
|
|
47
44
|
if (!Array.isArray(parsed) || !parsed.every((item) => typeof item === "string")) {
|
|
48
|
-
throw new Error("--args must be
|
|
45
|
+
throw new Error("--args and --resume-args must be JSON string arrays");
|
|
49
46
|
}
|
|
50
47
|
return parsed;
|
|
51
48
|
}
|
|
52
|
-
function
|
|
49
|
+
function readJson(dataDir, name) {
|
|
50
|
+
try {
|
|
51
|
+
const value = JSON.parse(fs.readFileSync(path.join(dataDir, name), "utf8"));
|
|
52
|
+
return value && typeof value === "object" ? value : {};
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** The node's config.yaml, or the one the daemon would create from legacy files
|
|
59
|
+
* — never a bare file that would skip that migration. */
|
|
60
|
+
function nodeConfig(dataDir) {
|
|
61
|
+
return readNodeConfig(dataDir) ?? mergeLegacyIntoNodeConfig(readJson(dataDir, "cli.json"), readJson(dataDir, "settings.json"));
|
|
62
|
+
}
|
|
63
|
+
function agentId(args) {
|
|
53
64
|
const id = positional(args)[1]?.trim().toLowerCase() ?? "";
|
|
54
65
|
if (!/^[a-z][a-z0-9-]{1,47}$/.test(id))
|
|
55
66
|
throw new Error("Agent id must be a lowercase slug of 2-48 characters");
|
|
67
|
+
return id;
|
|
68
|
+
}
|
|
69
|
+
/** `bivy agent add` flags → the `adapter` block config.yaml and plugins share. */
|
|
70
|
+
function agentConfig(args, id) {
|
|
56
71
|
const command = option(args, "--command")?.trim();
|
|
57
72
|
if (!command)
|
|
58
73
|
throw new Error("--command is required");
|
|
@@ -61,29 +76,26 @@ function localManifest(args) {
|
|
|
61
76
|
throw new Error("--transport must be acp or process");
|
|
62
77
|
const adapterArgs = parseArgs(option(args, "--args"));
|
|
63
78
|
const name = option(args, "--name")?.trim() || id.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
64
|
-
|
|
65
|
-
|
|
79
|
+
if (transport === "acp") {
|
|
80
|
+
for (const flag of ["--prompt-mode", "--parser", "--resume-args"])
|
|
81
|
+
if (option(args, flag))
|
|
82
|
+
throw new Error(`${flag} applies to --transport process only`);
|
|
83
|
+
return { name, adapter: { kind: "acp", command, ...(adapterArgs.length ? { args: adapterArgs } : {}) } };
|
|
84
|
+
}
|
|
85
|
+
const promptMode = option(args, "--prompt-mode") ?? "stdin";
|
|
86
|
+
if (promptMode !== "stdin" && promptMode !== "argv")
|
|
66
87
|
throw new Error("--prompt-mode must be stdin or argv");
|
|
67
|
-
const
|
|
88
|
+
const parser = option(args, "--parser");
|
|
89
|
+
const resumeArgs = option(args, "--resume-args");
|
|
68
90
|
return {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
requires: { bivy: recommendedBivyRange(currentBivyVersion()) },
|
|
78
|
-
contributes: {
|
|
79
|
-
agents: [{
|
|
80
|
-
id,
|
|
81
|
-
name,
|
|
82
|
-
authOwner: "agent",
|
|
83
|
-
adapter: transport === "acp"
|
|
84
|
-
? { kind: "acp", command, ...(adapterArgs.length ? { args: adapterArgs } : {}) }
|
|
85
|
-
: { kind: "process", command, ...(adapterArgs.length ? { args: adapterArgs } : {}), promptMode },
|
|
86
|
-
}],
|
|
91
|
+
name,
|
|
92
|
+
adapter: {
|
|
93
|
+
kind: "process",
|
|
94
|
+
command,
|
|
95
|
+
...(adapterArgs.length ? { args: adapterArgs } : {}),
|
|
96
|
+
promptMode,
|
|
97
|
+
...(parser ? { structured: { args: adapterArgs, parser } } : {}),
|
|
98
|
+
...(resumeArgs ? { resume: { args: parseArgs(resumeArgs) } } : {}),
|
|
87
99
|
},
|
|
88
100
|
};
|
|
89
101
|
}
|
|
@@ -94,64 +106,80 @@ async function main() {
|
|
|
94
106
|
const dataDir = path.resolve(option(args, "--data-dir") ?? defaultDataDir());
|
|
95
107
|
if (!command || command === "help" || args.includes("--help") || args.includes("-h")) {
|
|
96
108
|
console.log(`Usage:
|
|
97
|
-
bivy agent add <id> --command <command> [--
|
|
109
|
+
bivy agent add <id> --command <command> [--args '["..."]'] [--name <name>] [--force]
|
|
110
|
+
ACP agent (default): --transport acp
|
|
111
|
+
Any other CLI: --transport process [--prompt-mode stdin|argv]
|
|
112
|
+
[--parser <id>] [--resume-args '["...","{id}"]']
|
|
98
113
|
bivy agent list [--json]
|
|
99
|
-
bivy agent remove <
|
|
114
|
+
bivy agent remove <id>
|
|
100
115
|
|
|
101
|
-
|
|
116
|
+
Agents are saved under agents: in <data-dir>/config.yaml and appear in the
|
|
117
|
+
picker right away. Edit that file directly for every adapter option.`);
|
|
102
118
|
return;
|
|
103
119
|
}
|
|
104
120
|
try {
|
|
105
121
|
if (command === "add") {
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
|
|
122
|
+
const id = agentId(args);
|
|
123
|
+
const spec = agentConfig(args, id);
|
|
124
|
+
const config = nodeConfig(dataDir);
|
|
125
|
+
const replaced = Boolean(config.agents?.[id]);
|
|
126
|
+
if (replaced && !args.includes("--force"))
|
|
127
|
+
throw new Error(`Agent ${id} is already configured; pass --force to replace it`);
|
|
128
|
+
if (!replaced) {
|
|
109
129
|
const runtimeModule = await import("./runtime/index.js");
|
|
110
|
-
const retained = runtimeModule.canonicalAgentId(
|
|
130
|
+
const retained = runtimeModule.canonicalAgentId(id);
|
|
111
131
|
if (retained)
|
|
112
|
-
throw new Error(`Agent id ${
|
|
113
|
-
}
|
|
114
|
-
const temp = fs.mkdtempSync(path.join(os.tmpdir(), "bivy-agent-add-"));
|
|
115
|
-
const file = path.join(temp, "bivy.plugin.yaml");
|
|
116
|
-
try {
|
|
117
|
-
fs.writeFileSync(file, stringifyYaml(manifest), { mode: 0o600 });
|
|
118
|
-
const result = installPlugin(file, { dataDir, force: args.includes("--force"), bivyVersion: currentBivyVersion() });
|
|
119
|
-
const body = { ok: true, id: manifest.metadata.id, path: result.path, replaced: result.replaced, restartRequired: true };
|
|
120
|
-
if (json)
|
|
121
|
-
console.log(JSON.stringify(body, null, 2));
|
|
122
|
-
else {
|
|
123
|
-
console.log(`${result.replaced ? "Updated" : "Added"} agent integration ${manifest.metadata.name} (${manifest.metadata.id}).`);
|
|
124
|
-
console.log("Restart Bivy to activate it: bivy restart");
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
finally {
|
|
128
|
-
fs.rmSync(temp, { recursive: true, force: true });
|
|
132
|
+
throw new Error(`Agent id ${id} conflicts with ${retained}; choose a unique id`);
|
|
129
133
|
}
|
|
134
|
+
writeNodeConfig(dataDir, { ...config, agents: { ...config.agents, [id]: spec } });
|
|
135
|
+
const body = { ok: true, id, path: nodeConfigPath(dataDir), replaced };
|
|
136
|
+
if (json)
|
|
137
|
+
console.log(JSON.stringify(body, null, 2));
|
|
138
|
+
else
|
|
139
|
+
console.log(`${replaced ? "Updated" : "Added"} ${"name" in spec ? spec.name : id} (${id}). It is in the agent picker now.`);
|
|
130
140
|
return;
|
|
131
141
|
}
|
|
132
142
|
if (command === "list" || command === "ls") {
|
|
133
|
-
const
|
|
143
|
+
const configured = Object.entries(readNodeConfig(dataDir)?.agents ?? {}).map(([id, spec]) => ({
|
|
144
|
+
id,
|
|
145
|
+
name: "adapter" in spec ? spec.name ?? id : spec.label ?? id,
|
|
146
|
+
source: "config",
|
|
147
|
+
transport: "adapter" in spec ? spec.adapter.kind : `extends ${spec.extends}`,
|
|
148
|
+
command: "adapter" in spec ? spec.adapter.command : spec.command ?? spec.extends,
|
|
149
|
+
}));
|
|
150
|
+
const packaged = listInstalledPlugins(dataDir).flatMap((plugin) => plugin.manifest?.contributes.agents.map((agent) => ({
|
|
134
151
|
id: agent.id,
|
|
135
152
|
name: agent.name,
|
|
136
|
-
|
|
137
|
-
version: plugin.manifest.metadata.version,
|
|
153
|
+
source: `${plugin.id}@${plugin.manifest.metadata.version}`,
|
|
138
154
|
transport: agent.adapter.kind,
|
|
139
155
|
command: agent.adapter.command,
|
|
140
|
-
|
|
156
|
+
...(plugin.errors.length ? { status: "invalid" } : {}),
|
|
141
157
|
})) ?? []);
|
|
158
|
+
const rows = [...configured, ...packaged];
|
|
142
159
|
if (json)
|
|
143
160
|
console.log(JSON.stringify({ ok: true, agents: rows }, null, 2));
|
|
144
161
|
else if (!rows.length)
|
|
145
|
-
console.log("No
|
|
162
|
+
console.log("No agents added on this machine. Add one with: bivy agent add <id> --command <command>");
|
|
146
163
|
else
|
|
147
164
|
for (const row of rows)
|
|
148
|
-
console.log(`${row.id}\t${row.transport}\t${row.command}\t${row.
|
|
165
|
+
console.log(`${row.id}\t${row.transport}\t${row.command}\t${row.source}`);
|
|
149
166
|
return;
|
|
150
167
|
}
|
|
151
168
|
if (command === "remove" || command === "rm") {
|
|
152
169
|
const id = positional(args)[1];
|
|
153
170
|
if (!id)
|
|
154
|
-
fail("Usage: bivy agent remove <
|
|
171
|
+
fail("Usage: bivy agent remove <id>", json);
|
|
172
|
+
const config = readNodeConfig(dataDir);
|
|
173
|
+
if (config?.agents?.[id]) {
|
|
174
|
+
const { [id]: _removed, ...agents } = config.agents;
|
|
175
|
+
writeNodeConfig(dataDir, { ...config, agents });
|
|
176
|
+
if (json)
|
|
177
|
+
console.log(JSON.stringify({ ok: true, id, removed: true }));
|
|
178
|
+
else
|
|
179
|
+
console.log(`Removed ${id}.`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Agents added before config.yaml held them were single-agent plugin packages.
|
|
155
183
|
const installed = listInstalledPlugins(dataDir);
|
|
156
184
|
const direct = installed.find((plugin) => plugin.id === id);
|
|
157
185
|
const matches = direct ? [direct] : installed.filter((plugin) => plugin.manifest?.contributes.agents.some((agent) => agent.id === id));
|
|
@@ -162,7 +190,7 @@ Adds an existing user-owned agent through the same declarative package contract
|
|
|
162
190
|
if (json)
|
|
163
191
|
console.log(JSON.stringify({ ok: true, id, packageId, removed }));
|
|
164
192
|
else
|
|
165
|
-
console.log(removed ? `Removed ${id} (${packageId})
|
|
193
|
+
console.log(removed ? `Removed ${id} (${packageId}).` : `Agent ${id} is not configured.`);
|
|
166
194
|
return;
|
|
167
195
|
}
|
|
168
196
|
fail(`Unknown agent command: ${command}`, json);
|
package/dist/node-config.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
7
|
+
import { parseAgentAdapter } from "./plugin-sdk/manifest.js";
|
|
7
8
|
export const NODE_CONFIG_FILE = "config.yaml";
|
|
8
9
|
export const NODE_CONFIG_VERSION = 1;
|
|
9
10
|
const ROOT_KEYS = ["version", "node", "defaults", "safety", "sessions", "github", "automation", "agents", "environment"];
|
|
@@ -170,6 +171,23 @@ export function validateNodeConfig(value) {
|
|
|
170
171
|
errors.push(`${at} must be an object`);
|
|
171
172
|
continue;
|
|
172
173
|
}
|
|
174
|
+
if (spec.adapter !== undefined) {
|
|
175
|
+
unknownKeys(spec, ["name", "description", "hidden", "authOwner", "adapter"], at, errors);
|
|
176
|
+
const adapter = parseAgentAdapter(spec.adapter, `${at}.adapter`, errors);
|
|
177
|
+
const authOwner = spec.authOwner;
|
|
178
|
+
if (authOwner !== undefined && authOwner !== "agent" && authOwner !== "bivy" && authOwner !== "mixed")
|
|
179
|
+
errors.push(`${at}.authOwner must be agent, bivy, or mixed`);
|
|
180
|
+
const name = optionalString(spec.name, `${at}.name`, errors, 120);
|
|
181
|
+
const description = optionalString(spec.description, `${at}.description`, errors, 500);
|
|
182
|
+
const hidden = optionalBoolean(spec.hidden, `${at}.hidden`, errors);
|
|
183
|
+
if (adapter)
|
|
184
|
+
agents[id] = { name, description, hidden, authOwner, adapter };
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (spec.extends === undefined) {
|
|
188
|
+
errors.push(`${at} needs an adapter (any ACP or CLI command) or extends (a maintained agent id)`);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
173
191
|
unknownKeys(spec, ["extends", "label", "command", "args", "jsonArgs", "parserId", "promptMode", "hidden"], at, errors);
|
|
174
192
|
const extend = optionalString(spec.extends, `${at}.extends`, errors, 80);
|
|
175
193
|
const list = (value, key) => {
|
package/dist/plugin-cli.js
CHANGED
|
@@ -247,13 +247,13 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
247
247
|
if (!input)
|
|
248
248
|
fail("Usage: bivy plugin install <path> [--force]", json);
|
|
249
249
|
const result = installPlugin(input, { dataDir, force: args.includes("--force") });
|
|
250
|
-
const body = { ok: true, installed: result.manifest.metadata.id, version: result.manifest.metadata.version, path: result.path, replaced: result.replaced
|
|
250
|
+
const body = { ok: true, installed: result.manifest.metadata.id, version: result.manifest.metadata.version, path: result.path, replaced: result.replaced };
|
|
251
251
|
if (json)
|
|
252
252
|
outputJson(body);
|
|
253
253
|
else {
|
|
254
254
|
console.log(`${result.replaced ? "Updated" : "Installed"} ${result.manifest.metadata.name} (${result.manifest.metadata.id}@${result.manifest.metadata.version}).`);
|
|
255
255
|
console.log(`Stored at ${result.path}`);
|
|
256
|
-
console.log("
|
|
256
|
+
console.log("It is in the agent picker now.");
|
|
257
257
|
}
|
|
258
258
|
return;
|
|
259
259
|
}
|
|
@@ -300,12 +300,11 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
300
300
|
const removed = removePlugin(id, dataDir);
|
|
301
301
|
if (!removed)
|
|
302
302
|
fail(`Plugin ${id} is not installed`, json);
|
|
303
|
-
const body = { ok: true, removed: id
|
|
303
|
+
const body = { ok: true, removed: id };
|
|
304
304
|
if (json)
|
|
305
305
|
outputJson(body);
|
|
306
306
|
else {
|
|
307
307
|
console.log(`Removed plugin ${id}.`);
|
|
308
|
-
console.log("Restart Bivy to unload it: bivy restart");
|
|
309
308
|
}
|
|
310
309
|
return;
|
|
311
310
|
}
|
|
@@ -179,6 +179,21 @@ function parseAcpAdapter(raw, at, errors) {
|
|
|
179
179
|
const args = stringList(raw.args, `${at}.args`, errors);
|
|
180
180
|
return command ? { kind: "acp", command, ...(args ? { args } : {}) } : undefined;
|
|
181
181
|
}
|
|
182
|
+
/** Validate one `adapter` block. Plugin manifests and node `config.yaml` agents
|
|
183
|
+
* share it, so an agent is described the same way wherever it is declared. */
|
|
184
|
+
export function parseAgentAdapter(value, at, errors) {
|
|
185
|
+
const raw = object(value);
|
|
186
|
+
if (!raw) {
|
|
187
|
+
errors.push(`${at} must be an object`);
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
190
|
+
if (raw.kind === "process")
|
|
191
|
+
return parseProcessAdapter(raw, at, errors);
|
|
192
|
+
if (raw.kind === "acp")
|
|
193
|
+
return parseAcpAdapter(raw, at, errors);
|
|
194
|
+
errors.push(`${at}.kind must be process or acp`);
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
182
197
|
function parseAgent(value, index, errors) {
|
|
183
198
|
const at = `contributes.agents[${index}]`;
|
|
184
199
|
const raw = object(value);
|
|
@@ -198,16 +213,7 @@ function parseAgent(value, index, errors) {
|
|
|
198
213
|
if (authOwner !== undefined && authOwner !== "agent" && authOwner !== "bivy" && authOwner !== "mixed") {
|
|
199
214
|
errors.push(`${at}.authOwner must be agent, bivy, or mixed`);
|
|
200
215
|
}
|
|
201
|
-
const
|
|
202
|
-
let adapter;
|
|
203
|
-
if (!adapterRaw)
|
|
204
|
-
errors.push(`${at}.adapter must be an object`);
|
|
205
|
-
else if (adapterRaw.kind === "process")
|
|
206
|
-
adapter = parseProcessAdapter(adapterRaw, `${at}.adapter`, errors);
|
|
207
|
-
else if (adapterRaw.kind === "acp")
|
|
208
|
-
adapter = parseAcpAdapter(adapterRaw, `${at}.adapter`, errors);
|
|
209
|
-
else
|
|
210
|
-
errors.push(`${at}.adapter.kind must be process or acp`);
|
|
216
|
+
const adapter = parseAgentAdapter(raw.adapter, `${at}.adapter`, errors);
|
|
211
217
|
if (!id || !name || !adapter)
|
|
212
218
|
return undefined;
|
|
213
219
|
return {
|
|
@@ -351,5 +357,7 @@ export function recommendedBivyRange(currentVersion) {
|
|
|
351
357
|
const parsed = parseSemver(currentVersion);
|
|
352
358
|
if (!parsed)
|
|
353
359
|
throw new Error(`Cannot generate a compatibility range from invalid Bivy version ${currentVersion}`);
|
|
354
|
-
|
|
360
|
+
// The manifest contract is versioned by apiVersion, so a plugin keeps working
|
|
361
|
+
// across Bivy minor releases; only a major release may break it.
|
|
362
|
+
return `>=${parsed.major}.${parsed.minor}.${parsed.patch} <${parsed.major + 1}.0.0`;
|
|
355
363
|
}
|
|
@@ -241,6 +241,7 @@ export function bivyProtocolParser() {
|
|
|
241
241
|
// appended verbatim (a shim emits an explicit break when it wants one), so it
|
|
242
242
|
// opts out of inferred segment boundaries.
|
|
243
243
|
const acc = new TurnAccumulator({ provider: "bivy-protocol", protocol: "protocol" }, false);
|
|
244
|
+
let sessionRef;
|
|
244
245
|
return {
|
|
245
246
|
onLine(line) {
|
|
246
247
|
const events = [];
|
|
@@ -255,9 +256,23 @@ export function bivyProtocolParser() {
|
|
|
255
256
|
return events; // ignore non-JSON noise (banners etc.)
|
|
256
257
|
}
|
|
257
258
|
const type = String(msg.type ?? "");
|
|
258
|
-
if (type === "
|
|
259
|
+
if (type === "session.started") {
|
|
260
|
+
// The adapter's own conversation id; `resume.args` gets it back as {id}.
|
|
261
|
+
if (typeof msg.sessionId === "string" && msg.sessionId.trim())
|
|
262
|
+
sessionRef = msg.sessionId.trim();
|
|
263
|
+
}
|
|
264
|
+
else if (type === "message.delta") {
|
|
259
265
|
acc.appendText(String(msg.text ?? ""), events);
|
|
260
266
|
}
|
|
267
|
+
else if (type === "message.reasoning") {
|
|
268
|
+
acc.appendReasoning(String(msg.text ?? ""), events);
|
|
269
|
+
}
|
|
270
|
+
else if (type === "usage") {
|
|
271
|
+
const tokens = extractTokenUsage(msg.tokens ?? msg);
|
|
272
|
+
const costUsd = typeof msg.costUsd === "number" ? { costUsd: msg.costUsd } : {};
|
|
273
|
+
if (tokens || costUsd.costUsd !== undefined)
|
|
274
|
+
acc.addUsage({ ...tokens, ...costUsd });
|
|
275
|
+
}
|
|
261
276
|
else if (type === "session.status") {
|
|
262
277
|
const status = String(msg.status ?? "");
|
|
263
278
|
if (status === "working")
|
|
@@ -288,6 +303,8 @@ export function bivyProtocolParser() {
|
|
|
288
303
|
return events;
|
|
289
304
|
},
|
|
290
305
|
messages: () => acc.history(),
|
|
306
|
+
usage: () => acc.usageSnapshot,
|
|
307
|
+
sessionRef: () => sessionRef,
|
|
291
308
|
};
|
|
292
309
|
}
|
|
293
310
|
/** Parser for Claude Code CLI `--output-format stream-json`. */
|
package/dist/runtime/index.js
CHANGED
|
@@ -55,6 +55,8 @@ import { ProtocolRuntime, protocolRuntimeFromEnv, protocolCommandsFromEnv } from
|
|
|
55
55
|
import { codexSlashCommands, opencodeSlashCommands } from "./slash-commands.js";
|
|
56
56
|
import { withExactCapabilitySurface } from "./types.js";
|
|
57
57
|
import { installedAgentContributions } from "../plugins/store.js";
|
|
58
|
+
import { readNodeConfig } from "../node-config.js";
|
|
59
|
+
import { defaultDataDir } from "../data-dir.js";
|
|
58
60
|
import { forkCapsFromInfo } from "../session/fork-matrix.js";
|
|
59
61
|
import { currentBivyVersion } from "../app-version.js";
|
|
60
62
|
const PREFLIGHT_BEHAVIORS = {
|
|
@@ -116,54 +118,81 @@ function genericCliInfo() {
|
|
|
116
118
|
: "Set BIVY_AGENT_COMMAND to enable this universal CLI runtime.",
|
|
117
119
|
};
|
|
118
120
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
/**
|
|
122
|
+
* The node's own agents, read from `<data-dir>/config.yaml` on every registry
|
|
123
|
+
* build so an edit or `bivy agent add` shows up without a restart. The legacy
|
|
124
|
+
* BIVY_CUSTOM_AGENTS projection is used only on a node with no config.yaml.
|
|
125
|
+
*/
|
|
126
|
+
function configuredAgents(dataDir = defaultDataDir()) {
|
|
127
|
+
try {
|
|
128
|
+
const config = readNodeConfig(dataDir);
|
|
129
|
+
if (config)
|
|
130
|
+
return { agents: Object.entries(config.agents ?? {}) };
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
return { agents: [], error: error instanceof Error ? error.message : String(error) };
|
|
134
|
+
}
|
|
121
135
|
const raw = process.env.BIVY_CUSTOM_AGENTS?.trim();
|
|
122
136
|
if (!raw)
|
|
123
|
-
return
|
|
137
|
+
return { agents: [] };
|
|
124
138
|
try {
|
|
125
139
|
const parsed = JSON.parse(raw);
|
|
126
140
|
if (!Array.isArray(parsed))
|
|
127
|
-
return
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (!/^[a-z][a-z0-9-]{1,47}$/.test(id) || out.has(id))
|
|
134
|
-
continue;
|
|
135
|
-
if (typeof item.extends !== "string" || !isAgentProfileId(item.extends))
|
|
136
|
-
continue;
|
|
137
|
-
const base = AGENT_PROFILES[item.extends];
|
|
138
|
-
const strings = (v) => Array.isArray(v) && v.every((x) => typeof x === "string") ? v : undefined;
|
|
139
|
-
out.set(id, {
|
|
140
|
-
...base,
|
|
141
|
-
displayName: typeof item.label === "string" && item.label.trim() ? item.label.trim() : id,
|
|
142
|
-
command: typeof item.command === "string" && item.command.trim() ? item.command.trim() : base.command,
|
|
143
|
-
...(strings(item.args) ? { args: strings(item.args) } : {}),
|
|
144
|
-
...(strings(item.jsonArgs) ? { jsonArgs: strings(item.jsonArgs) } : {}),
|
|
145
|
-
...(typeof item.parserId === "string" ? { parserId: item.parserId } : {}),
|
|
146
|
-
...(item.promptMode === "stdin" || item.promptMode === "argv" ? { promptMode: item.promptMode } : {}),
|
|
147
|
-
hidden: item.hidden === true,
|
|
148
|
-
supportTier: "experimental",
|
|
149
|
-
testedVersion: undefined,
|
|
150
|
-
install: undefined,
|
|
151
|
-
// `extends` reuses declarative launch syntax, never maintained host code.
|
|
152
|
-
// A different executable must not inherit Codex/OpenCode/Grok credential,
|
|
153
|
-
// store, slash-command, or native-discovery behavior by accident.
|
|
154
|
-
behaviors: undefined,
|
|
155
|
-
...(base.resume ? { resume: { template: base.resume.template, ...(base.resume.newTemplate ? { newTemplate: base.resume.newTemplate } : {}) } } : {}),
|
|
156
|
-
});
|
|
157
|
-
}
|
|
141
|
+
return { agents: [] };
|
|
142
|
+
return {
|
|
143
|
+
agents: parsed
|
|
144
|
+
.filter((item) => Boolean(item) && typeof item.id === "string")
|
|
145
|
+
.map(({ id, ...spec }) => [id.trim().toLowerCase(), spec]),
|
|
146
|
+
};
|
|
158
147
|
}
|
|
159
148
|
catch {
|
|
160
149
|
// Invalid operator configuration is ignored; packaged integrations remain usable.
|
|
150
|
+
return { agents: [] };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function extendsAgentSpec(id, item) {
|
|
154
|
+
if (!isAgentProfileId(item.extends))
|
|
155
|
+
return undefined;
|
|
156
|
+
const base = AGENT_PROFILES[item.extends];
|
|
157
|
+
const strings = (v) => Array.isArray(v) && v.every((x) => typeof x === "string") ? v : undefined;
|
|
158
|
+
return {
|
|
159
|
+
...base,
|
|
160
|
+
displayName: typeof item.label === "string" && item.label.trim() ? item.label.trim() : id,
|
|
161
|
+
command: typeof item.command === "string" && item.command.trim() ? item.command.trim() : base.command,
|
|
162
|
+
...(strings(item.args) ? { args: strings(item.args) } : {}),
|
|
163
|
+
...(strings(item.jsonArgs) ? { jsonArgs: strings(item.jsonArgs) } : {}),
|
|
164
|
+
...(typeof item.parserId === "string" ? { parserId: item.parserId } : {}),
|
|
165
|
+
...(item.promptMode === "stdin" || item.promptMode === "argv" ? { promptMode: item.promptMode } : {}),
|
|
166
|
+
hidden: item.hidden === true,
|
|
167
|
+
supportTier: "experimental",
|
|
168
|
+
testedVersion: undefined,
|
|
169
|
+
install: undefined,
|
|
170
|
+
// `extends` reuses declarative launch syntax, never maintained host code.
|
|
171
|
+
// A different executable must not inherit Codex/OpenCode/Grok credential,
|
|
172
|
+
// store, slash-command, or native-discovery behavior by accident.
|
|
173
|
+
behaviors: undefined,
|
|
174
|
+
...(base.resume ? { resume: { template: base.resume.template, ...(base.resume.newTemplate ? { newTemplate: base.resume.newTemplate } : {}) } } : {}),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function customAgentSpecs(dataDir) {
|
|
178
|
+
const out = new Map();
|
|
179
|
+
for (const [id, item] of configuredAgents(dataDir).agents) {
|
|
180
|
+
if (!/^[a-z][a-z0-9-]{1,47}$/.test(id) || out.has(id) || !item || typeof item !== "object")
|
|
181
|
+
continue;
|
|
182
|
+
const spec = "adapter" in item
|
|
183
|
+
? adapterAgentSpec({ name: item.name || id, ...item }, "Configured on this machine")
|
|
184
|
+
: extendsAgentSpec(id, item);
|
|
185
|
+
if (spec)
|
|
186
|
+
out.set(id, spec);
|
|
161
187
|
}
|
|
162
188
|
return out;
|
|
163
189
|
}
|
|
164
190
|
export function pluginAgentConflictDiagnostics(dataDir) {
|
|
165
191
|
const installed = installedAgentContributions(dataDir);
|
|
166
192
|
const errors = [...installed.errors];
|
|
193
|
+
const configError = configuredAgents(dataDir).error;
|
|
194
|
+
if (configError)
|
|
195
|
+
errors.push(configError);
|
|
167
196
|
for (const conflict of agentRegistry(dataDir).diagnostics()) {
|
|
168
197
|
if (conflict.rejectedOrigin.kind !== "package" || conflict.rejectedOrigin.location !== "installed")
|
|
169
198
|
continue;
|
|
@@ -171,18 +200,18 @@ export function pluginAgentConflictDiagnostics(dataDir) {
|
|
|
171
200
|
}
|
|
172
201
|
return errors;
|
|
173
202
|
}
|
|
174
|
-
/** Translate
|
|
175
|
-
|
|
176
|
-
|
|
203
|
+
/** Translate one `adapter` declaration (plugin manifest or config.yaml) into
|
|
204
|
+
* the shared CLI/ACP launch shape. */
|
|
205
|
+
function adapterAgentSpec(agent, packageName) {
|
|
177
206
|
const common = {
|
|
178
207
|
displayName: agent.name,
|
|
179
208
|
command: agent.adapter.command,
|
|
180
|
-
packageName
|
|
209
|
+
packageName,
|
|
181
210
|
promptMode: "stdin",
|
|
182
211
|
hidden: agent.hidden === true,
|
|
183
212
|
supportTier: "experimental",
|
|
184
213
|
authOwner: agent.authOwner ?? "agent",
|
|
185
|
-
blurb: agent.description ??
|
|
214
|
+
blurb: agent.description ?? `${packageName}.`,
|
|
186
215
|
install: undefined,
|
|
187
216
|
};
|
|
188
217
|
if (agent.adapter.kind === "acp") {
|
|
@@ -982,10 +1011,11 @@ function agentRegistry(pluginDataDir) {
|
|
|
982
1011
|
}
|
|
983
1012
|
// Machine configuration and installed packages use the exact same lifecycle.
|
|
984
1013
|
// Earlier registrations win, and every retained/rejected origin remains explicit.
|
|
985
|
-
for (const [id, spec] of customAgentSpecs())
|
|
1014
|
+
for (const [id, spec] of customAgentSpecs(pluginDataDir))
|
|
986
1015
|
registry.register(cliRegistration(id, spec, { kind: "config" }));
|
|
987
1016
|
for (const contribution of installedAgentContributions(pluginDataDir).agents) {
|
|
988
|
-
|
|
1017
|
+
const { agent, pluginName } = contribution;
|
|
1018
|
+
registry.register(cliRegistration(agent.id, adapterAgentSpec({ ...agent, description: agent.description ?? `Agent contributed by ${pluginName}.` }, `${pluginName} plugin`), {
|
|
989
1019
|
kind: "package",
|
|
990
1020
|
packageId: contribution.pluginId,
|
|
991
1021
|
packageVersion: contribution.pluginVersion,
|
package/dist/server.js
CHANGED
|
@@ -240,9 +240,6 @@ try {
|
|
|
240
240
|
if (process.env[key] === undefined)
|
|
241
241
|
process.env[key] = value;
|
|
242
242
|
}
|
|
243
|
-
if (canonicalNodeConfig.agents && process.env.BIVY_CUSTOM_AGENTS === undefined) {
|
|
244
|
-
process.env.BIVY_CUSTOM_AGENTS = JSON.stringify(Object.entries(canonicalNodeConfig.agents).map(([id, spec]) => ({ id, ...spec })));
|
|
245
|
-
}
|
|
246
243
|
if (canonicalNodeConfig.automation?.checks && process.env.BIVY_AUTOMATION_CHECKS === undefined) {
|
|
247
244
|
process.env.BIVY_AUTOMATION_CHECKS = JSON.stringify(canonicalNodeConfig.automation.checks);
|
|
248
245
|
}
|
package/package.json
CHANGED