@bivy/bivy 0.21.1 → 0.21.2-staging.10
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/archive-install.mjs +150 -0
- package/bin/bivy.mjs +66 -7
- package/bin/cli-commands.mjs +1 -1
- package/bin/guides/add-an-agent.md +39 -0
- package/bin/prune-sessions.mjs +46 -14
- package/dist/agent-cli.js +91 -63
- package/dist/agents/claude-code/runtime.js +19 -10
- package/dist/apps/styles.css +1 -50
- package/dist/automation-reseal.js +2 -0
- package/dist/node-config.js +18 -0
- package/dist/node-update.js +26 -1
- 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 +38 -34
- package/dist/worktree.js +17 -0
- package/package.json +1 -1
- package/web/assets/AutomationsView-DRwMTpsz.js +51 -0
- package/web/assets/{ChangesCard-CxQKGf6b.js → ChangesCard-C5rvJ8Cq.js} +3 -3
- package/web/assets/{LibraryView-D3uu06xU.js → LibraryView-CIJ-Pfv-.js} +1 -1
- package/web/assets/ProviderConnect-DiFSOT6L.js +1 -0
- package/web/assets/SessionList-CxRibKT5.js +1 -0
- package/web/assets/{Settings-DrK5DKuy.js → Settings-BukJBTwA.js} +8 -8
- package/web/assets/{Sheet-C8gx0OoI.js → Sheet-CfbcQeWy.js} +1 -1
- package/web/assets/{Terminal-DrqMIIhr.js → Terminal-7Q0Os9n9.js} +5 -5
- package/web/assets/{VoiceSettings-CBBuyKN7.js → VoiceSettings-DwIi92pR.js} +1 -1
- package/web/assets/{abnfDiagram-VCTEODGH-CgIjZZoL.js → abnfDiagram-VCTEODGH-Czjfvfve.js} +1 -1
- package/web/assets/architecture-7GRP2DOG-DD5qRKOE.js +1 -0
- package/web/assets/{architectureDiagram-5GKGNRK7-BMOlU5hT.js → architectureDiagram-5GKGNRK7-DYay_IWI.js} +1 -1
- package/web/assets/{blockDiagram-I7D4REHJ-DkwymH1p.js → blockDiagram-I7D4REHJ-CPlmINAT.js} +1 -1
- package/web/assets/{c4Diagram-7LVT6UL2-B0UDgtWS.js → c4Diagram-7LVT6UL2-DYcLdJNN.js} +1 -1
- package/web/assets/channel-BUg-Y_4E.js +1 -0
- package/web/assets/{chunk-4HAMMTFA-1y76NzcS.js → chunk-4HAMMTFA-CdyB2GrZ.js} +1 -1
- package/web/assets/{chunk-75Z2AOVW-CPFkG_FO.js → chunk-75Z2AOVW-D7h9tbn8.js} +1 -1
- package/web/assets/{chunk-DU6HZSFF-Dy4LQ-iK.js → chunk-DU6HZSFF-BISdfBlT.js} +1 -1
- package/web/assets/{chunk-F27PBJKO-cjpOzWOK.js → chunk-F27PBJKO-2JkB2zU9.js} +1 -1
- package/web/assets/{chunk-GMAD6QVW-CpYC8bxK.js → chunk-GMAD6QVW-BfxS2p5H.js} +1 -1
- package/web/assets/{chunk-GVQU2GXP-DUT271z8.js → chunk-GVQU2GXP-vBPS78dh.js} +1 -1
- package/web/assets/{chunk-IMKFNOWR-C-G3w5yT.js → chunk-IMKFNOWR-C1PvbEPt.js} +1 -1
- package/web/assets/{chunk-L3NEJ4N5-C5N0po6u.js → chunk-L3NEJ4N5-wug0ZtiI.js} +1 -1
- package/web/assets/{chunk-OSK3NFVY-7_vZDCyQ.js → chunk-OSK3NFVY-CSx22WTp.js} +1 -1
- package/web/assets/{chunk-P2QGCYS3-BhXxw6WP.js → chunk-P2QGCYS3-BEJVL1Uw.js} +1 -1
- package/web/assets/{chunk-POPQ4Y6H-iw5O0upC.js → chunk-POPQ4Y6H-DuEsyMhL.js} +1 -1
- package/web/assets/{chunk-PWAF6VOD-C8u0Giuo.js → chunk-PWAF6VOD-DVLOKu0r.js} +1 -1
- package/web/assets/{chunk-SHT3W25Y-BM_QeCu-.js → chunk-SHT3W25Y-Opayr0C_.js} +1 -1
- package/web/assets/{chunk-SVP7TREG-cLDkReFI.js → chunk-SVP7TREG-DgHJUbyu.js} +1 -1
- package/web/assets/{chunk-TICWLB2K-C8fbKbO_.js → chunk-TICWLB2K-DOOCLkvY.js} +1 -1
- package/web/assets/classDiagram-ZZMXUADV-DPpdYhAI.js +1 -0
- package/web/assets/classDiagram-v2-VYDZK3BY-DPpdYhAI.js +1 -0
- package/web/assets/{cynefin-OW5HDTMX-6kzYNsQ0.js → cynefin-OW5HDTMX-1MBj3btC.js} +1 -1
- package/web/assets/{cynefinDiagram-5FMLGOSQ-B3tl5cyI.js → cynefinDiagram-5FMLGOSQ-Cc6rrG7K.js} +1 -1
- package/web/assets/{dagre-GXQ25YYZ-mHX4cPQQ.js → dagre-GXQ25YYZ-DFG3Ndtk.js} +1 -1
- package/web/assets/{diagram-S7CK7UJ4-DhOtHdTh.js → diagram-S7CK7UJ4-SiM6_pqS.js} +1 -1
- package/web/assets/{diagram-UQ7AKVKN-B6BD-P5q.js → diagram-UQ7AKVKN-BUI0Ju7Z.js} +1 -1
- package/web/assets/{diagram-VSXAHHWV-DWXWifeJ.js → diagram-VSXAHHWV-CXMYZkbm.js} +1 -1
- package/web/assets/{diagram-VX7I27RA-DcU0vblN.js → diagram-VX7I27RA-CNCbmY76.js} +1 -1
- package/web/assets/{diagram-Z3DM3KII-Tc32VaTG.js → diagram-Z3DM3KII-Cf3mrdyV.js} +1 -1
- package/web/assets/{ebnfDiagram-PWID7BFC-fbSC7Ytl.js → ebnfDiagram-PWID7BFC-Ban7TN8N.js} +1 -1
- package/web/assets/{erDiagram-RLTQ6QDP-VCd4vHPL.js → erDiagram-RLTQ6QDP-yDvXNUaN.js} +1 -1
- package/web/assets/eventmodeling-NTZA5JFV-BktxSOvh.js +1 -0
- package/web/assets/flowDiagram-HODETNUW-CuT_k3cJ.js +1 -0
- package/web/assets/{ganttDiagram-EL5Y4UJY-qlPJPHC9.js → ganttDiagram-EL5Y4UJY-CRtk9R_T.js} +1 -1
- package/web/assets/{gitGraph-4MIJSDKK-Frn6_UlZ.js → gitGraph-4MIJSDKK-DVCE-pa5.js} +1 -1
- package/web/assets/{gitGraphDiagram-WWUBYQGX-CJv1U3iy.js → gitGraphDiagram-WWUBYQGX-BGNEoPtQ.js} +1 -1
- package/web/assets/{index-DoVsvSyc.js → index-Bkcd452U.js} +2 -2
- package/web/assets/index-CkoStrAe.css +1 -0
- package/web/assets/{info-A6RAGUB7-x2ehGecF.js → info-A6RAGUB7-CmlSnIUY.js} +1 -1
- package/web/assets/{infoDiagram-27XIBGKW-CzHIphTX.js → infoDiagram-27XIBGKW-CV6e39nj.js} +1 -1
- package/web/assets/{ishikawaDiagram-5VMMS53U-1aIpAo5f.js → ishikawaDiagram-5VMMS53U-BZ317iz5.js} +1 -1
- package/web/assets/{journeyDiagram-3NMN7TZE-DZFs2eed.js → journeyDiagram-3NMN7TZE-aRfKE67B.js} +1 -1
- package/web/assets/{kanban-definition-UXKFOSKX-CFPF7vk4.js → kanban-definition-UXKFOSKX-DRAkPvek.js} +1 -1
- package/web/assets/{mermaid-parser.core-eET97OnM.js → mermaid-parser.core-BzKTg0uA.js} +3 -3
- package/web/assets/{mermaid.core-D-l2iyMD.js → mermaid.core-cF9385Jc.js} +4 -4
- package/web/assets/{mindmap-definition-YA3MSWOX-CzO4WiUd.js → mindmap-definition-YA3MSWOX-CLiDn2-4.js} +1 -1
- package/web/assets/mount-DdwNnK_L.js +36 -0
- package/web/assets/{packet-AYTQ26CC-DiT50-0V.js → packet-AYTQ26CC-C_A2hl_f.js} +1 -1
- package/web/assets/{pegDiagram-XKGWAZYB-DG3Ad8mS.js → pegDiagram-XKGWAZYB-DzWlf-iX.js} +1 -1
- package/web/assets/{pie-WAS4IAKB-BGJZoyxN.js → pie-WAS4IAKB-C8_wbVGt.js} +1 -1
- package/web/assets/{pieDiagram-E7YTZNPT-C_IdKjBF.js → pieDiagram-E7YTZNPT-CqlAZb6C.js} +1 -1
- package/web/assets/{quadrantDiagram-AXDQQJYC-CVvfK1li.js → quadrantDiagram-AXDQQJYC-BmL5gqum.js} +1 -1
- package/web/assets/{radar-RG4KPBEZ-C14GlZJM.js → radar-RG4KPBEZ-wXteP-ND.js} +1 -1
- package/web/assets/{railroad-74A4TZTK-Cg3OXIZS.js → railroad-74A4TZTK-CiAiQ98V.js} +1 -1
- package/web/assets/railroad-abnf-HS5TGJTU-7KYSR92O.js +1 -0
- package/web/assets/railroad-ebnf-LZEXJU2U-Dt8zrJqM.js +1 -0
- package/web/assets/railroad-peg-WCYAUIDC-NioqZSpU.js +1 -0
- package/web/assets/{railroadDiagram-O6MQD6OU-_oTlqB8E.js → railroadDiagram-O6MQD6OU-m83Fr2E6.js} +1 -1
- package/web/assets/{requirementDiagram-BXWQKSXE-6_cpGq7p.js → requirementDiagram-BXWQKSXE-DdiUlbNn.js} +1 -1
- package/web/assets/{sankeyDiagram-P5KCCOFB-CIDdTiIM.js → sankeyDiagram-P5KCCOFB-DgRmQoC3.js} +1 -1
- package/web/assets/{sequenceDiagram-WJ2MYXX4-OmglouV4.js → sequenceDiagram-WJ2MYXX4-CLvzUry_.js} +1 -1
- package/web/assets/{settingsRoute-Y4aQ907P.js → settingsRoute-CY886TNp.js} +1 -1
- package/web/assets/{stateDiagram-D77RDMKH-lmR5zsjq.js → stateDiagram-D77RDMKH-CXEsod-X.js} +1 -1
- package/web/assets/stateDiagram-v2-MP3YSRHH-sOazOE8m.js +1 -0
- package/web/assets/{swimlanes-42K2YHIH-BRP1y0vF.js → swimlanes-42K2YHIH-BIXrBhfr.js} +1 -1
- package/web/assets/swimlanesDiagram-VR7AAH4N-CEQROFH_.js +8 -0
- package/web/assets/{timeline-definition-24CTP7MA-74uE748S.js → timeline-definition-24CTP7MA-CHNGDuRJ.js} +1 -1
- package/web/assets/{treeView-Q6P3EWNA-C7rSzN0q.js → treeView-Q6P3EWNA-B5PrKFUx.js} +1 -1
- package/web/assets/{treemap-WGGIJYW6-B-Eqh7wJ.js → treemap-WGGIJYW6-CyfiLDp3.js} +1 -1
- package/web/assets/useStore-BirjskHz.js +16 -0
- package/web/assets/{vennDiagram-4TSXK5OY-9HXkWxEw.js → vennDiagram-4TSXK5OY-DsQXSlTg.js} +1 -1
- package/web/assets/{wardley-WFR3VGLG-Bvwh4mZv.js → wardley-WFR3VGLG-BzGhnDYD.js} +1 -1
- package/web/assets/{wardleyDiagram-VM6X3IG4-D_t1chE2.js → wardleyDiagram-VM6X3IG4-BIxMxWJH.js} +1 -1
- package/web/assets/{xychartDiagram-S5SC5T6Z-BwaxhGtz.js → xychartDiagram-S5SC5T6Z-B6m5ol80.js} +1 -1
- package/web/index.html +2 -2
- package/web/sw.js +1 -1
- package/web/assets/AutomationsView-Cw3-fec-.js +0 -51
- package/web/assets/ProviderConnect-BVnL5_ML.js +0 -1
- package/web/assets/SessionList-qypmhsdJ.js +0 -1
- package/web/assets/architecture-7GRP2DOG-DjnOFWJ6.js +0 -1
- package/web/assets/channel-B7AEPkF6.js +0 -1
- package/web/assets/classDiagram-ZZMXUADV-BEtpnW4V.js +0 -1
- package/web/assets/classDiagram-v2-VYDZK3BY-BEtpnW4V.js +0 -1
- package/web/assets/eventmodeling-NTZA5JFV-CYiUVM8V.js +0 -1
- package/web/assets/flowDiagram-HODETNUW-kSHsw5bd.js +0 -1
- package/web/assets/index-CtJDDeFG.css +0 -1
- package/web/assets/mount-Cnz2WLxO.js +0 -36
- package/web/assets/railroad-abnf-HS5TGJTU-CzpKO8Mq.js +0 -1
- package/web/assets/railroad-ebnf-LZEXJU2U-BBhUgg4O.js +0 -1
- package/web/assets/railroad-peg-WCYAUIDC-C2WJ96PH.js +0 -1
- package/web/assets/stateDiagram-v2-MP3YSRHH-DJ79XS5i.js +0 -1
- package/web/assets/swimlanesDiagram-VR7AAH4N-D08eaEQz.js +0 -8
- package/web/assets/useStore-TM8ZVr6X.js +0 -16
|
@@ -691,6 +691,8 @@ class ClaudeSession {
|
|
|
691
691
|
messages = [];
|
|
692
692
|
streaming = false;
|
|
693
693
|
name;
|
|
694
|
+
/** Set by the runtime so a disposed session leaves its registry. */
|
|
695
|
+
onDispose;
|
|
694
696
|
models = [];
|
|
695
697
|
currentModel;
|
|
696
698
|
desiredModel;
|
|
@@ -1390,6 +1392,7 @@ class ClaudeSession {
|
|
|
1390
1392
|
}
|
|
1391
1393
|
}
|
|
1392
1394
|
this.emitter.removeAllListeners();
|
|
1395
|
+
this.onDispose?.();
|
|
1393
1396
|
}
|
|
1394
1397
|
async getModels() {
|
|
1395
1398
|
// Re-query the live agent so the picker reflects the current lineup instead
|
|
@@ -1515,6 +1518,16 @@ export class ClaudeCodeRuntime {
|
|
|
1515
1518
|
constructor(options = {}) {
|
|
1516
1519
|
this.options = options;
|
|
1517
1520
|
}
|
|
1521
|
+
/** Each session holds its whole transcript, so it stays registered only
|
|
1522
|
+
* while open; a closed one would otherwise be kept for the daemon's lifetime. */
|
|
1523
|
+
track(session) {
|
|
1524
|
+
this.sessions.push(session);
|
|
1525
|
+
session.onDispose = () => {
|
|
1526
|
+
const i = this.sessions.indexOf(session);
|
|
1527
|
+
if (i >= 0)
|
|
1528
|
+
this.sessions.splice(i, 1);
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1518
1531
|
/** Claude Code runs Anthropic models; the authoritative list comes from the
|
|
1519
1532
|
* live query per session, so the session-less catalog is the known lineup. */
|
|
1520
1533
|
listCatalog() {
|
|
@@ -1522,12 +1535,12 @@ export class ClaudeCodeRuntime {
|
|
|
1522
1535
|
}
|
|
1523
1536
|
async createSession(options) {
|
|
1524
1537
|
const session = new ClaudeSession({ ...(await withSessionCredentials(this.options, options.credentialLabels)), instructions: options.instructions?.text }, options.workspace, options.toolInterceptor, options.toolProvider);
|
|
1525
|
-
this.
|
|
1538
|
+
this.track(session);
|
|
1526
1539
|
return { session };
|
|
1527
1540
|
}
|
|
1528
1541
|
async openSession(options) {
|
|
1529
1542
|
const session = new ClaudeSession({ ...(await withSessionCredentials(this.options, options.credentialLabels)), instructions: options.instructions?.text }, options.workspace, options.toolInterceptor, options.toolProvider, options.sessionFile);
|
|
1530
|
-
this.
|
|
1543
|
+
this.track(session);
|
|
1531
1544
|
return {
|
|
1532
1545
|
session,
|
|
1533
1546
|
warning: "Resumed Claude Code session history is replayed by the agent on the next turn; prior messages are not preloaded.",
|
|
@@ -1732,15 +1745,11 @@ export class ClaudeCodeRuntime {
|
|
|
1732
1745
|
// Drop (and tear down) any in-memory handle so the fallback branch of
|
|
1733
1746
|
// listSessions (used when no sessionsDir is set) can't re-advertise it, and a
|
|
1734
1747
|
// still-live child doesn't outlive the delete.
|
|
1735
|
-
for (
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
try {
|
|
1739
|
-
s.dispose();
|
|
1740
|
-
}
|
|
1741
|
-
catch { /* already disposed by the caller's close */ }
|
|
1742
|
-
this.sessions.splice(i, 1);
|
|
1748
|
+
for (const s of this.sessions.filter((s) => s.id === sessionId || (sessionFile && s.sessionFile === sessionFile))) {
|
|
1749
|
+
try {
|
|
1750
|
+
s.dispose();
|
|
1743
1751
|
}
|
|
1752
|
+
catch { /* already disposed by the caller's close */ }
|
|
1744
1753
|
}
|
|
1745
1754
|
// Prefer the exact path the SDK reports (that's the file it would re-read on
|
|
1746
1755
|
// the next list); fall back to locating the transcript by id across Claude's
|
package/dist/apps/styles.css
CHANGED
|
@@ -581,6 +581,7 @@ input, textarea, select { font-family: inherit; font-size: 16px; }
|
|
|
581
581
|
large remote image can't blow out the chat column; rounded to match chips. */
|
|
582
582
|
.msg.assistant img { max-width: 100%; height: auto; border-radius: var(--radius-md); }
|
|
583
583
|
.msg.assistant img.md-image { max-height: 420px; object-fit: contain; }
|
|
584
|
+
.msg.assistant img.md-image[src] { cursor: zoom-in; }
|
|
584
585
|
/* Before ChatView's hydrate effect resolves data-remote-src to a fetched blob:
|
|
585
586
|
URL (or if the node's fetch fails/is disallowed), the element has no `src` and
|
|
586
587
|
would otherwise collapse to nothing — give it a visible placeholder box so a
|
|
@@ -1102,13 +1103,6 @@ button.sheet-action:last-child { border-bottom: 0; }
|
|
|
1102
1103
|
.composer-btn.stop { background: var(--ink); color: var(--bg); }
|
|
1103
1104
|
.composer-btn.stop.is-stopping { width: auto; border-radius: var(--radius-full); padding-inline: var(--space-3); display: inline-flex; gap: var(--space-2); }
|
|
1104
1105
|
.composer-btn.stop:active { transform: scale(0.92); }
|
|
1105
|
-
/* "Steer current turn": an explicit interrupt, distinct from the default Send
|
|
1106
|
-
(which queues while busy — see FollowupQueue). --accent is already a warm
|
|
1107
|
-
orange (the Send button), so this borrows --unseen's blue instead — a color
|
|
1108
|
-
used nowhere else in the composer — to read as clearly "not a normal send." */
|
|
1109
|
-
.composer-btn.steer { background: var(--unseen); color: var(--unseen-contrast); }
|
|
1110
|
-
.composer-btn.steer:hover { filter: brightness(1.1); }
|
|
1111
|
-
.composer-btn.steer:active { transform: scale(0.92); }
|
|
1112
1106
|
.attach-pill { padding: 4px 8px; }
|
|
1113
1107
|
|
|
1114
1108
|
/* Voice-input trigger: a circular icon button seated just left of Send. */
|
|
@@ -1181,49 +1175,6 @@ button.sheet-action:last-child { border-bottom: 0; }
|
|
|
1181
1175
|
.attach-thumb-btn { flex: none; border: 0; padding: 0; background: none; cursor: zoom-in; line-height: 0; border-radius: var(--radius-sm); overflow: hidden; }
|
|
1182
1176
|
.attach-thumb { display: block; width: 28px; height: 28px; object-fit: cover; border-radius: var(--radius-sm); }
|
|
1183
1177
|
|
|
1184
|
-
/* Queued follow-ups (issue #154) — a panel just above the composer showing
|
|
1185
|
-
prompts held back while the session is busy, with their order and delivery
|
|
1186
|
-
state. Reuses .queue-action-btn (GithubQueue's button look) for edit/
|
|
1187
|
-
remove/reorder/send-next so this reads as the same "manage a queue"
|
|
1188
|
-
pattern already established there, rather than inventing a new one. */
|
|
1189
|
-
.followup-queue {
|
|
1190
|
-
margin: var(--space-2) var(--space-3) 0; display: flex; flex-direction: column; gap: var(--space-2);
|
|
1191
|
-
max-height: calc(var(--app-h, 100dvh) * 0.32); min-height: 0; overflow: hidden;
|
|
1192
|
-
}
|
|
1193
|
-
.followup-queue-head { flex: none; font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--muted); padding: 0 var(--space-1); }
|
|
1194
|
-
.followup-list {
|
|
1195
|
-
display: flex; flex-direction: column; gap: var(--space-2); min-height: 0; overflow-y: auto;
|
|
1196
|
-
overscroll-behavior: contain; -webkit-overflow-scrolling: touch; padding: 0 var(--space-1) var(--space-1);
|
|
1197
|
-
}
|
|
1198
|
-
.followup-card { flex: none; padding: 0; overflow: hidden; }
|
|
1199
|
-
.followup-card.locked { opacity: 0.75; }
|
|
1200
|
-
.followup-card-row {
|
|
1201
|
-
display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px; padding: 8px 10px;
|
|
1202
|
-
}
|
|
1203
|
-
.followup-card-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--radius-md); }
|
|
1204
|
-
.followup-reorder { display: flex; flex-direction: column; gap: 2px; flex: none; }
|
|
1205
|
-
/* The text keeps a readable width; on a phone the actions wrap onto their own line. */
|
|
1206
|
-
.followup-body { flex: 1 1 12rem; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
|
|
1207
|
-
.followup-status { font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.02em; }
|
|
1208
|
-
.followup-card.locked .followup-status { color: var(--accent); }
|
|
1209
|
-
.followup-text { font-size: 14px; color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
1210
|
-
.followup-attachments { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
|
|
1211
|
-
.followup-card-actions { display: flex; align-items: center; gap: 6px; flex: none; flex-wrap: wrap; justify-content: flex-end; margin-left: auto; }
|
|
1212
|
-
.followup-edit { display: flex; flex-direction: column; gap: 8px; padding: 8px 10px; }
|
|
1213
|
-
.followup-edit-input { resize: vertical; min-height: 44px; }
|
|
1214
|
-
.followup-edit-actions { display: flex; justify-content: flex-end; gap: 8px; }
|
|
1215
|
-
/* Bigger tap targets for the reorder/edit/remove icon buttons specifically —
|
|
1216
|
-
the shared .queue-action-btn.icon sizing (from GithubQueue) is a bit tight
|
|
1217
|
-
for a control someone taps repeatedly on a phone to walk an item up a queue. */
|
|
1218
|
-
.followup-reorder .queue-action-btn.icon,
|
|
1219
|
-
.followup-card-actions .queue-action-btn.icon {
|
|
1220
|
-
min-width: 32px; min-height: 32px;
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
/* Inline re-time field on a scheduled follow-up row (FollowupQueue ✎). */
|
|
1224
|
-
.schedule-field { display: flex; flex-direction: column; gap: 4px; }
|
|
1225
|
-
.schedule-label { font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.02em; }
|
|
1226
|
-
|
|
1227
1178
|
/* Drag-and-drop drop zone over the composer card. */
|
|
1228
1179
|
.composer-card.dragging { border-color: var(--accent); }
|
|
1229
1180
|
.composer-drop {
|
|
@@ -13,6 +13,8 @@ export async function resealAutomationTemplates(opts) {
|
|
|
13
13
|
const plaintext = opts.retired.map((key) => tryOpen(key, payload)).find((text) => text !== undefined);
|
|
14
14
|
if (plaintext === undefined) {
|
|
15
15
|
result.unreadable.push(template.id);
|
|
16
|
+
if (!template.instructionsUnreadable)
|
|
17
|
+
await opts.markUnreadable(template.id);
|
|
16
18
|
continue;
|
|
17
19
|
}
|
|
18
20
|
await opts.save(template.id, `${prefix}${seal(opts.current, plaintext)}`);
|
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/node-update.js
CHANGED
|
@@ -18,7 +18,28 @@ export function managedInstall(packageRoot) {
|
|
|
18
18
|
}
|
|
19
19
|
return null;
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* True when this daemon runs from a GitHub release archive install: the
|
|
23
|
+
* launcher's BIVY_ARCHIVE_ROOT names the install holding `packageRoot` (the
|
|
24
|
+
* same check bin/archive-install.mjs makes for the CLI).
|
|
25
|
+
*/
|
|
26
|
+
export function isArchiveInstall(packageRoot, archiveRoot = process.env.BIVY_ARCHIVE_ROOT) {
|
|
27
|
+
if (!archiveRoot)
|
|
28
|
+
return false;
|
|
29
|
+
try {
|
|
30
|
+
return fs.realpathSync(path.join(archiveRoot, "app")) === fs.realpathSync(packageRoot)
|
|
31
|
+
&& fs.existsSync(path.join(archiveRoot, "bivy-archive.json"));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Where to look for a newer version on the install's recorded channel: the npm
|
|
39
|
+
* registry, or for an archive install the channel's GitHub release manifest
|
|
40
|
+
* (URLs as in bin/archive-install.mjs). Both answer with `{ version }`.
|
|
41
|
+
*/
|
|
42
|
+
export function updateRegistryUrl(appDir, override, archive = false) {
|
|
22
43
|
if (override)
|
|
23
44
|
return override;
|
|
24
45
|
let channel = "latest";
|
|
@@ -28,6 +49,10 @@ export function updateRegistryUrl(appDir, override) {
|
|
|
28
49
|
channel = recorded;
|
|
29
50
|
}
|
|
30
51
|
catch { /* Existing installs track stable. */ }
|
|
52
|
+
if (archive) {
|
|
53
|
+
const releases = process.env.BIVY_RELEASES_URL || "https://github.com/bivysh/bivy/releases";
|
|
54
|
+
return channel === "latest" ? `${releases}/latest/download/manifest.json` : `${releases}/download/${encodeURIComponent(channel)}/manifest.json`;
|
|
55
|
+
}
|
|
31
56
|
return `https://registry.npmjs.org/%40bivy%2Fbivy/${encodeURIComponent(channel)}`;
|
|
32
57
|
}
|
|
33
58
|
/** Running version is fixed for this process, never re-read after an install. */
|
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,
|