agent-relay-server 0.120.0 → 0.120.1
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/docs/openapi.json +1 -1
- package/package.json +2 -2
- package/public/assets/{MessageBubble-BJXjk3GB.js → MessageBubble-jpshgh3K.js} +2 -2
- package/public/assets/{MessageBubble-BJXjk3GB.js.map → MessageBubble-jpshgh3K.js.map} +1 -1
- package/public/assets/PlanGraphPanel-CjVG-Jsq.js +2 -0
- package/public/assets/PlanGraphPanel-CjVG-Jsq.js.map +1 -0
- package/public/assets/{activity-BUt4FiFQ.js → activity-DLTFF8Dz.js} +2 -2
- package/public/assets/{activity-BUt4FiFQ.js.map → activity-DLTFF8Dz.js.map} +1 -1
- package/public/assets/{agent-profiles-C1dvEaGS.js → agent-profiles-BcCLIrON.js} +2 -2
- package/public/assets/{agent-profiles-C1dvEaGS.js.map → agent-profiles-BcCLIrON.js.map} +1 -1
- package/public/assets/{agents-DvJZ0Tcz.js → agents-QemvuxKO.js} +2 -2
- package/public/assets/{agents-DvJZ0Tcz.js.map → agents-QemvuxKO.js.map} +1 -1
- package/public/assets/{automation-DICpRUGK.js → automation-DACSOffp.js} +2 -2
- package/public/assets/{automation-DICpRUGK.js.map → automation-DACSOffp.js.map} +1 -1
- package/public/assets/{chat-_I9nvxj2.js → chat-xKXCafN9.js} +2 -2
- package/public/assets/{chat-_I9nvxj2.js.map → chat-xKXCafN9.js.map} +1 -1
- package/public/assets/{formatted-body-ChERiS0y.js → formatted-body-Dg9R3RD7.js} +3 -3
- package/public/assets/{formatted-body-ChERiS0y.js.map → formatted-body-Dg9R3RD7.js.map} +1 -1
- package/public/assets/{formatted-body-impl-CiaUIrP3.js → formatted-body-impl-DlasTD8X.js} +2 -2
- package/public/assets/{formatted-body-impl-CiaUIrP3.js.map → formatted-body-impl-DlasTD8X.js.map} +1 -1
- package/public/assets/{index-CtBqO8vm.js → index-D_fNJnsI.js} +6 -6
- package/public/assets/{index-CtBqO8vm.js.map → index-D_fNJnsI.js.map} +1 -1
- package/public/assets/{insights-DBjaf3N5.js → insights-D48n2ojF.js} +2 -2
- package/public/assets/{insights-DBjaf3N5.js.map → insights-D48n2ojF.js.map} +1 -1
- package/public/assets/{maintenance-EWlKbynM.js → maintenance-LQ_U3Nqy.js} +2 -2
- package/public/assets/{maintenance-EWlKbynM.js.map → maintenance-LQ_U3Nqy.js.map} +1 -1
- package/public/assets/{managed-agents-DzjKwBh4.js → managed-agents-Cfh8FvY8.js} +2 -2
- package/public/assets/{managed-agents-DzjKwBh4.js.map → managed-agents-Cfh8FvY8.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-bwLB6Gc-.js → markdown-preview-impl-BSa7zYx1.js} +2 -2
- package/public/assets/{markdown-preview-impl-bwLB6Gc-.js.map → markdown-preview-impl-BSa7zYx1.js.map} +1 -1
- package/public/assets/{memory-DEwJsFNK.js → memory-Dr1iYKkC.js} +2 -2
- package/public/assets/{memory-DEwJsFNK.js.map → memory-Dr1iYKkC.js.map} +1 -1
- package/public/assets/{messages-BSMkT9X2.js → messages-DRKulS-2.js} +2 -2
- package/public/assets/{messages-BSMkT9X2.js.map → messages-DRKulS-2.js.map} +1 -1
- package/public/assets/{orchestrators-Bl8k3KxQ.js → orchestrators-BguyeViG.js} +2 -2
- package/public/assets/{orchestrators-Bl8k3KxQ.js.map → orchestrators-BguyeViG.js.map} +1 -1
- package/public/assets/{overview-CdFe7Dmc.js → overview-Dbq2lr7Y.js} +2 -2
- package/public/assets/{overview-CdFe7Dmc.js.map → overview-Dbq2lr7Y.js.map} +1 -1
- package/public/assets/{pairs-0ewxtvFZ.js → pairs-DD7i2scF.js} +2 -2
- package/public/assets/{pairs-0ewxtvFZ.js.map → pairs-DD7i2scF.js.map} +1 -1
- package/public/assets/{prompt-settings-CMQ2tHe0.js → prompt-settings-DwgrPKY3.js} +2 -2
- package/public/assets/{prompt-settings-CMQ2tHe0.js.map → prompt-settings-DwgrPKY3.js.map} +1 -1
- package/public/assets/{registry-DntwLc8a.js → registry-DCR3xFpe.js} +2 -2
- package/public/assets/{registry-DntwLc8a.js.map → registry-DCR3xFpe.js.map} +1 -1
- package/public/assets/{security-CYwC-26O.js → security-BH8kTdA-.js} +2 -2
- package/public/assets/{security-CYwC-26O.js.map → security-BH8kTdA-.js.map} +1 -1
- package/public/assets/{select-B19D5ToH.js → select-CpyCRBK3.js} +2 -2
- package/public/assets/{select-B19D5ToH.js.map → select-CpyCRBK3.js.map} +1 -1
- package/public/assets/{settings-BzYvlKIL.js → settings-DYNFGC2-.js} +2 -2
- package/public/assets/{settings-BzYvlKIL.js.map → settings-DYNFGC2-.js.map} +1 -1
- package/public/assets/{tasks-vZ5VRoO8.js → tasks-rdy9Nsbf.js} +2 -2
- package/public/assets/{tasks-vZ5VRoO8.js.map → tasks-rdy9Nsbf.js.map} +1 -1
- package/public/assets/{teams-E3rI5gkA.js → teams-DHloBagJ.js} +2 -2
- package/public/assets/{teams-E3rI5gkA.js.map → teams-DHloBagJ.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-24nB6STe.js → terminal-viewer-impl-DWTuNejR.js} +2 -2
- package/public/assets/{terminal-viewer-impl-24nB6STe.js.map → terminal-viewer-impl-DWTuNejR.js.map} +1 -1
- package/public/assets/{work-queue-Bql5E-C3.js → work-queue-CShT7nTC.js} +2 -2
- package/public/assets/{work-queue-Bql5E-C3.js.map → work-queue-CShT7nTC.js.map} +1 -1
- package/public/index.html +2 -2
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/src/config.ts +24 -3
- package/public/assets/PlanGraphPanel-DI5JOnm1.js +0 -2
- package/public/assets/PlanGraphPanel-DI5JOnm1.js.map +0 -1
package/docs/openapi.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Agent Relay API",
|
|
5
|
-
"version": "0.120.
|
|
5
|
+
"version": "0.120.1",
|
|
6
6
|
"description": "Real-time message bus for inter-agent communication. Agent-first: this spec is designed for machine consumption — agents can self-discover the full API surface via GET /api/spec.",
|
|
7
7
|
"license": {
|
|
8
8
|
"name": "MIT",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-relay-server",
|
|
3
|
-
"version": "0.120.
|
|
3
|
+
"version": "0.120.1",
|
|
4
4
|
"description": "Lightweight HTTP message relay for inter-agent communication across machines",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"CONTRIBUTING.md"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"agent-relay-channels-host": "0.120.
|
|
40
|
+
"agent-relay-channels-host": "0.120.1",
|
|
41
41
|
"agent-relay-providers": "0.104.3",
|
|
42
42
|
"agent-relay-sdk": "0.2.107",
|
|
43
43
|
"ajv": "^8.20.0"
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{Gn as t,Kn as n,S as r,Sn as i,Ut as a,a as o,bn as s,en as c,er as l,hr as u,ir as d,l as f,nr as p,pn as m,pt as h,r as g}from"./lucide-react-Dc3ZwCBi.js";import{t as _,y as v}from"./store-BuuJUn70.js";import{o as y}from"./types-uVOXgvMT.js";import{V as b,_ as x,f as S,k as C,v as ee}from"./display-exQDWum3.js";import{t as w}from"./agent-type-icon-BEz393ra.js";import{t as T}from"./formatted-body-ChERiS0y.js";import{A as E,L as D,M as O,O as te,P as ne,T as k,h as A,k as j,l as M,n as N,o as P,t as F,w as I}from"./index-CtBqO8vm.js";import{t as L}from"./user-avatar-DuwCFrER.js";var R=e(u(),1);function re(){return(0,R.useSyncExternalStore)(e=>v.subscribe(e),()=>v.getPlayingKey(),()=>null)}var z=p();function ie({msg:e,peer:t}){return(0,z.jsx)(A,{msg:e,issueRepo:_(e=>e.agentsById[t]?.issueRepo)})}function B({agent:e,onClick:t}){let n=!!(t&&e);return(0,z.jsx)(`button`,{type:`button`,title:e?e.label||e.name||e.id:void 0,onClick:t,disabled:!n,className:l(`shrink-0 self-end transition-opacity`,n?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,z.jsx)(w,{agent:e})})}function ae({agentId:e,onOpen:t}){let n=_(t=>t.agentsById[e]??null);return(0,z.jsx)(B,{agent:n,onClick:n?()=>t(n):void 0})}function V({attachment:e,outbound:t}){let n=e.kind===`image`,r=e.title||e.artifactId,[i,u]=(0,R.useState)(null),[f,p]=(0,R.useState)(null),[m,_]=(0,R.useState)(!1),[v,y]=(0,R.useState)(!1);(0,R.useEffect)(()=>{let t=!1,n=null;async function r(){_(!0),p(null),u(null);try{let r=await d(`/artifacts/${encodeURIComponent(e.artifactId)}/content`);if(t)return;n=URL.createObjectURL(r),u(n)}catch(e){t||p(e.message)}finally{t||_(!1)}}return r(),()=>{t=!0,n&&URL.revokeObjectURL(n)}},[e.artifactId]);function b(){if(!i)return;let e=document.createElement(`a`);e.href=i,e.download=r,document.body.appendChild(e),e.click(),e.remove()}return n?(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(`button`,{type:`button`,onClick:()=>i&&y(!0),disabled:!i,className:`group/img mt-2 block overflow-hidden rounded-md border border-foreground/10 bg-black/10 text-left disabled:cursor-default relative`,children:i?(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(`img`,{src:i,alt:r,loading:`lazy`,className:`block max-h-72 max-w-full object-contain`}),(0,z.jsx)(`span`,{className:`absolute inset-0 flex items-center justify-center bg-black/0 group-hover/img:bg-black/30 transition-colors`,children:(0,z.jsx)(g,{className:`w-6 h-6 text-white opacity-0 group-hover/img:opacity-100 transition-opacity drop-shadow-md`})})]}):(0,z.jsxs)(`span`,{className:l(`flex min-h-20 items-center gap-2 px-3 py-2 text-xs`,f?`text-red-400`:`text-muted-foreground`),children:[m?(0,z.jsx)(h,{className:`h-4 w-4 animate-spin`}):(0,z.jsx)(s,{className:`h-4 w-4`}),f||`Loading image`]})}),i&&(0,z.jsx)(F,{open:v,onOpenChange:y,children:(0,z.jsxs)(N,{className:`max-w-[95vw] max-h-[95vh] w-auto p-0 bg-black/95 border-zinc-800 gap-0 overflow-hidden`,showCloseButton:!1,children:[(0,z.jsx)(P,{className:`sr-only`,children:r}),(0,z.jsxs)(`div`,{className:`relative flex items-center justify-center min-h-[200px]`,children:[(0,z.jsx)(`img`,{src:i,alt:r,className:`max-w-[93vw] max-h-[90vh] object-contain`}),(0,z.jsxs)(`div`,{className:`absolute top-2 right-2 flex gap-1`,children:[(0,z.jsx)(`button`,{type:`button`,onClick:b,className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Download`,children:(0,z.jsx)(c,{className:`w-4 h-4`})}),(0,z.jsx)(`button`,{type:`button`,onClick:()=>y(!1),className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Close`,children:(0,z.jsx)(o,{className:`w-4 h-4`})})]})]})]})})]}):(0,z.jsxs)(`button`,{type:`button`,onClick:b,disabled:!i,className:l(`mt-2 flex w-full items-center gap-2 rounded-md border px-2.5 py-2 text-left text-xs transition-colors disabled:cursor-default disabled:opacity-70`,t?`border-primary-foreground/20 bg-primary-foreground/10 hover:bg-primary-foreground/15`:`border-border bg-background hover:bg-muted`),children:[m?(0,z.jsx)(h,{className:`h-4 w-4 shrink-0 animate-spin`}):f?(0,z.jsx)(s,{className:`h-4 w-4 shrink-0 text-red-400`}):(0,z.jsx)(a,{className:`h-4 w-4 shrink-0`}),(0,z.jsx)(`span`,{className:l(`min-w-0 flex-1 truncate`,f&&`text-red-400`),children:f||r}),(0,z.jsx)(c,{className:`h-3.5 w-3.5 shrink-0`})]})}function H({open:e,onToggle:t,onReact:n}){return(0,z.jsxs)(`div`,{className:`relative shrink-0`,children:[(0,z.jsx)(`button`,{type:`button`,title:`Add reaction`,onClick:t,className:l(`inline-flex h-5 w-6 items-center justify-center rounded-full border border-border bg-popover text-muted-foreground shadow-sm transition hover:bg-muted hover:text-foreground`,e?`opacity-100`:`opacity-0 pointer-events-none md:group-hover/msg:opacity-100 md:group-hover/msg:pointer-events-auto`),children:(0,z.jsx)(r,{className:`h-3 w-3`})}),e&&(0,z.jsx)(`div`,{className:`absolute bottom-full left-0 z-30 mb-1 flex gap-0.5 rounded-full border border-border bg-popover p-0.5 shadow-md`,children:I.map(e=>(0,z.jsx)(`button`,{type:`button`,title:`React ${e}`,onClick:()=>n(e),className:`inline-flex h-6 w-7 items-center justify-center rounded-full text-xs transition-colors hover:bg-muted`,children:e},e))})]})}function U({msg:e,peer:r}){let[a,o]=(0,R.useState)(!1),s=_(e=>e.agentsById),c=_(e=>e.openAgentDetail),u=e.from===r,d=(u?e.to:e.from)||``,f=s[u?r:d]?.issueRepo??s[r]?.issueRepo,p=!d||d===`broadcast`||d.includes(`:`),m=C(e),h=k(e.occurredAt??e.createdAt),g=u?t:n,v=s[e.from??``]??null;function y(e){e.stopPropagation(),v&&c(v)}return(0,z.jsx)(`div`,{"data-msg-id":e.id,className:`flex justify-center mb-1.5`,children:(0,z.jsxs)(`div`,{className:`w-full max-w-[94%] rounded-md border border-border/50 bg-muted/15 px-2.5 py-1`,children:[(0,z.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs cursor-pointer select-none`,onClick:()=>o(e=>!e),children:[(0,z.jsx)(i,{className:l(`h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform`,a&&`rotate-90`)}),(0,z.jsx)(`button`,{type:`button`,title:v?v.label||v.name||v.id:void 0,disabled:!v,onClick:y,className:l(`shrink-0 transition-opacity`,v?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,z.jsx)(w,{agent:v,className:`!w-4 !h-4 !rounded-sm opacity-70`})}),(0,z.jsx)(g,{className:`h-3 w-3 shrink-0 text-muted-foreground/70`}),(0,z.jsxs)(`span`,{className:`shrink-0 text-muted-foreground`,children:[`Message `,u?`to`:`from`]}),p?(0,z.jsx)(`span`,{className:`shrink-0 font-medium text-muted-foreground`,children:S(d,s)}):(0,z.jsx)(M,{agentId:d,className:`shrink-0 px-0.5 text-foreground/80`,noIcon:!0}),!a&&(0,z.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-muted-foreground/50`,children:m}),(0,z.jsx)(`span`,{className:`ml-auto shrink-0 text-muted-foreground/40`,children:h})]}),a&&(0,z.jsx)(`div`,{className:`mt-1.5 pl-5`,children:(0,z.jsx)(T,{text:m,rawBody:e.body,issueRepo:f,className:`text-sm leading-relaxed text-foreground/90`})})]})})}var W=(0,R.memo)(function({msg:e,peer:t,showAvatar:n=!1,onOpenReferencedPath:r,onPreviewReferencedPath:i,onPreviewReferencedPathEnd:a}){if(!y(e.from)&&!y(e.to))return(0,z.jsx)(U,{msg:e,peer:t});let o=e.kind===`session`?e.payload?.session:void 0;if(o?.type===`prompt`&&o?.origin===`provider`)return(0,z.jsx)(ie,{msg:e,peer:t});let s=y(e.from),c=_(e=>e.reactToMessage),u=_(e=>e.voiceTtsEnabled),d=re(),p=_(e=>{let n=e.agentsById[t]?.meta?.cwd;return typeof n==`string`?n:``}),h=_(e=>e.agentsById[t]?.issueRepo),g=_(e=>e.usersById),S=_(e=>e.users.length>1),w=s?b(e.from,g):void 0,A=_(e=>e.openAgentDetail);_(e=>e.openTaskDetailById);let[M,N]=(0,R.useState)(!1),P=(0,R.useRef)(null),F=(0,R.useRef)(null),I=(0,R.useRef)(!1),B=C(e),W=k(e.occurredAt??e.createdAt),G=O(e),K=te(e),q=s?ne(e,t):null,J=q?.icon,Y=d===String(e.id),oe=u&&!s&&!E(e)&&B.trim().length>0;function se(t){t.stopPropagation(),Y?v.bargeIn():v.speak(e.body,String(e.id))}(0,R.useEffect)(()=>{if(!M)return;function e(e){j()||P.current&&!P.current.contains(e.target)&&N(!1)}return document.addEventListener(`mousedown`,e),document.addEventListener(`touchstart`,e),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`touchstart`,e)}},[M]);function X(e){return D(p||`/`,e)}function Z(e,t){let n=X(e);r?.(n,t)}function ce(e,t,n){let r=X(e);i?.(r,t,n)}function Q(t){c(e,t),N(!1)}function le(e){e.button===0&&(F.current={x:e.clientX,y:e.clientY},I.current=!1)}function ue(e){let t=F.current;t&&(Math.abs(e.clientX-t.x)>3||Math.abs(e.clientY-t.y)>3)&&(I.current=!0)}function de(t){if(!(E(e)||s)&&!t.target.closest(`a,button,input,textarea,select,[role="button"]`)){if(I.current||j()){I.current=!1;return}N(e=>!e)}}let $=n&&!E(e);return(0,z.jsxs)(`div`,{"data-msg-id":e.id,className:l(`group/msg flex mb-3 items-end`,s?`justify-end`:`justify-start`,$&&`gap-1.5`),children:[!s&&$&&(0,z.jsx)(ae,{agentId:t,onOpen:A}),(0,z.jsxs)(`div`,{ref:P,className:l(`relative`,$?`max-w-[calc(85%-2rem)] md:max-w-[calc(75%-2rem)]`:`max-w-[85%] md:max-w-[75%]`),children:[oe&&(0,z.jsx)(`button`,{type:`button`,title:Y?`Stop playback`:`Play aloud`,onClick:se,className:l(`absolute -top-2 -right-2 z-20 inline-flex h-6 w-6 items-center justify-center rounded-full border bg-popover shadow-sm transition`,Y?`border-primary/50 text-primary opacity-100`:`border-border text-muted-foreground opacity-70 hover:bg-muted hover:text-foreground hover:opacity-100 [@media(hover:hover)]:md:opacity-0 [@media(hover:hover)]:md:group-hover/msg:opacity-100`),children:Y?(0,z.jsx)(m,{className:`h-3.5 w-3.5 animate-pulse`}):(0,z.jsx)(f,{className:`h-3.5 w-3.5`})}),(0,z.jsxs)(`div`,{className:l(`rounded-2xl px-3.5 py-2 text-sm select-text`,!s&&!E(e)&&`cursor-pointer`,s?`bg-primary text-primary-foreground rounded-br-sm`:`bg-card ring-1 ring-foreground/10 rounded-bl-sm`),onPointerDown:le,onPointerMove:ue,onClick:de,children:[s&&S&&(0,z.jsx)(`p`,{className:l(`text-[11px] font-semibold mb-0.5 text-right`,x(e.from)),children:ee(e.from,g)}),e.subject&&(0,z.jsx)(`p`,{className:l(`text-xs font-semibold mb-1`,s?`text-primary-foreground/70`:`text-muted-foreground`),children:e.subject}),(0,z.jsx)(T,{text:B,rawBody:e.body,issueRepo:h,className:`leading-relaxed`,onPrimary:s,onOpenPath:Z,onPreviewPath:ce,onPreviewPathEnd:a,resolvePathTitle:(e,t)=>`${X(e)}${t?`:${t}`:``}`}),G.map(t=>(0,z.jsx)(V,{attachment:t,outbound:s},`${e.id}-${t.artifactId}-${t.role||``}`)),(0,z.jsxs)(`p`,{className:l(`text-xs mt-1 flex items-center gap-1.5`,s?`justify-end text-primary-foreground/60`:`justify-end text-muted-foreground`),children:[(0,z.jsxs)(`span`,{className:`opacity-40`,children:[`#`,e.id]}),(0,z.jsx)(`span`,{children:W}),q&&J&&(0,z.jsxs)(`span`,{className:l(`inline-flex items-center gap-1`,q.tone===`ok`&&`text-emerald-300`,q.tone===`warn`&&`text-amber-200`,q.tone===`danger`&&`text-red-200`),title:q.title,children:[(0,z.jsx)(J,{className:`h-3 w-3`}),q.label]})]})]}),!E(e)&&K.length>0&&(0,z.jsxs)(`div`,{className:l(`flex flex-wrap items-center gap-0.5 -mt-1 relative z-10`,s?`justify-end pr-2`:`justify-start pl-2`),children:[!s&&(0,z.jsx)(H,{open:M,onToggle:()=>N(e=>!e),onReact:Q}),K.map(e=>(0,z.jsxs)(`button`,{type:`button`,title:e.actors.join(`, `),onClick:()=>Q(e.emoji),className:l(`inline-flex h-5 items-center gap-0.5 rounded-full border px-1.5 text-[11px] shadow-sm transition-colors`,e.mine?`border-primary/40 bg-primary/10 text-primary`:`border-border bg-background hover:bg-muted text-foreground`),children:[(0,z.jsx)(`span`,{children:e.emoji}),e.count>1&&(0,z.jsx)(`span`,{children:e.count})]},e.emoji))]}),!E(e)&&!s&&K.length===0&&(0,z.jsx)(`div`,{className:`absolute -bottom-2.5 left-1 z-20`,children:(0,z.jsx)(H,{open:M,onToggle:()=>N(e=>!e),onReact:Q})})]}),s&&$&&(0,z.jsx)(`span`,{className:`shrink-0 self-end opacity-60`,children:(0,z.jsx)(L,{user:w,address:e.from,distinct:S})})]})});export{W as t};
|
|
2
|
-
//# sourceMappingURL=MessageBubble-
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{Gn as t,Kn as n,S as r,Sn as i,Ut as a,a as o,bn as s,en as c,er as l,hr as u,ir as d,l as f,nr as p,pn as m,pt as h,r as g}from"./lucide-react-Dc3ZwCBi.js";import{t as _,y as v}from"./store-BuuJUn70.js";import{o as y}from"./types-uVOXgvMT.js";import{V as b,_ as x,f as S,k as C,v as ee}from"./display-exQDWum3.js";import{t as w}from"./agent-type-icon-BEz393ra.js";import{t as T}from"./formatted-body-Dg9R3RD7.js";import{A as E,L as D,M as O,O as te,P as ne,T as k,h as A,k as j,l as M,n as N,o as P,t as F,w as I}from"./index-D_fNJnsI.js";import{t as L}from"./user-avatar-DuwCFrER.js";var R=e(u(),1);function re(){return(0,R.useSyncExternalStore)(e=>v.subscribe(e),()=>v.getPlayingKey(),()=>null)}var z=p();function ie({msg:e,peer:t}){return(0,z.jsx)(A,{msg:e,issueRepo:_(e=>e.agentsById[t]?.issueRepo)})}function B({agent:e,onClick:t}){let n=!!(t&&e);return(0,z.jsx)(`button`,{type:`button`,title:e?e.label||e.name||e.id:void 0,onClick:t,disabled:!n,className:l(`shrink-0 self-end transition-opacity`,n?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,z.jsx)(w,{agent:e})})}function ae({agentId:e,onOpen:t}){let n=_(t=>t.agentsById[e]??null);return(0,z.jsx)(B,{agent:n,onClick:n?()=>t(n):void 0})}function V({attachment:e,outbound:t}){let n=e.kind===`image`,r=e.title||e.artifactId,[i,u]=(0,R.useState)(null),[f,p]=(0,R.useState)(null),[m,_]=(0,R.useState)(!1),[v,y]=(0,R.useState)(!1);(0,R.useEffect)(()=>{let t=!1,n=null;async function r(){_(!0),p(null),u(null);try{let r=await d(`/artifacts/${encodeURIComponent(e.artifactId)}/content`);if(t)return;n=URL.createObjectURL(r),u(n)}catch(e){t||p(e.message)}finally{t||_(!1)}}return r(),()=>{t=!0,n&&URL.revokeObjectURL(n)}},[e.artifactId]);function b(){if(!i)return;let e=document.createElement(`a`);e.href=i,e.download=r,document.body.appendChild(e),e.click(),e.remove()}return n?(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(`button`,{type:`button`,onClick:()=>i&&y(!0),disabled:!i,className:`group/img mt-2 block overflow-hidden rounded-md border border-foreground/10 bg-black/10 text-left disabled:cursor-default relative`,children:i?(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(`img`,{src:i,alt:r,loading:`lazy`,className:`block max-h-72 max-w-full object-contain`}),(0,z.jsx)(`span`,{className:`absolute inset-0 flex items-center justify-center bg-black/0 group-hover/img:bg-black/30 transition-colors`,children:(0,z.jsx)(g,{className:`w-6 h-6 text-white opacity-0 group-hover/img:opacity-100 transition-opacity drop-shadow-md`})})]}):(0,z.jsxs)(`span`,{className:l(`flex min-h-20 items-center gap-2 px-3 py-2 text-xs`,f?`text-red-400`:`text-muted-foreground`),children:[m?(0,z.jsx)(h,{className:`h-4 w-4 animate-spin`}):(0,z.jsx)(s,{className:`h-4 w-4`}),f||`Loading image`]})}),i&&(0,z.jsx)(F,{open:v,onOpenChange:y,children:(0,z.jsxs)(N,{className:`max-w-[95vw] max-h-[95vh] w-auto p-0 bg-black/95 border-zinc-800 gap-0 overflow-hidden`,showCloseButton:!1,children:[(0,z.jsx)(P,{className:`sr-only`,children:r}),(0,z.jsxs)(`div`,{className:`relative flex items-center justify-center min-h-[200px]`,children:[(0,z.jsx)(`img`,{src:i,alt:r,className:`max-w-[93vw] max-h-[90vh] object-contain`}),(0,z.jsxs)(`div`,{className:`absolute top-2 right-2 flex gap-1`,children:[(0,z.jsx)(`button`,{type:`button`,onClick:b,className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Download`,children:(0,z.jsx)(c,{className:`w-4 h-4`})}),(0,z.jsx)(`button`,{type:`button`,onClick:()=>y(!1),className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Close`,children:(0,z.jsx)(o,{className:`w-4 h-4`})})]})]})]})})]}):(0,z.jsxs)(`button`,{type:`button`,onClick:b,disabled:!i,className:l(`mt-2 flex w-full items-center gap-2 rounded-md border px-2.5 py-2 text-left text-xs transition-colors disabled:cursor-default disabled:opacity-70`,t?`border-primary-foreground/20 bg-primary-foreground/10 hover:bg-primary-foreground/15`:`border-border bg-background hover:bg-muted`),children:[m?(0,z.jsx)(h,{className:`h-4 w-4 shrink-0 animate-spin`}):f?(0,z.jsx)(s,{className:`h-4 w-4 shrink-0 text-red-400`}):(0,z.jsx)(a,{className:`h-4 w-4 shrink-0`}),(0,z.jsx)(`span`,{className:l(`min-w-0 flex-1 truncate`,f&&`text-red-400`),children:f||r}),(0,z.jsx)(c,{className:`h-3.5 w-3.5 shrink-0`})]})}function H({open:e,onToggle:t,onReact:n}){return(0,z.jsxs)(`div`,{className:`relative shrink-0`,children:[(0,z.jsx)(`button`,{type:`button`,title:`Add reaction`,onClick:t,className:l(`inline-flex h-5 w-6 items-center justify-center rounded-full border border-border bg-popover text-muted-foreground shadow-sm transition hover:bg-muted hover:text-foreground`,e?`opacity-100`:`opacity-0 pointer-events-none md:group-hover/msg:opacity-100 md:group-hover/msg:pointer-events-auto`),children:(0,z.jsx)(r,{className:`h-3 w-3`})}),e&&(0,z.jsx)(`div`,{className:`absolute bottom-full left-0 z-30 mb-1 flex gap-0.5 rounded-full border border-border bg-popover p-0.5 shadow-md`,children:I.map(e=>(0,z.jsx)(`button`,{type:`button`,title:`React ${e}`,onClick:()=>n(e),className:`inline-flex h-6 w-7 items-center justify-center rounded-full text-xs transition-colors hover:bg-muted`,children:e},e))})]})}function U({msg:e,peer:r}){let[a,o]=(0,R.useState)(!1),s=_(e=>e.agentsById),c=_(e=>e.openAgentDetail),u=e.from===r,d=(u?e.to:e.from)||``,f=s[u?r:d]?.issueRepo??s[r]?.issueRepo,p=!d||d===`broadcast`||d.includes(`:`),m=C(e),h=k(e.occurredAt??e.createdAt),g=u?t:n,v=s[e.from??``]??null;function y(e){e.stopPropagation(),v&&c(v)}return(0,z.jsx)(`div`,{"data-msg-id":e.id,className:`flex justify-center mb-1.5`,children:(0,z.jsxs)(`div`,{className:`w-full max-w-[94%] rounded-md border border-border/50 bg-muted/15 px-2.5 py-1`,children:[(0,z.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs cursor-pointer select-none`,onClick:()=>o(e=>!e),children:[(0,z.jsx)(i,{className:l(`h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform`,a&&`rotate-90`)}),(0,z.jsx)(`button`,{type:`button`,title:v?v.label||v.name||v.id:void 0,disabled:!v,onClick:y,className:l(`shrink-0 transition-opacity`,v?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,z.jsx)(w,{agent:v,className:`!w-4 !h-4 !rounded-sm opacity-70`})}),(0,z.jsx)(g,{className:`h-3 w-3 shrink-0 text-muted-foreground/70`}),(0,z.jsxs)(`span`,{className:`shrink-0 text-muted-foreground`,children:[`Message `,u?`to`:`from`]}),p?(0,z.jsx)(`span`,{className:`shrink-0 font-medium text-muted-foreground`,children:S(d,s)}):(0,z.jsx)(M,{agentId:d,className:`shrink-0 px-0.5 text-foreground/80`,noIcon:!0}),!a&&(0,z.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-muted-foreground/50`,children:m}),(0,z.jsx)(`span`,{className:`ml-auto shrink-0 text-muted-foreground/40`,children:h})]}),a&&(0,z.jsx)(`div`,{className:`mt-1.5 pl-5`,children:(0,z.jsx)(T,{text:m,rawBody:e.body,issueRepo:f,className:`text-sm leading-relaxed text-foreground/90`})})]})})}var W=(0,R.memo)(function({msg:e,peer:t,showAvatar:n=!1,onOpenReferencedPath:r,onPreviewReferencedPath:i,onPreviewReferencedPathEnd:a}){if(!y(e.from)&&!y(e.to))return(0,z.jsx)(U,{msg:e,peer:t});let o=e.kind===`session`?e.payload?.session:void 0;if(o?.type===`prompt`&&o?.origin===`provider`)return(0,z.jsx)(ie,{msg:e,peer:t});let s=y(e.from),c=_(e=>e.reactToMessage),u=_(e=>e.voiceTtsEnabled),d=re(),p=_(e=>{let n=e.agentsById[t]?.meta?.cwd;return typeof n==`string`?n:``}),h=_(e=>e.agentsById[t]?.issueRepo),g=_(e=>e.usersById),S=_(e=>e.users.length>1),w=s?b(e.from,g):void 0,A=_(e=>e.openAgentDetail);_(e=>e.openTaskDetailById);let[M,N]=(0,R.useState)(!1),P=(0,R.useRef)(null),F=(0,R.useRef)(null),I=(0,R.useRef)(!1),B=C(e),W=k(e.occurredAt??e.createdAt),G=O(e),K=te(e),q=s?ne(e,t):null,J=q?.icon,Y=d===String(e.id),oe=u&&!s&&!E(e)&&B.trim().length>0;function se(t){t.stopPropagation(),Y?v.bargeIn():v.speak(e.body,String(e.id))}(0,R.useEffect)(()=>{if(!M)return;function e(e){j()||P.current&&!P.current.contains(e.target)&&N(!1)}return document.addEventListener(`mousedown`,e),document.addEventListener(`touchstart`,e),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`touchstart`,e)}},[M]);function X(e){return D(p||`/`,e)}function Z(e,t){let n=X(e);r?.(n,t)}function ce(e,t,n){let r=X(e);i?.(r,t,n)}function Q(t){c(e,t),N(!1)}function le(e){e.button===0&&(F.current={x:e.clientX,y:e.clientY},I.current=!1)}function ue(e){let t=F.current;t&&(Math.abs(e.clientX-t.x)>3||Math.abs(e.clientY-t.y)>3)&&(I.current=!0)}function de(t){if(!(E(e)||s)&&!t.target.closest(`a,button,input,textarea,select,[role="button"]`)){if(I.current||j()){I.current=!1;return}N(e=>!e)}}let $=n&&!E(e);return(0,z.jsxs)(`div`,{"data-msg-id":e.id,className:l(`group/msg flex mb-3 items-end`,s?`justify-end`:`justify-start`,$&&`gap-1.5`),children:[!s&&$&&(0,z.jsx)(ae,{agentId:t,onOpen:A}),(0,z.jsxs)(`div`,{ref:P,className:l(`relative`,$?`max-w-[calc(85%-2rem)] md:max-w-[calc(75%-2rem)]`:`max-w-[85%] md:max-w-[75%]`),children:[oe&&(0,z.jsx)(`button`,{type:`button`,title:Y?`Stop playback`:`Play aloud`,onClick:se,className:l(`absolute -top-2 -right-2 z-20 inline-flex h-6 w-6 items-center justify-center rounded-full border bg-popover shadow-sm transition`,Y?`border-primary/50 text-primary opacity-100`:`border-border text-muted-foreground opacity-70 hover:bg-muted hover:text-foreground hover:opacity-100 [@media(hover:hover)]:md:opacity-0 [@media(hover:hover)]:md:group-hover/msg:opacity-100`),children:Y?(0,z.jsx)(m,{className:`h-3.5 w-3.5 animate-pulse`}):(0,z.jsx)(f,{className:`h-3.5 w-3.5`})}),(0,z.jsxs)(`div`,{className:l(`rounded-2xl px-3.5 py-2 text-sm select-text`,!s&&!E(e)&&`cursor-pointer`,s?`bg-primary text-primary-foreground rounded-br-sm`:`bg-card ring-1 ring-foreground/10 rounded-bl-sm`),onPointerDown:le,onPointerMove:ue,onClick:de,children:[s&&S&&(0,z.jsx)(`p`,{className:l(`text-[11px] font-semibold mb-0.5 text-right`,x(e.from)),children:ee(e.from,g)}),e.subject&&(0,z.jsx)(`p`,{className:l(`text-xs font-semibold mb-1`,s?`text-primary-foreground/70`:`text-muted-foreground`),children:e.subject}),(0,z.jsx)(T,{text:B,rawBody:e.body,issueRepo:h,className:`leading-relaxed`,onPrimary:s,onOpenPath:Z,onPreviewPath:ce,onPreviewPathEnd:a,resolvePathTitle:(e,t)=>`${X(e)}${t?`:${t}`:``}`}),G.map(t=>(0,z.jsx)(V,{attachment:t,outbound:s},`${e.id}-${t.artifactId}-${t.role||``}`)),(0,z.jsxs)(`p`,{className:l(`text-xs mt-1 flex items-center gap-1.5`,s?`justify-end text-primary-foreground/60`:`justify-end text-muted-foreground`),children:[(0,z.jsxs)(`span`,{className:`opacity-40`,children:[`#`,e.id]}),(0,z.jsx)(`span`,{children:W}),q&&J&&(0,z.jsxs)(`span`,{className:l(`inline-flex items-center gap-1`,q.tone===`ok`&&`text-emerald-300`,q.tone===`warn`&&`text-amber-200`,q.tone===`danger`&&`text-red-200`),title:q.title,children:[(0,z.jsx)(J,{className:`h-3 w-3`}),q.label]})]})]}),!E(e)&&K.length>0&&(0,z.jsxs)(`div`,{className:l(`flex flex-wrap items-center gap-0.5 -mt-1 relative z-10`,s?`justify-end pr-2`:`justify-start pl-2`),children:[!s&&(0,z.jsx)(H,{open:M,onToggle:()=>N(e=>!e),onReact:Q}),K.map(e=>(0,z.jsxs)(`button`,{type:`button`,title:e.actors.join(`, `),onClick:()=>Q(e.emoji),className:l(`inline-flex h-5 items-center gap-0.5 rounded-full border px-1.5 text-[11px] shadow-sm transition-colors`,e.mine?`border-primary/40 bg-primary/10 text-primary`:`border-border bg-background hover:bg-muted text-foreground`),children:[(0,z.jsx)(`span`,{children:e.emoji}),e.count>1&&(0,z.jsx)(`span`,{children:e.count})]},e.emoji))]}),!E(e)&&!s&&K.length===0&&(0,z.jsx)(`div`,{className:`absolute -bottom-2.5 left-1 z-20`,children:(0,z.jsx)(H,{open:M,onToggle:()=>N(e=>!e),onReact:Q})})]}),s&&$&&(0,z.jsx)(`span`,{className:`shrink-0 self-end opacity-60`,children:(0,z.jsx)(L,{user:w,address:e.from,distinct:S})})]})});export{W as t};
|
|
2
|
+
//# sourceMappingURL=MessageBubble-jpshgh3K.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageBubble-BJXjk3GB.js","names":[],"sources":["../../dashboard/src/components/chat/hooks/useTtsPlayingKey.ts","../../dashboard/src/components/chat/MessageBubble.tsx"],"sourcesContent":["\nimport { useSyncExternalStore } from 'react'\nimport { voiceTts } from '@/lib/voice'\n\nexport function useTtsPlayingKey(): string | null {\n return useSyncExternalStore(\n (cb) => voiceTts.subscribe(cb),\n () => voiceTts.getPlayingKey(),\n () => null,\n )\n}\n","\nimport { memo, useEffect, useRef, useState, type MouseEvent as ReactMouseEvent } from 'react'\nimport { AlertCircle, ArrowLeft, ArrowRight, ChevronRight, CircleStop, Download, FileText, Loader2, SmilePlus, Volume2, X, ZoomIn } from 'lucide-react'\nimport { voiceTts } from '@/lib/voice'\nimport { useRelayStore } from '@/store'\nimport { FormattedBody } from '@/components/shared/formatted-body'\nimport { AgentLink, InitialPromptBubble } from './TraceView'\nimport { AgentTypeIcon } from '@/components/shared/agent-type-icon'\nimport { Button } from '@/components/ui/button'\nimport { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'\nimport { apiBlob } from '@/lib/api'\nimport { displayTarget, humanAccentClass, humanDisplayName, messageBody, resolveUser } from '@/lib/display'\nimport { isHumanAgentId } from 'agent-relay-sdk/types'\nimport { UserAvatar } from '@/components/shared/user-avatar'\nimport type { Agent, AttachmentRef, Message } from '@/types'\nimport { cn } from '@/lib/utils'\nimport type { ReferencedPathInfo } from './types'\nimport {\n chatTimestamp,\n groupedReactions,\n hasActiveTextSelection,\n isReactionEvent,\n messageAttachments,\n outboundReceipt,\n QUICK_REACTIONS,\n resolveRelativePath,\n} from './helpers'\nimport { useTtsPlayingKey } from './hooks/useTtsPlayingKey'\n\n// Spawn prompts (origin:\"provider\") are system-initiated — reuse InitialPromptBubble's\n// panel style rather than masquerading as an operator prompt bubble (Tier A, #420).\nfunction SpawnPromptBubble({ msg, peer }: { msg: Message; peer: string }) {\n const issueRepo = useRelayStore((s) => s.agentsById[peer]?.issueRepo)\n return <InitialPromptBubble msg={msg} issueRepo={issueRepo} />\n}\n\n// Small tappable sender avatar placed at the edge of a bubble (Tier B, #420).\n// Uses AgentTypeIcon so provider icon resolution stays in one place (DRY ratchet).\nfunction BubbleAvatar({ agent, onClick }: { agent: Agent | null; onClick?: () => void }) {\n const canClick = Boolean(onClick && agent)\n return (\n <button\n type=\"button\"\n title={agent ? (agent.label || agent.name || agent.id) : undefined}\n onClick={onClick}\n disabled={!canClick}\n className={cn('shrink-0 self-end transition-opacity', canClick ? 'cursor-pointer hover:opacity-80' : 'cursor-default opacity-60')}\n >\n <AgentTypeIcon agent={agent} />\n </button>\n )\n}\n\n// Subscribes to the agent store independently so MessageBubble's text body\n// doesn't re-render (and lose text selection) when agentsById[peer] gets a new\n// reference from a lastSeen/status poll update (#691).\nfunction ConnectedBubbleAvatar({ agentId, onOpen }: { agentId: string; onOpen: (agent: Agent) => void }) {\n const agent = useRelayStore((s) => s.agentsById[agentId] ?? null)\n return <BubbleAvatar agent={agent} onClick={agent ? () => onOpen(agent) : undefined} />\n}\n\nfunction AttachmentPreview({ attachment, outbound }: { attachment: AttachmentRef; outbound: boolean }) {\n const isImage = attachment.kind === 'image'\n const title = attachment.title || attachment.artifactId\n const [objectUrl, setObjectUrl] = useState<string | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(false)\n const [lightboxOpen, setLightboxOpen] = useState(false)\n\n useEffect(() => {\n let cancelled = false\n let url: string | null = null\n\n async function load() {\n setLoading(true)\n setError(null)\n setObjectUrl(null)\n try {\n const blob = await apiBlob(`/artifacts/${encodeURIComponent(attachment.artifactId)}/content`)\n if (cancelled) return\n url = URL.createObjectURL(blob)\n setObjectUrl(url)\n } catch (e) {\n if (!cancelled) setError((e as Error).message)\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n\n void load()\n return () => {\n cancelled = true\n if (url) URL.revokeObjectURL(url)\n }\n }, [attachment.artifactId])\n\n function download() {\n if (!objectUrl) return\n const a = document.createElement('a')\n a.href = objectUrl\n a.download = title\n document.body.appendChild(a)\n a.click()\n a.remove()\n }\n\n if (isImage) {\n return (\n <>\n <button\n type=\"button\"\n onClick={() => objectUrl && setLightboxOpen(true)}\n disabled={!objectUrl}\n className=\"group/img mt-2 block overflow-hidden rounded-md border border-foreground/10 bg-black/10 text-left disabled:cursor-default relative\"\n >\n {objectUrl ? (\n <>\n <img src={objectUrl} alt={title} loading=\"lazy\" className=\"block max-h-72 max-w-full object-contain\" />\n <span className=\"absolute inset-0 flex items-center justify-center bg-black/0 group-hover/img:bg-black/30 transition-colors\">\n <ZoomIn className=\"w-6 h-6 text-white opacity-0 group-hover/img:opacity-100 transition-opacity drop-shadow-md\" />\n </span>\n </>\n ) : (\n <span className={cn('flex min-h-20 items-center gap-2 px-3 py-2 text-xs', error ? 'text-red-400' : 'text-muted-foreground')}>\n {loading ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <AlertCircle className=\"h-4 w-4\" />}\n {error || 'Loading image'}\n </span>\n )}\n </button>\n {objectUrl && (\n <Dialog open={lightboxOpen} onOpenChange={setLightboxOpen}>\n <DialogContent className=\"max-w-[95vw] max-h-[95vh] w-auto p-0 bg-black/95 border-zinc-800 gap-0 overflow-hidden\" showCloseButton={false}>\n <DialogTitle className=\"sr-only\">{title}</DialogTitle>\n <div className=\"relative flex items-center justify-center min-h-[200px]\">\n <img src={objectUrl} alt={title} className=\"max-w-[93vw] max-h-[90vh] object-contain\" />\n <div className=\"absolute top-2 right-2 flex gap-1\">\n <button type=\"button\" onClick={download} className=\"rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors\" title=\"Download\">\n <Download className=\"w-4 h-4\" />\n </button>\n <button type=\"button\" onClick={() => setLightboxOpen(false)} className=\"rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors\" title=\"Close\">\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n )}\n </>\n )\n }\n\n return (\n <button\n type=\"button\"\n onClick={download}\n disabled={!objectUrl}\n className={cn(\n 'mt-2 flex w-full items-center gap-2 rounded-md border px-2.5 py-2 text-left text-xs transition-colors disabled:cursor-default disabled:opacity-70',\n outbound ? 'border-primary-foreground/20 bg-primary-foreground/10 hover:bg-primary-foreground/15' : 'border-border bg-background hover:bg-muted'\n )}\n >\n {loading ? <Loader2 className=\"h-4 w-4 shrink-0 animate-spin\" /> : error ? <AlertCircle className=\"h-4 w-4 shrink-0 text-red-400\" /> : <FileText className=\"h-4 w-4 shrink-0\" />}\n <span className={cn('min-w-0 flex-1 truncate', error && 'text-red-400')}>{error || title}</span>\n <Download className=\"h-3.5 w-3.5 shrink-0\" />\n </button>\n )\n}\n\nfunction AddReaction({ open, onToggle, onReact }: { open: boolean; onToggle: () => void; onReact: (emoji: string) => void }) {\n return (\n <div className=\"relative shrink-0\">\n <button\n type=\"button\"\n title=\"Add reaction\"\n onClick={onToggle}\n className={cn(\n 'inline-flex h-5 w-6 items-center justify-center rounded-full border border-border bg-popover text-muted-foreground shadow-sm transition hover:bg-muted hover:text-foreground',\n open ? 'opacity-100' : 'opacity-0 pointer-events-none md:group-hover/msg:opacity-100 md:group-hover/msg:pointer-events-auto'\n )}\n >\n <SmilePlus className=\"h-3 w-3\" />\n </button>\n {open && (\n <div className=\"absolute bottom-full left-0 z-30 mb-1 flex gap-0.5 rounded-full border border-border bg-popover p-0.5 shadow-md\">\n {QUICK_REACTIONS.map((emoji) => (\n <button\n key={emoji}\n type=\"button\"\n title={`React ${emoji}`}\n onClick={() => onReact(emoji)}\n className=\"inline-flex h-6 w-7 items-center justify-center rounded-full text-xs transition-colors hover:bg-muted\"\n >\n {emoji}\n </button>\n ))}\n </div>\n )}\n </div>\n )\n}\n\n// Inter-agent (peer↔peer) messages render as a compact, foldable one-liner — distinct\n// from the user↔agent bubbles, never persisted-open (#508). The other party is shown via\n// AgentLink (label-preferred + clickable, #522); fan-out targets (broadcast/tag:/cap:/…)\n// fall back to a plain displayTarget label since they don't resolve to one agent.\nfunction PeerMessageLine({ msg, peer }: { msg: Message; peer: string }) {\n const [expanded, setExpanded] = useState(false)\n const agentsById = useRelayStore((s) => s.agentsById)\n const openAgentDetail = useRelayStore((s) => s.openAgentDetail)\n const isOutbound = msg.from === peer\n const otherParty = (isOutbound ? msg.to : msg.from) || ''\n // Per-project #NNN linkify (#533/#543): outbound folded lines use the selected\n // peer's repo; inbound inter-agent lines use the originating agent's repo.\n const issueRepo = agentsById[isOutbound ? peer : otherParty]?.issueRepo ?? agentsById[peer]?.issueRepo\n const isSelector = !otherParty || otherParty === 'broadcast' || otherParty.includes(':')\n const body = messageBody(msg)\n const time = chatTimestamp(msg.occurredAt ?? (msg.createdAt as string))\n const DirIcon = isOutbound ? ArrowRight : ArrowLeft\n const senderAgent = agentsById[msg.from ?? ''] ?? null\n function openSenderDetail(e: ReactMouseEvent<HTMLButtonElement>) {\n e.stopPropagation()\n if (senderAgent) openAgentDetail(senderAgent)\n }\n\n return (\n <div data-msg-id={msg.id} className=\"flex justify-center mb-1.5\">\n <div className=\"w-full max-w-[94%] rounded-md border border-border/50 bg-muted/15 px-2.5 py-1\">\n <div className=\"flex items-center gap-1.5 text-xs cursor-pointer select-none\" onClick={() => setExpanded((v) => !v)}>\n <ChevronRight className={cn('h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform', expanded && 'rotate-90')} />\n <button\n type=\"button\"\n title={senderAgent ? (senderAgent.label || senderAgent.name || senderAgent.id) : undefined}\n disabled={!senderAgent}\n onClick={openSenderDetail}\n className={cn('shrink-0 transition-opacity', senderAgent ? 'cursor-pointer hover:opacity-80' : 'cursor-default opacity-60')}\n >\n <AgentTypeIcon agent={senderAgent} className=\"!w-4 !h-4 !rounded-sm opacity-70\" />\n </button>\n <DirIcon className=\"h-3 w-3 shrink-0 text-muted-foreground/70\" />\n <span className=\"shrink-0 text-muted-foreground\">Message {isOutbound ? 'to' : 'from'}</span>\n {isSelector\n ? <span className=\"shrink-0 font-medium text-muted-foreground\">{displayTarget(otherParty, agentsById)}</span>\n : <AgentLink agentId={otherParty} className=\"shrink-0 px-0.5 text-foreground/80\" noIcon />}\n {!expanded && <span className=\"min-w-0 flex-1 truncate text-muted-foreground/50\">{body}</span>}\n <span className=\"ml-auto shrink-0 text-muted-foreground/40\">{time}</span>\n </div>\n {expanded && (\n <div className=\"mt-1.5 pl-5\">\n <FormattedBody text={body} rawBody={msg.body} issueRepo={issueRepo} className=\"text-sm leading-relaxed text-foreground/90\" />\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport const MessageBubble = memo(function MessageBubble({\n msg,\n peer,\n showAvatar = false,\n onOpenReferencedPath,\n onPreviewReferencedPath,\n onPreviewReferencedPathEnd,\n}: {\n msg: Message\n peer: string\n /** Show the sender avatar on this bubble (last in a consecutive same-sender run). */\n showAvatar?: boolean\n onOpenReferencedPath?: (path: string, line?: number, type?: ReferencedPathInfo['type']) => void\n onPreviewReferencedPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewReferencedPathEnd?: () => void\n}) {\n // Inter-agent (peer↔peer) messages get a folded one-liner instead of a bubble (#508).\n // This early-return precedes all hooks, which is safe here: the timeline keys each\n // MessageBubble by msg id (VirtualTimeline.timelineEntryKey), and from/to are immutable\n // for a given id — so a mounted instance never flips between the two render paths.\n // De-singletoned human addressing (#390): any human side — legacy bare `'user'` or a\n // per-user `user:<id>` sink — renders as a user↔agent bubble; only agent↔agent stays a\n // folded peer line. isHumanAgentId keeps multi-human threads from collapsing to peer lines.\n if (!isHumanAgentId(msg.from) && !isHumanAgentId(msg.to)) {\n return <PeerMessageLine msg={msg} peer={peer} />\n }\n // Spawn prompts (kind:\"session\", type:\"prompt\", origin:\"provider\") are system-initiated\n // and must NOT masquerade as operator-typed prompts. Render with the InitialPromptBubble\n // panel style instead (#420 Tier A). Immutable payload — safe as a pre-hook early return.\n const sessionPayload = msg.kind === 'session'\n ? (msg.payload?.session as { type?: string; origin?: string } | undefined)\n : undefined\n if (sessionPayload?.type === 'prompt' && sessionPayload?.origin === 'provider') {\n return <SpawnPromptBubble msg={msg} peer={peer} />\n }\n const isOutbound = isHumanAgentId(msg.from)\n const reactToMessage = useRelayStore((s) => s.reactToMessage)\n const voiceTtsEnabled = useRelayStore((s) => s.voiceTtsEnabled)\n const ttsPlayingKey = useTtsPlayingKey()\n const peerCwd = useRelayStore((s) => {\n const cwd = s.agentsById[peer]?.meta?.cwd\n return typeof cwd === 'string' ? cwd : ''\n })\n // Per-project #NNN linkify (#533): the peer agent's resolved project repo.\n const peerIssueRepo = useRelayStore((s) => s.agentsById[peer]?.issueRepo)\n // Human attribution (#390): resolve which user sent an outbound bubble, and whether more\n // than one human exists. With the single seeded user:default, multiHuman is false and the\n // name tag / colored avatar stay off — the bubble renders exactly as before.\n const usersById = useRelayStore((s) => s.usersById)\n const multiHuman = useRelayStore((s) => s.users.length > 1)\n const sendingUser = isOutbound ? resolveUser(msg.from, usersById) : undefined\n const openAgentDetail = useRelayStore((s) => s.openAgentDetail)\n const openTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n const [showQuickReact, setShowQuickReact] = useState(false)\n const bubbleRef = useRef<HTMLDivElement>(null)\n const pointerStartRef = useRef<{ x: number; y: number } | null>(null)\n const suppressBubbleClickRef = useRef(false)\n const body = messageBody(msg)\n const time = chatTimestamp(msg.occurredAt ?? (msg.createdAt as string))\n const attachments = messageAttachments(msg)\n const reactions = groupedReactions(msg)\n const receipt = isOutbound ? outboundReceipt(msg, peer) : null\n const ReceiptIcon = receipt?.icon\n // On-demand TTS: replay any agent response through the same Kokoro/browser\n // pipeline. Only when the speaker is on, and only for inbound non-reaction text.\n const isTtsPlaying = ttsPlayingKey === String(msg.id)\n const showPlayButton = voiceTtsEnabled && !isOutbound && !isReactionEvent(msg) && body.trim().length > 0\n function togglePlay(e: React.MouseEvent) {\n e.stopPropagation()\n if (isTtsPlaying) voiceTts.bargeIn()\n else voiceTts.speak(msg.body, String(msg.id))\n }\n useEffect(() => {\n if (!showQuickReact) return\n function dismiss(e: MouseEvent | TouchEvent) {\n if (hasActiveTextSelection()) return\n if (bubbleRef.current && !bubbleRef.current.contains(e.target as Node)) setShowQuickReact(false)\n }\n document.addEventListener('mousedown', dismiss)\n document.addEventListener('touchstart', dismiss)\n return () => { document.removeEventListener('mousedown', dismiss); document.removeEventListener('touchstart', dismiss) }\n }, [showQuickReact])\n\n function resolvedReferencedPath(path: string): string {\n return resolveRelativePath(peerCwd || '/', path)\n }\n\n function openReferencedPath(path: string, line?: number) {\n const absolute = resolvedReferencedPath(path)\n onOpenReferencedPath?.(absolute, line)\n }\n\n function previewReferencedPath(path: string, line?: number, anchor?: DOMRect) {\n const absolute = resolvedReferencedPath(path)\n onPreviewReferencedPath?.(absolute, line, anchor)\n }\n\n function handleReact(emoji: string) {\n reactToMessage(msg, emoji)\n setShowQuickReact(false)\n }\n\n function handleBubblePointerDown(e: React.PointerEvent<HTMLDivElement>) {\n if (e.button !== 0) return\n pointerStartRef.current = { x: e.clientX, y: e.clientY }\n suppressBubbleClickRef.current = false\n }\n\n function handleBubblePointerMove(e: React.PointerEvent<HTMLDivElement>) {\n const start = pointerStartRef.current\n if (!start) return\n if (Math.abs(e.clientX - start.x) > 3 || Math.abs(e.clientY - start.y) > 3) {\n suppressBubbleClickRef.current = true\n }\n }\n\n function handleBubbleClick(e: React.MouseEvent<HTMLDivElement>) {\n if (isReactionEvent(msg) || isOutbound) return\n const target = e.target as HTMLElement\n if (target.closest('a,button,input,textarea,select,[role=\"button\"]')) return\n if (suppressBubbleClickRef.current || hasActiveTextSelection()) {\n suppressBubbleClickRef.current = false\n return\n }\n setShowQuickReact((v) => !v)\n }\n\n const hasAvatar = showAvatar && !isReactionEvent(msg)\n\n return (\n <div data-msg-id={msg.id} className={cn('group/msg flex mb-3 items-end', isOutbound ? 'justify-end' : 'justify-start', hasAvatar && 'gap-1.5')}>\n {!isOutbound && hasAvatar && <ConnectedBubbleAvatar agentId={peer} onOpen={openAgentDetail} />}\n <div ref={bubbleRef} className={cn('relative', hasAvatar ? 'max-w-[calc(85%-2rem)] md:max-w-[calc(75%-2rem)]' : 'max-w-[85%] md:max-w-[75%]')}>\n {showPlayButton && (\n <button\n type=\"button\"\n title={isTtsPlaying ? 'Stop playback' : 'Play aloud'}\n onClick={togglePlay}\n className={cn(\n 'absolute -top-2 -right-2 z-20 inline-flex h-6 w-6 items-center justify-center rounded-full border bg-popover shadow-sm transition',\n isTtsPlaying\n ? 'border-primary/50 text-primary opacity-100'\n // Hide-until-hover only on hover-capable devices; touch screens (iPad)\n // have no hover, so keep the button visible there instead of opacity-0.\n : 'border-border text-muted-foreground opacity-70 hover:bg-muted hover:text-foreground hover:opacity-100 [@media(hover:hover)]:md:opacity-0 [@media(hover:hover)]:md:group-hover/msg:opacity-100'\n )}\n >\n {isTtsPlaying ? <CircleStop className=\"h-3.5 w-3.5 animate-pulse\" /> : <Volume2 className=\"h-3.5 w-3.5\" />}\n </button>\n )}\n <div\n className={cn(\n 'rounded-2xl px-3.5 py-2 text-sm select-text',\n !isOutbound && !isReactionEvent(msg) && 'cursor-pointer',\n isOutbound\n ? 'bg-primary text-primary-foreground rounded-br-sm'\n : 'bg-card ring-1 ring-foreground/10 rounded-bl-sm'\n )}\n onPointerDown={handleBubblePointerDown}\n onPointerMove={handleBubblePointerMove}\n onClick={handleBubbleClick}\n >\n {isOutbound && multiHuman && (\n <p className={cn('text-[11px] font-semibold mb-0.5 text-right', humanAccentClass(msg.from))}>\n {humanDisplayName(msg.from, usersById)}\n </p>\n )}\n {msg.subject && (\n <p className={cn('text-xs font-semibold mb-1', isOutbound ? 'text-primary-foreground/70' : 'text-muted-foreground')}>\n {msg.subject}\n </p>\n )}\n <FormattedBody\n text={body}\n rawBody={msg.body}\n issueRepo={peerIssueRepo}\n className=\"leading-relaxed\"\n onPrimary={isOutbound}\n onOpenPath={openReferencedPath}\n onPreviewPath={previewReferencedPath}\n onPreviewPathEnd={onPreviewReferencedPathEnd}\n resolvePathTitle={(path, line) => `${resolvedReferencedPath(path)}${line ? `:${line}` : ''}`}\n />\n {attachments.map((attachment) => (\n <AttachmentPreview key={`${msg.id}-${attachment.artifactId}-${attachment.role || ''}`} attachment={attachment} outbound={isOutbound} />\n ))}\n <p className={cn('text-xs mt-1 flex items-center gap-1.5', isOutbound ? 'justify-end text-primary-foreground/60' : 'justify-end text-muted-foreground')}>\n <span className=\"opacity-40\">#{msg.id}</span>\n <span>{time}</span>\n {receipt && ReceiptIcon && (\n <span\n className={cn(\n 'inline-flex items-center gap-1',\n receipt.tone === 'ok' && 'text-emerald-300',\n receipt.tone === 'warn' && 'text-amber-200',\n receipt.tone === 'danger' && 'text-red-200'\n )}\n title={receipt.title}\n >\n <ReceiptIcon className=\"h-3 w-3\" />\n {receipt.label}\n </span>\n )}\n </p>\n </div>\n {!isReactionEvent(msg) && reactions.length > 0 && (\n <div className={cn('flex flex-wrap items-center gap-0.5 -mt-1 relative z-10', isOutbound ? 'justify-end pr-2' : 'justify-start pl-2')}>\n {!isOutbound && <AddReaction open={showQuickReact} onToggle={() => setShowQuickReact((v) => !v)} onReact={handleReact} />}\n {reactions.map((reaction) => (\n <button\n key={reaction.emoji}\n type=\"button\"\n title={reaction.actors.join(', ')}\n onClick={() => handleReact(reaction.emoji)}\n className={cn(\n 'inline-flex h-5 items-center gap-0.5 rounded-full border px-1.5 text-[11px] shadow-sm transition-colors',\n reaction.mine\n ? 'border-primary/40 bg-primary/10 text-primary'\n : 'border-border bg-background hover:bg-muted text-foreground'\n )}\n >\n <span>{reaction.emoji}</span>\n {reaction.count > 1 && <span>{reaction.count}</span>}\n </button>\n ))}\n </div>\n )}\n {!isReactionEvent(msg) && !isOutbound && reactions.length === 0 && (\n <div className=\"absolute -bottom-2.5 left-1 z-20\">\n <AddReaction open={showQuickReact} onToggle={() => setShowQuickReact((v) => !v)} onReact={handleReact} />\n </div>\n )}\n </div>\n {isOutbound && hasAvatar && (\n <span className=\"shrink-0 self-end opacity-60\">\n <UserAvatar user={sendingUser} address={msg.from} distinct={multiHuman} />\n </span>\n )}\n </div>\n )\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MessageBubble-jpshgh3K.js","names":[],"sources":["../../dashboard/src/components/chat/hooks/useTtsPlayingKey.ts","../../dashboard/src/components/chat/MessageBubble.tsx"],"sourcesContent":["\nimport { useSyncExternalStore } from 'react'\nimport { voiceTts } from '@/lib/voice'\n\nexport function useTtsPlayingKey(): string | null {\n return useSyncExternalStore(\n (cb) => voiceTts.subscribe(cb),\n () => voiceTts.getPlayingKey(),\n () => null,\n )\n}\n","\nimport { memo, useEffect, useRef, useState, type MouseEvent as ReactMouseEvent } from 'react'\nimport { AlertCircle, ArrowLeft, ArrowRight, ChevronRight, CircleStop, Download, FileText, Loader2, SmilePlus, Volume2, X, ZoomIn } from 'lucide-react'\nimport { voiceTts } from '@/lib/voice'\nimport { useRelayStore } from '@/store'\nimport { FormattedBody } from '@/components/shared/formatted-body'\nimport { AgentLink, InitialPromptBubble } from './TraceView'\nimport { AgentTypeIcon } from '@/components/shared/agent-type-icon'\nimport { Button } from '@/components/ui/button'\nimport { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'\nimport { apiBlob } from '@/lib/api'\nimport { displayTarget, humanAccentClass, humanDisplayName, messageBody, resolveUser } from '@/lib/display'\nimport { isHumanAgentId } from 'agent-relay-sdk/types'\nimport { UserAvatar } from '@/components/shared/user-avatar'\nimport type { Agent, AttachmentRef, Message } from '@/types'\nimport { cn } from '@/lib/utils'\nimport type { ReferencedPathInfo } from './types'\nimport {\n chatTimestamp,\n groupedReactions,\n hasActiveTextSelection,\n isReactionEvent,\n messageAttachments,\n outboundReceipt,\n QUICK_REACTIONS,\n resolveRelativePath,\n} from './helpers'\nimport { useTtsPlayingKey } from './hooks/useTtsPlayingKey'\n\n// Spawn prompts (origin:\"provider\") are system-initiated — reuse InitialPromptBubble's\n// panel style rather than masquerading as an operator prompt bubble (Tier A, #420).\nfunction SpawnPromptBubble({ msg, peer }: { msg: Message; peer: string }) {\n const issueRepo = useRelayStore((s) => s.agentsById[peer]?.issueRepo)\n return <InitialPromptBubble msg={msg} issueRepo={issueRepo} />\n}\n\n// Small tappable sender avatar placed at the edge of a bubble (Tier B, #420).\n// Uses AgentTypeIcon so provider icon resolution stays in one place (DRY ratchet).\nfunction BubbleAvatar({ agent, onClick }: { agent: Agent | null; onClick?: () => void }) {\n const canClick = Boolean(onClick && agent)\n return (\n <button\n type=\"button\"\n title={agent ? (agent.label || agent.name || agent.id) : undefined}\n onClick={onClick}\n disabled={!canClick}\n className={cn('shrink-0 self-end transition-opacity', canClick ? 'cursor-pointer hover:opacity-80' : 'cursor-default opacity-60')}\n >\n <AgentTypeIcon agent={agent} />\n </button>\n )\n}\n\n// Subscribes to the agent store independently so MessageBubble's text body\n// doesn't re-render (and lose text selection) when agentsById[peer] gets a new\n// reference from a lastSeen/status poll update (#691).\nfunction ConnectedBubbleAvatar({ agentId, onOpen }: { agentId: string; onOpen: (agent: Agent) => void }) {\n const agent = useRelayStore((s) => s.agentsById[agentId] ?? null)\n return <BubbleAvatar agent={agent} onClick={agent ? () => onOpen(agent) : undefined} />\n}\n\nfunction AttachmentPreview({ attachment, outbound }: { attachment: AttachmentRef; outbound: boolean }) {\n const isImage = attachment.kind === 'image'\n const title = attachment.title || attachment.artifactId\n const [objectUrl, setObjectUrl] = useState<string | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(false)\n const [lightboxOpen, setLightboxOpen] = useState(false)\n\n useEffect(() => {\n let cancelled = false\n let url: string | null = null\n\n async function load() {\n setLoading(true)\n setError(null)\n setObjectUrl(null)\n try {\n const blob = await apiBlob(`/artifacts/${encodeURIComponent(attachment.artifactId)}/content`)\n if (cancelled) return\n url = URL.createObjectURL(blob)\n setObjectUrl(url)\n } catch (e) {\n if (!cancelled) setError((e as Error).message)\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n\n void load()\n return () => {\n cancelled = true\n if (url) URL.revokeObjectURL(url)\n }\n }, [attachment.artifactId])\n\n function download() {\n if (!objectUrl) return\n const a = document.createElement('a')\n a.href = objectUrl\n a.download = title\n document.body.appendChild(a)\n a.click()\n a.remove()\n }\n\n if (isImage) {\n return (\n <>\n <button\n type=\"button\"\n onClick={() => objectUrl && setLightboxOpen(true)}\n disabled={!objectUrl}\n className=\"group/img mt-2 block overflow-hidden rounded-md border border-foreground/10 bg-black/10 text-left disabled:cursor-default relative\"\n >\n {objectUrl ? (\n <>\n <img src={objectUrl} alt={title} loading=\"lazy\" className=\"block max-h-72 max-w-full object-contain\" />\n <span className=\"absolute inset-0 flex items-center justify-center bg-black/0 group-hover/img:bg-black/30 transition-colors\">\n <ZoomIn className=\"w-6 h-6 text-white opacity-0 group-hover/img:opacity-100 transition-opacity drop-shadow-md\" />\n </span>\n </>\n ) : (\n <span className={cn('flex min-h-20 items-center gap-2 px-3 py-2 text-xs', error ? 'text-red-400' : 'text-muted-foreground')}>\n {loading ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <AlertCircle className=\"h-4 w-4\" />}\n {error || 'Loading image'}\n </span>\n )}\n </button>\n {objectUrl && (\n <Dialog open={lightboxOpen} onOpenChange={setLightboxOpen}>\n <DialogContent className=\"max-w-[95vw] max-h-[95vh] w-auto p-0 bg-black/95 border-zinc-800 gap-0 overflow-hidden\" showCloseButton={false}>\n <DialogTitle className=\"sr-only\">{title}</DialogTitle>\n <div className=\"relative flex items-center justify-center min-h-[200px]\">\n <img src={objectUrl} alt={title} className=\"max-w-[93vw] max-h-[90vh] object-contain\" />\n <div className=\"absolute top-2 right-2 flex gap-1\">\n <button type=\"button\" onClick={download} className=\"rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors\" title=\"Download\">\n <Download className=\"w-4 h-4\" />\n </button>\n <button type=\"button\" onClick={() => setLightboxOpen(false)} className=\"rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors\" title=\"Close\">\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n )}\n </>\n )\n }\n\n return (\n <button\n type=\"button\"\n onClick={download}\n disabled={!objectUrl}\n className={cn(\n 'mt-2 flex w-full items-center gap-2 rounded-md border px-2.5 py-2 text-left text-xs transition-colors disabled:cursor-default disabled:opacity-70',\n outbound ? 'border-primary-foreground/20 bg-primary-foreground/10 hover:bg-primary-foreground/15' : 'border-border bg-background hover:bg-muted'\n )}\n >\n {loading ? <Loader2 className=\"h-4 w-4 shrink-0 animate-spin\" /> : error ? <AlertCircle className=\"h-4 w-4 shrink-0 text-red-400\" /> : <FileText className=\"h-4 w-4 shrink-0\" />}\n <span className={cn('min-w-0 flex-1 truncate', error && 'text-red-400')}>{error || title}</span>\n <Download className=\"h-3.5 w-3.5 shrink-0\" />\n </button>\n )\n}\n\nfunction AddReaction({ open, onToggle, onReact }: { open: boolean; onToggle: () => void; onReact: (emoji: string) => void }) {\n return (\n <div className=\"relative shrink-0\">\n <button\n type=\"button\"\n title=\"Add reaction\"\n onClick={onToggle}\n className={cn(\n 'inline-flex h-5 w-6 items-center justify-center rounded-full border border-border bg-popover text-muted-foreground shadow-sm transition hover:bg-muted hover:text-foreground',\n open ? 'opacity-100' : 'opacity-0 pointer-events-none md:group-hover/msg:opacity-100 md:group-hover/msg:pointer-events-auto'\n )}\n >\n <SmilePlus className=\"h-3 w-3\" />\n </button>\n {open && (\n <div className=\"absolute bottom-full left-0 z-30 mb-1 flex gap-0.5 rounded-full border border-border bg-popover p-0.5 shadow-md\">\n {QUICK_REACTIONS.map((emoji) => (\n <button\n key={emoji}\n type=\"button\"\n title={`React ${emoji}`}\n onClick={() => onReact(emoji)}\n className=\"inline-flex h-6 w-7 items-center justify-center rounded-full text-xs transition-colors hover:bg-muted\"\n >\n {emoji}\n </button>\n ))}\n </div>\n )}\n </div>\n )\n}\n\n// Inter-agent (peer↔peer) messages render as a compact, foldable one-liner — distinct\n// from the user↔agent bubbles, never persisted-open (#508). The other party is shown via\n// AgentLink (label-preferred + clickable, #522); fan-out targets (broadcast/tag:/cap:/…)\n// fall back to a plain displayTarget label since they don't resolve to one agent.\nfunction PeerMessageLine({ msg, peer }: { msg: Message; peer: string }) {\n const [expanded, setExpanded] = useState(false)\n const agentsById = useRelayStore((s) => s.agentsById)\n const openAgentDetail = useRelayStore((s) => s.openAgentDetail)\n const isOutbound = msg.from === peer\n const otherParty = (isOutbound ? msg.to : msg.from) || ''\n // Per-project #NNN linkify (#533/#543): outbound folded lines use the selected\n // peer's repo; inbound inter-agent lines use the originating agent's repo.\n const issueRepo = agentsById[isOutbound ? peer : otherParty]?.issueRepo ?? agentsById[peer]?.issueRepo\n const isSelector = !otherParty || otherParty === 'broadcast' || otherParty.includes(':')\n const body = messageBody(msg)\n const time = chatTimestamp(msg.occurredAt ?? (msg.createdAt as string))\n const DirIcon = isOutbound ? ArrowRight : ArrowLeft\n const senderAgent = agentsById[msg.from ?? ''] ?? null\n function openSenderDetail(e: ReactMouseEvent<HTMLButtonElement>) {\n e.stopPropagation()\n if (senderAgent) openAgentDetail(senderAgent)\n }\n\n return (\n <div data-msg-id={msg.id} className=\"flex justify-center mb-1.5\">\n <div className=\"w-full max-w-[94%] rounded-md border border-border/50 bg-muted/15 px-2.5 py-1\">\n <div className=\"flex items-center gap-1.5 text-xs cursor-pointer select-none\" onClick={() => setExpanded((v) => !v)}>\n <ChevronRight className={cn('h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform', expanded && 'rotate-90')} />\n <button\n type=\"button\"\n title={senderAgent ? (senderAgent.label || senderAgent.name || senderAgent.id) : undefined}\n disabled={!senderAgent}\n onClick={openSenderDetail}\n className={cn('shrink-0 transition-opacity', senderAgent ? 'cursor-pointer hover:opacity-80' : 'cursor-default opacity-60')}\n >\n <AgentTypeIcon agent={senderAgent} className=\"!w-4 !h-4 !rounded-sm opacity-70\" />\n </button>\n <DirIcon className=\"h-3 w-3 shrink-0 text-muted-foreground/70\" />\n <span className=\"shrink-0 text-muted-foreground\">Message {isOutbound ? 'to' : 'from'}</span>\n {isSelector\n ? <span className=\"shrink-0 font-medium text-muted-foreground\">{displayTarget(otherParty, agentsById)}</span>\n : <AgentLink agentId={otherParty} className=\"shrink-0 px-0.5 text-foreground/80\" noIcon />}\n {!expanded && <span className=\"min-w-0 flex-1 truncate text-muted-foreground/50\">{body}</span>}\n <span className=\"ml-auto shrink-0 text-muted-foreground/40\">{time}</span>\n </div>\n {expanded && (\n <div className=\"mt-1.5 pl-5\">\n <FormattedBody text={body} rawBody={msg.body} issueRepo={issueRepo} className=\"text-sm leading-relaxed text-foreground/90\" />\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport const MessageBubble = memo(function MessageBubble({\n msg,\n peer,\n showAvatar = false,\n onOpenReferencedPath,\n onPreviewReferencedPath,\n onPreviewReferencedPathEnd,\n}: {\n msg: Message\n peer: string\n /** Show the sender avatar on this bubble (last in a consecutive same-sender run). */\n showAvatar?: boolean\n onOpenReferencedPath?: (path: string, line?: number, type?: ReferencedPathInfo['type']) => void\n onPreviewReferencedPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewReferencedPathEnd?: () => void\n}) {\n // Inter-agent (peer↔peer) messages get a folded one-liner instead of a bubble (#508).\n // This early-return precedes all hooks, which is safe here: the timeline keys each\n // MessageBubble by msg id (VirtualTimeline.timelineEntryKey), and from/to are immutable\n // for a given id — so a mounted instance never flips between the two render paths.\n // De-singletoned human addressing (#390): any human side — legacy bare `'user'` or a\n // per-user `user:<id>` sink — renders as a user↔agent bubble; only agent↔agent stays a\n // folded peer line. isHumanAgentId keeps multi-human threads from collapsing to peer lines.\n if (!isHumanAgentId(msg.from) && !isHumanAgentId(msg.to)) {\n return <PeerMessageLine msg={msg} peer={peer} />\n }\n // Spawn prompts (kind:\"session\", type:\"prompt\", origin:\"provider\") are system-initiated\n // and must NOT masquerade as operator-typed prompts. Render with the InitialPromptBubble\n // panel style instead (#420 Tier A). Immutable payload — safe as a pre-hook early return.\n const sessionPayload = msg.kind === 'session'\n ? (msg.payload?.session as { type?: string; origin?: string } | undefined)\n : undefined\n if (sessionPayload?.type === 'prompt' && sessionPayload?.origin === 'provider') {\n return <SpawnPromptBubble msg={msg} peer={peer} />\n }\n const isOutbound = isHumanAgentId(msg.from)\n const reactToMessage = useRelayStore((s) => s.reactToMessage)\n const voiceTtsEnabled = useRelayStore((s) => s.voiceTtsEnabled)\n const ttsPlayingKey = useTtsPlayingKey()\n const peerCwd = useRelayStore((s) => {\n const cwd = s.agentsById[peer]?.meta?.cwd\n return typeof cwd === 'string' ? cwd : ''\n })\n // Per-project #NNN linkify (#533): the peer agent's resolved project repo.\n const peerIssueRepo = useRelayStore((s) => s.agentsById[peer]?.issueRepo)\n // Human attribution (#390): resolve which user sent an outbound bubble, and whether more\n // than one human exists. With the single seeded user:default, multiHuman is false and the\n // name tag / colored avatar stay off — the bubble renders exactly as before.\n const usersById = useRelayStore((s) => s.usersById)\n const multiHuman = useRelayStore((s) => s.users.length > 1)\n const sendingUser = isOutbound ? resolveUser(msg.from, usersById) : undefined\n const openAgentDetail = useRelayStore((s) => s.openAgentDetail)\n const openTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n const [showQuickReact, setShowQuickReact] = useState(false)\n const bubbleRef = useRef<HTMLDivElement>(null)\n const pointerStartRef = useRef<{ x: number; y: number } | null>(null)\n const suppressBubbleClickRef = useRef(false)\n const body = messageBody(msg)\n const time = chatTimestamp(msg.occurredAt ?? (msg.createdAt as string))\n const attachments = messageAttachments(msg)\n const reactions = groupedReactions(msg)\n const receipt = isOutbound ? outboundReceipt(msg, peer) : null\n const ReceiptIcon = receipt?.icon\n // On-demand TTS: replay any agent response through the same Kokoro/browser\n // pipeline. Only when the speaker is on, and only for inbound non-reaction text.\n const isTtsPlaying = ttsPlayingKey === String(msg.id)\n const showPlayButton = voiceTtsEnabled && !isOutbound && !isReactionEvent(msg) && body.trim().length > 0\n function togglePlay(e: React.MouseEvent) {\n e.stopPropagation()\n if (isTtsPlaying) voiceTts.bargeIn()\n else voiceTts.speak(msg.body, String(msg.id))\n }\n useEffect(() => {\n if (!showQuickReact) return\n function dismiss(e: MouseEvent | TouchEvent) {\n if (hasActiveTextSelection()) return\n if (bubbleRef.current && !bubbleRef.current.contains(e.target as Node)) setShowQuickReact(false)\n }\n document.addEventListener('mousedown', dismiss)\n document.addEventListener('touchstart', dismiss)\n return () => { document.removeEventListener('mousedown', dismiss); document.removeEventListener('touchstart', dismiss) }\n }, [showQuickReact])\n\n function resolvedReferencedPath(path: string): string {\n return resolveRelativePath(peerCwd || '/', path)\n }\n\n function openReferencedPath(path: string, line?: number) {\n const absolute = resolvedReferencedPath(path)\n onOpenReferencedPath?.(absolute, line)\n }\n\n function previewReferencedPath(path: string, line?: number, anchor?: DOMRect) {\n const absolute = resolvedReferencedPath(path)\n onPreviewReferencedPath?.(absolute, line, anchor)\n }\n\n function handleReact(emoji: string) {\n reactToMessage(msg, emoji)\n setShowQuickReact(false)\n }\n\n function handleBubblePointerDown(e: React.PointerEvent<HTMLDivElement>) {\n if (e.button !== 0) return\n pointerStartRef.current = { x: e.clientX, y: e.clientY }\n suppressBubbleClickRef.current = false\n }\n\n function handleBubblePointerMove(e: React.PointerEvent<HTMLDivElement>) {\n const start = pointerStartRef.current\n if (!start) return\n if (Math.abs(e.clientX - start.x) > 3 || Math.abs(e.clientY - start.y) > 3) {\n suppressBubbleClickRef.current = true\n }\n }\n\n function handleBubbleClick(e: React.MouseEvent<HTMLDivElement>) {\n if (isReactionEvent(msg) || isOutbound) return\n const target = e.target as HTMLElement\n if (target.closest('a,button,input,textarea,select,[role=\"button\"]')) return\n if (suppressBubbleClickRef.current || hasActiveTextSelection()) {\n suppressBubbleClickRef.current = false\n return\n }\n setShowQuickReact((v) => !v)\n }\n\n const hasAvatar = showAvatar && !isReactionEvent(msg)\n\n return (\n <div data-msg-id={msg.id} className={cn('group/msg flex mb-3 items-end', isOutbound ? 'justify-end' : 'justify-start', hasAvatar && 'gap-1.5')}>\n {!isOutbound && hasAvatar && <ConnectedBubbleAvatar agentId={peer} onOpen={openAgentDetail} />}\n <div ref={bubbleRef} className={cn('relative', hasAvatar ? 'max-w-[calc(85%-2rem)] md:max-w-[calc(75%-2rem)]' : 'max-w-[85%] md:max-w-[75%]')}>\n {showPlayButton && (\n <button\n type=\"button\"\n title={isTtsPlaying ? 'Stop playback' : 'Play aloud'}\n onClick={togglePlay}\n className={cn(\n 'absolute -top-2 -right-2 z-20 inline-flex h-6 w-6 items-center justify-center rounded-full border bg-popover shadow-sm transition',\n isTtsPlaying\n ? 'border-primary/50 text-primary opacity-100'\n // Hide-until-hover only on hover-capable devices; touch screens (iPad)\n // have no hover, so keep the button visible there instead of opacity-0.\n : 'border-border text-muted-foreground opacity-70 hover:bg-muted hover:text-foreground hover:opacity-100 [@media(hover:hover)]:md:opacity-0 [@media(hover:hover)]:md:group-hover/msg:opacity-100'\n )}\n >\n {isTtsPlaying ? <CircleStop className=\"h-3.5 w-3.5 animate-pulse\" /> : <Volume2 className=\"h-3.5 w-3.5\" />}\n </button>\n )}\n <div\n className={cn(\n 'rounded-2xl px-3.5 py-2 text-sm select-text',\n !isOutbound && !isReactionEvent(msg) && 'cursor-pointer',\n isOutbound\n ? 'bg-primary text-primary-foreground rounded-br-sm'\n : 'bg-card ring-1 ring-foreground/10 rounded-bl-sm'\n )}\n onPointerDown={handleBubblePointerDown}\n onPointerMove={handleBubblePointerMove}\n onClick={handleBubbleClick}\n >\n {isOutbound && multiHuman && (\n <p className={cn('text-[11px] font-semibold mb-0.5 text-right', humanAccentClass(msg.from))}>\n {humanDisplayName(msg.from, usersById)}\n </p>\n )}\n {msg.subject && (\n <p className={cn('text-xs font-semibold mb-1', isOutbound ? 'text-primary-foreground/70' : 'text-muted-foreground')}>\n {msg.subject}\n </p>\n )}\n <FormattedBody\n text={body}\n rawBody={msg.body}\n issueRepo={peerIssueRepo}\n className=\"leading-relaxed\"\n onPrimary={isOutbound}\n onOpenPath={openReferencedPath}\n onPreviewPath={previewReferencedPath}\n onPreviewPathEnd={onPreviewReferencedPathEnd}\n resolvePathTitle={(path, line) => `${resolvedReferencedPath(path)}${line ? `:${line}` : ''}`}\n />\n {attachments.map((attachment) => (\n <AttachmentPreview key={`${msg.id}-${attachment.artifactId}-${attachment.role || ''}`} attachment={attachment} outbound={isOutbound} />\n ))}\n <p className={cn('text-xs mt-1 flex items-center gap-1.5', isOutbound ? 'justify-end text-primary-foreground/60' : 'justify-end text-muted-foreground')}>\n <span className=\"opacity-40\">#{msg.id}</span>\n <span>{time}</span>\n {receipt && ReceiptIcon && (\n <span\n className={cn(\n 'inline-flex items-center gap-1',\n receipt.tone === 'ok' && 'text-emerald-300',\n receipt.tone === 'warn' && 'text-amber-200',\n receipt.tone === 'danger' && 'text-red-200'\n )}\n title={receipt.title}\n >\n <ReceiptIcon className=\"h-3 w-3\" />\n {receipt.label}\n </span>\n )}\n </p>\n </div>\n {!isReactionEvent(msg) && reactions.length > 0 && (\n <div className={cn('flex flex-wrap items-center gap-0.5 -mt-1 relative z-10', isOutbound ? 'justify-end pr-2' : 'justify-start pl-2')}>\n {!isOutbound && <AddReaction open={showQuickReact} onToggle={() => setShowQuickReact((v) => !v)} onReact={handleReact} />}\n {reactions.map((reaction) => (\n <button\n key={reaction.emoji}\n type=\"button\"\n title={reaction.actors.join(', ')}\n onClick={() => handleReact(reaction.emoji)}\n className={cn(\n 'inline-flex h-5 items-center gap-0.5 rounded-full border px-1.5 text-[11px] shadow-sm transition-colors',\n reaction.mine\n ? 'border-primary/40 bg-primary/10 text-primary'\n : 'border-border bg-background hover:bg-muted text-foreground'\n )}\n >\n <span>{reaction.emoji}</span>\n {reaction.count > 1 && <span>{reaction.count}</span>}\n </button>\n ))}\n </div>\n )}\n {!isReactionEvent(msg) && !isOutbound && reactions.length === 0 && (\n <div className=\"absolute -bottom-2.5 left-1 z-20\">\n <AddReaction open={showQuickReact} onToggle={() => setShowQuickReact((v) => !v)} onReact={handleReact} />\n </div>\n )}\n </div>\n {isOutbound && hasAvatar && (\n <span className=\"shrink-0 self-end opacity-60\">\n <UserAvatar user={sendingUser} address={msg.from} distinct={multiHuman} />\n </span>\n )}\n </div>\n )\n})\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{Mt as t,Qn as n,Zn as r,er as i,hr as a,mt as o,nr as s,rr as c,wn as l,xn as u}from"./lucide-react-Dc3ZwCBi.js";import{i as d,r as f,t as p}from"./store-BuuJUn70.js";import{Q as m}from"./display-exQDWum3.js";import{t as h}from"./task-chip-kxk4eNm1.js";import{J as g}from"./index-D_fNJnsI.js";var _=e(a(),1),v=`(max-width: 767px)`;function y(e){if(typeof window>`u`)return()=>{};let t=window.matchMedia(v);return t.addEventListener(`change`,e),()=>t.removeEventListener(`change`,e)}function b(){return(0,_.useSyncExternalStore)(y,()=>typeof window<`u`?window.matchMedia(v).matches:!1,()=>!1)}var x=s(),S=196,C=84,w=52,T=16,E=14,D=[`landed`,`in-progress`,`pending`,`blocked`],O={landed:{label:`landed`,dot:`bg-emerald-400`,accent:`border-l-emerald-400`,text:`text-emerald-300`,wire:`#34d399`},"in-progress":{label:`in progress`,dot:`bg-sky-400`,accent:`border-l-sky-400`,text:`text-sky-300`,wire:`#38bdf8`},pending:{label:`pending`,dot:`bg-amber-400`,accent:`border-l-amber-400`,text:`text-amber-300`,wire:`#fbbf24`},blocked:{label:`blocked`,dot:`bg-red-400`,accent:`border-l-red-400`,text:`text-red-300`,wire:`#f87171`}},k=new Set([...m,`completed`]);function A(e){return k.has(e.status)}function j(e,t){return t?e:e.filter(e=>!A(e))}function M(e){return e?e.label||(e.policyName?`policy:${e.policyName}`:e.agentId||``):``}function N(e){let t=new Map(e.nodes.map(e=>[e.id,e])),n=new Map(e.nodes.map((e,t)=>[e.id,t])),r=new Map,i=new Map(e.nodes.map(e=>[e.id,0]));for(let n of e.edges)!t.has(n.from)||!t.has(n.to)||(r.set(n.from,[...r.get(n.from)??[],n.to]),i.set(n.to,(i.get(n.to)??0)+1));let a=new Map(e.nodes.map(e=>[e.id,0])),o=e.nodes.filter(e=>(i.get(e.id)??0)===0).map(e=>e.id),s=new Set;for(;o.length;){let e=o.shift();s.add(e);let t=a.get(e)??0;for(let n of r.get(e)??[])a.set(n,Math.max(a.get(n)??0,t+1)),i.set(n,(i.get(n)??0)-1),(i.get(n)??0)===0&&o.push(n)}let c=Math.max(0,...Array.from(a.values()))+1;for(let t of e.nodes)s.has(t.id)||a.set(t.id,c++);let l=new Map;for(let t of e.nodes){let e=a.get(t.id)??0;l.set(e,[...l.get(e)??[],t])}let u=[...l.entries()].sort(([e],[t])=>e-t),d=Math.max(1,...u.map(([,e])=>e.length)),f=C+T,p=[];u.forEach(([e,t],r)=>{let i=[...t].sort((e,t)=>(e.lane||``).localeCompare(t.lane||``)||(n.get(e.id)??0)-(n.get(t.id)??0)),a=(d-i.length)*f/2;i.forEach((t,n)=>{p.push({node:t,layer:e,x:E+r*(S+w),y:E+a+n*f})})});let m=u.length;return{nodes:p,width:Math.max(320,E*2+m*S+Math.max(0,m-1)*w),height:Math.max(C+E*2,E*2+d*C+Math.max(0,d-1)*T)}}function P(e,t){let n=e.x+S,r=e.y+C/2,i=t.x,a=t.y+C/2,o=n+Math.max(20,(i-n)/2);return`M ${n} ${r} C ${o} ${r}, ${o} ${a}, ${i} ${a}`}function F({threadId:e,teamId:a,channel:o}){let s=a?d(a,o):e?f(e,o):``,c=p(e=>a?e.plansByTeam[s]:s?e.plansByThread[s]:null),m=p(e=>e.fetchPlan),v=p(e=>e.setPlanArchived),y=p(e=>e.planCollapsed),w=p(e=>e.setPlanCollapsed),T=p(e=>e.issueRefsById),E=p(e=>e.deliverableTasks),k=p(e=>e.openTaskDetailById),A=b(),[j,F]=(0,_.useState)(!1),I=y??A;(0,_.useEffect)(()=>{if(a){m({teamId:a,channel:o});return}e&&m({threadId:e,channel:o})},[o,m,a,e]);let L=(0,_.useMemo)(()=>c?N(c):null,[c]),R=(0,_.useMemo)(()=>new Map(L?.nodes.map(e=>[e.node.id,e])??[]),[L]),z=(0,_.useMemo)(()=>{if(!c)return[];let e=new Map;for(let t of c.nodes)e.set(t.status,(e.get(t.status)??0)+1);return D.filter(t=>e.has(t)).map(t=>({status:t,count:e.get(t)??0}))},[c]);if(!c||!L)return null;if(c.archived&&!j)return(0,x.jsx)(`section`,{className:`shrink-0 border-b border-border bg-card/45 px-3 py-2`,children:(0,x.jsxs)(`button`,{type:`button`,onClick:()=>F(!0),className:`flex min-w-0 items-center gap-2 text-left text-muted-foreground hover:text-foreground`,"aria-expanded":`false`,children:[(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60`,children:(0,x.jsx)(r,{className:`h-3 w-3`})}),(0,x.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-[11px] font-semibold uppercase tracking-wider`,children:`Show archived plan (1)`}),(0,x.jsx)(l,{className:`h-3.5 w-3.5 shrink-0`})]})});if(c.archived)return(0,x.jsx)(`section`,{className:`shrink-0 border-b border-border bg-card/45 px-3 py-2`,children:(0,x.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60`,children:(0,x.jsx)(r,{className:`h-3 w-3 text-muted-foreground`})}),(0,x.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,x.jsx)(`div`,{className:`truncate text-[13px] font-semibold leading-tight text-muted-foreground`,children:c.title||`Plan`}),(0,x.jsx)(`div`,{className:`truncate text-[11px] leading-tight text-muted-foreground`,children:`Archived plan`})]}),(0,x.jsx)(`span`,{className:`shrink-0 rounded border border-border bg-muted/40 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground`,children:`archived`}),(0,x.jsx)(`button`,{type:`button`,onClick:()=>F(!1),className:`shrink-0 rounded border border-border bg-muted/40 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground hover:bg-muted/60 hover:text-foreground`,"aria-expanded":`true`,children:`Hide`}),(0,x.jsx)(`button`,{type:`button`,onClick:()=>void v(c.id,!1),title:`Unarchive plan`,"aria-label":`Unarchive plan`,className:`flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground`,children:(0,x.jsx)(n,{className:`h-3.5 w-3.5`})})]})});let B=c.nodes.length,V=z.find(e=>e.status===`landed`)?.count??0,H=`${B} node${B===1?``:`s`}${V>0?` · ${V} landed`:``}`;return(0,x.jsxs)(`section`,{className:`shrink-0 border-b border-border bg-card/45 px-3 py-2`,children:[(0,x.jsxs)(`div`,{className:i(`flex min-w-0 items-center gap-2`,!I&&`mb-2`),children:[(0,x.jsxs)(`button`,{type:`button`,onClick:()=>w(!I),"aria-expanded":!I,"aria-label":I?`Expand plan graph`:`Collapse plan graph`,className:`flex min-w-0 flex-1 items-center gap-2 text-left`,children:[(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60`,children:(0,x.jsx)(t,{className:`h-3 w-3 text-muted-foreground`})}),(0,x.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,x.jsx)(`div`,{className:`truncate text-[13px] font-semibold leading-tight text-foreground`,children:c.title||`Plan`}),(0,x.jsx)(`div`,{className:`truncate text-[11px] leading-tight text-muted-foreground`,children:I?H:c.objective||H})]})]}),z.length>0&&(0,x.jsx)(`div`,{className:`hidden shrink-0 items-center gap-2.5 sm:flex`,children:z.map(({status:e,count:t})=>(0,x.jsxs)(`span`,{className:`flex items-center gap-1 text-[10px] text-muted-foreground`,children:[(0,x.jsx)(`span`,{className:i(`h-1.5 w-1.5 rounded-full`,O[e].dot)}),(0,x.jsx)(`span`,{className:i(`font-medium tabular-nums`,O[e].text),children:t}),(0,x.jsx)(`span`,{children:O[e].label})]},e))}),(0,x.jsxs)(`span`,{className:`shrink-0 rounded bg-muted/60 px-1.5 py-0.5 text-[10px] font-medium tabular-nums text-muted-foreground`,children:[`v`,c.version]}),(0,x.jsx)(`button`,{type:`button`,onClick:()=>void v(c.id,!0),title:`Archive plan`,"aria-label":`Archive plan`,className:`flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground`,children:(0,x.jsx)(r,{className:`h-3.5 w-3.5`})}),(0,x.jsx)(`button`,{type:`button`,onClick:()=>w(!I),"aria-expanded":!I,"aria-label":I?`Expand plan graph`:`Collapse plan graph`,className:`flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground`,children:I?(0,x.jsx)(l,{className:`h-3.5 w-3.5`}):(0,x.jsx)(u,{className:`h-3.5 w-3.5`})})]}),I?null:c.nodes.length===0?(0,x.jsx)(`div`,{className:`rounded-md border border-dashed border-border px-3 py-3 text-center text-xs text-muted-foreground`,children:`No nodes yet — the plan graph fills in as the team is composed.`}):(0,x.jsx)(`div`,{className:`max-h-[min(46vh,360px)] overflow-auto`,children:(0,x.jsxs)(`div`,{className:`relative`,style:{width:L.width,height:L.height},children:[(0,x.jsxs)(`svg`,{className:`pointer-events-none absolute inset-0`,width:L.width,height:L.height,"aria-hidden":`true`,children:[(0,x.jsx)(`defs`,{children:D.map(e=>(0,x.jsx)(`marker`,{id:`plan-arrow-${e}`,viewBox:`0 0 8 8`,refX:`6.5`,refY:`4`,markerWidth:`6`,markerHeight:`6`,orient:`auto-start-reverse`,children:(0,x.jsx)(`path`,{d:`M0 0 L8 4 L0 8 z`,fill:O[e].wire})},e))}),c.edges.map(e=>{let t=R.get(e.from),n=R.get(e.to);if(!t||!n)return null;let r=O[t.node.status];return(0,x.jsx)(`path`,{d:P(t,n),stroke:r.wire,strokeOpacity:.7,strokeWidth:2,fill:`none`,strokeLinecap:`round`,markerEnd:`url(#plan-arrow-${t.node.status})`},`${e.from}->${e.to}`)})]}),L.nodes.map(({node:e,x:t,y:n})=>{let r=O[e.status],a=M(e.ownerRef),o=e.refs?.issueId?T[e.refs.issueId]:null,s=e.refs?.taskId?parseInt(e.refs.taskId,10):null,c=s==null?void 0:E.find(e=>e.id===s)?.title;return(0,x.jsxs)(`article`,{className:i(`group absolute flex flex-col justify-between rounded-md border border-l-[3px] bg-background/95 p-2`,`shadow-sm transition-all hover:-translate-y-px hover:border-border hover:shadow-md hover:bg-background`,r.accent),style:{left:t,top:n,width:S,height:C},title:e.scopeBlurb||e.label,children:[(0,x.jsx)(`div`,{className:`truncate text-xs font-semibold leading-tight text-foreground`,children:e.label}),(e.scopeBlurb||e.id)&&(0,x.jsx)(`div`,{className:`truncate text-[10px] leading-tight text-muted-foreground`,children:e.scopeBlurb||e.id}),(o||s!=null)&&(0,x.jsxs)(`div`,{className:`flex min-w-0 flex-wrap gap-1`,children:[o&&(0,x.jsx)(g,{issueRef:o,compact:!0,className:`max-w-full`}),s!=null&&(0,x.jsx)(h,{id:s,title:c,onClick:()=>k(s),className:`max-w-full`})]}),(0,x.jsxs)(`div`,{className:`flex min-w-0 items-center justify-between gap-1.5`,children:[(0,x.jsxs)(`div`,{className:`flex shrink-0 items-center gap-1.5`,children:[(0,x.jsx)(`span`,{className:i(`h-1.5 w-1.5 shrink-0 rounded-full`,r.dot)}),(0,x.jsx)(`span`,{className:i(`text-[10px] font-medium`,r.text),children:r.label})]}),(0,x.jsxs)(`div`,{className:`flex min-w-0 items-center gap-1.5`,children:[a&&(0,x.jsx)(`span`,{className:`truncate text-[10px] text-muted-foreground`,children:a}),e.lane&&(0,x.jsx)(`span`,{className:`shrink-0 rounded-sm bg-muted/70 px-1 py-px text-[9px] font-medium uppercase tracking-wide text-muted-foreground`,children:e.lane})]})]})]},e.id)})]})})]})}function I({agentId:e}){let t=p(e=>e.openTaskDetailById),n=p(e=>e.sessionTasksCollapsed),r=p(e=>e.setSessionTasksCollapsed),[a,s]=(0,_.useState)([]),[d,f]=(0,_.useState)(!1);if((0,_.useEffect)(()=>{c(`GET`,`/task-entities?createdBy=`+encodeURIComponent(e)).then(e=>s(e)).catch(()=>{})},[e]),a.length===0)return null;let m=a.filter(A).length,g=j(a,d);return(0,x.jsxs)(`section`,{className:`shrink-0 border-b border-border bg-card/45 px-3 py-2`,children:[(0,x.jsxs)(`button`,{type:`button`,onClick:()=>r(!n),"aria-expanded":!n,"aria-label":n?`Expand session tasks`:`Collapse session tasks`,className:i(`flex w-full min-w-0 items-center gap-2 text-left`,!n&&`mb-1.5`),children:[(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60`,children:(0,x.jsx)(o,{className:`h-3 w-3 text-muted-foreground`})}),(0,x.jsxs)(`span`,{className:`flex-1 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground`,children:[`Tasks from this session (`,g.length,`)`]}),(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground`,children:n?(0,x.jsx)(l,{className:`h-3.5 w-3.5`}):(0,x.jsx)(u,{className:`h-3.5 w-3.5`})})]}),!n&&(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(`div`,{className:`flex flex-wrap gap-1`,children:g.map(e=>(0,x.jsx)(h,{id:e.id,title:e.title,status:e.status,onClick:()=>t(e.id),compact:!0},e.id))}),m>0&&(0,x.jsx)(`button`,{type:`button`,onClick:()=>f(!d),className:`mt-1.5 rounded border border-border bg-muted/30 px-2 py-0.5 text-[10px] font-medium text-muted-foreground hover:bg-muted/50 hover:text-foreground`,"aria-pressed":d,children:d?`Hide ${m} closed`:`+ ${m} closed`})]})]})}export{I as n,F as t};
|
|
2
|
+
//# sourceMappingURL=PlanGraphPanel-CjVG-Jsq.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlanGraphPanel-CjVG-Jsq.js","names":[],"sources":["../../dashboard/src/components/chat/hooks/useIsMobile.ts","../../dashboard/src/components/chat/PlanGraphPanel.tsx"],"sourcesContent":["import { useSyncExternalStore } from 'react'\n\n// Reactive narrow-viewport flag. Subscribes to the same `max-width: 767px`\n// breakpoint the chat single-pane layout uses, via useSyncExternalStore — an\n// external-store subscription, not effect-driven derived state — so it stays\n// correct across resize/rotate without a useEffect.\nconst QUERY = '(max-width: 767px)'\n\nfunction subscribe(cb: () => void): () => void {\n if (typeof window === 'undefined') return () => {}\n const mql = window.matchMedia(QUERY)\n mql.addEventListener('change', cb)\n return () => mql.removeEventListener('change', cb)\n}\n\nexport function useIsMobile(): boolean {\n return useSyncExternalStore(\n subscribe,\n () => (typeof window !== 'undefined' ? window.matchMedia(QUERY).matches : false),\n () => false,\n )\n}\n","import { useEffect, useMemo, useState } from 'react'\nimport { Archive, ArchiveRestore, ChevronDown, ChevronUp, GitBranch, ListTodo } from 'lucide-react'\nimport { useRelayStore } from '@/store'\nimport type { Plan, PlanNode, PlanOwnerRef, PlanStatus, Task } from '@/types'\nimport { cn } from '@/lib/utils'\nimport { CLOSED_TASK_STATUSES } from '@/lib/constants'\nimport { planLookupKey, planTeamLookupKey } from '@/store/slices/plans'\nimport { IssueRefChip } from '@/components/shared/issue-ref-chip'\nimport { TaskChip } from '@/components/shared/task-chip'\nimport { useIsMobile } from './hooks/useIsMobile'\nimport { api } from '@/lib/api'\n\n// Left-to-right DAG: dependency depth flows along x (sequential gates), nodes\n// that share a depth stack along y (parallel lanes). This keeps the panel SHORT\n// — it's an operator overlay above the chat, not a full view — and lets the\n// gate-vs-lane structure read at a glance. Height is capped + scrollable below.\nconst NODE_W = 196\nconst NODE_H = 84\nconst COL_GAP = 52\nconst ROW_GAP = 16\nconst PAD = 14\n\nconst STATUS_ORDER: PlanStatus[] = ['landed', 'in-progress', 'pending', 'blocked']\n\nconst STATUS_STYLE: Record<\n PlanStatus,\n { label: string; dot: string; accent: string; text: string; wire: string }\n> = {\n landed: { label: 'landed', dot: 'bg-emerald-400', accent: 'border-l-emerald-400', text: 'text-emerald-300', wire: '#34d399' },\n 'in-progress': { label: 'in progress', dot: 'bg-sky-400', accent: 'border-l-sky-400', text: 'text-sky-300', wire: '#38bdf8' },\n pending: { label: 'pending', dot: 'bg-amber-400', accent: 'border-l-amber-400', text: 'text-amber-300', wire: '#fbbf24' },\n blocked: { label: 'blocked', dot: 'bg-red-400', accent: 'border-l-red-400', text: 'text-red-300', wire: '#f87171' },\n}\n\n// CLOSED_TASK_STATUSES (done/failed/canceled) is the shared terminal-status\n// set; 'completed' is a legacy status value some older records still carry.\nconst COMPLETED_TASK_STATUSES = new Set([...CLOSED_TASK_STATUSES, 'completed'])\n\nexport function isCompletedSessionTask(task: Pick<Task, 'status'>): boolean {\n return COMPLETED_TASK_STATUSES.has(task.status)\n}\n\nexport function visibleSessionTasks(tasks: Task[], showCompleted: boolean): Task[] {\n return showCompleted ? tasks : tasks.filter((task) => !isCompletedSessionTask(task))\n}\n\ninterface LayoutNode {\n node: PlanNode\n x: number\n y: number\n layer: number\n}\n\nfunction ownerLabel(owner?: PlanOwnerRef): string {\n if (!owner) return ''\n return owner.label || (owner.policyName ? `policy:${owner.policyName}` : owner.agentId || '')\n}\n\nfunction layerPlan(plan: Plan): { nodes: LayoutNode[]; width: number; height: number } {\n const nodeById = new Map(plan.nodes.map((node) => [node.id, node]))\n const indexById = new Map(plan.nodes.map((node, index) => [node.id, index]))\n const outgoing = new Map<string, string[]>()\n const indegree = new Map(plan.nodes.map((node) => [node.id, 0]))\n for (const edge of plan.edges) {\n if (!nodeById.has(edge.from) || !nodeById.has(edge.to)) continue\n outgoing.set(edge.from, [...(outgoing.get(edge.from) ?? []), edge.to])\n indegree.set(edge.to, (indegree.get(edge.to) ?? 0) + 1)\n }\n\n const layerById = new Map(plan.nodes.map((node) => [node.id, 0]))\n const queue = plan.nodes\n .filter((node) => (indegree.get(node.id) ?? 0) === 0)\n .map((node) => node.id)\n const visited = new Set<string>()\n\n while (queue.length) {\n const id = queue.shift()!\n visited.add(id)\n const fromLayer = layerById.get(id) ?? 0\n for (const to of outgoing.get(id) ?? []) {\n layerById.set(to, Math.max(layerById.get(to) ?? 0, fromLayer + 1))\n indegree.set(to, (indegree.get(to) ?? 0) - 1)\n if ((indegree.get(to) ?? 0) === 0) queue.push(to)\n }\n }\n\n let fallbackLayer = Math.max(0, ...Array.from(layerById.values())) + 1\n for (const node of plan.nodes) {\n if (visited.has(node.id)) continue\n layerById.set(node.id, fallbackLayer++)\n }\n\n const layers = new Map<number, PlanNode[]>()\n for (const node of plan.nodes) {\n const layer = layerById.get(node.id) ?? 0\n layers.set(layer, [...(layers.get(layer) ?? []), node])\n }\n\n const orderedLayers = [...layers.entries()].sort(([a], [b]) => a - b)\n const maxRows = Math.max(1, ...orderedLayers.map(([, nodes]) => nodes.length))\n const rowPitch = NODE_H + ROW_GAP\n\n const layoutNodes: LayoutNode[] = []\n orderedLayers.forEach(([layer, nodes], col) => {\n const ordered = [...nodes].sort((a, b) => {\n const lane = (a.lane || '').localeCompare(b.lane || '')\n return lane || ((indexById.get(a.id) ?? 0) - (indexById.get(b.id) ?? 0))\n })\n // Center each column vertically against the tallest column so the spine\n // runs through the middle and the parallel fan reads as a fan.\n const offset = ((maxRows - ordered.length) * rowPitch) / 2\n ordered.forEach((node, row) => {\n layoutNodes.push({\n node,\n layer,\n x: PAD + col * (NODE_W + COL_GAP),\n y: PAD + offset + row * rowPitch,\n })\n })\n })\n\n const cols = orderedLayers.length\n return {\n nodes: layoutNodes,\n width: Math.max(320, PAD * 2 + cols * NODE_W + Math.max(0, cols - 1) * COL_GAP),\n height: Math.max(NODE_H + PAD * 2, PAD * 2 + maxRows * NODE_H + Math.max(0, maxRows - 1) * ROW_GAP),\n }\n}\n\nfunction edgePath(from: LayoutNode, to: LayoutNode): string {\n const x1 = from.x + NODE_W\n const y1 = from.y + NODE_H / 2\n const x2 = to.x\n const y2 = to.y + NODE_H / 2\n const midX = x1 + Math.max(20, (x2 - x1) / 2)\n return `M ${x1} ${y1} C ${midX} ${y1}, ${midX} ${y2}, ${x2} ${y2}`\n}\n\nexport function PlanGraphPanel({ threadId, teamId, channel }: { threadId?: string | number | null; teamId?: string | null; channel?: string }) {\n const key = teamId ? planTeamLookupKey(teamId, channel) : threadId ? planLookupKey(threadId, channel) : ''\n const plan = useRelayStore((s) => teamId ? s.plansByTeam[key] : key ? s.plansByThread[key] : null)\n const fetchPlan = useRelayStore((s) => s.fetchPlan)\n const setPlanArchived = useRelayStore((s) => s.setPlanArchived)\n const planCollapsed = useRelayStore((s) => s.planCollapsed)\n const setPlanCollapsed = useRelayStore((s) => s.setPlanCollapsed)\n const issueRefsById = useRelayStore((s) => s.issueRefsById)\n const deliverableTasks = useRelayStore((s) => s.deliverableTasks)\n const openTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n const isMobile = useIsMobile()\n const [showArchivedPlan, setShowArchivedPlan] = useState(false)\n // null = no manual toggle yet → follow the viewport default. The graph\n // dominates a phone screen, so it starts collapsed there and expanded on\n // desktop; an explicit user toggle (persisted) overrides the default.\n const collapsed = planCollapsed ?? isMobile\n\n useEffect(() => {\n if (teamId) {\n void fetchPlan({ teamId, channel })\n return\n }\n if (threadId) void fetchPlan({ threadId, channel })\n }, [channel, fetchPlan, teamId, threadId])\n\n const layout = useMemo(() => plan ? layerPlan(plan) : null, [plan])\n const nodeById = useMemo(() => new Map(layout?.nodes.map((node) => [node.node.id, node]) ?? []), [layout])\n\n const legend = useMemo(() => {\n if (!plan) return []\n const counts = new Map<PlanStatus, number>()\n for (const node of plan.nodes) counts.set(node.status, (counts.get(node.status) ?? 0) + 1)\n return STATUS_ORDER.filter((status) => counts.has(status)).map((status) => ({\n status,\n count: counts.get(status) ?? 0,\n }))\n }, [plan])\n\n if (!plan || !layout) return null\n\n if (plan.archived && !showArchivedPlan) {\n return (\n <section className=\"shrink-0 border-b border-border bg-card/45 px-3 py-2\">\n <button\n type=\"button\"\n onClick={() => setShowArchivedPlan(true)}\n className=\"flex min-w-0 items-center gap-2 text-left text-muted-foreground hover:text-foreground\"\n aria-expanded=\"false\"\n >\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60\">\n <Archive className=\"h-3 w-3\" />\n </span>\n <span className=\"min-w-0 flex-1 truncate text-[11px] font-semibold uppercase tracking-wider\">\n Show archived plan (1)\n </span>\n <ChevronDown className=\"h-3.5 w-3.5 shrink-0\" />\n </button>\n </section>\n )\n }\n\n if (plan.archived) {\n return (\n <section className=\"shrink-0 border-b border-border bg-card/45 px-3 py-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60\">\n <Archive className=\"h-3 w-3 text-muted-foreground\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-[13px] font-semibold leading-tight text-muted-foreground\">{plan.title || 'Plan'}</div>\n <div className=\"truncate text-[11px] leading-tight text-muted-foreground\">Archived plan</div>\n </div>\n <span className=\"shrink-0 rounded border border-border bg-muted/40 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground\">\n archived\n </span>\n <button\n type=\"button\"\n onClick={() => setShowArchivedPlan(false)}\n className=\"shrink-0 rounded border border-border bg-muted/40 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground hover:bg-muted/60 hover:text-foreground\"\n aria-expanded=\"true\"\n >\n Hide\n </button>\n <button\n type=\"button\"\n onClick={() => void setPlanArchived(plan.id, false)}\n title=\"Unarchive plan\"\n aria-label=\"Unarchive plan\"\n className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground\"\n >\n <ArchiveRestore className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </section>\n )\n }\n\n const nodeCount = plan.nodes.length\n const landedCount = legend.find((entry) => entry.status === 'landed')?.count ?? 0\n // Thin summary for the collapsed bar (the legend dots are hidden on mobile),\n // e.g. \"5 nodes · 4 landed\".\n const summary = `${nodeCount} node${nodeCount === 1 ? '' : 's'}${landedCount > 0 ? ` · ${landedCount} landed` : ''}`\n\n return (\n <section className=\"shrink-0 border-b border-border bg-card/45 px-3 py-2\">\n <div className={cn('flex min-w-0 items-center gap-2', !collapsed && 'mb-2')}>\n <button\n type=\"button\"\n onClick={() => setPlanCollapsed(!collapsed)}\n aria-expanded={!collapsed}\n aria-label={collapsed ? 'Expand plan graph' : 'Collapse plan graph'}\n className=\"flex min-w-0 flex-1 items-center gap-2 text-left\"\n >\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60\">\n <GitBranch className=\"h-3 w-3 text-muted-foreground\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-[13px] font-semibold leading-tight text-foreground\">{plan.title || 'Plan'}</div>\n <div className=\"truncate text-[11px] leading-tight text-muted-foreground\">\n {collapsed ? summary : (plan.objective || summary)}\n </div>\n </div>\n </button>\n {legend.length > 0 && (\n <div className=\"hidden shrink-0 items-center gap-2.5 sm:flex\">\n {legend.map(({ status, count }) => (\n <span key={status} className=\"flex items-center gap-1 text-[10px] text-muted-foreground\">\n <span className={cn('h-1.5 w-1.5 rounded-full', STATUS_STYLE[status].dot)} />\n <span className={cn('font-medium tabular-nums', STATUS_STYLE[status].text)}>{count}</span>\n <span>{STATUS_STYLE[status].label}</span>\n </span>\n ))}\n </div>\n )}\n <span className=\"shrink-0 rounded bg-muted/60 px-1.5 py-0.5 text-[10px] font-medium tabular-nums text-muted-foreground\">\n v{plan.version}\n </span>\n <button\n type=\"button\"\n onClick={() => void setPlanArchived(plan.id, true)}\n title=\"Archive plan\"\n aria-label=\"Archive plan\"\n className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground\"\n >\n <Archive className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setPlanCollapsed(!collapsed)}\n aria-expanded={!collapsed}\n aria-label={collapsed ? 'Expand plan graph' : 'Collapse plan graph'}\n className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground\"\n >\n {collapsed ? <ChevronDown className=\"h-3.5 w-3.5\" /> : <ChevronUp className=\"h-3.5 w-3.5\" />}\n </button>\n </div>\n {collapsed ? null : plan.nodes.length === 0 ? (\n <div className=\"rounded-md border border-dashed border-border px-3 py-3 text-center text-xs text-muted-foreground\">\n No nodes yet — the plan graph fills in as the team is composed.\n </div>\n ) : (\n <div className=\"max-h-[min(46vh,360px)] overflow-auto\">\n <div className=\"relative\" style={{ width: layout.width, height: layout.height }}>\n <svg className=\"pointer-events-none absolute inset-0\" width={layout.width} height={layout.height} aria-hidden=\"true\">\n <defs>\n {STATUS_ORDER.map((status) => (\n <marker\n key={status}\n id={`plan-arrow-${status}`}\n viewBox=\"0 0 8 8\"\n refX=\"6.5\"\n refY=\"4\"\n markerWidth=\"6\"\n markerHeight=\"6\"\n orient=\"auto-start-reverse\"\n >\n <path d=\"M0 0 L8 4 L0 8 z\" fill={STATUS_STYLE[status].wire} />\n </marker>\n ))}\n </defs>\n {plan.edges.map((edge) => {\n const from = nodeById.get(edge.from)\n const to = nodeById.get(edge.to)\n if (!from || !to) return null\n const wire = STATUS_STYLE[from.node.status]\n return (\n <path\n key={`${edge.from}->${edge.to}`}\n d={edgePath(from, to)}\n stroke={wire.wire}\n strokeOpacity={0.7}\n strokeWidth={2}\n fill=\"none\"\n strokeLinecap=\"round\"\n markerEnd={`url(#plan-arrow-${from.node.status})`}\n />\n )\n })}\n </svg>\n {layout.nodes.map(({ node, x, y }) => {\n const status = STATUS_STYLE[node.status]\n const owner = ownerLabel(node.ownerRef)\n const issueRef = node.refs?.issueId ? issueRefsById[node.refs.issueId] : null\n const taskNumId = node.refs?.taskId ? parseInt(node.refs.taskId, 10) : null\n const taskTitle = taskNumId != null ? deliverableTasks.find((t) => t.id === taskNumId)?.title : undefined\n return (\n <article\n key={node.id}\n className={cn(\n 'group absolute flex flex-col justify-between rounded-md border border-l-[3px] bg-background/95 p-2',\n 'shadow-sm transition-all hover:-translate-y-px hover:border-border hover:shadow-md hover:bg-background',\n status.accent,\n )}\n style={{ left: x, top: y, width: NODE_W, height: NODE_H }}\n title={node.scopeBlurb || node.label}\n >\n <div className=\"truncate text-xs font-semibold leading-tight text-foreground\">{node.label}</div>\n {(node.scopeBlurb || node.id) && (\n <div className=\"truncate text-[10px] leading-tight text-muted-foreground\">{node.scopeBlurb || node.id}</div>\n )}\n {(issueRef || taskNumId != null) && (\n <div className=\"flex min-w-0 flex-wrap gap-1\">\n {issueRef && <IssueRefChip issueRef={issueRef} compact className=\"max-w-full\" />}\n {taskNumId != null && (\n <TaskChip id={taskNumId} title={taskTitle} onClick={() => openTaskDetailById(taskNumId)} className=\"max-w-full\" />\n )}\n </div>\n )}\n <div className=\"flex min-w-0 items-center justify-between gap-1.5\">\n <div className=\"flex shrink-0 items-center gap-1.5\">\n <span className={cn('h-1.5 w-1.5 shrink-0 rounded-full', status.dot)} />\n <span className={cn('text-[10px] font-medium', status.text)}>{status.label}</span>\n </div>\n <div className=\"flex min-w-0 items-center gap-1.5\">\n {owner && <span className=\"truncate text-[10px] text-muted-foreground\">{owner}</span>}\n {node.lane && (\n <span className=\"shrink-0 rounded-sm bg-muted/70 px-1 py-px text-[9px] font-medium uppercase tracking-wide text-muted-foreground\">\n {node.lane}\n </span>\n )}\n </div>\n </div>\n </article>\n )\n })}\n </div>\n </div>\n )}\n </section>\n )\n}\n\n// Tasks created/triggered from a session (#856, #860). Fetches server-side via createdBy filter.\nexport function SessionTasksPanel({ agentId }: { agentId: string }) {\n const openTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n const sessionTasksCollapsed = useRelayStore((s) => s.sessionTasksCollapsed)\n const setSessionTasksCollapsed = useRelayStore((s) => s.setSessionTasksCollapsed)\n const [sessionTasks, setSessionTasks] = useState<Task[]>([])\n const [showCompletedTasks, setShowCompletedTasks] = useState(false)\n useEffect(() => {\n void api<import('@/types').Task[]>('GET', '/task-entities?createdBy=' + encodeURIComponent(agentId))\n .then((tasks) => setSessionTasks(tasks))\n .catch(() => {})\n }, [agentId])\n if (sessionTasks.length === 0) return null\n const completedCount = sessionTasks.filter(isCompletedSessionTask).length\n const shownTasks = visibleSessionTasks(sessionTasks, showCompletedTasks)\n return (\n <section className=\"shrink-0 border-b border-border bg-card/45 px-3 py-2\">\n <button\n type=\"button\"\n onClick={() => setSessionTasksCollapsed(!sessionTasksCollapsed)}\n aria-expanded={!sessionTasksCollapsed}\n aria-label={sessionTasksCollapsed ? 'Expand session tasks' : 'Collapse session tasks'}\n className={cn('flex w-full min-w-0 items-center gap-2 text-left', !sessionTasksCollapsed && 'mb-1.5')}\n >\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60\">\n <ListTodo className=\"h-3 w-3 text-muted-foreground\" />\n </span>\n <span className=\"flex-1 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground\">\n Tasks from this session ({shownTasks.length})\n </span>\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground\">\n {sessionTasksCollapsed ? <ChevronDown className=\"h-3.5 w-3.5\" /> : <ChevronUp className=\"h-3.5 w-3.5\" />}\n </span>\n </button>\n {!sessionTasksCollapsed && (\n <>\n <div className=\"flex flex-wrap gap-1\">\n {shownTasks.map((task) => (\n <TaskChip\n key={task.id}\n id={task.id}\n title={task.title}\n status={task.status}\n onClick={() => openTaskDetailById(task.id)}\n compact\n />\n ))}\n </div>\n {completedCount > 0 && (\n <button\n type=\"button\"\n onClick={() => setShowCompletedTasks(!showCompletedTasks)}\n className=\"mt-1.5 rounded border border-border bg-muted/30 px-2 py-0.5 text-[10px] font-medium text-muted-foreground hover:bg-muted/50 hover:text-foreground\"\n aria-pressed={showCompletedTasks}\n >\n {showCompletedTasks ? `Hide ${completedCount} closed` : `+ ${completedCount} closed`}\n </button>\n )}\n </>\n )}\n </section>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{$n as t,Bt as n,Fn as r,Gt as i,R as a,d as o,er as s,gt as c,hr as l,it as u,mn as d,nr as f,rt as p,un as m}from"./lucide-react-Dc3ZwCBi.js";import{f as h,t as g}from"./store-BuuJUn70.js";import{K as _,f as v,h as y,rt as b}from"./display-exQDWum3.js";import{t as x}from"./badge-BhUZ1cAb.js";import{t as S}from"./button-C-V4oij1.js";import{t as C}from"./task-chip-kxk4eNm1.js";import{U as w,c as T,f as E,g as D,s as O,x as k,y as A}from"./index-
|
|
2
|
-
//# sourceMappingURL=activity-
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{$n as t,Bt as n,Fn as r,Gt as i,R as a,d as o,er as s,gt as c,hr as l,it as u,mn as d,nr as f,rt as p,un as m}from"./lucide-react-Dc3ZwCBi.js";import{f as h,t as g}from"./store-BuuJUn70.js";import{K as _,f as v,h as y,rt as b}from"./display-exQDWum3.js";import{t as x}from"./badge-BhUZ1cAb.js";import{t as S}from"./button-C-V4oij1.js";import{t as C}from"./task-chip-kxk4eNm1.js";import{U as w,c as T,f as E,g as D,s as O,x as k,y as A}from"./index-D_fNJnsI.js";import{r as j,t as M}from"./card-Br1JLjTH.js";import{t as N}from"./MessageBubble-jpshgh3K.js";var P=e(l(),1),F=f(),I={question:d,reply:p,message:u,operator:o,pair:c,task:m,state:a},L={question:`bg-blue-500/10 text-blue-400 border-blue-500/20`,reply:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,message:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`,operator:`bg-purple-500/10 text-purple-400 border-purple-500/20`,pair:`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`,task:`bg-orange-500/10 text-orange-400 border-orange-500/20`,state:`bg-cyan-500/10 text-cyan-400 border-cyan-500/20`},R=[`question`,`reply`,`message`,`operator`,`pair`,`task`,`state`],z=[`chat`,`session`,`system`,`task`];function B(e){let t=e?.surface;if(!t||typeof t!=`object`)return null;let n=t,r=[];return typeof n.component==`string`&&r.push(n.component),typeof n.view==`string`&&n.view!==n.component&&r.push(n.view),typeof n.session==`string`&&r.push(`tab ${n.session.slice(0,6)}`),typeof n.client==`string`&&r.push(`client ${n.client.slice(0,6)}`),r.length?r.join(` · `):null}function V({entry:e,agentsById:t}){if(e.type===`notice`)return(0,F.jsx)(`div`,{className:`px-1`,children:(0,F.jsx)(D,{msg:e.msg})});if(e.type===`agent-action`)return(0,F.jsx)(`div`,{className:`px-1`,children:(0,F.jsx)(T,{entry:e})});if(e.type===`activity`)return(0,F.jsx)(`div`,{className:`px-1 opacity-60`,children:(0,F.jsx)(O,{steps:e.steps,showReasoning:!1})});if(e.type===`status`)return(0,F.jsx)(`div`,{className:`px-1`,children:(0,F.jsx)(A,{event:e.event})});if(e.type===`date`)return(0,F.jsx)(`div`,{className:`px-1`,children:(0,F.jsx)(E,{date:e.date})});if(e.type===`created`||e.type===`import-boundary`||e.type===`imported-message`||e.type!==`message`)return null;let{msg:n}=e,r=v(n.from,t),i=v(n.to,t);return(0,F.jsxs)(`div`,{children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-1.5 px-3 mb-0.5`,children:[(0,F.jsx)(`span`,{className:`text-xs font-mono text-muted-foreground truncate max-w-[140px]`,title:n.from,children:r}),(0,F.jsx)(`span`,{className:`text-xs text-muted-foreground shrink-0`,children:`→`}),(0,F.jsx)(`span`,{className:`text-xs font-mono text-muted-foreground truncate max-w-[140px]`,title:n.to,children:i}),(0,F.jsx)(x,{variant:`outline`,className:`text-[10px] px-1 py-0 shrink-0 ml-1`,children:n.kind})]}),(0,F.jsx)(N,{msg:n,peer:b})]})}function H(){let e=g(e=>e.messages),t=g(e=>e.agentsById),[n,r]=(0,P.useState)(``),[i,a]=(0,P.useState)(``),o=(0,P.useMemo)(()=>{let t=e;if(n){let e=n.toLowerCase();t=t.filter(t=>t.from.toLowerCase().includes(e)||t.to.toLowerCase().includes(e))}return i&&(t=t.filter(e=>e.kind===i)),t},[e,n,i]),s=(0,P.useMemo)(()=>k(o,[]),[o]);return(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,F.jsx)(`input`,{type:`text`,className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm w-44`,placeholder:`Filter by agent…`,value:n,onChange:e=>r(e.target.value)}),(0,F.jsxs)(`select`,{className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm`,value:i,onChange:e=>a(e.target.value),children:[(0,F.jsx)(`option`,{value:``,children:`All kinds`}),z.map(e=>(0,F.jsx)(`option`,{value:e,children:e},e))]}),(0,F.jsx)(x,{variant:`secondary`,className:`ml-auto`,children:o.length})]}),(0,F.jsx)(w,{className:`h-[calc(100dvh-13rem)]`,children:(0,F.jsxs)(`div`,{className:`space-y-1 pr-2`,children:[s.length===0&&(0,F.jsx)(`div`,{className:`text-center text-muted-foreground py-16 text-sm`,children:`No messages yet`}),s.map((e,n)=>(0,F.jsx)(V,{entry:e,agentsById:t},n))]})})]})}function U(){let e=h(),n=g(e=>e.activityFilter),a=g(e=>e.activityEvents),o=g(e=>e.operatorActivity),s=g(e=>e.set),c=g(e=>e.openActivityItem),l=g(e=>e.openTaskDetailById),u=g(e=>e.exportActivity),d=(0,P.useMemo)(()=>{let e=[...a,...o],t=new Set,n=[];for(let r of e){let e=r.clientId||String(r.id);t.has(e)||(t.add(e),n.push(r))}return n.map(e=>({...e,ts:e.ts??new Date(e.createdAt).getTime()})).sort((e,t)=>(t.ts??0)-(e.ts??0))},[a,o]),f=(0,P.useMemo)(()=>n?d.filter(e=>e.kind===n):d,[d,n]);return(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,F.jsxs)(`select`,{className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm`,value:n,onChange:e=>s({activityFilter:e.target.value}),children:[(0,F.jsx)(`option`,{value:``,children:`All types`}),R.map(e=>(0,F.jsx)(`option`,{value:e,children:e},e))]}),(0,F.jsxs)(S,{size:`sm`,variant:`outline`,onClick:()=>u(`markdown`),children:[(0,F.jsx)(i,{className:`w-4 h-4 mr-1`}),` Markdown`]}),(0,F.jsxs)(S,{size:`sm`,variant:`outline`,onClick:()=>u(`json`),children:[(0,F.jsx)(r,{className:`w-4 h-4 mr-1`}),` JSON`]}),(0,F.jsx)(x,{variant:`secondary`,className:`ml-auto`,children:f.length})]}),(0,F.jsx)(w,{className:`h-[calc(100dvh-13rem)]`,children:(0,F.jsxs)(`div`,{className:`space-y-2 pr-2`,children:[f.length===0&&(0,F.jsx)(`div`,{className:`text-center text-muted-foreground py-16 text-sm`,children:`No activity yet`}),f.map(n=>{let r=I[n.kind]||t,i=L[n.kind]||L.message,a=B(n.metadata);return(0,F.jsx)(M,{className:`cursor-pointer hover:bg-accent/40 transition-colors`,onClick:()=>c(n),children:(0,F.jsxs)(j,{className:`p-3 flex gap-3`,children:[(0,F.jsx)(`span`,{className:`inline-flex items-center justify-center w-7 h-7 rounded-md border shrink-0 mt-0.5 ${i}`,children:(0,F.jsx)(r,{className:`w-3.5 h-3.5`})}),(0,F.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,F.jsx)(`span`,{className:`font-medium text-sm truncate`,children:n.title}),(0,F.jsx)(x,{variant:`outline`,className:`text-xs px-1.5 py-0 border ${i}`,children:n.kind}),(0,F.jsx)(`span`,{className:`text-xs text-muted-foreground ml-auto shrink-0`,title:y(n.ts??n.createdAt),children:_(e,n.ts??n.createdAt)})]}),n.body&&(0,F.jsx)(`p`,{className:`text-xs text-muted-foreground mt-0.5 line-clamp-2`,children:n.body}),n.meta&&(0,F.jsx)(`p`,{className:`text-xs text-muted-foreground/60 mt-0.5 font-mono truncate`,children:n.meta}),a&&(0,F.jsxs)(`p`,{className:`text-xs text-muted-foreground/60 mt-0.5 truncate`,title:a,children:[(0,F.jsx)(`span`,{className:`opacity-70`,children:`via`}),` `,a]}),n.taskId!=null&&(0,F.jsx)(`div`,{className:`mt-1`,children:(0,F.jsx)(C,{id:n.taskId,onClick:()=>l(n.taskId)})})]})]})},n.clientId||n.id)})]})})]})}function W(){let[e,r]=(0,P.useState)(`firehose`);return(0,F.jsxs)(`div`,{className:`space-y-4`,children:[(0,F.jsxs)(`div`,{className:`flex items-center justify-between flex-wrap gap-2`,children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2`,children:[e===`firehose`?(0,F.jsx)(n,{className:`w-5 h-5 text-muted-foreground`}):(0,F.jsx)(t,{className:`w-5 h-5 text-muted-foreground`}),(0,F.jsx)(`h2`,{className:`text-lg font-semibold`,children:e===`firehose`?`Firehose`:`Events`})]}),(0,F.jsxs)(`div`,{className:`flex items-center gap-1 rounded-md border border-input overflow-hidden`,children:[(0,F.jsx)(`button`,{className:s(`px-3 py-1.5 text-sm transition-colors`,e===`firehose`?`bg-primary text-primary-foreground`:`hover:bg-accent`),onClick:()=>r(`firehose`),children:`Firehose`}),(0,F.jsx)(`button`,{className:s(`px-3 py-1.5 text-sm transition-colors`,e===`events`?`bg-primary text-primary-foreground`:`hover:bg-accent`),onClick:()=>r(`events`),children:`Events`})]})]}),e===`firehose`?(0,F.jsx)(H,{}):(0,F.jsx)(U,{})]})}export{W as ActivityView};
|
|
2
|
+
//# sourceMappingURL=activity-DLTFF8Dz.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity-BUt4FiFQ.js","names":[],"sources":["../../dashboard/src/components/views/activity.tsx"],"sourcesContent":["import { useRelayStore, useNow } from '@/store'\nimport { Card, CardContent } from '@/components/ui/card'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { ScrollArea } from '@/components/ui/scroll-area'\nimport {\n Activity, MessageCircle, MessageSquareReply, HelpCircle, User,\n Link, ClipboardList, Radio, FileDown, Braces, Flame,\n} from 'lucide-react'\nimport { timeAgo, fmtTime, displayTarget } from '@/lib/display'\nimport type { ActivityEvent, Agent, Message } from '@/types'\nimport { useMemo, useState } from 'react'\nimport { TaskChip } from '@/components/shared/task-chip'\nimport { buildTimeline, type TimelineEntry } from '@/components/chat/sessionTrace'\nimport { AgentActionEntry, NoticePill, ActivityTrace, StatusMarker, DateSeparator } from '@/components/chat/TraceView'\nimport { MessageBubble } from '@/components/chat/MessageBubble'\nimport { HUMAN_AGENT_ID } from '@/lib/constants'\nimport { cn } from '@/lib/utils'\n\nconst KIND_ICONS: Record<string, React.ElementType> = {\n question: HelpCircle,\n reply: MessageSquareReply,\n message: MessageCircle,\n operator: User,\n pair: Link,\n task: ClipboardList,\n state: Radio,\n}\n\nconst KIND_COLORS: Record<string, string> = {\n question: 'bg-blue-500/10 text-blue-400 border-blue-500/20',\n reply: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n message: 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20',\n operator: 'bg-purple-500/10 text-purple-400 border-purple-500/20',\n pair: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n task: 'bg-orange-500/10 text-orange-400 border-orange-500/20',\n state: 'bg-cyan-500/10 text-cyan-400 border-cyan-500/20',\n}\n\nconst EVENT_KIND_LABELS = ['question', 'reply', 'message', 'operator', 'pair', 'task', 'state'] as const\nconst MSG_KIND_LABELS = ['chat', 'session', 'system', 'task'] as const\n\n// Compact attribution line for dashboard-initiated lifecycle commands (#172): identifies the\n// surface that issued a restart/shutdown/reconnect — component, SPA view, tab session, browser.\nfunction formatSurface(metadata: Record<string, unknown> | undefined): string | null {\n const surface = metadata?.surface\n if (!surface || typeof surface !== 'object') return null\n const s = surface as Record<string, unknown>\n const parts: string[] = []\n if (typeof s.component === 'string') parts.push(s.component)\n if (typeof s.view === 'string' && s.view !== s.component) parts.push(s.view)\n if (typeof s.session === 'string') parts.push(`tab ${s.session.slice(0, 6)}`)\n if (typeof s.client === 'string') parts.push(`client ${s.client.slice(0, 6)}`)\n return parts.length ? parts.join(' · ') : null\n}\n\n// Render one firehose timeline entry using Tier-1 exported components.\n// No entry-render code is duplicated here — body formatting, reactions, code blocks,\n// notice pills, status markers, and date separators all live in the shared exports.\nfunction FirehoseRow({ entry, agentsById }: { entry: TimelineEntry; agentsById: Record<string, Agent> }) {\n if (entry.type === 'notice') return <div className=\"px-1\"><NoticePill msg={entry.msg} /></div>\n if (entry.type === 'agent-action') return <div className=\"px-1\"><AgentActionEntry entry={entry} /></div>\n if (entry.type === 'activity') return (\n <div className=\"px-1 opacity-60\">\n <ActivityTrace steps={entry.steps} showReasoning={false} />\n </div>\n )\n if (entry.type === 'status') return <div className=\"px-1\"><StatusMarker event={entry.event} /></div>\n if (entry.type === 'date') return <div className=\"px-1\"><DateSeparator date={entry.date} /></div>\n if (entry.type === 'created' || entry.type === 'import-boundary' || entry.type === 'imported-message') return null\n if (entry.type !== 'message') return null\n\n const { msg } = entry\n const fromLabel = displayTarget(msg.from, agentsById)\n const toLabel = displayTarget(msg.to, agentsById)\n return (\n <div>\n {/* Attribution header: both sides of the routing, not shown by MessageBubble alone */}\n <div className=\"flex items-center gap-1.5 px-3 mb-0.5\">\n <span className=\"text-xs font-mono text-muted-foreground truncate max-w-[140px]\" title={msg.from}>{fromLabel}</span>\n <span className=\"text-xs text-muted-foreground shrink-0\">→</span>\n <span className=\"text-xs font-mono text-muted-foreground truncate max-w-[140px]\" title={msg.to}>{toLabel}</span>\n <Badge variant=\"outline\" className=\"text-[10px] px-1 py-0 shrink-0 ml-1\">{msg.kind}</Badge>\n </div>\n {/* peer=HUMAN_AGENT_ID: agent messages render inbound (left), user messages outbound (right) */}\n <MessageBubble msg={msg} peer={HUMAN_AGENT_ID} />\n </div>\n )\n}\n\nfunction FirehoseTab() {\n const messages = useRelayStore((s) => s.messages) as Message[]\n const agentsById = useRelayStore((s) => s.agentsById)\n const [agentFilter, setAgentFilter] = useState('')\n const [kindFilter, setKindFilter] = useState('')\n\n const filteredMessages = useMemo(() => {\n let msgs = messages\n if (agentFilter) {\n const q = agentFilter.toLowerCase()\n msgs = msgs.filter((m) => m.from.toLowerCase().includes(q) || m.to.toLowerCase().includes(q))\n }\n if (kindFilter) msgs = msgs.filter((m) => m.kind === kindFilter)\n return msgs\n }, [messages, agentFilter, kindFilter])\n\n const timeline = useMemo(() => buildTimeline(filteredMessages, []), [filteredMessages])\n\n return (\n <>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <input\n type=\"text\"\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm w-44\"\n placeholder=\"Filter by agent…\"\n value={agentFilter}\n onChange={(e) => setAgentFilter(e.target.value)}\n />\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={kindFilter}\n onChange={(e) => setKindFilter(e.target.value)}\n >\n <option value=\"\">All kinds</option>\n {MSG_KIND_LABELS.map((k) => <option key={k} value={k}>{k}</option>)}\n </select>\n <Badge variant=\"secondary\" className=\"ml-auto\">{filteredMessages.length}</Badge>\n </div>\n\n <ScrollArea className=\"h-[calc(100dvh-13rem)]\">\n <div className=\"space-y-1 pr-2\">\n {timeline.length === 0 && (\n <div className=\"text-center text-muted-foreground py-16 text-sm\">No messages yet</div>\n )}\n {timeline.map((entry, i) => (\n <FirehoseRow key={i} entry={entry} agentsById={agentsById} />\n ))}\n </div>\n </ScrollArea>\n </>\n )\n}\n\nfunction EventsTab() {\n const now = useNow()\n const activityFilter = useRelayStore((s) => s.activityFilter)\n const activityEvents = useRelayStore((s) => s.activityEvents)\n const operatorActivity = useRelayStore((s) => s.operatorActivity)\n const set = useRelayStore((s) => s.set)\n const openActivityItem = useRelayStore((s) => s.openActivityItem)\n const openTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n const exportActivity = useRelayStore((s) => s.exportActivity)\n\n const allItems = useMemo(() => {\n const merged = [...activityEvents, ...operatorActivity]\n const seen = new Set<string>()\n const deduped: ActivityEvent[] = []\n for (const item of merged) {\n const key = item.clientId || String(item.id)\n if (!seen.has(key)) { seen.add(key); deduped.push(item) }\n }\n return deduped\n .map((e) => ({ ...e, ts: e.ts ?? new Date(e.createdAt as string).getTime() }))\n .sort((a, b) => (b.ts ?? 0) - (a.ts ?? 0))\n }, [activityEvents, operatorActivity])\n\n const filtered = useMemo(() => {\n if (!activityFilter) return allItems\n return allItems.filter((e) => e.kind === activityFilter)\n }, [allItems, activityFilter])\n\n return (\n <>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={activityFilter}\n onChange={(e) => set({ activityFilter: e.target.value })}\n >\n <option value=\"\">All types</option>\n {EVENT_KIND_LABELS.map((k) => <option key={k} value={k}>{k}</option>)}\n </select>\n <Button size=\"sm\" variant=\"outline\" onClick={() => exportActivity('markdown')}>\n <FileDown className=\"w-4 h-4 mr-1\" /> Markdown\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={() => exportActivity('json')}>\n <Braces className=\"w-4 h-4 mr-1\" /> JSON\n </Button>\n <Badge variant=\"secondary\" className=\"ml-auto\">{filtered.length}</Badge>\n </div>\n\n <ScrollArea className=\"h-[calc(100dvh-13rem)]\">\n <div className=\"space-y-2 pr-2\">\n {filtered.length === 0 && (\n <div className=\"text-center text-muted-foreground py-16 text-sm\">No activity yet</div>\n )}\n {filtered.map((item) => {\n const Icon = KIND_ICONS[item.kind] || Activity\n const colorClass = KIND_COLORS[item.kind] || KIND_COLORS.message\n const surface = formatSurface(item.metadata)\n return (\n <Card\n key={item.clientId || item.id}\n className=\"cursor-pointer hover:bg-accent/40 transition-colors\"\n onClick={() => openActivityItem(item)}\n >\n <CardContent className=\"p-3 flex gap-3\">\n <span className={`inline-flex items-center justify-center w-7 h-7 rounded-md border shrink-0 mt-0.5 ${colorClass}`}>\n <Icon className=\"w-3.5 h-3.5\" />\n </span>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"font-medium text-sm truncate\">{item.title}</span>\n <Badge variant=\"outline\" className={`text-xs px-1.5 py-0 border ${colorClass}`}>{item.kind}</Badge>\n <span className=\"text-xs text-muted-foreground ml-auto shrink-0\" title={fmtTime(item.ts ?? item.createdAt)}>\n {timeAgo(now, item.ts ?? item.createdAt)}\n </span>\n </div>\n {item.body && (\n <p className=\"text-xs text-muted-foreground mt-0.5 line-clamp-2\">{item.body}</p>\n )}\n {item.meta && (\n <p className=\"text-xs text-muted-foreground/60 mt-0.5 font-mono truncate\">{item.meta}</p>\n )}\n {surface && (\n <p className=\"text-xs text-muted-foreground/60 mt-0.5 truncate\" title={surface}>\n <span className=\"opacity-70\">via</span> {surface}\n </p>\n )}\n {item.taskId != null && (\n <div className=\"mt-1\">\n <TaskChip id={item.taskId} onClick={() => openTaskDetailById(item.taskId!)} />\n </div>\n )}\n </div>\n </CardContent>\n </Card>\n )\n })}\n </div>\n </ScrollArea>\n </>\n )\n}\n\ntype ActivityTab = 'firehose' | 'events'\n\nexport function ActivityView() {\n const [tab, setTab] = useState<ActivityTab>('firehose')\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n {tab === 'firehose'\n ? <Flame className=\"w-5 h-5 text-muted-foreground\" />\n : <Activity className=\"w-5 h-5 text-muted-foreground\" />}\n <h2 className=\"text-lg font-semibold\">{tab === 'firehose' ? 'Firehose' : 'Events'}</h2>\n </div>\n <div className=\"flex items-center gap-1 rounded-md border border-input overflow-hidden\">\n <button\n className={cn('px-3 py-1.5 text-sm transition-colors', tab === 'firehose' ? 'bg-primary text-primary-foreground' : 'hover:bg-accent')}\n onClick={() => setTab('firehose')}\n >\n Firehose\n </button>\n <button\n className={cn('px-3 py-1.5 text-sm transition-colors', tab === 'events' ? 'bg-primary text-primary-foreground' : 'hover:bg-accent')}\n onClick={() => setTab('events')}\n >\n Events\n </button>\n </div>\n </div>\n\n {tab === 'firehose' ? <FirehoseTab /> : <EventsTab />}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"activity-DLTFF8Dz.js","names":[],"sources":["../../dashboard/src/components/views/activity.tsx"],"sourcesContent":["import { useRelayStore, useNow } from '@/store'\nimport { Card, CardContent } from '@/components/ui/card'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { ScrollArea } from '@/components/ui/scroll-area'\nimport {\n Activity, MessageCircle, MessageSquareReply, HelpCircle, User,\n Link, ClipboardList, Radio, FileDown, Braces, Flame,\n} from 'lucide-react'\nimport { timeAgo, fmtTime, displayTarget } from '@/lib/display'\nimport type { ActivityEvent, Agent, Message } from '@/types'\nimport { useMemo, useState } from 'react'\nimport { TaskChip } from '@/components/shared/task-chip'\nimport { buildTimeline, type TimelineEntry } from '@/components/chat/sessionTrace'\nimport { AgentActionEntry, NoticePill, ActivityTrace, StatusMarker, DateSeparator } from '@/components/chat/TraceView'\nimport { MessageBubble } from '@/components/chat/MessageBubble'\nimport { HUMAN_AGENT_ID } from '@/lib/constants'\nimport { cn } from '@/lib/utils'\n\nconst KIND_ICONS: Record<string, React.ElementType> = {\n question: HelpCircle,\n reply: MessageSquareReply,\n message: MessageCircle,\n operator: User,\n pair: Link,\n task: ClipboardList,\n state: Radio,\n}\n\nconst KIND_COLORS: Record<string, string> = {\n question: 'bg-blue-500/10 text-blue-400 border-blue-500/20',\n reply: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n message: 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20',\n operator: 'bg-purple-500/10 text-purple-400 border-purple-500/20',\n pair: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n task: 'bg-orange-500/10 text-orange-400 border-orange-500/20',\n state: 'bg-cyan-500/10 text-cyan-400 border-cyan-500/20',\n}\n\nconst EVENT_KIND_LABELS = ['question', 'reply', 'message', 'operator', 'pair', 'task', 'state'] as const\nconst MSG_KIND_LABELS = ['chat', 'session', 'system', 'task'] as const\n\n// Compact attribution line for dashboard-initiated lifecycle commands (#172): identifies the\n// surface that issued a restart/shutdown/reconnect — component, SPA view, tab session, browser.\nfunction formatSurface(metadata: Record<string, unknown> | undefined): string | null {\n const surface = metadata?.surface\n if (!surface || typeof surface !== 'object') return null\n const s = surface as Record<string, unknown>\n const parts: string[] = []\n if (typeof s.component === 'string') parts.push(s.component)\n if (typeof s.view === 'string' && s.view !== s.component) parts.push(s.view)\n if (typeof s.session === 'string') parts.push(`tab ${s.session.slice(0, 6)}`)\n if (typeof s.client === 'string') parts.push(`client ${s.client.slice(0, 6)}`)\n return parts.length ? parts.join(' · ') : null\n}\n\n// Render one firehose timeline entry using Tier-1 exported components.\n// No entry-render code is duplicated here — body formatting, reactions, code blocks,\n// notice pills, status markers, and date separators all live in the shared exports.\nfunction FirehoseRow({ entry, agentsById }: { entry: TimelineEntry; agentsById: Record<string, Agent> }) {\n if (entry.type === 'notice') return <div className=\"px-1\"><NoticePill msg={entry.msg} /></div>\n if (entry.type === 'agent-action') return <div className=\"px-1\"><AgentActionEntry entry={entry} /></div>\n if (entry.type === 'activity') return (\n <div className=\"px-1 opacity-60\">\n <ActivityTrace steps={entry.steps} showReasoning={false} />\n </div>\n )\n if (entry.type === 'status') return <div className=\"px-1\"><StatusMarker event={entry.event} /></div>\n if (entry.type === 'date') return <div className=\"px-1\"><DateSeparator date={entry.date} /></div>\n if (entry.type === 'created' || entry.type === 'import-boundary' || entry.type === 'imported-message') return null\n if (entry.type !== 'message') return null\n\n const { msg } = entry\n const fromLabel = displayTarget(msg.from, agentsById)\n const toLabel = displayTarget(msg.to, agentsById)\n return (\n <div>\n {/* Attribution header: both sides of the routing, not shown by MessageBubble alone */}\n <div className=\"flex items-center gap-1.5 px-3 mb-0.5\">\n <span className=\"text-xs font-mono text-muted-foreground truncate max-w-[140px]\" title={msg.from}>{fromLabel}</span>\n <span className=\"text-xs text-muted-foreground shrink-0\">→</span>\n <span className=\"text-xs font-mono text-muted-foreground truncate max-w-[140px]\" title={msg.to}>{toLabel}</span>\n <Badge variant=\"outline\" className=\"text-[10px] px-1 py-0 shrink-0 ml-1\">{msg.kind}</Badge>\n </div>\n {/* peer=HUMAN_AGENT_ID: agent messages render inbound (left), user messages outbound (right) */}\n <MessageBubble msg={msg} peer={HUMAN_AGENT_ID} />\n </div>\n )\n}\n\nfunction FirehoseTab() {\n const messages = useRelayStore((s) => s.messages) as Message[]\n const agentsById = useRelayStore((s) => s.agentsById)\n const [agentFilter, setAgentFilter] = useState('')\n const [kindFilter, setKindFilter] = useState('')\n\n const filteredMessages = useMemo(() => {\n let msgs = messages\n if (agentFilter) {\n const q = agentFilter.toLowerCase()\n msgs = msgs.filter((m) => m.from.toLowerCase().includes(q) || m.to.toLowerCase().includes(q))\n }\n if (kindFilter) msgs = msgs.filter((m) => m.kind === kindFilter)\n return msgs\n }, [messages, agentFilter, kindFilter])\n\n const timeline = useMemo(() => buildTimeline(filteredMessages, []), [filteredMessages])\n\n return (\n <>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <input\n type=\"text\"\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm w-44\"\n placeholder=\"Filter by agent…\"\n value={agentFilter}\n onChange={(e) => setAgentFilter(e.target.value)}\n />\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={kindFilter}\n onChange={(e) => setKindFilter(e.target.value)}\n >\n <option value=\"\">All kinds</option>\n {MSG_KIND_LABELS.map((k) => <option key={k} value={k}>{k}</option>)}\n </select>\n <Badge variant=\"secondary\" className=\"ml-auto\">{filteredMessages.length}</Badge>\n </div>\n\n <ScrollArea className=\"h-[calc(100dvh-13rem)]\">\n <div className=\"space-y-1 pr-2\">\n {timeline.length === 0 && (\n <div className=\"text-center text-muted-foreground py-16 text-sm\">No messages yet</div>\n )}\n {timeline.map((entry, i) => (\n <FirehoseRow key={i} entry={entry} agentsById={agentsById} />\n ))}\n </div>\n </ScrollArea>\n </>\n )\n}\n\nfunction EventsTab() {\n const now = useNow()\n const activityFilter = useRelayStore((s) => s.activityFilter)\n const activityEvents = useRelayStore((s) => s.activityEvents)\n const operatorActivity = useRelayStore((s) => s.operatorActivity)\n const set = useRelayStore((s) => s.set)\n const openActivityItem = useRelayStore((s) => s.openActivityItem)\n const openTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n const exportActivity = useRelayStore((s) => s.exportActivity)\n\n const allItems = useMemo(() => {\n const merged = [...activityEvents, ...operatorActivity]\n const seen = new Set<string>()\n const deduped: ActivityEvent[] = []\n for (const item of merged) {\n const key = item.clientId || String(item.id)\n if (!seen.has(key)) { seen.add(key); deduped.push(item) }\n }\n return deduped\n .map((e) => ({ ...e, ts: e.ts ?? new Date(e.createdAt as string).getTime() }))\n .sort((a, b) => (b.ts ?? 0) - (a.ts ?? 0))\n }, [activityEvents, operatorActivity])\n\n const filtered = useMemo(() => {\n if (!activityFilter) return allItems\n return allItems.filter((e) => e.kind === activityFilter)\n }, [allItems, activityFilter])\n\n return (\n <>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={activityFilter}\n onChange={(e) => set({ activityFilter: e.target.value })}\n >\n <option value=\"\">All types</option>\n {EVENT_KIND_LABELS.map((k) => <option key={k} value={k}>{k}</option>)}\n </select>\n <Button size=\"sm\" variant=\"outline\" onClick={() => exportActivity('markdown')}>\n <FileDown className=\"w-4 h-4 mr-1\" /> Markdown\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={() => exportActivity('json')}>\n <Braces className=\"w-4 h-4 mr-1\" /> JSON\n </Button>\n <Badge variant=\"secondary\" className=\"ml-auto\">{filtered.length}</Badge>\n </div>\n\n <ScrollArea className=\"h-[calc(100dvh-13rem)]\">\n <div className=\"space-y-2 pr-2\">\n {filtered.length === 0 && (\n <div className=\"text-center text-muted-foreground py-16 text-sm\">No activity yet</div>\n )}\n {filtered.map((item) => {\n const Icon = KIND_ICONS[item.kind] || Activity\n const colorClass = KIND_COLORS[item.kind] || KIND_COLORS.message\n const surface = formatSurface(item.metadata)\n return (\n <Card\n key={item.clientId || item.id}\n className=\"cursor-pointer hover:bg-accent/40 transition-colors\"\n onClick={() => openActivityItem(item)}\n >\n <CardContent className=\"p-3 flex gap-3\">\n <span className={`inline-flex items-center justify-center w-7 h-7 rounded-md border shrink-0 mt-0.5 ${colorClass}`}>\n <Icon className=\"w-3.5 h-3.5\" />\n </span>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"font-medium text-sm truncate\">{item.title}</span>\n <Badge variant=\"outline\" className={`text-xs px-1.5 py-0 border ${colorClass}`}>{item.kind}</Badge>\n <span className=\"text-xs text-muted-foreground ml-auto shrink-0\" title={fmtTime(item.ts ?? item.createdAt)}>\n {timeAgo(now, item.ts ?? item.createdAt)}\n </span>\n </div>\n {item.body && (\n <p className=\"text-xs text-muted-foreground mt-0.5 line-clamp-2\">{item.body}</p>\n )}\n {item.meta && (\n <p className=\"text-xs text-muted-foreground/60 mt-0.5 font-mono truncate\">{item.meta}</p>\n )}\n {surface && (\n <p className=\"text-xs text-muted-foreground/60 mt-0.5 truncate\" title={surface}>\n <span className=\"opacity-70\">via</span> {surface}\n </p>\n )}\n {item.taskId != null && (\n <div className=\"mt-1\">\n <TaskChip id={item.taskId} onClick={() => openTaskDetailById(item.taskId!)} />\n </div>\n )}\n </div>\n </CardContent>\n </Card>\n )\n })}\n </div>\n </ScrollArea>\n </>\n )\n}\n\ntype ActivityTab = 'firehose' | 'events'\n\nexport function ActivityView() {\n const [tab, setTab] = useState<ActivityTab>('firehose')\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n {tab === 'firehose'\n ? <Flame className=\"w-5 h-5 text-muted-foreground\" />\n : <Activity className=\"w-5 h-5 text-muted-foreground\" />}\n <h2 className=\"text-lg font-semibold\">{tab === 'firehose' ? 'Firehose' : 'Events'}</h2>\n </div>\n <div className=\"flex items-center gap-1 rounded-md border border-input overflow-hidden\">\n <button\n className={cn('px-3 py-1.5 text-sm transition-colors', tab === 'firehose' ? 'bg-primary text-primary-foreground' : 'hover:bg-accent')}\n onClick={() => setTab('firehose')}\n >\n Firehose\n </button>\n <button\n className={cn('px-3 py-1.5 text-sm transition-colors', tab === 'events' ? 'bg-primary text-primary-foreground' : 'hover:bg-accent')}\n onClick={() => setTab('events')}\n >\n Events\n </button>\n </div>\n </div>\n\n {tab === 'firehose' ? <FirehoseTab /> : <EventsTab />}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{G as t,Jt as n,V as r,an as i,en as a,g as o,hr as s,nr as c,p as l,pt as u,qt as d}from"./lucide-react-Dc3ZwCBi.js";import{t as f}from"./store-BuuJUn70.js";import{h as p,p as m,u as h}from"./types-uVOXgvMT.js";import{t as g}from"./badge-BhUZ1cAb.js";import{t as _}from"./button-C-V4oij1.js";import{a as v,i as y,n as b,o as x,r as S,t as C}from"./index-
|
|
2
|
-
//# sourceMappingURL=agent-profiles-
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{G as t,Jt as n,V as r,an as i,en as a,g as o,hr as s,nr as c,p as l,pt as u,qt as d}from"./lucide-react-Dc3ZwCBi.js";import{t as f}from"./store-BuuJUn70.js";import{h as p,p as m,u as h}from"./types-uVOXgvMT.js";import{t as g}from"./badge-BhUZ1cAb.js";import{t as _}from"./button-C-V4oij1.js";import{a as v,i as y,n as b,o as x,r as S,t as C}from"./index-D_fNJnsI.js";var w=e(s(),1),T=c();function E(e){let t=m(),n=e.provider??``;return n&&n!==`any`&&t.includes(n)?n:t[0]??n}function D(e){if(Array.isArray(e))return e.map(D);if(e&&typeof e==`object`){let t={};for(let[n,r]of Object.entries(e))n===`files`&&Array.isArray(r)?t[n]=r.map(e=>{if(e&&typeof e==`object`&&typeof e.content==`string`){let t=e.content;return{...e,content:`<${t.length} chars elided>`}}return D(e)}):t[n]=D(r);return t}return e}function O({profile:e,open:t,onOpenChange:n}){let r=f(e=>e.apiCall),[i,o]=(0,w.useState)(()=>E(e)),[s,c]=(0,w.useState)(!1),[l,d]=(0,w.useState)(null),[g,O]=(0,w.useState)(null),k=(0,w.useCallback)(async t=>{c(!0),d(null);try{O((await r(`GET`,`/agent-profiles/${encodeURIComponent(e.name)}/resolved?provider=${t}`)).profile)}catch(e){O(null),d(h(e))}finally{c(!1)}},[r,e.name]);(0,w.useEffect)(()=>{if(t){let t=E(e);o(t),k(t)}},[t,e,k]);function A(e){e!==i&&(o(e),k(e))}function j(){if(!g)return;let t=new Blob([JSON.stringify(g,null,2)],{type:`application/json`}),n=URL.createObjectURL(t),r=document.createElement(`a`);r.href=n,r.download=`${e.name}.${i}.resolved.json`,document.body.appendChild(r),r.click(),r.remove(),URL.revokeObjectURL(n)}let M=g?JSON.stringify(D(g),null,2):``;return(0,T.jsx)(C,{open:t,onOpenChange:n,children:(0,T.jsxs)(b,{className:`max-w-3xl`,children:[(0,T.jsxs)(v,{children:[(0,T.jsxs)(x,{className:`font-mono text-sm`,children:[e.name,` — resolved projection`]}),(0,T.jsxs)(S,{children:[`The fully-resolved agent-profile projection (`,(0,T.jsx)(`code`,{children:`withResolvedProvisioning`}),`) for the selected provider. File contents are elided below; download the full JSON for the complete bundle.`]})]}),(0,T.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,T.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`Provider`}),(0,T.jsx)(`div`,{className:`flex items-center gap-1`,children:m().map(e=>(0,T.jsx)(_,{variant:e===i?`default`:`outline`,size:`sm`,className:`h-7 px-3 text-xs`,disabled:s,onClick:()=>A(e),children:p(e)},e))}),(0,T.jsx)(`div`,{className:`flex-1`}),(0,T.jsxs)(_,{size:`sm`,className:`h-7 gap-1 text-xs`,disabled:s||!g,onClick:j,children:[(0,T.jsx)(a,{className:`h-3.5 w-3.5`}),`Download full JSON`]})]}),(0,T.jsx)(`div`,{className:`min-h-[16rem] overflow-auto rounded-md border border-border bg-muted/30 p-3`,children:s?(0,T.jsxs)(`div`,{className:`flex items-center gap-2 text-xs text-muted-foreground`,children:[(0,T.jsx)(u,{className:`h-3.5 w-3.5 animate-spin`}),`Resolving projection…`]}):l?(0,T.jsx)(`div`,{className:`text-xs text-destructive`,children:l}):g?(0,T.jsx)(`pre`,{className:`max-h-[60vh] overflow-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed`,children:M}):(0,T.jsx)(`div`,{className:`text-xs text-muted-foreground`,children:`No projection available.`})}),(0,T.jsx)(y,{children:(0,T.jsx)(_,{variant:`outline`,size:`sm`,onClick:()=>n(!1),children:`Close`})})]})})}var k={host:`text-emerald-400 bg-emerald-500/10`,minimal:`text-amber-400 bg-amber-500/10`,isolated:`text-red-400 bg-red-500/10`,vanilla:`text-fuchsia-400 bg-fuchsia-500/10`};function A({relay:e}){return(0,T.jsx)(`div`,{className:`flex items-center gap-1`,children:[{key:`ctx`,on:e.context},{key:`skl`,on:e.skills},{key:`plg`,on:e.plugins},{key:`stl`,on:e.statusLine}].map(e=>(0,T.jsx)(`span`,{className:`inline-block h-1.5 w-1.5 rounded-full ${e.on?`bg-emerald-400`:`bg-muted-foreground/30`}`,title:`${e.key}: ${e.on?`on`:`off`}`},e.key))})}function j({profile:e}){let r=f(e=>e.openProfileModal),a=f(e=>e.deleteProfile),s=e.builtIn===!0,[c,l]=(0,w.useState)(!1);return(0,T.jsxs)(`div`,{className:`group flex flex-col gap-3 rounded-lg border border-border bg-card p-4`,children:[(0,T.jsxs)(`div`,{className:`flex items-start gap-2`,children:[(0,T.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,T.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,T.jsx)(`span`,{className:`text-base leading-none`,"aria-hidden":!0,children:e.icon?.trim()||`•`}),(0,T.jsx)(`span`,{className:`font-mono text-sm font-medium`,children:e.name}),s&&(0,T.jsx)(g,{variant:`secondary`,className:`text-[10px]`,children:`Built-in`}),e.overridden&&(0,T.jsx)(g,{variant:`outline`,className:`text-[10px] border-amber-500/30 bg-amber-500/10 text-amber-400`,children:`Overridden`})]}),e.description&&(0,T.jsx)(`p`,{className:`mt-0.5 text-xs text-muted-foreground line-clamp-2`,children:e.description})]}),(0,T.jsxs)(`div`,{className:`flex shrink-0 items-center gap-0.5 opacity-100 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity`,children:[(0,T.jsx)(_,{variant:`ghost`,size:`icon-sm`,className:`h-7 w-7`,title:`View`,onClick:()=>r(`view`,e),children:(0,T.jsx)(n,{className:`h-3.5 w-3.5`})}),(0,T.jsx)(_,{variant:`ghost`,size:`icon-sm`,className:`h-7 w-7`,title:`Edit`,onClick:()=>r(`edit`,e),children:(0,T.jsx)(t,{className:`h-3.5 w-3.5`})}),(0,T.jsx)(_,{variant:`ghost`,size:`icon-sm`,className:`h-7 w-7`,title:`Duplicate`,onClick:()=>r(`duplicate`,e),children:(0,T.jsx)(i,{className:`h-3.5 w-3.5`})}),(0,T.jsx)(_,{variant:`ghost`,size:`icon-sm`,className:`h-7 w-7`,title:`Resolved projection`,onClick:()=>l(!0),children:(0,T.jsx)(d,{className:`h-3.5 w-3.5`})}),(!s||e.overridden)&&(0,T.jsx)(_,{variant:`ghost`,size:`icon-sm`,className:`h-7 w-7 text-destructive`,title:s?`Reset to default`:`Delete`,onClick:()=>a(e),children:(0,T.jsx)(o,{className:`h-3.5 w-3.5`})})]})]}),(0,T.jsxs)(`div`,{className:`flex flex-wrap items-center gap-1.5 text-[10px]`,children:[(0,T.jsx)(g,{variant:`outline`,className:k[e.base]||``,children:e.base}),(0,T.jsx)(g,{variant:`outline`,children:e.provider||`any`}),(0,T.jsxs)(g,{variant:`outline`,children:[`fs: `,e.permissions.filesystem]}),e.permissions.mode&&(0,T.jsx)(g,{variant:`outline`,children:e.permissions.mode})]}),(0,T.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-4 gap-y-1 text-[10px] text-muted-foreground`,children:[(0,T.jsxs)(`span`,{className:`flex items-center gap-1`,children:[`Relay `,(0,T.jsx)(A,{relay:e.relay})]}),(0,T.jsxs)(`span`,{children:[`MCP: `,e.mcp.mode]}),(0,T.jsxs)(`span`,{children:[`Hooks: `,e.hooks.mode]}),e.instructions.globalInstructions===`ignore`&&(0,T.jsx)(`span`,{className:`text-amber-400`,children:`global: ignore`}),Object.keys(e.env||{}).length>0&&(0,T.jsxs)(`span`,{children:[Object.keys(e.env).length,` env vars`]})]}),(0,T.jsx)(O,{profile:e,open:c,onOpenChange:l})]})}function M(){let e=f(e=>e.agentProfiles),t=f(e=>e.openProfileModal),n=e.filter(e=>e.builtIn),i=e.filter(e=>!e.builtIn);return(0,T.jsxs)(`div`,{className:`space-y-4`,children:[(0,T.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,T.jsxs)(`div`,{className:`flex items-center gap-2 text-lg font-semibold`,children:[(0,T.jsx)(l,{className:`h-5 w-5 text-primary`}),`Agent Profiles`]}),(0,T.jsx)(g,{variant:`outline`,children:e.length}),(0,T.jsx)(`div`,{className:`flex-1`}),(0,T.jsxs)(_,{size:`sm`,className:`gap-1`,onClick:()=>t(`create`),children:[(0,T.jsx)(r,{className:`h-3.5 w-3.5`}),`Create Profile`]})]}),n.length>0&&(0,T.jsxs)(`div`,{children:[(0,T.jsx)(`h3`,{className:`mb-2 text-xs font-medium text-muted-foreground uppercase tracking-wider`,children:`Built-in`}),(0,T.jsx)(`div`,{className:`grid gap-3 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3`,children:n.map(e=>(0,T.jsx)(j,{profile:e},e.name))})]}),i.length>0&&(0,T.jsxs)(`div`,{children:[(0,T.jsx)(`h3`,{className:`mb-2 text-xs font-medium text-muted-foreground uppercase tracking-wider`,children:`Custom`}),(0,T.jsx)(`div`,{className:`grid gap-3 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3`,children:i.map(e=>(0,T.jsx)(j,{profile:e},e.name))})]}),e.length===0&&(0,T.jsx)(`div`,{className:`py-12 text-center text-sm text-muted-foreground`,children:`No agent profiles found. Create one to configure agent behavior.`})]})}export{M as AgentProfilesView};
|
|
2
|
+
//# sourceMappingURL=agent-profiles-BcCLIrON.js.map
|