@bivy/bivy 0.20.6-staging.1 → 0.20.6-staging.3
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/bivy.mjs +8 -1
- package/dist/apps/styles.css +54 -2
- package/dist/controllers/session-control.js +1 -3
- package/dist/server.js +40 -25
- package/dist/session/run-terminal.js +27 -3
- package/dist/terminal.js +1 -1
- package/package.json +1 -1
- package/web/assets/AutomationsView-BM8OM-LI.js +51 -0
- package/web/assets/{ChangesCard-CvoN20L_.js → ChangesCard-BQVgcOmR.js} +1 -1
- package/web/assets/LibraryView-CtIZ6j7-.js +2 -0
- package/web/assets/{ProviderConnect-NBSZCsr7.js → ProviderConnect-DVo_6v0N.js} +1 -1
- package/web/assets/Settings-BNoANEok.js +10 -0
- package/web/assets/{Sheet-CasWIEum.js → Sheet-CpIoowo5.js} +2 -2
- package/web/assets/{Terminal-DgmjWWQy.js → Terminal-LZ8azLT7.js} +6 -6
- package/web/assets/{VoiceSettings-DGKH9WLj.js → VoiceSettings-D_AqVrLp.js} +1 -1
- package/web/assets/{abnfDiagram-VCTEODGH-BRrCmuS-.js → abnfDiagram-VCTEODGH-q_V5KCFh.js} +1 -1
- package/web/assets/architecture-7GRP2DOG-B70g2stv.js +1 -0
- package/web/assets/{architectureDiagram-5GKGNRK7-BUwe8I2s.js → architectureDiagram-5GKGNRK7-DdXDQzrJ.js} +1 -1
- package/web/assets/{blockDiagram-I7D4REHJ-BXmJPr7n.js → blockDiagram-I7D4REHJ-D4y7qPKE.js} +1 -1
- package/web/assets/{c4Diagram-7LVT6UL2-CAkDUCpJ.js → c4Diagram-7LVT6UL2--VZeCr3J.js} +1 -1
- package/web/assets/channel-BjjvW3d2.js +1 -0
- package/web/assets/{chunk-4HAMMTFA-Dp_qmfIt.js → chunk-4HAMMTFA-BQNwB4-r.js} +1 -1
- package/web/assets/{chunk-75Z2AOVW-LIBFXHNW.js → chunk-75Z2AOVW-DaDi66X7.js} +1 -1
- package/web/assets/{chunk-DU6HZSFF-CHYFDPen.js → chunk-DU6HZSFF-Cz_K-PAg.js} +1 -1
- package/web/assets/{chunk-F27PBJKO-CW0w3qr4.js → chunk-F27PBJKO-CiA_VeMu.js} +1 -1
- package/web/assets/{chunk-GMAD6QVW-D70RC31m.js → chunk-GMAD6QVW-C6DIMkbp.js} +1 -1
- package/web/assets/{chunk-GVQU2GXP-BoMG4q_g.js → chunk-GVQU2GXP-ChQklEPw.js} +1 -1
- package/web/assets/{chunk-IMKFNOWR-DGuQZMQs.js → chunk-IMKFNOWR-vESMkHnU.js} +1 -1
- package/web/assets/{chunk-L3NEJ4N5-CPcSUaRw.js → chunk-L3NEJ4N5-DyRydj9b.js} +1 -1
- package/web/assets/{chunk-OSK3NFVY-QO8XYQf4.js → chunk-OSK3NFVY-uLvbjNZD.js} +1 -1
- package/web/assets/{chunk-P2QGCYS3-NmNC30bu.js → chunk-P2QGCYS3-C0ZbEWz8.js} +1 -1
- package/web/assets/{chunk-POPQ4Y6H-Bn53i2aN.js → chunk-POPQ4Y6H-UtEO9cBl.js} +1 -1
- package/web/assets/{chunk-PWAF6VOD-Ckjg0E7O.js → chunk-PWAF6VOD-BCKRGdT1.js} +1 -1
- package/web/assets/{chunk-SHT3W25Y-AZaqIQ5z.js → chunk-SHT3W25Y-D5xRgo_J.js} +1 -1
- package/web/assets/{chunk-SVP7TREG-n1FtRhfL.js → chunk-SVP7TREG-BBN7bkx7.js} +1 -1
- package/web/assets/{chunk-TICWLB2K-D-7UBqOb.js → chunk-TICWLB2K-D8rfNasl.js} +1 -1
- package/web/assets/classDiagram-ZZMXUADV--z5hYE5X.js +1 -0
- package/web/assets/classDiagram-v2-VYDZK3BY--z5hYE5X.js +1 -0
- package/web/assets/{cynefin-OW5HDTMX-CdsOpojj.js → cynefin-OW5HDTMX-B7BMuYuW.js} +1 -1
- package/web/assets/{cynefinDiagram-5FMLGOSQ-DHMkAw-p.js → cynefinDiagram-5FMLGOSQ-BA7LlBfv.js} +1 -1
- package/web/assets/{dagre-GXQ25YYZ-CGgIWy71.js → dagre-GXQ25YYZ-D9BwYxSN.js} +1 -1
- package/web/assets/{diagram-S7CK7UJ4-CAkU32pw.js → diagram-S7CK7UJ4-Ci4j13kz.js} +1 -1
- package/web/assets/{diagram-UQ7AKVKN-B9BI2i8L.js → diagram-UQ7AKVKN-DrMYnLGf.js} +1 -1
- package/web/assets/{diagram-VSXAHHWV-D_isYzYo.js → diagram-VSXAHHWV-Jk90pm4l.js} +1 -1
- package/web/assets/{diagram-VX7I27RA-BmqvhWpM.js → diagram-VX7I27RA-B_LPKIbj.js} +1 -1
- package/web/assets/{diagram-Z3DM3KII-Cn2lnK6g.js → diagram-Z3DM3KII-D-54FSSp.js} +1 -1
- package/web/assets/{ebnfDiagram-PWID7BFC-CA6jD4w3.js → ebnfDiagram-PWID7BFC-85GLU1Qx.js} +1 -1
- package/web/assets/{erDiagram-RLTQ6QDP-D8vwiypv.js → erDiagram-RLTQ6QDP-Cjqf9w-j.js} +1 -1
- package/web/assets/eventmodeling-NTZA5JFV-Bx1t8Ws7.js +1 -0
- package/web/assets/flowDiagram-HODETNUW-DTcYWz1a.js +1 -0
- package/web/assets/{ganttDiagram-EL5Y4UJY-D9TnkbWD.js → ganttDiagram-EL5Y4UJY-CllFDNIA.js} +1 -1
- package/web/assets/{gitGraph-4MIJSDKK-DmkQMKd4.js → gitGraph-4MIJSDKK-JbabbGQn.js} +1 -1
- package/web/assets/{gitGraphDiagram-WWUBYQGX-k633BXm0.js → gitGraphDiagram-WWUBYQGX-Bs0agVOW.js} +1 -1
- package/web/assets/{index-Dbc5eEmV.js → index-D1m9cVXn.js} +2 -2
- package/web/assets/index-Hl-z7c8G.css +1 -0
- package/web/assets/{info-A6RAGUB7-DIrl4iZa.js → info-A6RAGUB7-DoJ_vMJT.js} +1 -1
- package/web/assets/{infoDiagram-27XIBGKW-hNpj-RTV.js → infoDiagram-27XIBGKW-DIRLvOQG.js} +1 -1
- package/web/assets/{ishikawaDiagram-5VMMS53U-B5R97iuH.js → ishikawaDiagram-5VMMS53U-CHKavP5c.js} +1 -1
- package/web/assets/{journeyDiagram-3NMN7TZE-_zmo4-I1.js → journeyDiagram-3NMN7TZE-1ayVb9xW.js} +1 -1
- package/web/assets/{kanban-definition-UXKFOSKX-CFaqoxxH.js → kanban-definition-UXKFOSKX-BpuMJyfm.js} +1 -1
- package/web/assets/{line-DvqHwrR2.js → line--EI5rOWQ.js} +1 -1
- package/web/assets/{mermaid-parser.core-B79mvmV7.js → mermaid-parser.core-CexICl1c.js} +3 -3
- package/web/assets/{mermaid.core-fLmZLs32.js → mermaid.core-pumIMO4c.js} +4 -4
- package/web/assets/{mindmap-definition-YA3MSWOX-BUe0_gH7.js → mindmap-definition-YA3MSWOX-DFz_-UZn.js} +1 -1
- package/web/assets/mount--plvVxhF.js +27 -0
- package/web/assets/{packet-AYTQ26CC-Aj0EzH9_.js → packet-AYTQ26CC-DeYOUNLB.js} +1 -1
- package/web/assets/{pegDiagram-XKGWAZYB-CKbgKsPZ.js → pegDiagram-XKGWAZYB-BDVqO1lR.js} +1 -1
- package/web/assets/{pie-WAS4IAKB-DVr7PuUh.js → pie-WAS4IAKB-rSl6g03L.js} +1 -1
- package/web/assets/{pieDiagram-E7YTZNPT-CK_U235v.js → pieDiagram-E7YTZNPT-RE_qyV86.js} +1 -1
- package/web/assets/{quadrantDiagram-AXDQQJYC-DlRY8SNG.js → quadrantDiagram-AXDQQJYC-DvgsYDiT.js} +1 -1
- package/web/assets/{radar-RG4KPBEZ-C29uvfrD.js → radar-RG4KPBEZ-BluY7HPZ.js} +1 -1
- package/web/assets/{railroad-74A4TZTK-Bsl6sVlq.js → railroad-74A4TZTK-CI8HoIPX.js} +1 -1
- package/web/assets/railroad-abnf-HS5TGJTU-CW6o7azB.js +1 -0
- package/web/assets/railroad-ebnf-LZEXJU2U-iCDWKNcB.js +1 -0
- package/web/assets/railroad-peg-WCYAUIDC-B0qaz-kW.js +1 -0
- package/web/assets/{railroadDiagram-O6MQD6OU-2yZrB_cl.js → railroadDiagram-O6MQD6OU-BMjqzW-y.js} +1 -1
- package/web/assets/{requirementDiagram-BXWQKSXE-BGne8p4g.js → requirementDiagram-BXWQKSXE-Dw7cOOR8.js} +1 -1
- package/web/assets/{sankeyDiagram-P5KCCOFB-ChE-PJgx.js → sankeyDiagram-P5KCCOFB-pOTF6bRz.js} +1 -1
- package/web/assets/{sequenceDiagram-WJ2MYXX4-Dmr-MzeW.js → sequenceDiagram-WJ2MYXX4-ClQQJtjY.js} +1 -1
- package/web/assets/{settingsRoute-C5b_th3F.js → settingsRoute-STSKsBxR.js} +1 -1
- package/web/assets/{stateDiagram-D77RDMKH-EMQqubpF.js → stateDiagram-D77RDMKH-BLbq24-r.js} +1 -1
- package/web/assets/stateDiagram-v2-MP3YSRHH-QAOfSNO2.js +1 -0
- package/web/assets/{swimlanes-42K2YHIH-DshnggZ3.js → swimlanes-42K2YHIH-Bvyc9d-X.js} +1 -1
- package/web/assets/swimlanesDiagram-VR7AAH4N-D0Q3jk4u.js +8 -0
- package/web/assets/{timeline-definition-24CTP7MA-ZwyROueZ.js → timeline-definition-24CTP7MA-D19M8pqI.js} +1 -1
- package/web/assets/{treeView-Q6P3EWNA-Dcc62DZb.js → treeView-Q6P3EWNA-C0O23uvm.js} +1 -1
- package/web/assets/{treemap-WGGIJYW6-BklfTxbU.js → treemap-WGGIJYW6-Cqe8MA6f.js} +1 -1
- package/web/assets/{useStore-COKqKujw.js → useStore-7Pbk_2aC.js} +1 -1
- package/web/assets/{vennDiagram-4TSXK5OY-DbYhZINT.js → vennDiagram-4TSXK5OY-D40HW3dD.js} +1 -1
- package/web/assets/{wardley-WFR3VGLG-9C_Tmqe6.js → wardley-WFR3VGLG-D4IGg7gO.js} +1 -1
- package/web/assets/{wardleyDiagram-VM6X3IG4-DxjFuJhq.js → wardleyDiagram-VM6X3IG4-BRIm2egQ.js} +1 -1
- package/web/assets/{xychartDiagram-S5SC5T6Z-Cy3VPR-8.js → xychartDiagram-S5SC5T6Z-xMbNSxWK.js} +1 -1
- package/web/index.html +2 -2
- package/web/sw.js +1 -1
- package/web/assets/AutomationsView-vRpxGraI.js +0 -51
- package/web/assets/LibraryView-B-X0m4v6.js +0 -2
- package/web/assets/Settings-BJPZ_q9L.js +0 -10
- package/web/assets/architecture-7GRP2DOG-Kx7T2LBQ.js +0 -1
- package/web/assets/channel-hv9z71s4.js +0 -1
- package/web/assets/classDiagram-ZZMXUADV-kuXKTzrq.js +0 -1
- package/web/assets/classDiagram-v2-VYDZK3BY-kuXKTzrq.js +0 -1
- package/web/assets/eventmodeling-NTZA5JFV-D2DyVOaq.js +0 -1
- package/web/assets/flowDiagram-HODETNUW-B_K3Zdwc.js +0 -1
- package/web/assets/index-DPD-O0uT.css +0 -1
- package/web/assets/mount-CRyC02I5.js +0 -27
- package/web/assets/railroad-abnf-HS5TGJTU-D2U1pfm4.js +0 -1
- package/web/assets/railroad-ebnf-LZEXJU2U-Dvnc21w0.js +0 -1
- package/web/assets/railroad-peg-WCYAUIDC-CFx-oLDW.js +0 -1
- package/web/assets/stateDiagram-v2-MP3YSRHH-DAbT5gGC.js +0 -1
- package/web/assets/swimlanesDiagram-VR7AAH4N-B1V7grs-.js +0 -8
package/bin/bivy.mjs
CHANGED
|
@@ -2691,7 +2691,14 @@ blocked, or need the user to look at something. --json prints
|
|
|
2691
2691
|
const body = await res.json().catch(() => ({}));
|
|
2692
2692
|
if (!res.ok) return fail(sessionHttpError("Notify", res.status, body));
|
|
2693
2693
|
if (json) { console.log(JSON.stringify(body)); return; }
|
|
2694
|
-
const
|
|
2694
|
+
const noPush = { user_watching: "the user has the app open; add --urgent to push anyway", rate_limited: "this session pushed less than a minute ago", unavailable: "this machine isn't signed in to a Bivy account" }[body.push];
|
|
2695
|
+
// A terminal run (`bivy run`) has no chat to hold a card: the push is the notice.
|
|
2696
|
+
if (body.posted === false) {
|
|
2697
|
+
if (body.push === "sent") console.log(c.green("Pushed to the user's devices."));
|
|
2698
|
+
else console.log(c.yellow(`Not delivered: a terminal run has no chat to post in, and no push was sent (${noPush ?? body.push}).`));
|
|
2699
|
+
return;
|
|
2700
|
+
}
|
|
2701
|
+
const pushed = body.push === "sent" ? "and pushed to the user's devices" : noPush ? `(no push: ${noPush})` : "";
|
|
2695
2702
|
console.log(c.green(`Posted in the chat ${pushed}.`.replace(" .", ".")));
|
|
2696
2703
|
}
|
|
2697
2704
|
|
package/dist/apps/styles.css
CHANGED
|
@@ -284,6 +284,54 @@ input, textarea, select { font-family: inherit; font-size: 16px; }
|
|
|
284
284
|
.focus-view-btn[aria-pressed="true"] { color: var(--accent); background: var(--accent-soft); }
|
|
285
285
|
.focus-view-label { font-size: var(--text-xs); font-weight: var(--weight-semibold); }
|
|
286
286
|
|
|
287
|
+
/* ---- Side pane (wide screens): the session's changes, apps and artifacts
|
|
288
|
+
beside the chat instead of in a sheet over it. Same bodies as the sheets
|
|
289
|
+
(components/Panel.tsx), docked; below the breakpoint in App.tsx the pane
|
|
290
|
+
is never rendered and the sheets are used. */
|
|
291
|
+
.app.has-pane { grid-template-columns: minmax(300px, 22vw) minmax(0, 1fr) clamp(380px, 32vw, 600px); }
|
|
292
|
+
.side-pane {
|
|
293
|
+
display: flex; flex-direction: column; min-width: 0; min-height: 0; height: var(--app-h, 100dvh);
|
|
294
|
+
border-left: 1px solid var(--line); background: var(--surface);
|
|
295
|
+
}
|
|
296
|
+
.side-pane-head {
|
|
297
|
+
flex: none; display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
|
|
298
|
+
min-height: var(--topbar-h, 57px); padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
|
|
299
|
+
border-bottom: 1px solid var(--line);
|
|
300
|
+
}
|
|
301
|
+
.side-pane-tabs { min-width: 0; overflow-x: auto; }
|
|
302
|
+
.side-pane-tab { display: inline-flex; align-items: center; gap: var(--space-1); }
|
|
303
|
+
.seg-count { color: var(--muted); font-variant-numeric: tabular-nums; font-weight: var(--weight-normal); }
|
|
304
|
+
.side-pane-close { flex: none; color: var(--muted); }
|
|
305
|
+
.side-pane-close:hover { color: var(--ink); }
|
|
306
|
+
.side-pane-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }
|
|
307
|
+
/* A docked Panel: the sheet's title row becomes a quiet toolbar, its content
|
|
308
|
+
the pane's own scroller. */
|
|
309
|
+
.pane-panel { flex: 1; min-height: 0; display: flex; flex-direction: column; }
|
|
310
|
+
.pane-panel-head {
|
|
311
|
+
flex: none; display: flex; align-items: center; gap: var(--space-2);
|
|
312
|
+
min-height: 44px; padding: var(--space-1) var(--space-2) var(--space-1) var(--space-3);
|
|
313
|
+
border-bottom: 1px solid var(--line);
|
|
314
|
+
}
|
|
315
|
+
.pane-panel-title {
|
|
316
|
+
flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
317
|
+
font-size: var(--text-sm); font-weight: var(--weight-semibold);
|
|
318
|
+
}
|
|
319
|
+
.pane-panel-content { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; padding: var(--space-3); overscroll-behavior: contain; }
|
|
320
|
+
.pane-panel[data-size="full"] .pane-panel-content { padding: 0; overflow: hidden; }
|
|
321
|
+
.pane-panel[data-size="full"] .preview-peek { border-radius: 0; }
|
|
322
|
+
/* The pane's tab already says Terminal: keep the head to the pane toolbar's
|
|
323
|
+
height, with the live status and the actions that matter beside a chat. */
|
|
324
|
+
.side-pane .term-head { min-height: 44px; padding: var(--space-1) var(--space-2) var(--space-1) var(--space-3); gap: var(--space-2); }
|
|
325
|
+
.side-pane .term-title-label, .side-pane .term-zoom { display: none; }
|
|
326
|
+
.side-pane .term-title { flex: none; }
|
|
327
|
+
.side-pane .term-status { margin-left: 0; }
|
|
328
|
+
/* Narrow screens: Changes in the session view switch covers the column's
|
|
329
|
+
view, full height. Everything else in the column stays mounted, hidden. */
|
|
330
|
+
.main.changes-section > :not(.topbar):not(.banner):not(.main-changes) { display: none; }
|
|
331
|
+
.main-changes { flex: 1; min-height: 0; display: flex; flex-direction: column; background: var(--surface); }
|
|
332
|
+
.main-changes .pane-panel-content { padding-bottom: max(var(--space-3), env(safe-area-inset-bottom)); }
|
|
333
|
+
.topbar-actions .side-pane-toggle[aria-pressed="true"] { color: var(--accent); background: var(--accent-soft); }
|
|
334
|
+
|
|
287
335
|
/* ---- Header session actions (dot-menu: rename / open PR / delete) ---- */
|
|
288
336
|
.session-actions-wrap { position: relative; flex: none; }
|
|
289
337
|
.session-actions-btn { border: 0; background: transparent; color: var(--ink); font-size: 20px; line-height: 1; padding: 4px 8px; border-radius: var(--radius-sm); }
|
|
@@ -927,7 +975,8 @@ button.sheet-action:last-child { border-bottom: 0; }
|
|
|
927
975
|
would occupy and centers a compact card; the buttons are the old lock
|
|
928
976
|
banner's escape hatches (open the terminal / take over in chat) at a more
|
|
929
977
|
deliberate, explanatory scale. */
|
|
930
|
-
.tui-locked { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; padding: 24px 16px; overflow-y: auto; }
|
|
978
|
+
.tui-locked { flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: safe center; gap: var(--space-4); padding: 24px 16px; overflow-y: auto; }
|
|
979
|
+
.tui-locked-attention { width: min(440px, 100%); }
|
|
931
980
|
.tui-locked-card { width: min(440px, 100%); display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; }
|
|
932
981
|
.tui-locked-icon { display: inline-flex; align-items: center; justify-content: center; width: 52px; height: 52px; border-radius: var(--radius-md); background: color-mix(in srgb, var(--accent) 12%, var(--surface-2)); color: var(--accent); }
|
|
933
982
|
.tui-locked-title { margin: 2px 0 0; font-size: 18px; font-weight: 700; }
|
|
@@ -2610,9 +2659,12 @@ textarea.picker-search { resize: vertical; font-family: ui-monospace, monospace;
|
|
|
2610
2659
|
.term-head-actions {
|
|
2611
2660
|
display: flex; align-items: center; gap: 8px;
|
|
2612
2661
|
flex: 1 1 auto; min-width: 0; overflow-x: auto;
|
|
2613
|
-
justify-content: flex-end
|
|
2662
|
+
/* Right-aligned by an auto margin, not justify-content: flex-end, which
|
|
2663
|
+
would push overflow off the left edge where it can't be scrolled to. */
|
|
2664
|
+
justify-content: flex-start;
|
|
2614
2665
|
scrollbar-width: none; -webkit-overflow-scrolling: touch;
|
|
2615
2666
|
}
|
|
2667
|
+
.term-head-actions > :first-child { margin-left: auto; }
|
|
2616
2668
|
.term-head-actions::-webkit-scrollbar { display: none; }
|
|
2617
2669
|
.term-head-actions > * { flex: 0 0 auto; }
|
|
2618
2670
|
.term-attach-wrap { position: relative; }
|
|
@@ -17,12 +17,10 @@ export function createSessionControlCommands(ports) {
|
|
|
17
17
|
ctx.reply({ type: "session.resume.result", ok: true });
|
|
18
18
|
},
|
|
19
19
|
"session.question.answer"(input, ctx) {
|
|
20
|
-
const session = ports.resolve(input.sessionId);
|
|
21
20
|
const requestId = String(input.requestId ?? "");
|
|
22
|
-
if (!
|
|
21
|
+
if (!requestId || !ports.answer(input.sessionId, requestId, input)) {
|
|
23
22
|
return ctx.reply({ type: "session.question.answer.error", httpStatus: 404, error: "No matching session/question" });
|
|
24
23
|
}
|
|
25
|
-
ports.answer(session, requestId, input);
|
|
26
24
|
ctx.reply({ type: "session.question.answer.result", ok: true, requestId });
|
|
27
25
|
},
|
|
28
26
|
};
|
package/dist/server.js
CHANGED
|
@@ -476,7 +476,7 @@ questionManager.onRequest((request) => {
|
|
|
476
476
|
sessionId: request.sessionId,
|
|
477
477
|
attentionId: request.id,
|
|
478
478
|
title: "Bivy needs your input",
|
|
479
|
-
body: `${sessionNotifyLabel(resolveSession(request.sessionId))} is asking a question — tap to answer.`,
|
|
479
|
+
body: `${sessionNotifyLabel(resolveSession(request.sessionId), runTerms.liveRun(request.sessionId)?.name)} is asking a question — tap to answer.`,
|
|
480
480
|
});
|
|
481
481
|
});
|
|
482
482
|
questionManager.onResolved((request) => {
|
|
@@ -2330,7 +2330,7 @@ function currentAccess() {
|
|
|
2330
2330
|
});
|
|
2331
2331
|
}
|
|
2332
2332
|
const RELAY_COMMANDS = {
|
|
2333
|
-
...createAppCommands(appService, (id) => { const record = resolveSession(id); return record ? harnessDirFor(record) :
|
|
2333
|
+
...createAppCommands(appService, (id) => { const record = resolveSession(id); return record ? harnessDirFor(record) : runTerms.liveRun(id)?.workspace; }, (app) => {
|
|
2334
2334
|
const record = resolveSession(app.sessionId);
|
|
2335
2335
|
if (!record)
|
|
2336
2336
|
return;
|
|
@@ -2389,7 +2389,7 @@ const RELAY_COMMANDS = {
|
|
|
2389
2389
|
resolve: (sessionId) => resolveSession(sessionId),
|
|
2390
2390
|
pause: pauseSession,
|
|
2391
2391
|
resume: resumeSession,
|
|
2392
|
-
answer: (
|
|
2392
|
+
answer: (sessionId, requestId, input) => answerSessionQuestion(sessionId, requestId, input),
|
|
2393
2393
|
}),
|
|
2394
2394
|
// Session fork/move command cluster, extracted to controllers/fork-commands.ts
|
|
2395
2395
|
// (server.ts decomposition). Late-bound singletons (forkStandUp/forkRetire/
|
|
@@ -6405,9 +6405,13 @@ function recordMcpEvent(sessionId, event) {
|
|
|
6405
6405
|
* out, or aborted) — same tolerance as resolveApproval. The resolved broadcast
|
|
6406
6406
|
* is emitted from questionManager.onResolved, not here, so it fires exactly
|
|
6407
6407
|
* when the question actually settles (never for an already-discarded id). */
|
|
6408
|
-
|
|
6408
|
+
/** A question's session may be an open chat or a live `bivy run` (`bivy ask`). */
|
|
6409
|
+
function answerSessionQuestion(sessionId, requestId, msg) {
|
|
6410
|
+
if (!resolveSession(sessionId) && !runTerms.liveRun(typeof sessionId === "string" ? sessionId : ""))
|
|
6411
|
+
return false;
|
|
6409
6412
|
const answers = msg.answers && typeof msg.answers === "object" ? msg.answers : {};
|
|
6410
6413
|
questionManager.resolve(requestId, msg.cancelled ? { behavior: "cancelled" } : { behavior: "completed", answers });
|
|
6414
|
+
return true;
|
|
6411
6415
|
}
|
|
6412
6416
|
/** Agent id requested by a client command (`agent` or legacy `runtimeId`); empty = default. */
|
|
6413
6417
|
function agentFrom(msg) {
|
|
@@ -9211,6 +9215,7 @@ const runTerms = createRunTerminals({
|
|
|
9211
9215
|
sessionBusy: (record) => sessionBusy(record),
|
|
9212
9216
|
sessionTerminalsRecord: (sessionId, val) => sessionTerminals.record(sessionId, val),
|
|
9213
9217
|
sessionTerminalsForget: (sessionId) => sessionTerminals.forget(sessionId),
|
|
9218
|
+
replayPendingInteractions: (sessionId) => replayPendingInteractions(sessionId),
|
|
9214
9219
|
upsertSessionMetadata: (patch) => {
|
|
9215
9220
|
const input = patch;
|
|
9216
9221
|
// A run continuing a saved session (`bivy resume`) only moves its status;
|
|
@@ -11663,11 +11668,9 @@ app.post("/api/session/:id/attach", (req, res) => {
|
|
|
11663
11668
|
// `bivy context`: what an agent in this session needs to orient itself.
|
|
11664
11669
|
app.get("/api/session/:id/context", (req, res) => {
|
|
11665
11670
|
const record = openSessions.get(String(req.params.id));
|
|
11666
|
-
|
|
11667
|
-
|
|
11668
|
-
|
|
11669
|
-
res.json(buildAgentContext({
|
|
11670
|
-
session: {
|
|
11671
|
+
const run = record ? undefined : runTerms.liveRun(String(req.params.id));
|
|
11672
|
+
const session = record
|
|
11673
|
+
? {
|
|
11671
11674
|
id: record.id,
|
|
11672
11675
|
name: record.session.getName() || undefined,
|
|
11673
11676
|
agent: record.runtimeId,
|
|
@@ -11678,9 +11681,16 @@ app.get("/api/session/:id/context", (req, res) => {
|
|
|
11678
11681
|
forkedFrom: record.forkedFrom,
|
|
11679
11682
|
delegatedFrom: record.delegatedFrom?.sessionId,
|
|
11680
11683
|
busy: sessionBusy(record),
|
|
11681
|
-
}
|
|
11684
|
+
}
|
|
11685
|
+
// A `bivy run` terminal: the agent drives it from its own TUI, so it's live.
|
|
11686
|
+
: run && { id: run.sessionId, name: run.name, agent: run.agent ?? "run", workspace: run.workspace, busy: true };
|
|
11687
|
+
if (!session)
|
|
11688
|
+
return res.status(404).json({ error: "Session not found" });
|
|
11689
|
+
const { apps, previewAvailable } = appService.list(session.id);
|
|
11690
|
+
res.json(buildAgentContext({
|
|
11691
|
+
session,
|
|
11682
11692
|
machine: { name: identity.name, platform: process.platform, arch: process.arch },
|
|
11683
|
-
presence: presenceBook.get(
|
|
11693
|
+
presence: presenceBook.get(session.id),
|
|
11684
11694
|
userConnected: clients.size > 0 || (relay?.clientCount ?? 0) > 0,
|
|
11685
11695
|
apps,
|
|
11686
11696
|
previewAvailable,
|
|
@@ -11688,44 +11698,49 @@ app.get("/api/session/:id/context", (req, res) => {
|
|
|
11688
11698
|
});
|
|
11689
11699
|
// `bivy notify "<message>"`: a card in the chat, and a push naming the session
|
|
11690
11700
|
// (never the text) when nobody has the app open, or when it's urgent. At most
|
|
11691
|
-
// one push a minute per session; later notices still post a card.
|
|
11701
|
+
// one push a minute per session; later notices still post a card. A `bivy run`
|
|
11702
|
+
// terminal has no chat to hold the card, so its notice is the push alone.
|
|
11692
11703
|
const lastNotifyPush = new Map();
|
|
11693
11704
|
app.post("/api/session/:id/notify", (req, res) => {
|
|
11694
11705
|
const record = openSessions.get(String(req.params.id));
|
|
11695
|
-
|
|
11706
|
+
const run = record ? undefined : runTerms.liveRun(String(req.params.id));
|
|
11707
|
+
const sessionId = record?.id ?? run?.sessionId;
|
|
11708
|
+
if (!sessionId)
|
|
11696
11709
|
return res.status(404).json({ error: "Session not found" });
|
|
11697
11710
|
const text = typeof req.body?.text === "string" ? req.body.text.trim() : "";
|
|
11698
11711
|
const notice = { id: `notice-${randomBytes(8).toString("hex")}`, text, ...(req.body?.urgent === true ? { urgent: true } : {}) };
|
|
11699
11712
|
if (!isAgentNotice(notice))
|
|
11700
11713
|
return res.status(400).json({ error: `A notice needs text (up to ${MAX_NOTICE_TEXT} characters).` });
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
|
|
11704
|
-
|
|
11714
|
+
if (record) {
|
|
11715
|
+
eventLog.appendNotice(record.id, { afterMessageCount: record.session.getMessages().length, notice });
|
|
11716
|
+
broadcast(stampSessionEvent({ type: "session.event", sessionId: record.id, event: { type: "notice", id: notice.id, notice } }));
|
|
11717
|
+
}
|
|
11718
|
+
const watching = Boolean(record) && (clients.size > 0 || (relay?.clientCount ?? 0) > 0);
|
|
11719
|
+
const push = noticePush({ urgent: notice.urgent, userWatching: watching, lastPushAt: lastNotifyPush.get(sessionId), now: Date.now(), pushConfigured: Boolean(sessionAdvertiseTarget) });
|
|
11705
11720
|
if (push === "sent") {
|
|
11706
|
-
lastNotifyPush.set(
|
|
11721
|
+
lastNotifyPush.set(sessionId, Date.now());
|
|
11707
11722
|
void sendNotificationHint({
|
|
11708
11723
|
kind: "agent_notice",
|
|
11709
|
-
sessionId
|
|
11710
|
-
targetSessionId:
|
|
11711
|
-
title: sessionNotifyLabel(record),
|
|
11724
|
+
sessionId,
|
|
11725
|
+
targetSessionId: sessionId,
|
|
11726
|
+
title: sessionNotifyLabel(record, run?.name || "A terminal session"),
|
|
11712
11727
|
body: "Has a message for you — tap to read it.",
|
|
11713
11728
|
});
|
|
11714
11729
|
}
|
|
11715
|
-
res.json({ ok: true, id: notice.id, push, userWatching: watching });
|
|
11730
|
+
res.json({ ok: true, id: notice.id, posted: Boolean(record), push, userWatching: watching });
|
|
11716
11731
|
});
|
|
11717
11732
|
// `bivy ask`: the question card and "needs your input" push, for any agent.
|
|
11718
11733
|
// Returns at once with an id; `/wait` blocks up to 240s per call (under common
|
|
11719
11734
|
// HTTP client timeouts), so the CLI loops until the user answers.
|
|
11720
11735
|
app.post("/api/session/:id/ask", (req, res) => {
|
|
11721
|
-
const
|
|
11722
|
-
if (!
|
|
11736
|
+
const sessionId = openSessions.get(String(req.params.id))?.id ?? runTerms.liveRun(String(req.params.id))?.sessionId;
|
|
11737
|
+
if (!sessionId)
|
|
11723
11738
|
return res.status(404).json({ error: "Session not found" });
|
|
11724
11739
|
const questions = askQuestionsFrom(req.body?.questions);
|
|
11725
11740
|
if (typeof questions === "string")
|
|
11726
11741
|
return res.status(400).json({ error: questions });
|
|
11727
11742
|
const timeoutSeconds = Number(req.body?.timeoutSeconds);
|
|
11728
|
-
res.status(201).json(agentQuestions.ask(
|
|
11743
|
+
res.status(201).json(agentQuestions.ask(sessionId, questions, Number.isFinite(timeoutSeconds) && timeoutSeconds > 0 ? timeoutSeconds * 1000 : 10 * 60 * 1000));
|
|
11729
11744
|
});
|
|
11730
11745
|
app.get("/api/session/:id/ask/:askId", (req, res) => {
|
|
11731
11746
|
const result = agentQuestions.get(String(req.params.id), String(req.params.askId));
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// timer maps, the agent lookup tables) moves here. Credential projection follows
|
|
15
15
|
// the unified agent-integration model (#433): only an integration that declares
|
|
16
16
|
// bivy/mixed auth receives Bivy vault projections.
|
|
17
|
+
import { randomUUID } from "node:crypto";
|
|
17
18
|
import { provisionAgentRun } from "../runtime/credential-provisioning.js";
|
|
18
19
|
import { ingestAgentCredentials } from "../runtime/credential-ingest.js";
|
|
19
20
|
import { discoverCodexSessionForCwd } from "../runtime/codex-sessions.js";
|
|
@@ -21,6 +22,7 @@ import { discoverGrokSessionForCwd } from "../runtime/grok-sessions.js";
|
|
|
21
22
|
import { discoverGeminiFamilySessionForCwd } from "../runtime/gemini-sessions.js";
|
|
22
23
|
import { discoverOpenCodeSessionForCwd } from "../runtime/opencode-sessions.js";
|
|
23
24
|
import { discoverPiSessionForCwd } from "../runtime/pi-session-discovery.js";
|
|
25
|
+
import { bivySessionEnv } from "../runtime/session-env.js";
|
|
24
26
|
import { listMultiplexerSessions, attachCommand } from "../multiplexer.js";
|
|
25
27
|
// Agents whose pinned run-terminal session can be reopened as a governed chat.
|
|
26
28
|
const TAKEOVER_RUNTIME_BY_AGENT = {
|
|
@@ -115,6 +117,13 @@ export function createRunTerminals(deps) {
|
|
|
115
117
|
deps.broadcastTuiState(record.id, false);
|
|
116
118
|
void deps.refreshRecordAfterTui(record);
|
|
117
119
|
}
|
|
120
|
+
function liveRun(ref) {
|
|
121
|
+
if (!ref)
|
|
122
|
+
return undefined;
|
|
123
|
+
const run = terminals.list((m) => m.kind === "run" && !m.mux)
|
|
124
|
+
.find((t) => runTerminals.has(t.id) && (t.id === ref || t.meta.sessionId === ref));
|
|
125
|
+
return run && { termId: run.id, sessionId: run.meta.sessionId ?? run.id, workspace: run.workspace, name: run.meta.name, agent: run.meta.agent };
|
|
126
|
+
}
|
|
118
127
|
function adoptLiveRun(record) {
|
|
119
128
|
if (record.tuiTermId)
|
|
120
129
|
return;
|
|
@@ -287,12 +296,19 @@ export function createRunTerminals(deps) {
|
|
|
287
296
|
const name = explicitName || (spec.mux ? undefined : defaultRunName(spec.agent, commandLine, workspace));
|
|
288
297
|
const notifiable = !spec.mux;
|
|
289
298
|
const createdAt = Date.now();
|
|
299
|
+
// The agent inside gets the same session env a chat session's agent does, so
|
|
300
|
+
// `bivy notify`/`ask`/`app` reach this run: under its pinned session id, else
|
|
301
|
+
// under the terminal's own id (see liveRun). A mux attach joins a shell that
|
|
302
|
+
// already has its environment.
|
|
303
|
+
const termId = `term-${randomUUID()}`;
|
|
304
|
+
const sessionEnv = spec.mux ? {} : bivySessionEnv(spec.sessionId ?? termId);
|
|
290
305
|
try {
|
|
291
306
|
const id = terminals.open({
|
|
307
|
+
id: termId,
|
|
292
308
|
workspace,
|
|
293
309
|
command: spec.command,
|
|
294
310
|
args: spec.args,
|
|
295
|
-
env: { ...credentialEnv, ...spec.env },
|
|
311
|
+
env: { ...credentialEnv, ...sessionEnv, ...spec.env },
|
|
296
312
|
cols: spec.cols,
|
|
297
313
|
rows: spec.rows,
|
|
298
314
|
clientId: spec.clientId,
|
|
@@ -466,7 +482,15 @@ export function createRunTerminals(deps) {
|
|
|
466
482
|
function handleTerminalMessage(msg, emit, owned, clientId, viewRun) {
|
|
467
483
|
switch (msg.kind) {
|
|
468
484
|
case "terminal.list":
|
|
469
|
-
|
|
485
|
+
// Every client asks on connect: also re-send what live runs are waiting
|
|
486
|
+
// on, as a chat's history request does for its own cards.
|
|
487
|
+
void runTerminalList().then((listed) => {
|
|
488
|
+
emit({ type: "terminal.list", terminals: listed });
|
|
489
|
+
for (const t of listed) {
|
|
490
|
+
if (!t.mux)
|
|
491
|
+
deps.replayPendingInteractions(t.sessionId ?? t.termId);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
470
494
|
return true;
|
|
471
495
|
case "terminal.takeover": {
|
|
472
496
|
const takeoverTermId = typeof msg.termId === "string" && msg.termId ? msg.termId : undefined;
|
|
@@ -655,5 +679,5 @@ export function createRunTerminals(deps) {
|
|
|
655
679
|
return false;
|
|
656
680
|
}
|
|
657
681
|
}
|
|
658
|
-
return { runTerminalList, openRunTerminal, takeoverRunTerminal, handleTerminalMessage, addRunViewer, dropRunViewer, hasRunTerminal, hasLiveRunForSession, adoptLiveRun };
|
|
682
|
+
return { runTerminalList, openRunTerminal, takeoverRunTerminal, handleTerminalMessage, addRunViewer, dropRunViewer, hasRunTerminal, hasLiveRunForSession, adoptLiveRun, liveRun };
|
|
659
683
|
}
|
package/dist/terminal.js
CHANGED
|
@@ -77,7 +77,7 @@ export class TerminalManager {
|
|
|
77
77
|
return this.terminals.size;
|
|
78
78
|
}
|
|
79
79
|
open(options) {
|
|
80
|
-
const id = `term-${randomUUID()}`;
|
|
80
|
+
const id = options.id ?? `term-${randomUUID()}`;
|
|
81
81
|
const shell = options.command || defaultShell();
|
|
82
82
|
const shellArgs = options.command ? (options.args ?? []) : [];
|
|
83
83
|
// Ensure a UTF-8 locale so TUIs (e.g. Claude Code) render box-drawing and
|
package/package.json
CHANGED