agent-relay-server 0.121.4 → 0.122.0

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.
Files changed (132) hide show
  1. package/docs/openapi.json +1 -1
  2. package/package.json +5 -4
  3. package/public/assets/{MessageBubble-CLqxCG9b.js → MessageBubble-DRdUD-kW.js} +2 -2
  4. package/public/assets/{MessageBubble-CLqxCG9b.js.map → MessageBubble-DRdUD-kW.js.map} +1 -1
  5. package/public/assets/{PlanGraphPanel-BkkToFEb.js → PlanGraphPanel-siKMPCln.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-BkkToFEb.js.map → PlanGraphPanel-siKMPCln.js.map} +1 -1
  7. package/public/assets/{activity-D-ocGQGs.js → activity-D3MNcSaV.js} +2 -2
  8. package/public/assets/{activity-D-ocGQGs.js.map → activity-D3MNcSaV.js.map} +1 -1
  9. package/public/assets/{agent-profiles-CRPgYTyb.js → agent-profiles-CKzttq6G.js} +2 -2
  10. package/public/assets/{agent-profiles-CRPgYTyb.js.map → agent-profiles-CKzttq6G.js.map} +1 -1
  11. package/public/assets/{agent-type-icon-CeEoMouY.js → agent-type-icon-0ZJRP1Vy.js} +2 -2
  12. package/public/assets/{agent-type-icon-CeEoMouY.js.map → agent-type-icon-0ZJRP1Vy.js.map} +1 -1
  13. package/public/assets/{agents-Chw5E_2q.js → agents-CuVj_rfG.js} +2 -2
  14. package/public/assets/{agents-Chw5E_2q.js.map → agents-CuVj_rfG.js.map} +1 -1
  15. package/public/assets/{analytics-BmNDG0hR.js → analytics-iob6C27d.js} +2 -2
  16. package/public/assets/{analytics-BmNDG0hR.js.map → analytics-iob6C27d.js.map} +1 -1
  17. package/public/assets/{automation-D7g90kTv.js → automation-B2ixxs4q.js} +2 -2
  18. package/public/assets/{automation-D7g90kTv.js.map → automation-B2ixxs4q.js.map} +1 -1
  19. package/public/assets/{branch-state-badge-Bv7DhLBZ.js → branch-state-badge-CafBJWKo.js} +2 -2
  20. package/public/assets/{branch-state-badge-Bv7DhLBZ.js.map → branch-state-badge-CafBJWKo.js.map} +1 -1
  21. package/public/assets/{channels-CLwPeEPi.js → channels-C-WsbL2H.js} +2 -2
  22. package/public/assets/{channels-CLwPeEPi.js.map → channels-C-WsbL2H.js.map} +1 -1
  23. package/public/assets/{chat-Cgj7VKzc.js → chat-DeoVTVob.js} +2 -2
  24. package/public/assets/{chat-Cgj7VKzc.js.map → chat-DeoVTVob.js.map} +1 -1
  25. package/public/assets/{connectors-Br6fiTny.js → connectors-C2Ac03LJ.js} +2 -2
  26. package/public/assets/{connectors-Br6fiTny.js.map → connectors-C2Ac03LJ.js.map} +1 -1
  27. package/public/assets/display-CJzSoTMq.js +3 -0
  28. package/public/assets/display-CJzSoTMq.js.map +1 -0
  29. package/public/assets/{formatted-body-CmkuD23M.js → formatted-body-35XBOY5L.js} +3 -3
  30. package/public/assets/{formatted-body-CmkuD23M.js.map → formatted-body-35XBOY5L.js.map} +1 -1
  31. package/public/assets/{formatted-body-impl-DbdFmbwW.js → formatted-body-impl-RmDxI5Ux.js} +2 -2
  32. package/public/assets/{formatted-body-impl-DbdFmbwW.js.map → formatted-body-impl-RmDxI5Ux.js.map} +1 -1
  33. package/public/assets/index-D0xMQQb_.js +25 -0
  34. package/public/assets/{index-D9OogaB5.js.map → index-D0xMQQb_.js.map} +1 -1
  35. package/public/assets/{insights-qvaPqWCV.js → insights-B643SFvm.js} +2 -2
  36. package/public/assets/{insights-qvaPqWCV.js.map → insights-B643SFvm.js.map} +1 -1
  37. package/public/assets/{integrations-DYEGSqq_.js → integrations-hFIHeCF4.js} +2 -2
  38. package/public/assets/{integrations-DYEGSqq_.js.map → integrations-hFIHeCF4.js.map} +1 -1
  39. package/public/assets/{maintenance-C0HIEB-J.js → maintenance-CURvdp_Z.js} +2 -2
  40. package/public/assets/{maintenance-C0HIEB-J.js.map → maintenance-CURvdp_Z.js.map} +1 -1
  41. package/public/assets/{managed-agents-DdS7aI38.js → managed-agents-DrZtdlSn.js} +2 -2
  42. package/public/assets/{managed-agents-DdS7aI38.js.map → managed-agents-DrZtdlSn.js.map} +1 -1
  43. package/public/assets/{markdown-preview-impl-BnXMH1z1.js → markdown-preview-impl-CVDO--ek.js} +2 -2
  44. package/public/assets/{markdown-preview-impl-BnXMH1z1.js.map → markdown-preview-impl-CVDO--ek.js.map} +1 -1
  45. package/public/assets/{memory-CSwx7bz8.js → memory-Bm7dt_Qf.js} +2 -2
  46. package/public/assets/{memory-CSwx7bz8.js.map → memory-Bm7dt_Qf.js.map} +1 -1
  47. package/public/assets/{messages-Be9CmvDv.js → messages-DTq4i2KZ.js} +2 -2
  48. package/public/assets/{messages-Be9CmvDv.js.map → messages-DTq4i2KZ.js.map} +1 -1
  49. package/public/assets/{orchestrators-BgtvvHvo.js → orchestrators-Dra318Ap.js} +2 -2
  50. package/public/assets/{orchestrators-BgtvvHvo.js.map → orchestrators-Dra318Ap.js.map} +1 -1
  51. package/public/assets/{overview-BvhbPWtF.js → overview-yr1Vmk64.js} +2 -2
  52. package/public/assets/{overview-BvhbPWtF.js.map → overview-yr1Vmk64.js.map} +1 -1
  53. package/public/assets/{pairs-CpXSOMx0.js → pairs-mnlKDBGp.js} +2 -2
  54. package/public/assets/{pairs-CpXSOMx0.js.map → pairs-mnlKDBGp.js.map} +1 -1
  55. package/public/assets/projects-Dzdf-gkK.js +2 -0
  56. package/public/assets/{projects-DdPNPFJI.js.map → projects-Dzdf-gkK.js.map} +1 -1
  57. package/public/assets/prompt-settings-ByL8SNCi.js +2 -0
  58. package/public/assets/{prompt-settings-BpZse7-X.js.map → prompt-settings-ByL8SNCi.js.map} +1 -1
  59. package/public/assets/{registry-pb1gTZEg.js → registry-CXWyOtpr.js} +2 -2
  60. package/public/assets/{registry-pb1gTZEg.js.map → registry-CXWyOtpr.js.map} +1 -1
  61. package/public/assets/{security-BgcX1n9D.js → security-0SBsc_4W.js} +2 -2
  62. package/public/assets/{security-BgcX1n9D.js.map → security-0SBsc_4W.js.map} +1 -1
  63. package/public/assets/{select-DZPVpKPv.js → select-Bw5z2KJ_.js} +2 -2
  64. package/public/assets/{select-DZPVpKPv.js.map → select-Bw5z2KJ_.js.map} +1 -1
  65. package/public/assets/{settings-hd5kzdRB.js → settings-78Cyq_tA.js} +4 -4
  66. package/public/assets/{settings-hd5kzdRB.js.map → settings-78Cyq_tA.js.map} +1 -1
  67. package/public/assets/{store-DKTLubBT.js → store-BPrVuKiv.js} +4 -4
  68. package/public/assets/store-BPrVuKiv.js.map +1 -0
  69. package/public/assets/{tasks-C5d2LU5E.js → tasks-Cg2MMeri.js} +2 -2
  70. package/public/assets/{tasks-C5d2LU5E.js.map → tasks-Cg2MMeri.js.map} +1 -1
  71. package/public/assets/teams-DWv6UwFV.js +2 -0
  72. package/public/assets/{teams-B8f2pGJe.js.map → teams-DWv6UwFV.js.map} +1 -1
  73. package/public/assets/{terminal-viewer-impl-CxAscH0U.js → terminal-viewer-impl-DBLDjbIg.js} +2 -2
  74. package/public/assets/{terminal-viewer-impl-CxAscH0U.js.map → terminal-viewer-impl-DBLDjbIg.js.map} +1 -1
  75. package/public/assets/{types-uVOXgvMT.js → types-BeJUSfDj.js} +2 -2
  76. package/public/assets/types-BeJUSfDj.js.map +1 -0
  77. package/public/assets/{user-avatar-Dw6mzWhv.js → user-avatar-BsOLyVJj.js} +2 -2
  78. package/public/assets/{user-avatar-Dw6mzWhv.js.map → user-avatar-BsOLyVJj.js.map} +1 -1
  79. package/public/assets/{work-queue-9imc7aNK.js → work-queue-DmwvnF6F.js} +2 -2
  80. package/public/assets/{work-queue-9imc7aNK.js.map → work-queue-DmwvnF6F.js.map} +1 -1
  81. package/public/assets/{workspaces-CLYc3yF3.js → workspaces-dIBivJN8.js} +2 -2
  82. package/public/assets/{workspaces-CLYc3yF3.js.map → workspaces-dIBivJN8.js.map} +1 -1
  83. package/public/index.html +6 -6
  84. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  85. package/runner/plugins/claude/monitors/relay-monitor.provisioned.mjs +44 -938
  86. package/runner/src/adapter.ts +53 -18
  87. package/runner/src/config.ts +1 -4
  88. package/src/agent-card-projection.ts +5 -1
  89. package/src/agent-command-activity.ts +39 -0
  90. package/src/automations/reconcile.ts +2 -2
  91. package/src/bus.ts +6 -4
  92. package/src/cli/workspace.ts +3 -1
  93. package/src/config.ts +10 -1
  94. package/src/db/agents.ts +138 -32
  95. package/src/db/message-reads.ts +50 -0
  96. package/src/db/messages.ts +48 -9
  97. package/src/db/migrations.ts +54 -0
  98. package/src/db/provider-quota-burn.ts +96 -67
  99. package/src/db/provider-quotas.ts +29 -7
  100. package/src/db/scheduled-timers.ts +21 -0
  101. package/src/db/schema.ts +1 -0
  102. package/src/db/tasks.ts +33 -3
  103. package/src/db/work-evidence.ts +10 -7
  104. package/src/event-loop-lag.ts +15 -0
  105. package/src/gate-resolver.ts +24 -1
  106. package/src/maintenance/jobs.ts +23 -6
  107. package/src/managed-policy.ts +35 -5
  108. package/src/mcp-plan-tools.ts +13 -1
  109. package/src/project-instructions.ts +36 -2
  110. package/src/quota-advisory.ts +6 -4
  111. package/src/quota-band-transition.ts +23 -14
  112. package/src/routes/agent-sessions.ts +39 -7
  113. package/src/routes/agents.ts +3 -2
  114. package/src/routes/tasks.ts +16 -4
  115. package/src/routes/workspaces.ts +35 -10
  116. package/src/services/plan-graph.ts +27 -9
  117. package/src/services/register-agent.ts +12 -1
  118. package/src/services/send-message.ts +43 -15
  119. package/src/services/spawn-agent.ts +3 -0
  120. package/src/spawn-command.ts +8 -0
  121. package/src/spawn-targets.ts +19 -34
  122. package/src/sse.ts +25 -3
  123. package/src/workspace-merge.ts +25 -5
  124. package/src/workspace-orphans.ts +13 -6
  125. package/public/assets/display-exQDWum3.js +0 -3
  126. package/public/assets/display-exQDWum3.js.map +0 -1
  127. package/public/assets/index-D9OogaB5.js +0 -25
  128. package/public/assets/projects-DdPNPFJI.js +0 -2
  129. package/public/assets/prompt-settings-BpZse7-X.js +0 -2
  130. package/public/assets/store-DKTLubBT.js.map +0 -1
  131. package/public/assets/teams-B8f2pGJe.js +0 -2
  132. package/public/assets/types-uVOXgvMT.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import{B as e,N as t,U as n,b as r,nr as i,y as a,z as o}from"./lucide-react-DuhlF2Sj.js";import{t as s}from"./store-DKTLubBT.js";import{J as c,Y as l,l as u}from"./display-exQDWum3.js";import{t as d}from"./badge-DCIT3Irj.js";import{t as f}from"./button-CzdTkRIf.js";import{o as p,r as m,s as h,t as g}from"./card-C3BGuVC6.js";var _=i();function v({connector:i}){let v=s(e=>e.runConnectorAction),y=s(e=>e.openConfirm),b=s(e=>e.showNotification),x=u(i),S=i.runtime,C=i.manifest?.commands||{},w=S.installed,T=!!C.start&&w&&!S.running,E=!!C.stop&&S.running,D=!!C.restart&&w,O=!!C.doctor&&w,k=i.displayName||i.id,A=async e=>{try{await v(i.id,e),b(`${e.charAt(0).toUpperCase()+e.slice(1)} completed for ${k}`)}catch{}};return(0,_.jsxs)(g,{className:`hover:border-zinc-600 transition-colors`,children:[(0,_.jsx)(p,{className:`pb-2 pt-4 px-4`,children:(0,_.jsxs)(`div`,{className:`flex items-start gap-2`,children:[(0,_.jsx)(`div`,{className:`p-1.5 rounded-md ${S.running?`bg-emerald-500/10`:`bg-zinc-800`}`,children:(0,_.jsx)(e,{className:`w-4 h-4 ${S.running?`text-emerald-400`:`text-zinc-500`}`})}),(0,_.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,_.jsx)(h,{className:`text-sm font-medium truncate`,children:i.displayName||i.id}),(0,_.jsx)(`div`,{className:`text-xs text-muted-foreground mt-0.5`,children:i.kind})]}),(0,_.jsx)(d,{className:`text-[10px] shrink-0 ${c(x.tone)}`,children:x.label})]})}),(0,_.jsxs)(m,{className:`px-4 pb-4 space-y-2.5`,children:[i.description&&(0,_.jsx)(`p`,{className:`text-xs text-muted-foreground leading-relaxed line-clamp-2`,children:i.description}),S.detail&&(0,_.jsx)(`div`,{className:`text-xs px-2 py-1 rounded ${S.status===`error`?`bg-red-500/10 text-red-400`:S.status===`warn`?`bg-yellow-500/10 text-yellow-400`:`bg-zinc-800 text-muted-foreground`}`,children:S.detail}),l(i.capabilities).length>0&&(0,_.jsx)(`div`,{className:`flex flex-wrap gap-1`,children:l(i.capabilities).map(e=>(0,_.jsx)(d,{variant:`secondary`,className:`text-[9px] px-1 py-0 h-4`,children:e},e))}),i.version&&(0,_.jsxs)(`div`,{className:`text-xs text-muted-foreground`,children:[`v`,i.version]}),(0,_.jsxs)(`div`,{className:`flex flex-wrap gap-1.5 pt-0.5`,children:[T&&(0,_.jsxs)(f,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1 text-emerald-400 border-emerald-500/30 hover:text-emerald-300`,onClick:()=>A(`start`),children:[(0,_.jsx)(n,{className:`w-3 h-3`}),`Start`]}),E&&(0,_.jsxs)(f,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1 text-red-400 border-red-500/30 hover:text-red-300`,onClick:()=>y(`Stop Connector`,`Stop ${i.displayName||i.id}?`,()=>A(`stop`)),children:[(0,_.jsx)(r,{className:`w-3 h-3`}),`Stop`]}),D&&(0,_.jsxs)(f,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1`,onClick:()=>y(`Restart Connector`,`Restart ${i.displayName||i.id}?`,()=>A(`restart`)),children:[(0,_.jsx)(t,{className:`w-3 h-3`}),`Restart`]}),O&&(0,_.jsxs)(f,{size:`sm`,variant:`ghost`,className:`h-7 text-xs gap-1 text-muted-foreground`,onClick:()=>A(`doctor`),children:[(0,_.jsx)(a,{className:`w-3 h-3`}),`Doctor`]}),!w&&C.install&&(0,_.jsxs)(f,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1`,onClick:()=>A(`install`),children:[(0,_.jsx)(o,{className:`w-3 h-3`}),`Install`]}),w&&C.uninstall&&(0,_.jsx)(f,{size:`sm`,variant:`ghost`,className:`h-7 text-xs text-muted-foreground hover:text-red-400`,onClick:()=>y(`Uninstall Connector`,`Uninstall ${i.displayName||i.id}? This will remove the systemd service.`,()=>A(`uninstall`)),children:`Uninstall`})]})]})]})}function y(){let e=s(e=>e.connectors),t=e.filter(e=>e.runtime.running).length;return(0,_.jsxs)(`div`,{className:`space-y-4`,children:[(0,_.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,_.jsxs)(`div`,{children:[(0,_.jsx)(`h2`,{className:`text-base font-semibold`,children:`Connectors`}),(0,_.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Installed service connectors and integrations`})]}),(0,_.jsxs)(`div`,{className:`flex items-center gap-2 ml-auto`,children:[(0,_.jsxs)(d,{variant:`outline`,className:`text-xs`,children:[e.length,` total`]}),t>0&&(0,_.jsxs)(d,{className:`text-xs bg-emerald-500/15 text-emerald-400 border-emerald-500/30`,children:[t,` running`]})]})]}),e.length===0?(0,_.jsx)(`div`,{className:`text-center py-16 text-muted-foreground text-sm`,children:`No connectors registered.`}):(0,_.jsx)(`div`,{className:`grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4`,children:e.map(e=>(0,_.jsx)(v,{connector:e},e.id))})]})}export{y as ConnectorsView};
2
- //# sourceMappingURL=connectors-Br6fiTny.js.map
1
+ import{B as e,N as t,U as n,b as r,nr as i,y as a,z as o}from"./lucide-react-DuhlF2Sj.js";import{t as s}from"./store-BPrVuKiv.js";import{J as c,Y as l,l as u}from"./display-CJzSoTMq.js";import{t as d}from"./badge-DCIT3Irj.js";import{t as f}from"./button-CzdTkRIf.js";import{o as p,r as m,s as h,t as g}from"./card-C3BGuVC6.js";var _=i();function v({connector:i}){let v=s(e=>e.runConnectorAction),y=s(e=>e.openConfirm),b=s(e=>e.showNotification),x=u(i),S=i.runtime,C=i.manifest?.commands||{},w=S.installed,T=!!C.start&&w&&!S.running,E=!!C.stop&&S.running,D=!!C.restart&&w,O=!!C.doctor&&w,k=i.displayName||i.id,A=async e=>{try{await v(i.id,e),b(`${e.charAt(0).toUpperCase()+e.slice(1)} completed for ${k}`)}catch{}};return(0,_.jsxs)(g,{className:`hover:border-zinc-600 transition-colors`,children:[(0,_.jsx)(p,{className:`pb-2 pt-4 px-4`,children:(0,_.jsxs)(`div`,{className:`flex items-start gap-2`,children:[(0,_.jsx)(`div`,{className:`p-1.5 rounded-md ${S.running?`bg-emerald-500/10`:`bg-zinc-800`}`,children:(0,_.jsx)(e,{className:`w-4 h-4 ${S.running?`text-emerald-400`:`text-zinc-500`}`})}),(0,_.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,_.jsx)(h,{className:`text-sm font-medium truncate`,children:i.displayName||i.id}),(0,_.jsx)(`div`,{className:`text-xs text-muted-foreground mt-0.5`,children:i.kind})]}),(0,_.jsx)(d,{className:`text-[10px] shrink-0 ${c(x.tone)}`,children:x.label})]})}),(0,_.jsxs)(m,{className:`px-4 pb-4 space-y-2.5`,children:[i.description&&(0,_.jsx)(`p`,{className:`text-xs text-muted-foreground leading-relaxed line-clamp-2`,children:i.description}),S.detail&&(0,_.jsx)(`div`,{className:`text-xs px-2 py-1 rounded ${S.status===`error`?`bg-red-500/10 text-red-400`:S.status===`warn`?`bg-yellow-500/10 text-yellow-400`:`bg-zinc-800 text-muted-foreground`}`,children:S.detail}),l(i.capabilities).length>0&&(0,_.jsx)(`div`,{className:`flex flex-wrap gap-1`,children:l(i.capabilities).map(e=>(0,_.jsx)(d,{variant:`secondary`,className:`text-[9px] px-1 py-0 h-4`,children:e},e))}),i.version&&(0,_.jsxs)(`div`,{className:`text-xs text-muted-foreground`,children:[`v`,i.version]}),(0,_.jsxs)(`div`,{className:`flex flex-wrap gap-1.5 pt-0.5`,children:[T&&(0,_.jsxs)(f,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1 text-emerald-400 border-emerald-500/30 hover:text-emerald-300`,onClick:()=>A(`start`),children:[(0,_.jsx)(n,{className:`w-3 h-3`}),`Start`]}),E&&(0,_.jsxs)(f,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1 text-red-400 border-red-500/30 hover:text-red-300`,onClick:()=>y(`Stop Connector`,`Stop ${i.displayName||i.id}?`,()=>A(`stop`)),children:[(0,_.jsx)(r,{className:`w-3 h-3`}),`Stop`]}),D&&(0,_.jsxs)(f,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1`,onClick:()=>y(`Restart Connector`,`Restart ${i.displayName||i.id}?`,()=>A(`restart`)),children:[(0,_.jsx)(t,{className:`w-3 h-3`}),`Restart`]}),O&&(0,_.jsxs)(f,{size:`sm`,variant:`ghost`,className:`h-7 text-xs gap-1 text-muted-foreground`,onClick:()=>A(`doctor`),children:[(0,_.jsx)(a,{className:`w-3 h-3`}),`Doctor`]}),!w&&C.install&&(0,_.jsxs)(f,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1`,onClick:()=>A(`install`),children:[(0,_.jsx)(o,{className:`w-3 h-3`}),`Install`]}),w&&C.uninstall&&(0,_.jsx)(f,{size:`sm`,variant:`ghost`,className:`h-7 text-xs text-muted-foreground hover:text-red-400`,onClick:()=>y(`Uninstall Connector`,`Uninstall ${i.displayName||i.id}? This will remove the systemd service.`,()=>A(`uninstall`)),children:`Uninstall`})]})]})]})}function y(){let e=s(e=>e.connectors),t=e.filter(e=>e.runtime.running).length;return(0,_.jsxs)(`div`,{className:`space-y-4`,children:[(0,_.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,_.jsxs)(`div`,{children:[(0,_.jsx)(`h2`,{className:`text-base font-semibold`,children:`Connectors`}),(0,_.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Installed service connectors and integrations`})]}),(0,_.jsxs)(`div`,{className:`flex items-center gap-2 ml-auto`,children:[(0,_.jsxs)(d,{variant:`outline`,className:`text-xs`,children:[e.length,` total`]}),t>0&&(0,_.jsxs)(d,{className:`text-xs bg-emerald-500/15 text-emerald-400 border-emerald-500/30`,children:[t,` running`]})]})]}),e.length===0?(0,_.jsx)(`div`,{className:`text-center py-16 text-muted-foreground text-sm`,children:`No connectors registered.`}):(0,_.jsx)(`div`,{className:`grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4`,children:e.map(e=>(0,_.jsx)(v,{connector:e},e.id))})]})}export{y as ConnectorsView};
2
+ //# sourceMappingURL=connectors-C2Ac03LJ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"connectors-Br6fiTny.js","names":[],"sources":["../../dashboard/src/components/views/connectors.tsx"],"sourcesContent":["import { useRelayStore } from '@/store'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { connectorPresence, toneToColor, userFacingCapabilities } from '@/lib/display'\nimport { Plug, Play, Square, RotateCw, Stethoscope, Power } from 'lucide-react'\nimport type { ConnectorSummary } from '@/types'\n\nfunction ConnectorCard({ connector }: { connector: ConnectorSummary }) {\n const runConnectorAction = useRelayStore((s) => s.runConnectorAction)\n const openConfirm = useRelayStore((s) => s.openConfirm)\n const showNotification = useRelayStore((s) => s.showNotification)\n\n const presence = connectorPresence(connector)\n const runtime = connector.runtime\n const commands = connector.manifest?.commands || {}\n const installed = runtime.installed\n\n const canStart = Boolean(commands.start) && installed && !runtime.running\n const canStop = Boolean(commands.stop) && runtime.running\n const canRestart = Boolean(commands.restart) && installed\n const canDoctor = Boolean(commands.doctor) && installed\n\n const name = connector.displayName || connector.id\n const run = async (action: string) => {\n try {\n await runConnectorAction(connector.id, action)\n showNotification(`${action.charAt(0).toUpperCase() + action.slice(1)} completed for ${name}`)\n } catch { /* error modal already shown by store */ }\n }\n\n return (\n <Card className=\"hover:border-zinc-600 transition-colors\">\n <CardHeader className=\"pb-2 pt-4 px-4\">\n <div className=\"flex items-start gap-2\">\n <div className={`p-1.5 rounded-md ${runtime.running ? 'bg-emerald-500/10' : 'bg-zinc-800'}`}>\n <Plug className={`w-4 h-4 ${runtime.running ? 'text-emerald-400' : 'text-zinc-500'}`} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <CardTitle className=\"text-sm font-medium truncate\">{connector.displayName || connector.id}</CardTitle>\n <div className=\"text-xs text-muted-foreground mt-0.5\">{connector.kind}</div>\n </div>\n <Badge className={`text-[10px] shrink-0 ${toneToColor(presence.tone)}`}>\n {presence.label}\n </Badge>\n </div>\n </CardHeader>\n\n <CardContent className=\"px-4 pb-4 space-y-2.5\">\n {/* Description */}\n {connector.description && (\n <p className=\"text-xs text-muted-foreground leading-relaxed line-clamp-2\">{connector.description}</p>\n )}\n\n {/* Runtime status detail */}\n {runtime.detail && (\n <div className={`text-xs px-2 py-1 rounded ${runtime.status === 'error' ? 'bg-red-500/10 text-red-400' : runtime.status === 'warn' ? 'bg-yellow-500/10 text-yellow-400' : 'bg-zinc-800 text-muted-foreground'}`}>\n {runtime.detail}\n </div>\n )}\n\n {/* Capabilities */}\n {userFacingCapabilities(connector.capabilities).length > 0 && (\n <div className=\"flex flex-wrap gap-1\">\n {userFacingCapabilities(connector.capabilities).map((c) => (\n <Badge key={c} variant=\"secondary\" className=\"text-[9px] px-1 py-0 h-4\">{c}</Badge>\n ))}\n </div>\n )}\n\n {/* Version */}\n {connector.version && (\n <div className=\"text-xs text-muted-foreground\">v{connector.version}</div>\n )}\n\n {/* Action buttons */}\n <div className=\"flex flex-wrap gap-1.5 pt-0.5\">\n {canStart && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1 text-emerald-400 border-emerald-500/30 hover:text-emerald-300\"\n onClick={() => run('start')}\n >\n <Play className=\"w-3 h-3\" />\n Start\n </Button>\n )}\n {canStop && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1 text-red-400 border-red-500/30 hover:text-red-300\"\n onClick={() => openConfirm('Stop Connector', `Stop ${connector.displayName || connector.id}?`, () => run('stop'))}\n >\n <Square className=\"w-3 h-3\" />\n Stop\n </Button>\n )}\n {canRestart && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1\"\n onClick={() => openConfirm('Restart Connector', `Restart ${connector.displayName || connector.id}?`, () => run('restart'))}\n >\n <RotateCw className=\"w-3 h-3\" />\n Restart\n </Button>\n )}\n {canDoctor && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-7 text-xs gap-1 text-muted-foreground\"\n onClick={() => run('doctor')}\n >\n <Stethoscope className=\"w-3 h-3\" />\n Doctor\n </Button>\n )}\n {!installed && commands.install && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1\"\n onClick={() => run('install')}\n >\n <Power className=\"w-3 h-3\" />\n Install\n </Button>\n )}\n {installed && commands.uninstall && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-7 text-xs text-muted-foreground hover:text-red-400\"\n onClick={() => openConfirm('Uninstall Connector', `Uninstall ${connector.displayName || connector.id}? This will remove the systemd service.`, () => run('uninstall'))}\n >\n Uninstall\n </Button>\n )}\n </div>\n </CardContent>\n </Card>\n )\n}\n\nexport function ConnectorsView() {\n const connectors = useRelayStore((s) => s.connectors)\n\n const runningCount = connectors.filter((c) => c.runtime.running).length\n\n return (\n <div className=\"space-y-4\">\n {/* Header */}\n <div className=\"flex items-center gap-3\">\n <div>\n <h2 className=\"text-base font-semibold\">Connectors</h2>\n <p className=\"text-xs text-muted-foreground\">Installed service connectors and integrations</p>\n </div>\n <div className=\"flex items-center gap-2 ml-auto\">\n <Badge variant=\"outline\" className=\"text-xs\">\n {connectors.length} total\n </Badge>\n {runningCount > 0 && (\n <Badge className=\"text-xs bg-emerald-500/15 text-emerald-400 border-emerald-500/30\">\n {runningCount} running\n </Badge>\n )}\n </div>\n </div>\n\n {/* Cards */}\n {connectors.length === 0 ? (\n <div className=\"text-center py-16 text-muted-foreground text-sm\">\n No connectors registered.\n </div>\n ) : (\n <div className=\"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4\">\n {connectors.map((connector) => (\n <ConnectorCard key={connector.id} connector={connector} />\n ))}\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"iVAQA,SAAS,EAAc,CAAE,aAA8C,CACrE,IAAM,EAAqB,EAAe,GAAM,EAAE,mBAAmB,CAC/D,EAAc,EAAe,GAAM,EAAE,YAAY,CACjD,EAAmB,EAAe,GAAM,EAAE,iBAAiB,CAE3D,EAAW,EAAkB,EAAU,CACvC,EAAU,EAAU,QACpB,EAAW,EAAU,UAAU,UAAY,EAAE,CAC7C,EAAY,EAAQ,UAEpB,EAAW,EAAQ,EAAS,OAAU,GAAa,CAAC,EAAQ,QAC5D,EAAU,EAAQ,EAAS,MAAS,EAAQ,QAC5C,EAAa,EAAQ,EAAS,SAAY,EAC1C,EAAY,EAAQ,EAAS,QAAW,EAExC,EAAO,EAAU,aAAe,EAAU,GAC1C,EAAM,KAAO,IAAmB,CACpC,GAAI,CACF,MAAM,EAAmB,EAAU,GAAI,EAAO,CAC9C,EAAiB,GAAG,EAAO,OAAO,EAAE,CAAC,aAAa,CAAG,EAAO,MAAM,EAAE,CAAC,iBAAiB,IAAO,MACvF,IAGV,OACE,EAAA,EAAA,MAAC,EAAD,CAAM,UAAU,mDAAhB,EACE,EAAA,EAAA,KAAC,EAAD,CAAY,UAAU,2BACpB,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,kCAAf,EACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAW,oBAAoB,EAAQ,QAAU,oBAAsB,0BAC1E,EAAA,EAAA,KAAC,EAAD,CAAM,UAAW,WAAW,EAAQ,QAAU,mBAAqB,kBAAqB,CAAA,CACpF,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,0BAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAW,UAAU,wCAAgC,EAAU,aAAe,EAAU,GAAe,CAAA,EACvG,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,gDAAwC,EAAU,KAAW,CAAA,CACxE,IACN,EAAA,EAAA,KAAC,EAAD,CAAO,UAAW,wBAAwB,EAAY,EAAS,KAAK,YACjE,EAAS,MACJ,CAAA,CACJ,GACK,CAAA,EAEb,EAAA,EAAA,MAAC,EAAD,CAAa,UAAU,iCAAvB,CAEG,EAAU,cACT,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,sEAA8D,EAAU,YAAgB,CAAA,CAItG,EAAQ,SACP,EAAA,EAAA,KAAC,MAAD,CAAK,UAAW,6BAA6B,EAAQ,SAAW,QAAU,6BAA+B,EAAQ,SAAW,OAAS,mCAAqC,+CACvK,EAAQ,OACL,CAAA,CAIP,EAAuB,EAAU,aAAa,CAAC,OAAS,IACvD,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,gCACZ,EAAuB,EAAU,aAAa,CAAC,IAAK,IACnD,EAAA,EAAA,KAAC,EAAD,CAAe,QAAQ,YAAY,UAAU,oCAA4B,EAAU,CAAvE,EAAuE,CACnF,CACE,CAAA,CAIP,EAAU,UACT,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,yCAAf,CAA+C,IAAE,EAAU,QAAc,IAI3E,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,yCAAf,CACG,IACC,EAAA,EAAA,MAAC,EAAD,CACE,KAAK,KACL,QAAQ,UACR,UAAU,kFACV,YAAe,EAAI,QAAQ,UAJ7B,EAME,EAAA,EAAA,KAAC,EAAD,CAAM,UAAU,UAAY,CAAA,CAAA,QAErB,GAEV,IACC,EAAA,EAAA,MAAC,EAAD,CACE,KAAK,KACL,QAAQ,UACR,UAAU,sEACV,YAAe,EAAY,iBAAkB,QAAQ,EAAU,aAAe,EAAU,GAAG,OAAU,EAAI,OAAO,CAAC,UAJnH,EAME,EAAA,EAAA,KAAC,EAAD,CAAQ,UAAU,UAAY,CAAA,CAAA,OAEvB,GAEV,IACC,EAAA,EAAA,MAAC,EAAD,CACE,KAAK,KACL,QAAQ,UACR,UAAU,oBACV,YAAe,EAAY,oBAAqB,WAAW,EAAU,aAAe,EAAU,GAAG,OAAU,EAAI,UAAU,CAAC,UAJ5H,EAME,EAAA,EAAA,KAAC,EAAD,CAAU,UAAU,UAAY,CAAA,CAAA,UAEzB,GAEV,IACC,EAAA,EAAA,MAAC,EAAD,CACE,KAAK,KACL,QAAQ,QACR,UAAU,0CACV,YAAe,EAAI,SAAS,UAJ9B,EAME,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,UAAY,CAAA,CAAA,SAE5B,GAEV,CAAC,GAAa,EAAS,UACtB,EAAA,EAAA,MAAC,EAAD,CACE,KAAK,KACL,QAAQ,UACR,UAAU,oBACV,YAAe,EAAI,UAAU,UAJ/B,EAME,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,UAAY,CAAA,CAAA,UAEtB,GAEV,GAAa,EAAS,YACrB,EAAA,EAAA,KAAC,EAAD,CACE,KAAK,KACL,QAAQ,QACR,UAAU,uDACV,YAAe,EAAY,sBAAuB,aAAa,EAAU,aAAe,EAAU,GAAG,6CAAgD,EAAI,YAAY,CAAC,UACvK,YAEQ,CAAA,CAEP,GACM,GACT,GAIX,SAAgB,GAAiB,CAC/B,IAAM,EAAa,EAAe,GAAM,EAAE,WAAW,CAE/C,EAAe,EAAW,OAAQ,GAAM,EAAE,QAAQ,QAAQ,CAAC,OAEjE,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,qBAAf,EAEE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mCAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,mCAA0B,aAAe,CAAA,EACvD,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,yCAAgC,gDAAiD,CAAA,CAC1F,CAAA,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,2CAAf,EACE,EAAA,EAAA,MAAC,EAAD,CAAO,QAAQ,UAAU,UAAU,mBAAnC,CACG,EAAW,OAAO,SACb,GACP,EAAe,IACd,EAAA,EAAA,MAAC,EAAD,CAAO,UAAU,4EAAjB,CACG,EAAa,WACR,GAEN,GACF,GAGL,EAAW,SAAW,GACrB,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,2DAAkD,4BAE3D,CAAA,EAEN,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,gEACZ,EAAW,IAAK,IACf,EAAA,EAAA,KAAC,EAAD,CAA6C,YAAa,CAAtC,EAAU,GAA4B,CAC1D,CACE,CAAA,CAEJ"}
1
+ {"version":3,"file":"connectors-C2Ac03LJ.js","names":[],"sources":["../../dashboard/src/components/views/connectors.tsx"],"sourcesContent":["import { useRelayStore } from '@/store'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { connectorPresence, toneToColor, userFacingCapabilities } from '@/lib/display'\nimport { Plug, Play, Square, RotateCw, Stethoscope, Power } from 'lucide-react'\nimport type { ConnectorSummary } from '@/types'\n\nfunction ConnectorCard({ connector }: { connector: ConnectorSummary }) {\n const runConnectorAction = useRelayStore((s) => s.runConnectorAction)\n const openConfirm = useRelayStore((s) => s.openConfirm)\n const showNotification = useRelayStore((s) => s.showNotification)\n\n const presence = connectorPresence(connector)\n const runtime = connector.runtime\n const commands = connector.manifest?.commands || {}\n const installed = runtime.installed\n\n const canStart = Boolean(commands.start) && installed && !runtime.running\n const canStop = Boolean(commands.stop) && runtime.running\n const canRestart = Boolean(commands.restart) && installed\n const canDoctor = Boolean(commands.doctor) && installed\n\n const name = connector.displayName || connector.id\n const run = async (action: string) => {\n try {\n await runConnectorAction(connector.id, action)\n showNotification(`${action.charAt(0).toUpperCase() + action.slice(1)} completed for ${name}`)\n } catch { /* error modal already shown by store */ }\n }\n\n return (\n <Card className=\"hover:border-zinc-600 transition-colors\">\n <CardHeader className=\"pb-2 pt-4 px-4\">\n <div className=\"flex items-start gap-2\">\n <div className={`p-1.5 rounded-md ${runtime.running ? 'bg-emerald-500/10' : 'bg-zinc-800'}`}>\n <Plug className={`w-4 h-4 ${runtime.running ? 'text-emerald-400' : 'text-zinc-500'}`} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <CardTitle className=\"text-sm font-medium truncate\">{connector.displayName || connector.id}</CardTitle>\n <div className=\"text-xs text-muted-foreground mt-0.5\">{connector.kind}</div>\n </div>\n <Badge className={`text-[10px] shrink-0 ${toneToColor(presence.tone)}`}>\n {presence.label}\n </Badge>\n </div>\n </CardHeader>\n\n <CardContent className=\"px-4 pb-4 space-y-2.5\">\n {/* Description */}\n {connector.description && (\n <p className=\"text-xs text-muted-foreground leading-relaxed line-clamp-2\">{connector.description}</p>\n )}\n\n {/* Runtime status detail */}\n {runtime.detail && (\n <div className={`text-xs px-2 py-1 rounded ${runtime.status === 'error' ? 'bg-red-500/10 text-red-400' : runtime.status === 'warn' ? 'bg-yellow-500/10 text-yellow-400' : 'bg-zinc-800 text-muted-foreground'}`}>\n {runtime.detail}\n </div>\n )}\n\n {/* Capabilities */}\n {userFacingCapabilities(connector.capabilities).length > 0 && (\n <div className=\"flex flex-wrap gap-1\">\n {userFacingCapabilities(connector.capabilities).map((c) => (\n <Badge key={c} variant=\"secondary\" className=\"text-[9px] px-1 py-0 h-4\">{c}</Badge>\n ))}\n </div>\n )}\n\n {/* Version */}\n {connector.version && (\n <div className=\"text-xs text-muted-foreground\">v{connector.version}</div>\n )}\n\n {/* Action buttons */}\n <div className=\"flex flex-wrap gap-1.5 pt-0.5\">\n {canStart && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1 text-emerald-400 border-emerald-500/30 hover:text-emerald-300\"\n onClick={() => run('start')}\n >\n <Play className=\"w-3 h-3\" />\n Start\n </Button>\n )}\n {canStop && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1 text-red-400 border-red-500/30 hover:text-red-300\"\n onClick={() => openConfirm('Stop Connector', `Stop ${connector.displayName || connector.id}?`, () => run('stop'))}\n >\n <Square className=\"w-3 h-3\" />\n Stop\n </Button>\n )}\n {canRestart && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1\"\n onClick={() => openConfirm('Restart Connector', `Restart ${connector.displayName || connector.id}?`, () => run('restart'))}\n >\n <RotateCw className=\"w-3 h-3\" />\n Restart\n </Button>\n )}\n {canDoctor && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-7 text-xs gap-1 text-muted-foreground\"\n onClick={() => run('doctor')}\n >\n <Stethoscope className=\"w-3 h-3\" />\n Doctor\n </Button>\n )}\n {!installed && commands.install && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1\"\n onClick={() => run('install')}\n >\n <Power className=\"w-3 h-3\" />\n Install\n </Button>\n )}\n {installed && commands.uninstall && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-7 text-xs text-muted-foreground hover:text-red-400\"\n onClick={() => openConfirm('Uninstall Connector', `Uninstall ${connector.displayName || connector.id}? This will remove the systemd service.`, () => run('uninstall'))}\n >\n Uninstall\n </Button>\n )}\n </div>\n </CardContent>\n </Card>\n )\n}\n\nexport function ConnectorsView() {\n const connectors = useRelayStore((s) => s.connectors)\n\n const runningCount = connectors.filter((c) => c.runtime.running).length\n\n return (\n <div className=\"space-y-4\">\n {/* Header */}\n <div className=\"flex items-center gap-3\">\n <div>\n <h2 className=\"text-base font-semibold\">Connectors</h2>\n <p className=\"text-xs text-muted-foreground\">Installed service connectors and integrations</p>\n </div>\n <div className=\"flex items-center gap-2 ml-auto\">\n <Badge variant=\"outline\" className=\"text-xs\">\n {connectors.length} total\n </Badge>\n {runningCount > 0 && (\n <Badge className=\"text-xs bg-emerald-500/15 text-emerald-400 border-emerald-500/30\">\n {runningCount} running\n </Badge>\n )}\n </div>\n </div>\n\n {/* Cards */}\n {connectors.length === 0 ? (\n <div className=\"text-center py-16 text-muted-foreground text-sm\">\n No connectors registered.\n </div>\n ) : (\n <div className=\"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4\">\n {connectors.map((connector) => (\n <ConnectorCard key={connector.id} connector={connector} />\n ))}\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import{_ as e,c as t,f as n,g as r,m as i,o as a,s as o}from"./types-BeJUSfDj.js";var s=`user`,c=s,l=5e3,u=new Set([`done`,`failed`,`canceled`]),ee=new Set([`open`,`blocked`]),te={online:0,idle:0,busy:2,offline:3},d={idle:0,online:0,busy:2,offline:4},f={from:``,to:``,body:``,channel:``,subject:``,claimable:!1},p={toMode:`agent`,to:``,body:``,channel:``,subject:``,claimable:!1},m={pairId:``,from:``,body:``,subject:``},h={requesterId:``,targetId:``,objective:``},g={user:`User`,system:`Server`,channel:`MessagesSquare`,agent:`Bot`},_={user:`Human operator`,system:`System`,channel:`Channel`,agent:`Agent`};({...g,...Object.fromEntries(i().map(e=>[e,`Bot`]))}),{..._,...Object.fromEntries(i().map(e=>[e,`${r(e)} agent`]))};var v=[{key:`overview`,label:`Overview`,icon:`LayoutDashboard`},{key:`chat`,label:`Chat`,icon:`MessageCircle`},{key:`agents`,label:`Agents`,icon:`Bot`},{key:`managed`,label:`Managed Agents`,icon:`Workflow`},{key:`teams`,label:`Teams`,icon:`Users`},{key:`profiles`,label:`Agent Profiles`,icon:`UserCog`},{key:`orchestrators`,label:`Orchestrators`,icon:`Server`},{key:`workspaces`,label:`Workspaces`,icon:`GitBranch`},{key:`files`,label:`Files`,icon:`FolderTree`},{key:`channels`,label:`Channels`,icon:`MessagesSquare`},{key:`connectors`,label:`Connectors`,icon:`Plug`},{key:`integrations`,label:`Integrations`,icon:`PlugZap`},{key:`security`,label:`Security`,icon:`Shield`},{key:`memory`,label:`Memory`,icon:`BrainCircuit`},{key:`activity`,label:`Activity`,icon:`Activity`},{key:`pairs`,label:`Pairs`,icon:`Link`},{key:`messages`,label:`Messages`,icon:`Mail`},{key:`work`,label:`Work Queue`,icon:`ListChecks`},{key:`tasks`,label:`Tasks`,icon:`ClipboardList`},{key:`automation`,label:`Automation`,icon:`CalendarClock`},{key:`analytics`,label:`Analytics`,icon:`AreaChart`},{key:`insights`,label:`Insights`,icon:`Lightbulb`},{key:`projects`,label:`Projects`,icon:`Boxes`},{key:`prompts`,label:`Prompts`,icon:`FileText`},{key:`registry`,label:`Registry`,icon:`PackagePlus`},{key:`maintenance`,label:`Maintenance`,icon:`Wrench`},{key:`settings`,label:`Settings`,icon:`Settings`}],y={critical:`text-red-400 bg-red-500/10`,warning:`text-yellow-400 bg-yellow-500/10`,info:`text-blue-400 bg-blue-500/10`},ne={active:`bg-emerald-500/20 text-emerald-400`,pending:`bg-yellow-500/20 text-yellow-400`,ended:`bg-zinc-500/20 text-zinc-400`,rejected:`bg-red-500/20 text-red-400`,expired:`bg-red-500/20 text-red-400`};function re(e){let t=typeof e==`number`?e:new Date(e||0).getTime();return Number.isFinite(t)?t:0}var ie=[`lifecycle.`,`commands.`,`capabilities.`],ae={"+1":`👍`,":+1:":`👍`,thumbsup:`👍`,":thumbsup:":`👍`,"thumbs-up":`👍`,":thumbs-up:":`👍`,thumbs_up:`👍`,":thumbs_up:":`👍`,thumb_up:`👍`,":thumb_up:":`👍`,like:`👍`,":like:":`👍`,heart:`❤️`,":heart:":`❤️`,redheart:`❤️`,red_heart:`❤️`,":red_heart:":`❤️`,check:`✅`,":check:":`✅`,checkmark:`✅`,":checkmark:":`✅`,white_check_mark:`✅`,":white_check_mark:":`✅`,eyes:`👀`,":eyes:":`👀`};function b(e){let t=e.trim();return ae[t.toLowerCase()]??t}function oe(e){return e.filter(e=>!ie.some(t=>e.startsWith(t)))}function se(e,t=2){if(!e)return``;let n=e.replace(/\/+$/,``).split(`/`);return n.length<=t?e:n.slice(-t).join(`/`)}function x(e){return e.trim().replace(/\\/g,`/`).replace(/\/+$/,``)||`/`}function ce(e,t){if(!e?.trim()||!t?.trim())return!1;let n=x(e),r=x(t);return r===`/`?n.startsWith(`/`):n===`/`?!1:n===r||n.startsWith(r+`/`)}function S(e){return e?.meta?.builtin===!0||e?.id===`user`||e?.id===`system`}function C(e){let t=e?.tags||[],n=e?.capabilities||[],r=typeof e?.meta?.policyName==`string`?e.meta.policyName:``;return e?.kind===`channel`||e?.meta?.kind===`channel`||t.includes(`channel`)||t.some(e=>e.startsWith(`channel:`))||n.includes(`channel`)||n.some(e=>e.startsWith(`channel:`))||t.includes(`telegram`)||n.includes(`telegram`)||r===`telegram-main`}function w(e){return!!(typeof e?.meta?.automationId==`string`||typeof e?.meta?.automationRunId==`string`||e?.tags?.includes(`automation`))}function T(e){return S(e)||C(e)||w(e)}function E(e){if(e?.id===`user`)return`user`;if(e?.id===`system`)return`system`;if(C(e))return`channel`;let t=i(),n=typeof e?.meta?.provider==`string`?e.meta.provider.toLowerCase():void 0;if(n&&t.includes(n))return n;let r=[...e?.tags||[],e?.meta?.client,e?.meta?.runtime,e?.meta?.agentType].filter(e=>typeof e==`string`).map(e=>e.toLowerCase());for(let e of t)if(r.some(t=>t.includes(e)))return e;return`agent`}function D(e,t){if(!t?.lastSeen||t.status===`offline`||t.id===`user`||t.id===`system`)return!1;let r=t.meta?.transport;if(n(r)&&r.connected===!0)return!1;let i=new Date(t.lastSeen).getTime();return Number.isFinite(i)?e-i>6e4:!1}function O(e,t){if(!e||S(e)||C(e)||t===`resume`)return!1;if(t===`interrupt`)return e.providerCapabilities?.liveSession?.interrupt===!0&&e.status===`busy`;if(A(e)&&(t===`restart`||t===`shutdown`||t===`compact`||t===`clearContext`)&&!k(e))return!1;let n=e.providerCapabilities?.lifecycle;if(n){if(t===`restart`)return n.restartHard===!0;if(t===`shutdown`)return n.shutdownHard===!0;if(t===`reconnect`)return n.reconnect===!0}return t===`compact`?e.providerCapabilities?.context?.compact===!0:t===`clearContext`?e.providerCapabilities?.context?.clear===!0:e.meta?.runnerManaged===!0&&(t===`restart`||t===`shutdown`)}function k(e){return typeof e.meta?.tmuxSession==`string`&&e.meta.tmuxSession.trim().length>0}function A(t){return(typeof t?.meta?.provider==`string`?e(t.meta.provider):void 0)?.capabilityDefaults?.terminal?.mode===`live`}function j(e){return O(e,`restart`)||O(e,`shutdown`)||O(e,`reconnect`)||O(e,`compact`)||O(e,`clearContext`)}function M(e){return!!(e&&!S(e)&&!C(e)&&!j(e)&&(e.status===`offline`||e.status===`stale`))}function N(e,t){if(e.status!==`offline`)return!1;let n=typeof e.meta?.policyName==`string`?e.meta.policyName:``;if(!n)return!1;let r=t.find(e=>e.policy.name===n);return!!(r?.state.agentId&&r.state.agentId!==e.id)}function P(e,t,n=[]){let r=e.filter(e=>!N(e,n));return t?r:r.filter(e=>!T(e))}function F(e){return e?e.label||e.name||e.id.slice(-12):`?`}function I(e,n){let r=t(e);return r?n[r]:void 0}function L(e,t){let n=I(e,t);return n?.displayName?.trim()||n?.handle?.trim()||`User`}var R=[`text-sky-300`,`text-amber-300`,`text-emerald-300`,`text-violet-300`,`text-rose-300`,`text-cyan-300`];function z(e){let n=t(e)||``,r=0;for(let e=0;e<n.length;e++)r=r*31+n.charCodeAt(e)>>>0;return R[r%R.length]}function B(e,t){if(!e)return`?`;if(e===`broadcast`)return`broadcast`;if(e.startsWith(`tag:`))return`#`+e.slice(4);if(e.startsWith(`cap:`))return e.slice(4);if(e.startsWith(`label:`))return e.slice(6);if(e.startsWith(`team:`)){let n=e.slice(5),r=Object.values(t).find(e=>e.teamId===n);return r?`team:${F(r)}`:`team:${n.slice(-8)}`}let n=t[e];return n?F(n):e.slice(-8)}function V(e){if(!e)return``;let t=e.payload||{},n=t.message;if(n&&typeof n==`object`&&typeof n.text==`string`&&n.text.trim())return n.text;let r=t.interaction;if(r&&typeof r==`object`){let e=typeof r.title==`string`?r.title.trim():``,t=typeof r.description==`string`?r.description.trim():``;if(e&&t)return e+`
2
+ `+t;if(e)return e;if(t)return t}let i=t.reaction;if(i&&typeof i==`object`){let e=typeof i.name==`string`?i.name:``,t=typeof i.emoji==`string`?i.emoji:``,n=typeof i.value==`string`?i.value:``;return[`Reaction`,b(t||e||n)].filter(Boolean).join(`: `)}let a=t.activity;return a&&typeof a==`object`?[typeof a.kind==`string`?a.kind:`activity`,typeof a.state==`string`?a.state:``].filter(Boolean).join(` `):typeof t.text==`string`&&t.text.trim()?t.text:typeof t.message==`string`&&t.message.trim()?t.message:W(e.body||``)??(e.body||``)}function H(e){if(!e?.payload)return!1;let t=e.payload.event;return e.payload.reactionNotification===!0||t?.type===`message.reaction`}function U(e){if(!e)return null;let t=e.trim();if(!t.startsWith(`{`))return null;try{let e=JSON.parse(t);if(typeof e!=`object`||!e||Array.isArray(e))return null;let n=typeof e.text==`string`?e.text:typeof e.message==`string`?e.message:null;if(!n?.trim())return null;let r=new Set([`text`,`message`]);return{text:n,meta:Object.entries(e).filter(([e,t])=>!r.has(e)&&(typeof t==`string`||typeof t==`number`||typeof t==`boolean`)).map(([e,t])=>[e,String(t)]),raw:e}}catch{return null}}function W(e){return U(e)?.text??null}function G(e){let t=e?.subject||V(e)||``;return t.length>90?t.slice(0,90)+`...`:t}function le(e){return o(e.from)&&e.to?e.to:o(e.to)&&e.from?e.from:``}function K(e){return o(e.to)&&!o(e.from)}function ue(e){if(e.kind!==`session`)return!1;let t=e.payload?.session?.type;return t===`reasoning`||t===`tool`}function q(e){return e.kind===`session`&&a(e.payload)}function de(e,t){let n=e.trim(),r=t.trim();return r?n===r?!0:n.startsWith(`${r}\n\n`)||n.endsWith(`\n\n${r}`)||n.includes(`\n\n${r}\n\n`):!1}function fe(e,t,n){if(!K(n)||!q(n)||(n.readBy||[]).includes(`user`))return!1;let r=Number(e[t]||0);return n.id>(Number.isFinite(r)?r:0)}function pe(e){return(e.kind??`claim`)===`claim`&&ee.has(e.status)&&!e.claimedBy}function me(e){return!!(e.claimable&&!e.claimedBy&&!(e.kind===`task`&&Number.isSafeInteger(e.payload?.taskId)))}function he(e,t){return!e||!t?!1:e===`broadcast`||e===t.id?!0:e.startsWith(`tag:`)?(t.tags||[]).includes(e.slice(4)):e.startsWith(`cap:`)?(t.capabilities||[]).includes(e.slice(4)):e.startsWith(`label:`)?t.label===e.slice(6):e.startsWith(`team:`)?t.teamId===e.slice(5):!1}function ge(e,t){return e?t.from===e||t.to===e||t.resolvedToAgent===e:!1}function _e(e,t){if(!e||!t)return!1;let n=[t.id,t.type,...t.topicChannels||[]].filter(Boolean);if(e.channel&&n.includes(e.channel))return!0;let r=e.payload?.channel;if(r&&typeof r==`object`){let e=[r.agentId,r.provider,r.accountId].filter(Boolean);if(e.includes(t.id)||e.includes(t.type)||t.accountId&&e.includes(t.accountId))return!0}return!!(t.agentId&&(e.from===t.agentId||e.to===t.agentId))}function J(e){let t=e?.meta?.providerState;return!n(t)||typeof t.state!=`string`?null:{state:t.state,reason:typeof t.reason==`string`?t.reason:void 0,label:typeof t.label==`string`&&t.label?t.label:t.state,recommendedAction:typeof t.recommendedAction==`string`?t.recommendedAction:void 0,source:typeof t.source==`string`?t.source:void 0,raw:t.raw,updatedAt:typeof t.updatedAt==`number`?t.updatedAt:void 0,pendingApproval:ve(t.pendingApproval)}}function ve(e){if(!n(e)||typeof e.id!=`string`)return;let t=Array.isArray(e.choices)?e.choices.filter(n).map(e=>({id:e.id,label:typeof e.label==`string`?e.label:String(e.id||``)})).filter(e=>(e.id===`approve`||e.id===`approve-session`||e.id===`deny`||e.id===`abort`)&&!!e.label):[],r=Array.isArray(e.questions)?e.questions.filter(n).map(e=>({question:typeof e.question==`string`?e.question:``,header:typeof e.header==`string`?e.header:void 0,multiSelect:e.multiSelect===!0,options:Array.isArray(e.options)?e.options.filter(n).map(e=>({label:typeof e.label==`string`?e.label:String(e.label??``),description:typeof e.description==`string`?e.description:void 0})).filter(e=>!!e.label):[]})).filter(e=>!!e.question&&e.options.length>0):void 0;return{id:e.id,provider:typeof e.provider==`string`?e.provider:void 0,kind:typeof e.kind==`string`?e.kind:void 0,title:typeof e.title==`string`&&e.title?e.title:`Permission request`,body:typeof e.body==`string`?e.body:``,choices:t,...r&&r.length?{questions:r}:{}}}function Y(e){let t=J(e);return t?.state===`blocked`?t:null}function ye(e){return!!e?.id}function be(e){return ye(Y(e)?.pendingApproval)}function X(e){let t=e?.meta?.activeSubagents;if(!Array.isArray(t)){let t=typeof e?.meta?.activeSubagentCount==`number`?e.meta.activeSubagentCount:0;return t>0?Array.from({length:t},(e,t)=>({id:`subagent-${t+1}`,label:`Subagent ${t+1}`})):[]}return t.filter(n).map((e,t)=>{let n=typeof e.id==`string`&&e.id?e.id:`subagent-${t+1}`,r=typeof e.role==`string`&&e.role?e.role:void 0;return{id:n,label:typeof e.label==`string`&&e.label?e.label:r||n,role:r,startedAt:typeof e.startedAt==`number`?e.startedAt:void 0}})}function Z(e){let t=e?.meta?.activeWork;if(!Array.isArray(t))return null;let r=t.find(e=>n(e)&&e.kind===`background-script`);return!r||!n(r)?null:typeof r.label==`string`&&r.label?r.label:`background script`}function xe(e,t,n,r){let i=typeof t?.meta?.lifecycleAction==`string`?t.meta.lifecycleAction:``,a=D(e,t),o=t?.status!==`offline`&&!t?.ready&&a,s=t?.status!==`offline`&&!t?.ready&&!a,c=n.unread>0&&t?.status!==`busy`,l=Y(t),u=Se(t,n,r);if(t?.status===`offline`)return{label:`offline`,tone:`secondary`,icon:`PlugZap`,stale:!1,reconnecting:!1,badges:u};if(i.startsWith(`finalizing-`))return{label:`wrapping up (${i.slice(11)})`,tone:`warning`,icon:`Hourglass`,stale:a,reconnecting:o,badges:u};if(i===`shutting-down`)return{label:`shutting down`,tone:`warning`,icon:`Power`,stale:a,reconnecting:o,badges:u};if(i===`killing`)return{label:`killing`,tone:`danger`,icon:`Power`,stale:a,reconnecting:o,badges:u};if(i===`restarting`)return{label:`restarting`,tone:`warning`,icon:`RefreshCw`,stale:a,reconnecting:o,badges:u};if(t?.status===`stale`)return{label:`stale`,tone:`danger`,icon:`RefreshCw`,stale:!0,reconnecting:!0,badges:u};if(o)return{label:`reconnecting`,tone:`danger`,icon:`RefreshCw`,stale:a,reconnecting:o,badges:u};if(s)return{label:`online, not ready`,tone:`warning`,icon:`Loader`,stale:a,reconnecting:o,badges:u};if(l)return{label:`blocked: ${l.label}`,tone:`danger`,icon:`AlertCircle`,stale:a,reconnecting:o,badges:u};if(t?.status===`busy`){let e=Array.isArray(t?.meta?.busyReasons)?t.meta.busyReasons:[];return Z(t)&&!e.includes(`provider-turn`)&&!e.includes(`subagent`)?{label:`running background work`,tone:`warning`,icon:`Play`,stale:a,reconnecting:o,badges:u}:{label:`busy in turn`,tone:`warning`,icon:`Play`,stale:a,reconnecting:o,badges:u}}return r?.status===`active`?{label:`paired`,tone:`success`,icon:`Link`,stale:a,reconnecting:o,badges:u}:c?{label:`idle, unread`,tone:`danger`,icon:`Bell`,stale:a,reconnecting:o,badges:u}:{label:t?.status===`idle`?`idle`:`ready`,tone:`success`,icon:`CircleCheck`,stale:a,reconnecting:o,badges:u}}function Se(e,t,n){let r=[],i=Y(e),a=X(e).length;i&&r.push({label:i.reason?`blocked: ${i.reason}`:`blocked`,className:`bg-red-500/20 text-red-400`}),a&&r.push({label:a===1?`1 subagent`:a+` subagents`,className:`bg-cyan-500/20 text-cyan-300`});let o=Z(e);return o&&r.push({label:o,className:`bg-indigo-500/20 text-indigo-300`}),n?.status===`active`&&r.push({label:`paired`,className:`bg-emerald-500/20 text-emerald-400`}),n?.status===`pending`&&r.push({label:`pair invite`,className:`bg-yellow-500/20 text-yellow-400`}),t.needsHumanResponse&&r.push({label:`needs response`,className:`bg-amber-500/20 text-amber-300`}),t.unread&&r.push({label:t.unread+` unread`,className:`bg-red-500/20 text-red-400`}),t.claimableTasks&&r.push({label:t.claimableTasks+` claimable`,className:`bg-orange-500/20 text-orange-400`}),r}function Ce(){return{unread:0,needsHumanResponse:!1,pendingPairInvite:!1,claimableTasks:0,total:0,score:0}}function Q(e){return!e||e.status===`offline`?!1:e.targetHealth?.status===`ok`?!0:e.targetHealth?.status===`error`||e.targetHealth?.status===`warning`?!1:e.ready}function we(e){return e?e.targetHealth?.status===`error`?{label:`target broken`,tone:`danger`,icon:`AlertTriangle`,badges:[]}:e.targetHealth?.status===`warning`?{label:`target warning`,tone:`warning`,icon:`AlertCircle`,badges:[]}:e.status===`offline`?{label:`offline`,tone:`secondary`,icon:`PlugZap`,badges:[]}:e.status===`busy`?{label:`busy`,tone:`warning`,icon:`Activity`,badges:[]}:Q(e)?{label:`ready`,tone:`success`,icon:`CircleCheck`,badges:[]}:{label:`not ready`,tone:`warning`,icon:`Loader`,badges:[]}:{label:`unknown`,tone:`secondary`,icon:`PlugZap`,badges:[]}}function Te(e){let t=e?.runtime||{};return t.status===`error`?{label:`error`,tone:`danger`,icon:`AlertTriangle`,badges:[]}:t.status===`warn`?{label:`warning`,tone:`warning`,icon:`AlertCircle`,badges:[]}:t.running?{label:`running`,tone:`success`,icon:`CircleCheck`,badges:[]}:t.enabled===!1?{label:`disabled`,tone:`secondary`,icon:`Pause`,badges:[]}:t.status===`ok`?{label:`ok`,tone:`success`,icon:`CircleCheck`,badges:[]}:{label:`unknown`,tone:`secondary`,icon:`HelpCircle`,badges:[]}}function Ee(e){let t=e?.taskStats||{};return(t.waitingTasks||0)>0?{label:`waiting`,tone:`warning`,icon:`AlertCircle`,badges:[]}:(t.openTasks||0)>0?{label:`active`,tone:`info`,icon:`Activity`,badges:[]}:e?.configured?e.observed?{label:`quiet`,tone:`success`,icon:`CircleCheck`,badges:[]}:{label:`configured`,tone:`primary`,icon:`PlugZap`,badges:[]}:{label:`observed only`,tone:`secondary`,icon:`Eye`,badges:[]}}function De(e){let t=e?.health;return!t||t.status===`ok`?e?.version||e?.package?.version?`compatible`:`unknown version`:t.status===`upgrade-required`?`upgrade required`:t.status===`restart-required`?`restart required`:t.status===`unknown`?`unknown contract`:(t.issues||[]).some(e=>e.code===`protocol-mismatch`)?`protocol mismatch`:(t.issues||[]).some(e=>e.code===`package-drift`)?`package drift`:t.restartRequired?`restart required`:t.status}function $(e){let t=e?.health?.status;return t===`upgrade-required`?`text-red-400 bg-red-500/10`:t===`warn`||t===`restart-required`?`text-yellow-400 bg-yellow-500/10`:t===`unknown`?`text-zinc-400 bg-zinc-500/10`:`text-emerald-400 bg-emerald-500/10`}function Oe(e,t){if(!t)return``;let n=new Date(t).getTime();if(!Number.isFinite(n))return``;let r=Math.max(0,(e-n)/1e3);return r<60?Math.floor(r)+`s ago`:r<3600?Math.floor(r/60)+`m ago`:r<86400?Math.floor(r/3600)+`h ago`:Math.floor(r/86400)+`d ago`}function ke(e){return e?new Date(e).toLocaleString():``}function Ae(e){return String(e||`export`).replace(/[^a-z0-9._-]+/gi,`-`).replace(/^-+|-+$/g,``).slice(0,80)||`export`}function je(e){return e.filter(e=>e.status!==`ok`).map(e=>{let t={name:e.name,status:e.status,detail:e.detail||e.name,impact:Me(e),subjects:e.subjects||[],actions:[{label:`Inspect logs`,icon:`FileSearch`,copy:`agent-relay daemon logs`},{label:`Restart daemon`,icon:`RefreshCw`,copy:`agent-relay daemon restart`},{label:`Copy env`,icon:`Copy`,copy:`agent-relay doctor`}]};return e.name===`stale-live-agents`?t.actions.unshift({label:`Run reaper`,icon:`Eraser`,api:`POST`,path:`/system/reap`},{label:`Show stale`,icon:`Filter`,view:`agents`,preset:`offline_stale`}):e.name===`channel-delivery-targets`?t.actions.unshift({label:`Open channels`,icon:`MessagesSquare`,view:`channels`},{label:`Run reaper`,icon:`Eraser`,api:`POST`,path:`/system/reap`}):[`expired-message-claims`,`expired-task-claims`,`offline-claimed-tasks`].includes(e.name)&&t.actions.unshift({label:`Run reaper`,icon:`Eraser`,api:`POST`,path:`/system/reap`},{label:`Open work`,icon:`ListChecks`,view:`work`}),t})}function Me(e){return{database:`Relay persistence is unavailable; messages, state, and audit writes may fail.`,"stale-live-agents":`Agents may look online even though their heartbeat has stopped.`,"expired-message-claims":`Claimable messages may be stuck until the reaper releases expired claims.`,"expired-task-claims":`Tasks can appear owned by agents that no longer hold a live lease.`,"offline-claimed-tasks":`Offline agents are still shown as owners for active work.`,"channel-delivery-targets":`Inbound channel messages may be accepted but routed to no live delivery agent.`}[e.name]||`Relay health is degraded for this check.`}function Ne(e){return{success:`text-emerald-400`,warning:`text-yellow-400`,danger:`text-red-400`,info:`text-blue-400`,primary:`text-blue-400`,secondary:`text-zinc-400`}[e]||`text-zinc-400`}function Pe(e,t){return!e||e.status===`offline`?`bg-zinc-500 opacity-50`:typeof e.meta?.lifecycleAction==`string`&&e.meta.lifecycleAction.startsWith(`finalizing-`)?`bg-amber-500 animate-pulse`:e.meta?.lifecycleAction===`shutting-down`||e.meta?.lifecycleAction===`restarting`?`bg-yellow-500 animate-pulse`:e.meta?.lifecycleAction===`killing`||Y(e)?`bg-red-500 animate-pulse`:e.status===`busy`?`bg-yellow-500`:t&&D(t,e)?`bg-red-500 animate-pulse`:e.ready?`bg-emerald-500 shadow-[0_0_6px_theme(colors.emerald.500)]`:`bg-emerald-500 animate-pulse`}function Fe(e,t,n){if(typeof document>`u`||typeof URL>`u`||typeof Blob>`u`)return;let r=URL.createObjectURL(new Blob([t],{type:n})),i=document.createElement(`a`);i.href=r,i.download=e,i.click(),URL.revokeObjectURL(r)}export{f as $,ge as A,J as B,me as C,ue as D,H as E,$ as F,Pe as G,de as H,De as I,Ne as J,Oe as K,U as L,G as M,x as N,fe as O,b as P,u as Q,ce as R,q as S,K as T,Ae as U,I as V,se as W,P as X,oe as Y,d as Z,z as _,O as a,l as at,Ee as b,we as c,y as ct,F as d,p as et,B as f,je as g,ke as h,xe as i,c as it,_e as j,V as k,Te as l,te as lt,Ce as m,M as n,m as nt,E as o,v as ot,Fe as p,re as q,be as r,s as rt,Q as s,ne as st,X as t,h as tt,he as u,L as v,pe as w,D as x,le as y,Y as z};
3
+ //# sourceMappingURL=display-CJzSoTMq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"display-CJzSoTMq.js","names":[],"sources":["../../dashboard/src/lib/constants.ts","../../dashboard/src/lib/display.ts"],"sourcesContent":["import type { ComposeState, InboxComposeState, PairMessageState, PairInviteState, ViewName } from '@/types'\n\nconst PREF_PREFIX = 'ar-'\nexport const HUMAN_AGENT_ID = 'user'\nexport const INBOX_OPERATOR_ID = HUMAN_AGENT_ID\nexport const LIVE_REFRESH_MS = 5_000\n// Max live messages retained in memory. Must be >= the largest fetch limit\n// (chat view fetches 300) so SSE appends never trim already-loaded history.\nexport const MESSAGE_BUFFER_CAP = 500\n\nexport const CLOSED_TASK_STATUSES = new Set(['done', 'failed', 'canceled'])\nexport const WAITING_TASK_STATUSES = new Set(['open', 'blocked'])\n\nexport const STATUS_SORT_ORDER: Record<string, number> = { online: 0, idle: 0, busy: 2, offline: 3 }\nexport const CHAT_STATUS_SORT_ORDER: Record<string, number> = { idle: 0, online: 0, busy: 2, offline: 4 }\n\nexport const DEFAULT_COMPOSE: ComposeState = { from: '', to: '', body: '', channel: '', subject: '', claimable: false }\nexport const DEFAULT_INBOX_COMPOSE: InboxComposeState = { toMode: 'agent', to: '', body: '', channel: '', subject: '', claimable: false }\nexport const DEFAULT_PAIR_MESSAGE: PairMessageState = { pairId: '', from: '', body: '', subject: '' }\nexport const DEFAULT_PAIR_INVITE: PairInviteState = { requesterId: '', targetId: '', objective: '' }\n\n\nimport { getKnownProviderIds, getProviderLabel } from './provider-display'\n\nconst SYSTEM_TYPE_ICONS: Record<string, string> = {\n user: 'User',\n system: 'Server',\n channel: 'MessagesSquare',\n agent: 'Bot',\n}\n\nconst SYSTEM_TYPE_TITLES: Record<string, string> = {\n user: 'Human operator',\n system: 'System',\n channel: 'Channel',\n agent: 'Agent',\n}\n\nexport const AGENT_TYPE_ICONS: Record<string, string> = {\n ...SYSTEM_TYPE_ICONS,\n ...Object.fromEntries(getKnownProviderIds().map((id) => [id, 'Bot'])),\n}\n\nexport const AGENT_TYPE_TITLES: Record<string, string> = {\n ...SYSTEM_TYPE_TITLES,\n ...Object.fromEntries(getKnownProviderIds().map((id) => [id, `${getProviderLabel(id)} agent`])),\n}\n\nexport const NAV_ITEMS: Array<{ key: ViewName; label: string; icon: string }> = [\n { key: 'overview', label: 'Overview', icon: 'LayoutDashboard' },\n { key: 'chat', label: 'Chat', icon: 'MessageCircle' },\n { key: 'agents', label: 'Agents', icon: 'Bot' },\n { key: 'managed', label: 'Managed Agents', icon: 'Workflow' },\n { key: 'teams', label: 'Teams', icon: 'Users' },\n { key: 'profiles', label: 'Agent Profiles', icon: 'UserCog' },\n { key: 'orchestrators', label: 'Orchestrators', icon: 'Server' },\n { key: 'workspaces', label: 'Workspaces', icon: 'GitBranch' },\n { key: 'files', label: 'Files', icon: 'FolderTree' },\n { key: 'channels', label: 'Channels', icon: 'MessagesSquare' },\n { key: 'connectors', label: 'Connectors', icon: 'Plug' },\n { key: 'integrations', label: 'Integrations', icon: 'PlugZap' },\n { key: 'security', label: 'Security', icon: 'Shield' },\n { key: 'memory', label: 'Memory', icon: 'BrainCircuit' },\n { key: 'activity', label: 'Activity', icon: 'Activity' },\n { key: 'pairs', label: 'Pairs', icon: 'Link' },\n { key: 'messages', label: 'Messages', icon: 'Mail' },\n { key: 'work', label: 'Work Queue', icon: 'ListChecks' },\n { key: 'tasks', label: 'Tasks', icon: 'ClipboardList' },\n { key: 'automation', label: 'Automation', icon: 'CalendarClock' },\n { key: 'analytics', label: 'Analytics', icon: 'AreaChart' },\n { key: 'insights', label: 'Insights', icon: 'Lightbulb' },\n { key: 'projects', label: 'Projects', icon: 'Boxes' },\n { key: 'prompts', label: 'Prompts', icon: 'FileText' },\n { key: 'registry', label: 'Registry', icon: 'PackagePlus' },\n { key: 'maintenance', label: 'Maintenance', icon: 'Wrench' },\n { key: 'settings', label: 'Settings', icon: 'Settings' },\n]\n\nexport const SEVERITY_COLORS: Record<string, string> = {\n critical: 'text-red-400 bg-red-500/10',\n warning: 'text-yellow-400 bg-yellow-500/10',\n info: 'text-blue-400 bg-blue-500/10',\n}\n\nexport const PAIR_STATUS_COLORS: Record<string, string> = {\n active: 'bg-emerald-500/20 text-emerald-400',\n pending: 'bg-yellow-500/20 text-yellow-400',\n ended: 'bg-zinc-500/20 text-zinc-400',\n rejected: 'bg-red-500/20 text-red-400',\n expired: 'bg-red-500/20 text-red-400',\n}\n","import type {\n Agent, Message, PairSession, Task, ChannelSummary, ConnectorSummary,\n IntegrationSummary, Orchestrator, AgentType, PresenceInfo, PresenceBadge,\n AttentionInfo, InboxThread, HealthCheck, HealthDiagnostic, ManagedPolicyHealth,\n} from '@/types'\nimport type { User } from '@/types'\nimport { HUMAN_AGENT_ID, AGENT_TYPE_ICONS, AGENT_TYPE_TITLES, WAITING_TASK_STATUSES } from './constants'\nimport { getKnownProviderIds, getProviderManifest } from './provider-display'\nimport { isRecord, isHumanAgentId, isWorkFinalMessagePayload, userIdFromAddress } from 'agent-relay-sdk/types'\n\nexport function toTimestamp(value: unknown): number {\n const ts = typeof value === 'number' ? value : new Date((value as string) || 0).getTime()\n return Number.isFinite(ts) ? ts : 0\n}\n\nconst INTERNAL_CAP_PREFIXES = ['lifecycle.', 'commands.', 'capabilities.']\n\nconst REACTION_EMOJI_ALIASES: Record<string, string> = {\n '+1': '👍',\n ':+1:': '👍',\n thumbsup: '👍',\n ':thumbsup:': '👍',\n 'thumbs-up': '👍',\n ':thumbs-up:': '👍',\n thumbs_up: '👍',\n ':thumbs_up:': '👍',\n thumb_up: '👍',\n ':thumb_up:': '👍',\n like: '👍',\n ':like:': '👍',\n heart: '❤️',\n ':heart:': '❤️',\n redheart: '❤️',\n red_heart: '❤️',\n ':red_heart:': '❤️',\n check: '✅',\n ':check:': '✅',\n checkmark: '✅',\n ':checkmark:': '✅',\n white_check_mark: '✅',\n ':white_check_mark:': '✅',\n eyes: '👀',\n ':eyes:': '👀',\n}\n\nexport function normalizeReactionEmoji(value: string): string {\n const trimmed = value.trim()\n return REACTION_EMOJI_ALIASES[trimmed.toLowerCase()] ?? trimmed\n}\n\nexport function userFacingCapabilities(caps: string[]): string[] {\n return caps.filter((c) => !INTERNAL_CAP_PREFIXES.some((p) => c.startsWith(p)))\n}\n\nexport function shortPath(cwd: string | undefined | null, segments = 2): string {\n if (!cwd) return ''\n const parts = cwd.replace(/\\/+$/, '').split('/')\n return parts.length <= segments ? cwd : parts.slice(-segments).join('/')\n}\n\nexport function normalizePathForCompare(path: string): string {\n return path.trim().replace(/\\\\/g, '/').replace(/\\/+$/, '') || '/'\n}\n\nexport function pathWithinBase(path: string | undefined | null, baseDir: string | undefined | null): boolean {\n if (!path?.trim() || !baseDir?.trim()) return false\n const normalizedPath = normalizePathForCompare(path)\n const normalizedBase = normalizePathForCompare(baseDir)\n if (normalizedBase === '/') return normalizedPath.startsWith('/')\n if (normalizedPath === '/') return false\n return normalizedPath === normalizedBase || normalizedPath.startsWith(normalizedBase + '/')\n}\n\nfunction isBuiltInAgent(agent: Agent | null | undefined): boolean {\n return agent?.meta?.builtin === true || agent?.id === HUMAN_AGENT_ID || agent?.id === 'system'\n}\n\nfunction isChannelAgent(agent: Agent | null | undefined): boolean {\n const tags = agent?.tags || []\n const caps = agent?.capabilities || []\n const policyName = typeof agent?.meta?.policyName === 'string' ? agent.meta.policyName : ''\n return agent?.kind === 'channel' ||\n agent?.meta?.kind === 'channel' ||\n tags.includes('channel') ||\n tags.some((tag) => tag.startsWith('channel:')) ||\n caps.includes('channel') ||\n caps.some((cap) => cap.startsWith('channel:')) ||\n tags.includes('telegram') ||\n caps.includes('telegram') ||\n policyName === 'telegram-main'\n}\n\nfunction isAutomationAgent(agent: Agent | null | undefined): boolean {\n return Boolean(\n typeof agent?.meta?.automationId === 'string' ||\n typeof agent?.meta?.automationRunId === 'string' ||\n agent?.tags?.includes('automation')\n )\n}\n\nfunction isSystemAgent(agent: Agent | null | undefined): boolean {\n return isBuiltInAgent(agent) || isChannelAgent(agent) || isAutomationAgent(agent)\n}\n\nexport function agentType(agent: Agent | null | undefined): AgentType {\n if (agent?.id === HUMAN_AGENT_ID) return 'user'\n if (agent?.id === 'system') return 'system'\n if (isChannelAgent(agent)) return 'channel'\n\n const knownProviders = getKnownProviderIds()\n\n // #417: check the authoritative provider field first (exact match) so a name or id\n // containing a provider name as a substring can't mislabel the agent.\n const provider = typeof agent?.meta?.provider === 'string' ? agent.meta.provider.toLowerCase() : undefined\n if (provider && knownProviders.includes(provider)) return provider as AgentType\n\n // Fallback heuristic for agents registered without meta.provider. Only scan tags\n // and meta fields — not id or name, which are free-text and cause false positives.\n const values = [\n ...(agent?.tags || []),\n agent?.meta?.client as string,\n agent?.meta?.runtime as string,\n agent?.meta?.agentType as string,\n ]\n .filter((v): v is string => typeof v === 'string')\n .map((v) => v.toLowerCase())\n\n for (const pid of knownProviders) {\n if (values.some((v) => v.includes(pid))) return pid as AgentType\n }\n return 'agent'\n}\n\nfunction agentTypeIcon(agent: Agent | null | undefined): string {\n return AGENT_TYPE_ICONS[agentType(agent)] ?? AGENT_TYPE_ICONS.agent ?? 'ti-robot'\n}\n\nfunction agentTypeTitle(agent: Agent | null | undefined): string {\n return AGENT_TYPE_TITLES[agentType(agent)] ?? AGENT_TYPE_TITLES.agent ?? 'Agent'\n}\n\nexport function isAgentStale(now: number, agent: Agent | null | undefined): boolean {\n if (!agent?.lastSeen || agent.status === 'offline') return false\n if (agent.id === 'user' || agent.id === 'system') return false\n // A live bus connection is authoritative liveness: an idle-but-connected agent\n // is reachable, not stale, even if lastSeen has aged (e.g. heartbeat keepalive\n // gaps while idle). Genuine death flips transport.connected to false (or the\n // server reaps the agent offline), at which point the lastSeen check applies.\n const transport = agent.meta?.transport\n if (isRecord(transport) && transport.connected === true) return false\n const lastSeenMs = new Date(agent.lastSeen as string).getTime()\n if (!Number.isFinite(lastSeenMs)) return false\n return now - lastSeenMs > 60_000\n}\n\nexport type AgentControlAction = 'restart' | 'shutdown' | 'reconnect' | 'compact' | 'clearContext' | 'resume' | 'interrupt'\n\nexport function agentSupportsControlAction(agent: Agent | null | undefined, action: AgentControlAction): boolean {\n if (!agent || isBuiltInAgent(agent) || isChannelAgent(agent)) return false\n if (action === 'resume') return false\n // Interrupt is provider-independent and only meaningful while the agent is actually mid-turn.\n if (action === 'interrupt') return agent.providerCapabilities?.liveSession?.interrupt === true && agent.status === 'busy'\n const needsTmux = providerUsesLiveTerminal(agent) && (action === 'restart' || action === 'shutdown' || action === 'compact' || action === 'clearContext')\n if (needsTmux && !agentHasTmuxSession(agent)) return false\n const lifecycle = agent.providerCapabilities?.lifecycle\n if (lifecycle) {\n if (action === 'restart') return lifecycle.restartHard === true\n if (action === 'shutdown') return lifecycle.shutdownHard === true\n if (action === 'reconnect') return lifecycle.reconnect === true\n }\n if (action === 'compact') return agent.providerCapabilities?.context?.compact === true\n if (action === 'clearContext') return agent.providerCapabilities?.context?.clear === true\n return agent.meta?.runnerManaged === true && (action === 'restart' || action === 'shutdown')\n}\n\nfunction agentHasTmuxSession(agent: Agent): boolean {\n return typeof agent.meta?.tmuxSession === 'string' && agent.meta.tmuxSession.trim().length > 0\n}\n\nfunction providerUsesLiveTerminal(agent: Agent | null | undefined): boolean {\n const manifest = typeof agent?.meta?.provider === 'string' ? getProviderManifest(agent.meta.provider as string) : undefined\n return manifest?.capabilityDefaults?.terminal?.mode === 'live'\n}\n\nfunction agentSupportsControlActions(agent: Agent | null | undefined): boolean {\n return agentSupportsControlAction(agent, 'restart') ||\n agentSupportsControlAction(agent, 'shutdown') ||\n agentSupportsControlAction(agent, 'reconnect') ||\n agentSupportsControlAction(agent, 'compact') ||\n agentSupportsControlAction(agent, 'clearContext')\n}\n\nexport function agentCanBeForgotten(agent: Agent | null | undefined): boolean {\n return Boolean(agent && !isBuiltInAgent(agent) && !isChannelAgent(agent) && !agentSupportsControlActions(agent) && (agent.status === 'offline' || agent.status === 'stale'))\n}\n\nfunction isRetiredManagedRuntimeAgent(agent: Agent, managedPolicies: ManagedPolicyHealth[]): boolean {\n if (agent.status !== 'offline') return false\n const policyName = typeof agent.meta?.policyName === 'string' ? agent.meta.policyName : ''\n if (!policyName) return false\n const policy = managedPolicies.find((item) => item.policy.name === policyName)\n return Boolean(policy?.state.agentId && policy.state.agentId !== agent.id)\n}\n\nexport function visibleAgents(agents: Agent[], showBuiltIns: boolean, managedPolicies: ManagedPolicyHealth[] = []): Agent[] {\n const activeRuntimeAgents = agents.filter((a) => !isRetiredManagedRuntimeAgent(a, managedPolicies))\n return showBuiltIns ? activeRuntimeAgents : activeRuntimeAgents.filter((a) => !isSystemAgent(a))\n}\n\nexport function displayName(agent: Agent | null | undefined): string {\n if (!agent) return '?'\n return agent.label || agent.name || agent.id.slice(-12)\n}\n\n// Human attribution (#390). Resolve a human message address (`user` / `user:<id>`) to its\n// users-table row, or undefined for a non-human address / unknown id. The single bridge from\n// a wire address → a user record on the client, mirroring the SDK resolver on the server.\nexport function resolveUser(address: string | undefined, usersById: Record<string, User>): User | undefined {\n const userId = userIdFromAddress(address)\n return userId ? usersById[userId] : undefined\n}\n\n// Name to show for a human address. Prefers the user's displayName, then handle, then a\n// generic 'User' so a not-yet-loaded directory (or the seeded default) still reads sensibly —\n// which keeps today's single operator rendering exactly as before.\nexport function humanDisplayName(address: string | undefined, usersById: Record<string, User>): string {\n const user = resolveUser(address, usersById)\n return user?.displayName?.trim() || user?.handle?.trim() || 'User'\n}\n\n// Deterministic accent color per human so multiple operators are visually distinguishable in\n// one thread. Stable for a given user id; the single default operator never needs it (callers\n// only apply this once more than one human exists, preserving back-compat styling).\nconst HUMAN_ACCENT_CLASSES = [\n 'text-sky-300', 'text-amber-300', 'text-emerald-300', 'text-violet-300', 'text-rose-300', 'text-cyan-300',\n]\nexport function humanAccentClass(address: string | undefined): string {\n const userId = userIdFromAddress(address) || ''\n let hash = 0\n for (let i = 0; i < userId.length; i++) hash = (hash * 31 + userId.charCodeAt(i)) >>> 0\n return HUMAN_ACCENT_CLASSES[hash % HUMAN_ACCENT_CLASSES.length]!\n}\n\nexport function displayTarget(target: string, agentsById: Record<string, Agent>): string {\n if (!target) return '?'\n if (target === 'broadcast') return 'broadcast'\n if (target.startsWith('tag:')) return '#' + target.slice(4)\n if (target.startsWith('cap:')) return target.slice(4)\n if (target.startsWith('label:')) return target.slice(6)\n if (target.startsWith('team:')) {\n const teamId = target.slice(5)\n const member = Object.values(agentsById).find((a) => a.teamId === teamId)\n return member ? `team:${displayName(member)}` : `team:${teamId.slice(-8)}`\n }\n const agent = agentsById[target]\n return agent ? displayName(agent) : target.slice(-8)\n}\n\nexport function messageBody(msg: Message | null | undefined): string {\n if (!msg) return ''\n const payload = msg.payload || {}\n const channelMessage = payload.message as Record<string, unknown> | undefined\n if (channelMessage && typeof channelMessage === 'object' && typeof channelMessage.text === 'string' && (channelMessage.text as string).trim()) {\n return channelMessage.text as string\n }\n const interaction = payload.interaction as Record<string, unknown> | undefined\n if (interaction && typeof interaction === 'object') {\n const title = typeof interaction.title === 'string' ? interaction.title.trim() : ''\n const description = typeof interaction.description === 'string' ? interaction.description.trim() : ''\n if (title && description) return title + '\\n' + description\n if (title) return title\n if (description) return description\n }\n const reaction = payload.reaction as Record<string, unknown> | undefined\n if (reaction && typeof reaction === 'object') {\n const name = typeof reaction.name === 'string' ? reaction.name : ''\n const emoji = typeof reaction.emoji === 'string' ? reaction.emoji : ''\n const value = typeof reaction.value === 'string' ? reaction.value : ''\n const displayEmoji = normalizeReactionEmoji(emoji || name || value)\n return ['Reaction', displayEmoji].filter(Boolean).join(': ')\n }\n const activity = payload.activity as Record<string, unknown> | undefined\n if (activity && typeof activity === 'object') {\n const kind = typeof activity.kind === 'string' ? activity.kind : 'activity'\n const state = typeof activity.state === 'string' ? activity.state : ''\n return [kind, state].filter(Boolean).join(' ')\n }\n if (typeof payload.text === 'string' && (payload.text as string).trim()) return payload.text as string\n if (typeof payload.message === 'string' && (payload.message as string).trim()) return payload.message as string\n return extractTextFromJson(msg.body || '') ?? (msg.body || '')\n}\n\nexport function isReactionEventMessage(msg: Message | null | undefined): boolean {\n if (!msg?.payload) return false\n const event = msg.payload.event as Record<string, unknown> | undefined\n return msg.payload.reactionNotification === true || event?.type === 'message.reaction'\n}\n\ninterface ParsedJsonBody {\n text: string\n meta: Array<[string, string]>\n raw: Record<string, unknown>\n}\n\nexport function parseJsonBody(body: string): ParsedJsonBody | null {\n if (!body) return null\n const trimmed = body.trim()\n if (!trimmed.startsWith('{')) return null\n try {\n const obj = JSON.parse(trimmed)\n if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) return null\n const text = typeof obj.text === 'string' ? obj.text\n : typeof obj.message === 'string' ? obj.message\n : null\n if (!text?.trim()) return null\n const skipKeys = new Set(['text', 'message'])\n const meta = Object.entries(obj)\n .filter(([k, v]) => !skipKeys.has(k) && (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean'))\n .map(([k, v]) => [k, String(v)] as [string, string])\n return { text, meta, raw: obj }\n } catch {\n return null\n }\n}\n\nfunction extractTextFromJson(body: string): string | null {\n const parsed = parseJsonBody(body)\n return parsed?.text ?? null\n}\n\nexport function messagePreview(msg: Message | null | undefined): string {\n const text = msg?.subject || messageBody(msg) || ''\n return text.length > 90 ? text.slice(0, 90) + '...' : text\n}\n\n// De-singletoned human addressing (#393): a thread peer is the non-human side of any message\n// whose other side is a human address — the legacy bare `'user'` sink OR any `user:<id>` in the\n// namespace (`user:default` included). Today's single operator is still `'user'`, so this is a\n// no-op for the live deployment; per-user inbox scoping lands with auth/sessions (#389).\nexport function inboxPeer(msg: Message): string {\n if (isHumanAgentId(msg.from) && msg.to) return msg.to\n if (isHumanAgentId(msg.to) && msg.from) return msg.from\n return ''\n}\n\nexport function isHumanInboundMessage(msg: Message): boolean {\n return isHumanAgentId(msg.to) && !isHumanAgentId(msg.from)\n}\n\nexport function isSessionActivityStep(msg: Message): boolean {\n if (msg.kind !== 'session') return false\n const type = (msg.payload?.session as { type?: string } | undefined)?.type\n return type === 'reasoning' || type === 'tool'\n}\n\nexport function isBadgeWorthy(msg: Message): boolean {\n return msg.kind === 'session' && isWorkFinalMessagePayload(msg.payload)\n}\n\n// True when `block` appears as a whole \\n\\n-delimited segment of a response body —\n// the same boundary extractLastAssistantTurn joins narration on. Matches the entire\n// body (chatCaptureMode \"final\") or any internal block (\"full\") without naive-substring\n// false positives, and is robust to narration blocks that contain their own blank lines.\n// Used to suppress a narration trace step that the closing response bubble already repeats.\nexport function responseBodyContainsBlock(body: string, block: string): boolean {\n const b = body.trim()\n const t = block.trim()\n if (!t) return false\n if (b === t) return true\n return b.startsWith(`${t}\\n\\n`) || b.endsWith(`\\n\\n${t}`) || b.includes(`\\n\\n${t}\\n\\n`)\n}\n\nexport function isUnreadHumanMessage(readCursors: Record<string, number>, peer: string, msg: Message): boolean {\n if (!isHumanInboundMessage(msg)) return false\n if (!isBadgeWorthy(msg)) return false\n if ((msg.readBy || []).includes(HUMAN_AGENT_ID)) return false\n const cursor = Number(readCursors[peer] || 0)\n return msg.id > (Number.isFinite(cursor) ? cursor : 0)\n}\n\nexport function isClaimableTaskWaiting(task: Task): boolean {\n // #834 — only claim-lane tasks are claimable; deliverable Tasks are never claimed.\n return (task.kind ?? 'claim') === 'claim' && WAITING_TASK_STATUSES.has(task.status) && !task.claimedBy\n}\n\nexport function isClaimableMessageWaiting(msg: Message): boolean {\n return Boolean(msg.claimable && !msg.claimedBy && !(msg.kind === 'task' && Number.isSafeInteger((msg.payload as Record<string, unknown>)?.taskId)))\n}\n\nexport function dashboardTargetMatchesAgent(target: string, agent: Agent): boolean {\n if (!target || !agent) return false\n if (target === 'broadcast' || target === agent.id) return true\n if (target.startsWith('tag:')) return (agent.tags || []).includes(target.slice(4))\n if (target.startsWith('cap:')) return (agent.capabilities || []).includes(target.slice(4))\n if (target.startsWith('label:')) return agent.label === target.slice(6)\n if (target.startsWith('team:')) return agent.teamId === target.slice(5)\n return false\n}\n\nexport function messageMatchesAgentChat(agentId: string, message: Message): boolean {\n if (!agentId) return false\n if (message.from === agentId || message.to === agentId) return true\n if (message.resolvedToAgent === agentId) return true\n return false\n}\n\nexport function messageMatchesChannel(message: Message, channel: ChannelSummary): boolean {\n if (!message || !channel) return false\n const channelKeys = [channel.id, channel.type, ...(channel.topicChannels || [])].filter(Boolean)\n if (message.channel && channelKeys.includes(message.channel)) return true\n const payloadChannel = message.payload?.channel as Record<string, unknown> | undefined\n if (payloadChannel && typeof payloadChannel === 'object') {\n const keys = [payloadChannel.agentId, payloadChannel.provider, payloadChannel.accountId].filter(Boolean) as string[]\n if (keys.includes(channel.id) || keys.includes(channel.type) || (channel.accountId && keys.includes(channel.accountId))) return true\n }\n if (channel.agentId && (message.from === channel.agentId || message.to === channel.agentId)) return true\n return false\n}\n\ninterface ActiveSubagentInfo {\n id: string\n label: string\n role?: string\n startedAt?: number\n}\n\ninterface ProviderRuntimeState {\n state: string\n reason?: string\n label: string\n recommendedAction?: string\n source?: string\n raw?: unknown\n updatedAt?: number\n pendingApproval?: ProviderPendingApproval\n}\n\ninterface ProviderPendingApprovalChoice {\n id: 'approve' | 'approve-session' | 'deny' | 'abort'\n label: string\n}\n\ninterface ProviderQuestionOption {\n label: string\n description?: string\n}\n\nexport interface ProviderQuestion {\n question: string\n header?: string\n options: ProviderQuestionOption[]\n multiSelect?: boolean\n}\n\nexport interface ProviderPendingApproval {\n id: string\n provider?: string\n kind?: string\n title: string\n body: string\n choices: ProviderPendingApprovalChoice[]\n // Present when kind === 'questions' (Claude AskUserQuestion). The dashboard\n // renders these as a form and submits an 'answer' decision.\n questions?: ProviderQuestion[]\n}\n\nexport function providerRuntimeState(agent: Agent | null | undefined): ProviderRuntimeState | null {\n const raw = agent?.meta?.providerState\n if (!isRecord(raw) || typeof raw.state !== 'string') return null\n return {\n state: raw.state,\n reason: typeof raw.reason === 'string' ? raw.reason : undefined,\n label: typeof raw.label === 'string' && raw.label ? raw.label : raw.state,\n recommendedAction: typeof raw.recommendedAction === 'string' ? raw.recommendedAction : undefined,\n source: typeof raw.source === 'string' ? raw.source : undefined,\n raw: raw.raw,\n updatedAt: typeof raw.updatedAt === 'number' ? raw.updatedAt : undefined,\n pendingApproval: providerPendingApproval(raw.pendingApproval),\n }\n}\n\nfunction providerPendingApproval(value: unknown): ProviderPendingApproval | undefined {\n if (!isRecord(value) || typeof value.id !== 'string') return undefined\n const choices = Array.isArray(value.choices)\n ? value.choices.filter(isRecord).map((choice) => ({\n id: choice.id,\n label: typeof choice.label === 'string' ? choice.label : String(choice.id || ''),\n })).filter((choice): choice is ProviderPendingApprovalChoice =>\n (choice.id === 'approve' || choice.id === 'approve-session' || choice.id === 'deny' || choice.id === 'abort') && Boolean(choice.label)\n )\n : []\n const questions = Array.isArray(value.questions)\n ? value.questions.filter(isRecord).map((q): ProviderQuestion => ({\n question: typeof q.question === 'string' ? q.question : '',\n header: typeof q.header === 'string' ? q.header : undefined,\n multiSelect: q.multiSelect === true,\n options: Array.isArray(q.options)\n ? q.options.filter(isRecord).map((o) => ({\n label: typeof o.label === 'string' ? o.label : String(o.label ?? ''),\n description: typeof o.description === 'string' ? o.description : undefined,\n })).filter((o) => Boolean(o.label))\n : [],\n })).filter((q) => Boolean(q.question) && q.options.length > 0)\n : undefined\n return {\n id: value.id,\n provider: typeof value.provider === 'string' ? value.provider : undefined,\n kind: typeof value.kind === 'string' ? value.kind : undefined,\n title: typeof value.title === 'string' && value.title ? value.title : 'Permission request',\n body: typeof value.body === 'string' ? value.body : '',\n choices,\n ...(questions && questions.length ? { questions } : {}),\n }\n}\n\nexport function providerBlockedState(agent: Agent | null | undefined): ProviderRuntimeState | null {\n const state = providerRuntimeState(agent)\n return state?.state === 'blocked' ? state : null\n}\n\nexport function isPendingApprovalBadgeWorthy(approval: ProviderPendingApproval | null | undefined): boolean { return Boolean(approval?.id) }\n\nexport function agentNeedsHumanResponse(agent: Agent | null | undefined): boolean { return isPendingApprovalBadgeWorthy(providerBlockedState(agent)?.pendingApproval) }\n\nexport function activeSubagents(agent: Agent | null | undefined): ActiveSubagentInfo[] {\n const raw = agent?.meta?.activeSubagents\n if (!Array.isArray(raw)) {\n const count = typeof agent?.meta?.activeSubagentCount === 'number' ? agent.meta.activeSubagentCount : 0\n return count > 0 ? Array.from({ length: count }, (_, index) => ({ id: `subagent-${index + 1}`, label: `Subagent ${index + 1}` })) : []\n }\n return raw\n .filter(isRecord)\n .map((item, index) => {\n const id = typeof item.id === 'string' && item.id ? item.id : `subagent-${index + 1}`\n const role = typeof item.role === 'string' && item.role ? item.role : undefined\n const label = typeof item.label === 'string' && item.label ? item.label : role || id\n const startedAt = typeof item.startedAt === 'number' ? item.startedAt : undefined\n return { id, label, role, startedAt }\n })\n}\n\n// #502: a background shell (run_in_background) still running for this agent keeps\n// it busy even though its turn ended. The runner surfaces it as a `background-script`\n// work item in meta.activeWork carrying a human label (\"N background tasks\"); return\n// that label for a badge, or null when none are running.\nexport function activeBackgroundScriptLabel(agent: Agent | null | undefined): string | null {\n const raw = agent?.meta?.activeWork\n if (!Array.isArray(raw)) return null\n const item = raw.find((work) => isRecord(work) && work.kind === 'background-script')\n if (!item || !isRecord(item)) return null\n return typeof item.label === 'string' && item.label ? item.label : 'background script'\n}\n\nexport function agentPresence(\n now: number, agent: Agent | null | undefined, attention: AttentionInfo, pair: PairSession | null\n): PresenceInfo {\n const lifecycleAction = typeof agent?.meta?.lifecycleAction === 'string' ? agent.meta.lifecycleAction : ''\n const stale = isAgentStale(now, agent)\n const reconnecting = agent?.status !== 'offline' && !agent?.ready && stale\n const starting = agent?.status !== 'offline' && !agent?.ready && !stale\n const unreadIdle = attention.unread > 0 && agent?.status !== 'busy'\n const blocked = providerBlockedState(agent)\n const badges = presenceBadges(agent, attention, pair)\n\n if (agent?.status === 'offline') return { label: 'offline', tone: 'secondary', icon: 'PlugZap', stale: false, reconnecting: false, badges }\n // Pre-session-destroy window (#183): the runner is running end-of-session work before a\n // compact/clear/restart/shutdown. Distinct from plain \"busy\" and non-addressable.\n if (lifecycleAction.startsWith('finalizing-')) {\n return { label: `wrapping up (${lifecycleAction.slice('finalizing-'.length)})`, tone: 'warning', icon: 'Hourglass', stale, reconnecting, badges }\n }\n if (lifecycleAction === 'shutting-down') return { label: 'shutting down', tone: 'warning', icon: 'Power', stale, reconnecting, badges }\n if (lifecycleAction === 'killing') return { label: 'killing', tone: 'danger', icon: 'Power', stale, reconnecting, badges }\n if (lifecycleAction === 'restarting') return { label: 'restarting', tone: 'warning', icon: 'RefreshCw', stale, reconnecting, badges }\n if (agent?.status === 'stale') return { label: 'stale', tone: 'danger', icon: 'RefreshCw', stale: true, reconnecting: true, badges }\n if (reconnecting) return { label: 'reconnecting', tone: 'danger', icon: 'RefreshCw', stale, reconnecting, badges }\n if (starting) return { label: 'online, not ready', tone: 'warning', icon: 'Loader', stale, reconnecting, badges }\n if (blocked) return { label: `blocked: ${blocked.label}`, tone: 'danger', icon: 'AlertCircle', stale, reconnecting, badges }\n if (agent?.status === 'busy') {\n // #502: busy purely because a background shell outlived the turn (no active\n // provider turn / subagent) reads more truthfully as background work.\n const reasons = Array.isArray(agent?.meta?.busyReasons) ? agent.meta.busyReasons : []\n const backgroundOnly = activeBackgroundScriptLabel(agent) && !reasons.includes('provider-turn') && !reasons.includes('subagent')\n if (backgroundOnly) return { label: 'running background work', tone: 'warning', icon: 'Play', stale, reconnecting, badges }\n return { label: 'busy in turn', tone: 'warning', icon: 'Play', stale, reconnecting, badges }\n }\n if (pair?.status === 'active') return { label: 'paired', tone: 'success', icon: 'Link', stale, reconnecting, badges }\n if (unreadIdle) return { label: 'idle, unread', tone: 'danger', icon: 'Bell', stale, reconnecting, badges }\n return { label: agent?.status === 'idle' ? 'idle' : 'ready', tone: 'success', icon: 'CircleCheck', stale, reconnecting, badges }\n}\n\nfunction presenceBadges(agent: Agent | null | undefined, attention: AttentionInfo, pair: PairSession | null): PresenceBadge[] {\n const badges: PresenceBadge[] = []\n const blocked = providerBlockedState(agent)\n const subagentCount = activeSubagents(agent).length\n if (blocked) badges.push({ label: blocked.reason ? `blocked: ${blocked.reason}` : 'blocked', className: 'bg-red-500/20 text-red-400' })\n if (subagentCount) badges.push({ label: subagentCount === 1 ? '1 subagent' : subagentCount + ' subagents', className: 'bg-cyan-500/20 text-cyan-300' })\n const backgroundScriptLabel = activeBackgroundScriptLabel(agent)\n if (backgroundScriptLabel) badges.push({ label: backgroundScriptLabel, className: 'bg-indigo-500/20 text-indigo-300' })\n if (pair?.status === 'active') badges.push({ label: 'paired', className: 'bg-emerald-500/20 text-emerald-400' })\n if (pair?.status === 'pending') badges.push({ label: 'pair invite', className: 'bg-yellow-500/20 text-yellow-400' })\n if (attention.needsHumanResponse) badges.push({ label: 'needs response', className: 'bg-amber-500/20 text-amber-300' })\n if (attention.unread) badges.push({ label: attention.unread + ' unread', className: 'bg-red-500/20 text-red-400' })\n if (attention.claimableTasks) badges.push({ label: attention.claimableTasks + ' claimable', className: 'bg-orange-500/20 text-orange-400' })\n return badges\n}\n\nexport function emptyAttention(): AttentionInfo {\n return { unread: 0, needsHumanResponse: false, pendingPairInvite: false, claimableTasks: 0, total: 0, score: 0 }\n}\n\n\nexport function channelIsReady(channel: ChannelSummary | null): boolean {\n if (!channel || channel.status === 'offline') return false\n if (channel.targetHealth?.status === 'ok') return true\n if (channel.targetHealth?.status === 'error' || channel.targetHealth?.status === 'warning') return false\n return channel.ready\n}\n\nexport function channelPresence(channel: ChannelSummary | null): PresenceInfo {\n if (!channel) return { label: 'unknown', tone: 'secondary', icon: 'PlugZap', badges: [] }\n if (channel.targetHealth?.status === 'error') return { label: 'target broken', tone: 'danger', icon: 'AlertTriangle', badges: [] }\n if (channel.targetHealth?.status === 'warning') return { label: 'target warning', tone: 'warning', icon: 'AlertCircle', badges: [] }\n if (channel.status === 'offline') return { label: 'offline', tone: 'secondary', icon: 'PlugZap', badges: [] }\n if (channel.status === 'busy') return { label: 'busy', tone: 'warning', icon: 'Activity', badges: [] }\n if (!channelIsReady(channel)) return { label: 'not ready', tone: 'warning', icon: 'Loader', badges: [] }\n return { label: 'ready', tone: 'success', icon: 'CircleCheck', badges: [] }\n}\n\nexport function connectorPresence(connector: ConnectorSummary): PresenceInfo {\n const runtime = connector?.runtime || {}\n if (runtime.status === 'error') return { label: 'error', tone: 'danger', icon: 'AlertTriangle', badges: [] }\n if (runtime.status === 'warn') return { label: 'warning', tone: 'warning', icon: 'AlertCircle', badges: [] }\n if (runtime.running) return { label: 'running', tone: 'success', icon: 'CircleCheck', badges: [] }\n if (runtime.enabled === false) return { label: 'disabled', tone: 'secondary', icon: 'Pause', badges: [] }\n if (runtime.status === 'ok') return { label: 'ok', tone: 'success', icon: 'CircleCheck', badges: [] }\n return { label: 'unknown', tone: 'secondary', icon: 'HelpCircle', badges: [] }\n}\n\nexport function integrationPresence(integration: IntegrationSummary): PresenceInfo {\n const stats = integration?.taskStats || {}\n if ((stats.waitingTasks || 0) > 0) return { label: 'waiting', tone: 'warning', icon: 'AlertCircle', badges: [] }\n if ((stats.openTasks || 0) > 0) return { label: 'active', tone: 'info', icon: 'Activity', badges: [] }\n if (!integration?.configured) return { label: 'observed only', tone: 'secondary', icon: 'Eye', badges: [] }\n if (integration.observed) return { label: 'quiet', tone: 'success', icon: 'CircleCheck', badges: [] }\n return { label: 'configured', tone: 'primary', icon: 'PlugZap', badges: [] }\n}\n\nexport function orchestratorHealthLabel(orch: Orchestrator): string {\n const health = orch?.health\n if (!health || health.status === 'ok') return orch?.version || orch?.package?.version ? 'compatible' : 'unknown version'\n if (health.status === 'upgrade-required') return 'upgrade required'\n if (health.status === 'restart-required') return 'restart required'\n if (health.status === 'unknown') return 'unknown contract'\n if ((health.issues || []).some((i) => i.code === 'protocol-mismatch')) return 'protocol mismatch'\n if ((health.issues || []).some((i) => i.code === 'package-drift')) return 'package drift'\n if (health.restartRequired) return 'restart required'\n return health.status\n}\n\nexport function orchestratorHealthClass(orch: Orchestrator): string {\n const status = orch?.health?.status\n if (status === 'upgrade-required') return 'text-red-400 bg-red-500/10'\n if (status === 'warn' || status === 'restart-required') return 'text-yellow-400 bg-yellow-500/10'\n if (status === 'unknown') return 'text-zinc-400 bg-zinc-500/10'\n return 'text-emerald-400 bg-emerald-500/10'\n}\n\nexport function timeAgo(now: number, iso: string | number | null | undefined): string {\n if (!iso) return ''\n const ts = new Date(iso as string).getTime()\n if (!Number.isFinite(ts)) return ''\n const diff = Math.max(0, (now - ts) / 1000)\n if (diff < 60) return Math.floor(diff) + 's ago'\n if (diff < 3600) return Math.floor(diff / 60) + 'm ago'\n if (diff < 86400) return Math.floor(diff / 3600) + 'h ago'\n return Math.floor(diff / 86400) + 'd ago'\n}\n\nexport function fmtTime(iso: string | number | null | undefined): string {\n if (!iso) return ''\n return new Date(iso as string).toLocaleString()\n}\n\nexport function safeFilename(value: string): string {\n return String(value || 'export').replace(/[^a-z0-9._-]+/gi, '-').replace(/^-+|-+$/g, '').slice(0, 80) || 'export'\n}\n\nexport function healthDiagnostics(checks: HealthCheck[]): HealthDiagnostic[] {\n return checks.filter((c) => c.status !== 'ok').map((check) => {\n const base: HealthDiagnostic = {\n name: check.name,\n status: check.status,\n detail: check.detail || check.name,\n impact: healthImpact(check),\n subjects: check.subjects || [],\n actions: [\n { label: 'Inspect logs', icon: 'FileSearch', copy: 'agent-relay daemon logs' },\n { label: 'Restart daemon', icon: 'RefreshCw', copy: 'agent-relay daemon restart' },\n { label: 'Copy env', icon: 'Copy', copy: 'agent-relay doctor' },\n ],\n }\n if (check.name === 'stale-live-agents') {\n base.actions.unshift(\n { label: 'Run reaper', icon: 'Eraser', api: 'POST', path: '/system/reap' },\n { label: 'Show stale', icon: 'Filter', view: 'agents', preset: 'offline_stale' },\n )\n } else if (check.name === 'channel-delivery-targets') {\n base.actions.unshift(\n { label: 'Open channels', icon: 'MessagesSquare', view: 'channels' },\n { label: 'Run reaper', icon: 'Eraser', api: 'POST', path: '/system/reap' },\n )\n } else if (['expired-message-claims', 'expired-task-claims', 'offline-claimed-tasks'].includes(check.name)) {\n base.actions.unshift(\n { label: 'Run reaper', icon: 'Eraser', api: 'POST', path: '/system/reap' },\n { label: 'Open work', icon: 'ListChecks', view: 'work' },\n )\n }\n return base\n })\n}\n\nfunction healthImpact(check: HealthCheck): string {\n const impacts: Record<string, string> = {\n 'database': 'Relay persistence is unavailable; messages, state, and audit writes may fail.',\n 'stale-live-agents': 'Agents may look online even though their heartbeat has stopped.',\n 'expired-message-claims': 'Claimable messages may be stuck until the reaper releases expired claims.',\n 'expired-task-claims': 'Tasks can appear owned by agents that no longer hold a live lease.',\n 'offline-claimed-tasks': 'Offline agents are still shown as owners for active work.',\n 'channel-delivery-targets': 'Inbound channel messages may be accepted but routed to no live delivery agent.',\n }\n return impacts[check.name] || 'Relay health is degraded for this check.'\n}\n\nfunction toneToBadgeVariant(tone: string): 'default' | 'secondary' | 'destructive' | 'outline' {\n if (tone === 'danger' || tone === 'destructive') return 'destructive'\n if (tone === 'secondary') return 'secondary'\n return 'default'\n}\n\nexport function toneToColor(tone: string): string {\n const map: Record<string, string> = {\n success: 'text-emerald-400',\n warning: 'text-yellow-400',\n danger: 'text-red-400',\n info: 'text-blue-400',\n primary: 'text-blue-400',\n secondary: 'text-zinc-400',\n }\n return map[tone] || 'text-zinc-400'\n}\n\nfunction toneToBg(tone: string): string {\n const map: Record<string, string> = {\n success: 'bg-emerald-500',\n warning: 'bg-yellow-500',\n danger: 'bg-red-500',\n info: 'bg-blue-500',\n primary: 'bg-blue-500',\n secondary: 'bg-zinc-500',\n }\n return map[tone] || 'bg-zinc-500'\n}\n\nexport function statusDotColor(agent: Agent | null, now?: number): string {\n if (!agent) return 'bg-zinc-500 opacity-50'\n if (agent.status === 'offline') return 'bg-zinc-500 opacity-50'\n if (typeof agent.meta?.lifecycleAction === 'string' && agent.meta.lifecycleAction.startsWith('finalizing-')) return 'bg-amber-500 animate-pulse'\n if (agent.meta?.lifecycleAction === 'shutting-down' || agent.meta?.lifecycleAction === 'restarting') return 'bg-yellow-500 animate-pulse'\n if (agent.meta?.lifecycleAction === 'killing') return 'bg-red-500 animate-pulse'\n if (providerBlockedState(agent)) return 'bg-red-500 animate-pulse'\n if (agent.status === 'busy') return 'bg-yellow-500'\n if (now && isAgentStale(now, agent)) return 'bg-red-500 animate-pulse'\n if (!agent.ready) return 'bg-emerald-500 animate-pulse'\n return 'bg-emerald-500 shadow-[0_0_6px_theme(colors.emerald.500)]'\n}\n\nexport function downloadText(filename: string, text: string, type: string) {\n if (typeof document === 'undefined' || typeof URL === 'undefined' || typeof Blob === 'undefined') return\n const url = URL.createObjectURL(new Blob([text], { type }))\n const link = document.createElement('a')\n link.href = url\n link.download = filename\n link.click()\n URL.revokeObjectURL(url)\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./formatted-body-impl-DbdFmbwW.js","./chunk-CilyBKbf.js","./index-D9OogaB5.js","./preload-helper-C_JMBXql.js","./dist-B3e4B9pg.js","./dist-B-T7lg0a.js","./lucide-react-DuhlF2Sj.js","./dist-BIjlN-xB.js","./dist-B9G83DXv.js","./react-dom-DWBmcawP.js","./dist-DT-OV6sQ.js","./dist-C7aTlosb.js","./dist-BiLuYU6G.js","./dist-BInckSJC.js","./dist-DGfp5dLz.js","./es2015-CnKXYKpf.js","./dist-BlzVGRhh.js","./dist-OUPTEP6q.js","./dist-B6woQ9Tb.js","./store-DKTLubBT.js","./display-exQDWum3.js","./types-uVOXgvMT.js","./agent-type-icon-CeEoMouY.js","./copy-button-B3o1jpLu.js","./button-CzdTkRIf.js","./task-chip-DXfvXBDx.js","./badge-DCIT3Irj.js","./input-B6Q0NWCS.js","./themes-DjSwmAZN.js","./index-DcmG5gLR.css","./lib-BHsWPMKB.js","./lib-Cc7Il-6i.js"])))=>i.map(i=>d[i]);
2
- import{r as e}from"./chunk-CilyBKbf.js";import{hr as t,nr as n}from"./lucide-react-DuhlF2Sj.js";import{t as r}from"./preload-helper-C_JMBXql.js";var i=e(t(),1),a=n(),o=(0,i.lazy)(()=>r(()=>import(`./formatted-body-impl-DbdFmbwW.js`).then(e=>({default:e.FormattedBody})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]),import.meta.url));function s(e){return(0,a.jsx)(i.Suspense,{fallback:(0,a.jsx)(`span`,{className:e.className,children:e.text}),children:(0,a.jsx)(o,{...e})})}export{s as t};
3
- //# sourceMappingURL=formatted-body-CmkuD23M.js.map
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./formatted-body-impl-RmDxI5Ux.js","./chunk-CilyBKbf.js","./index-D0xMQQb_.js","./preload-helper-C_JMBXql.js","./dist-B3e4B9pg.js","./dist-B-T7lg0a.js","./lucide-react-DuhlF2Sj.js","./dist-BIjlN-xB.js","./dist-B9G83DXv.js","./react-dom-DWBmcawP.js","./dist-DT-OV6sQ.js","./dist-C7aTlosb.js","./dist-BiLuYU6G.js","./dist-BInckSJC.js","./dist-DGfp5dLz.js","./es2015-CnKXYKpf.js","./dist-BlzVGRhh.js","./dist-OUPTEP6q.js","./dist-B6woQ9Tb.js","./store-BPrVuKiv.js","./display-CJzSoTMq.js","./types-BeJUSfDj.js","./agent-type-icon-0ZJRP1Vy.js","./copy-button-B3o1jpLu.js","./button-CzdTkRIf.js","./task-chip-DXfvXBDx.js","./badge-DCIT3Irj.js","./input-B6Q0NWCS.js","./themes-DjSwmAZN.js","./index-DcmG5gLR.css","./lib-BHsWPMKB.js","./lib-Cc7Il-6i.js"])))=>i.map(i=>d[i]);
2
+ import{r as e}from"./chunk-CilyBKbf.js";import{hr as t,nr as n}from"./lucide-react-DuhlF2Sj.js";import{t as r}from"./preload-helper-C_JMBXql.js";var i=e(t(),1),a=n(),o=(0,i.lazy)(()=>r(()=>import(`./formatted-body-impl-RmDxI5Ux.js`).then(e=>({default:e.FormattedBody})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]),import.meta.url));function s(e){return(0,a.jsx)(i.Suspense,{fallback:(0,a.jsx)(`span`,{className:e.className,children:e.text}),children:(0,a.jsx)(o,{...e})})}export{s as t};
3
+ //# sourceMappingURL=formatted-body-35XBOY5L.js.map
@@ -1 +1 @@
1
- {"version":3,"mappings":";sKASM,uBACJ,OAAO,qCAAyB,KAAM,IAAO,CAAE,QAAS,EAAE,cAAe,EAAE,2HAC5E,CAID,SAAgB,EAAc,EAA2B,CACvD,OACE,SAAC,WAAD,CAAU,UAAU,SAAC,OAAD,CAAM,UAAW,EAAM,mBAAY,EAAM,KAAY,YACvE,SAAC,EAAD,CAAmB,GAAI,EAAS,EACvB","names":[],"ignoreList":[],"sources":["../../dashboard/src/components/shared/formatted-body.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react'\nimport type { FormattedBodyProps } from './formatted-body-impl'\n\n// Lazy boundary for react-markdown (#201). FormattedBody is statically imported\n// by the always-mounted ThreadModal, which would otherwise drag react-markdown +\n// remark/rehype plugins into the initial bundle. Behind this boundary the\n// markdown chunk loads on first actual render (thread opened, or chat/messages\n// view navigated to). The fallback renders the plain text so there's no blank\n// flash while the chunk resolves on first use.\nconst LazyFormattedBody = lazy(() =>\n import('./formatted-body-impl').then((m) => ({ default: m.FormattedBody })),\n)\n\nexport type { FormattedBodyProps }\n\nexport function FormattedBody(props: FormattedBodyProps) {\n return (\n <Suspense fallback={<span className={props.className}>{props.text}</span>}>\n <LazyFormattedBody {...props} />\n </Suspense>\n )\n}\n"],"file":"formatted-body-CmkuD23M.js"}
1
+ {"version":3,"mappings":";sKASM,uBACJ,OAAO,qCAAyB,KAAM,IAAO,CAAE,QAAS,EAAE,cAAe,EAAE,2HAC5E,CAID,SAAgB,EAAc,EAA2B,CACvD,OACE,SAAC,WAAD,CAAU,UAAU,SAAC,OAAD,CAAM,UAAW,EAAM,mBAAY,EAAM,KAAY,YACvE,SAAC,EAAD,CAAmB,GAAI,EAAS,EACvB","names":[],"ignoreList":[],"sources":["../../dashboard/src/components/shared/formatted-body.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react'\nimport type { FormattedBodyProps } from './formatted-body-impl'\n\n// Lazy boundary for react-markdown (#201). FormattedBody is statically imported\n// by the always-mounted ThreadModal, which would otherwise drag react-markdown +\n// remark/rehype plugins into the initial bundle. Behind this boundary the\n// markdown chunk loads on first actual render (thread opened, or chat/messages\n// view navigated to). The fallback renders the plain text so there's no blank\n// flash while the chunk resolves on first use.\nconst LazyFormattedBody = lazy(() =>\n import('./formatted-body-impl').then((m) => ({ default: m.FormattedBody })),\n)\n\nexport type { FormattedBodyProps }\n\nexport function FormattedBody(props: FormattedBodyProps) {\n return (\n <Suspense fallback={<span className={props.className}>{props.text}</span>}>\n <LazyFormattedBody {...props} />\n </Suspense>\n )\n}\n"],"file":"formatted-body-35XBOY5L.js"}
@@ -1,3 +1,3 @@
1
- import{r as e}from"./chunk-CilyBKbf.js";import{er as t,hr as n,nr as r}from"./lucide-react-DuhlF2Sj.js";import{t as i}from"./store-DKTLubBT.js";import{L as a}from"./display-exQDWum3.js";import{t as o}from"./task-chip-DXfvXBDx.js";import{X as s,ft as c}from"./index-D9OogaB5.js";import{i as l,n as u,r as d,t as f}from"./lib-BHsWPMKB.js";var p=e(n(),1),m=r(),h=/((?:[A-Za-z0-9][\w.-]*\/[A-Za-z0-9][\w.-]*)?#\d+)\b|(https?:\/\/[^\s`'")\]]+)|(\bT\d+\b(?!\/))|((?:\/[\w.@+-][^\s`'")\]]*|(?:\.{1,2}\/|[\w.@+-]+\/)[^\s`'")\]]*))/g,g=/\bT(\d+)\b(?!\/)/g,_=/((?:[A-Za-z0-9][\w.-]*\/[A-Za-z0-9][\w.-]*)?#(\d+))\b/g,v=/[.,;:!?]+$/,y=new Set([`src`,`app`,`apps`,`lib`,`server`,`client`,`dashboard`,`orchestrator`,`runner`,`sdk`,`codex`,`claude`,`public`,`docs`,`test`,`tests`,`scripts`,`config`,`configs`]),b={...d,attributes:{...d.attributes,code:[...d.attributes?.code||[],[`className`,/^language-[\w-]+$/]]}};function x(e){if(e===`/api`||e.startsWith(`/api/`))return!1;if(e.startsWith(`/`)||e.startsWith(`./`)||e.startsWith(`../`))return!0;let t=e.split(`/`).filter(Boolean),n=t[0]??``,r=t[t.length-1]??``;return n.startsWith(`.`)||y.has(n)?!0:r.includes(`.`)}function S(e){if(!e||typeof e!=`object`)return null;let t=e,n=typeof t.id==`number`?t.id:typeof t.id==`string`?Number(t.id):NaN;if(!Number.isSafeInteger(n)||n<=0)return null;let r=typeof t.title==`string`&&t.title.trim()?t.title:typeof t.label==`string`&&t.label.trim()?t.label:void 0,i=typeof t.status==`string`&&t.status.trim()?t.status:void 0;return{id:n,meta:{...r?{title:r}:{},...i?{status:i}:{}}}}function C(e){let t=new Set;for(let n of e.matchAll(g)){let e=Number(n[1]);Number.isSafeInteger(e)&&e>0&&t.add(e)}return[...t].sort((e,t)=>e-t)}function w(e,t,n){let r=new Set(e),i=new Map;if(r.size===0)return i;for(let e of t){let t=S(e);t&&r.has(t.id)&&i.set(t.id,t.meta)}for(let e of n){let t=S(e);t&&r.has(t.id)&&i.set(t.id,t.meta)}return i}function T(e,t,n){if(e.length===0)return``;let r=w(e,t,n);return e.map(e=>{let t=r.get(e);return`${e}:${t?.title??``}:${t?.status??``}`}).join(`|`)}function E(e,t){let n=new Set;for(let r of e.matchAll(_)){let e=r[1],i=r[2];if(!e||!i)continue;let a=e.lastIndexOf(`#`),o=e.slice(0,a)||t;o&&n.add(`${o}#${i}`)}return[...n].sort()}function D(e,t){let n=new Set(e),r=new Map;if(n.size===0)return r;for(let e of Object.values(t)){let t=`${e.repo}#${e.externalKey}`;n.has(t)&&r.set(t,e)}return r}function O(e,t){if(e.length===0)return``;let n=D(e,t);return e.map(e=>{let t=n.get(e);return`${e}:${t?.id??``}:${t?.status??``}:${t?.updatedAt??``}:${t?.url??``}`}).join(`|`)}function k(e,t,n,r){let i=[],a=0;for(let n of e.matchAll(h)){let o=n[1],s=n[2],c=n[3],l=n[4],u=o||s||c||l,d=n.index??0;if(!u||d<a)continue;if(c){let t=Number(c.slice(1));Number.isSafeInteger(t)&&t>0&&(d>a&&i.push({text:e.slice(a,d)}),i.push({text:c,taskId:t}),a=d+c.length);continue}if(o){let n=o.lastIndexOf(`#`),s=o.slice(0,n)||t,c=o.slice(n+1),l=e.slice(d+o.length).match(/^(?:\/\d+)+/)?.[0]??``;if(s){d>a&&i.push({text:e.slice(a,d)});let t=r?.get(`${s}#${c}`);i.push({text:o,issueUrl:`https://github.com/${s}/issues/${c}`,...t?{issueRef:t}:{}}),l&&i.push({text:l}),a=d+o.length+l.length}else l&&(d>a&&i.push({text:e.slice(a,d)}),i.push({text:o+l}),a=d+o.length+l.length);continue}if(s){let t=s.replace(v,``),n=s.slice(t.length);d>a&&i.push({text:e.slice(a,d)}),i.push({text:t,url:t}),n&&i.push({text:n}),a=d+s.length;continue}if(!u.includes(`/`))continue;let f=u.replace(v,``),p=u.slice(f.length),m=f.match(/^(.*):(\d+)$/),h=m?Number(m[2]):void 0;m&&(f=m[1]),x(f)&&(d>a&&i.push({text:e.slice(a,d)}),i.push({text:f,path:f,line:Number.isFinite(h)?h:void 0}),p&&i.push({text:p}),a=d+u.length)}return a<e.length&&i.push({text:e.slice(a)}),i}function A({text:e,onOpenPath:t,onPreviewPath:n,onPreviewPathEnd:r,isPathLinkable:i,resolvePathTitle:a,issueRepo:c,onOpenTask:l,loadedTasksById:u,issueRefsByKey:d}){let f=(0,p.useMemo)(()=>k(e,c,u,d),[e,c,u,d]);return f.length===1&&!f[0]?.path&&!f[0]?.url&&!f[0]?.issueUrl&&!f[0]?.taskId?(0,m.jsx)(m.Fragment,{children:e}):(0,m.jsx)(m.Fragment,{children:f.map((e,c)=>{if(e.taskId!=null){if(l){let t=u?.get(e.taskId);return(0,m.jsx)(o,{id:e.taskId,title:t?.title,status:t?.status,compact:!0,onClick:()=>l(e.taskId)},`task-${e.taskId}-${c}`)}return(0,m.jsx)(`span`,{children:e.text},c)}if(e.issueUrl)return e.issueRef?(0,m.jsx)(s,{issueRef:e.issueRef,compact:!0},`${e.issueUrl}-${c}`):(0,m.jsx)(`a`,{href:e.issueUrl,target:`_blank`,rel:`noreferrer`,className:`font-medium underline decoration-dotted underline-offset-2`,onClick:e=>e.stopPropagation(),children:e.text},`${e.issueUrl}-${c}`);if(e.url)return(0,m.jsx)(`a`,{href:e.url,target:`_blank`,rel:`noreferrer`,className:`underline decoration-dotted underline-offset-2`,onClick:e=>e.stopPropagation(),children:e.text},`${e.url}-${c}`);if(e.path&&t){if(i&&!i(e.path,e.line))return(0,m.jsxs)(`span`,{children:[e.text,e.line?`:${e.line}`:``]},`${e.path}-${c}`);let o=a?.(e.path,e.line);return(0,m.jsxs)(`button`,{type:`button`,className:`chat-link rounded-sm font-mono underline decoration-dotted underline-offset-2`,onClick:n=>{n.stopPropagation(),t(e.path,e.line)},onMouseEnter:t=>n?.(e.path,e.line,t.currentTarget.getBoundingClientRect()),onFocus:t=>n?.(e.path,e.line,t.currentTarget.getBoundingClientRect()),onMouseLeave:()=>r?.(),onBlur:()=>r?.(),title:o?`Open ${o}`:`Open file`,children:[e.text,e.line?`:${e.line}`:``]},`${e.path}-${c}`)}return(0,m.jsx)(`span`,{children:e.text},c)})})}var j=({className:e,children:t})=>{let n=/language-([\w-]+)/.exec(e||``),r=String(t).replace(/\n$/,``);return n?(0,m.jsx)(c,{content:r,path:`.${n[1]}`,languageHint:n[1],compact:!0}):(0,m.jsx)(`code`,{className:e,children:t})},M=({href:e,children:t})=>(0,m.jsx)(`a`,{href:e,target:`_blank`,rel:`noreferrer`,onClick:e=>e.stopPropagation(),children:t});function N(e,t,n=!1){return p.Children.map(e,e=>{if(typeof e==`string`)return n?e:(0,m.jsx)(A,{text:e,onOpenPath:t.onOpenPath,onPreviewPath:t.onPreviewPath,onPreviewPathEnd:t.onPreviewPathEnd,isPathLinkable:t.isPathLinkable,resolvePathTitle:t.resolvePathTitle,issueRepo:t.issueRepo,onOpenTask:t.onOpenTask,loadedTasksById:t.loadedTasksById,issueRefsByKey:t.issueRefsByKey});if(!p.isValidElement(e))return e;let r=e.type;if(r===A||r===M||r===`a`)return e;let i=e.props;if(i?.children==null)return e;let a=n||r===j||r===`code`||r===`pre`;return p.cloneElement(e,void 0,N(i.children,t,a))})}function P({text:e,rawBody:n,className:r,onOpenPath:o,onPreviewPath:s,onPreviewPathEnd:c,isPathLinkable:d,resolvePathTitle:h,issueRepo:g,onPrimary:_,onOpenTask:v}){let y=(0,p.useMemo)(()=>a(n||``),[n]),x=F(y?y.text:e),S=i(e=>e.stats?.defaultIssueRepo),k=g??S,A=(0,p.useMemo)(()=>C(x),[x]),P=(0,p.useMemo)(()=>E(x,k),[x,k]),I=i(e=>T(A,e.tasks,e.deliverableTasks)),L=i(e=>e.openTaskDetailById),R=i(e=>O(P,e.issueRefsById)),z=(0,p.useMemo)(()=>{let e=i.getState();return w(A,e.tasks,e.deliverableTasks)},[A,I]),B=(0,p.useMemo)(()=>D(P,i.getState().issueRefsById),[P,R]),V=v??L,H=(0,p.useMemo)(()=>{let e={onOpenPath:o,onPreviewPath:s,onPreviewPathEnd:c,isPathLinkable:d,resolvePathTitle:h,issueRepo:k,onOpenTask:V,loadedTasksById:z,issueRefsByKey:B},t=t=>({children:n})=>(0,m.jsx)(t,{children:N(n,e)});return{p:t(`p`),li:t(`li`),h1:t(`h1`),h2:t(`h2`),h3:t(`h3`),h4:t(`h4`),h5:t(`h5`),h6:t(`h6`),td:t(`td`),th:t(`th`),blockquote:t(`blockquote`),a:M,code:j}},[o,s,c,d,h,k,V,z,B]),U=(0,p.useMemo)(()=>(0,m.jsx)(l,{remarkPlugins:[f],rehypePlugins:[[u,b]],components:H,children:x}),[H,x]);return(0,m.jsxs)(`div`,{className:r,children:[(0,m.jsx)(`div`,{className:t(`chat-markdown`,_&&`chat-markdown-on-primary`),children:U}),y&&y.meta.length>0&&(0,m.jsx)(`div`,{className:`mt-1.5 flex flex-wrap gap-1`,children:y.meta.map(([e,t])=>(0,m.jsxs)(`span`,{className:`inline-flex text-[10px] leading-tight bg-muted px-1.5 py-0.5 rounded text-muted-foreground font-mono`,children:[e,`: `,t]},e))})]})}function F(e){return e.includes(`\\n`)?e.replace(/\\n/g,`
1
+ import{r as e}from"./chunk-CilyBKbf.js";import{er as t,hr as n,nr as r}from"./lucide-react-DuhlF2Sj.js";import{t as i}from"./store-BPrVuKiv.js";import{L as a}from"./display-CJzSoTMq.js";import{t as o}from"./task-chip-DXfvXBDx.js";import{X as s,ft as c}from"./index-D0xMQQb_.js";import{i as l,n as u,r as d,t as f}from"./lib-BHsWPMKB.js";var p=e(n(),1),m=r(),h=/((?:[A-Za-z0-9][\w.-]*\/[A-Za-z0-9][\w.-]*)?#\d+)\b|(https?:\/\/[^\s`'")\]]+)|(\bT\d+\b(?!\/))|((?:\/[\w.@+-][^\s`'")\]]*|(?:\.{1,2}\/|[\w.@+-]+\/)[^\s`'")\]]*))/g,g=/\bT(\d+)\b(?!\/)/g,_=/((?:[A-Za-z0-9][\w.-]*\/[A-Za-z0-9][\w.-]*)?#(\d+))\b/g,v=/[.,;:!?]+$/,y=new Set([`src`,`app`,`apps`,`lib`,`server`,`client`,`dashboard`,`orchestrator`,`runner`,`sdk`,`codex`,`claude`,`public`,`docs`,`test`,`tests`,`scripts`,`config`,`configs`]),b={...d,attributes:{...d.attributes,code:[...d.attributes?.code||[],[`className`,/^language-[\w-]+$/]]}};function x(e){if(e===`/api`||e.startsWith(`/api/`))return!1;if(e.startsWith(`/`)||e.startsWith(`./`)||e.startsWith(`../`))return!0;let t=e.split(`/`).filter(Boolean),n=t[0]??``,r=t[t.length-1]??``;return n.startsWith(`.`)||y.has(n)?!0:r.includes(`.`)}function S(e){if(!e||typeof e!=`object`)return null;let t=e,n=typeof t.id==`number`?t.id:typeof t.id==`string`?Number(t.id):NaN;if(!Number.isSafeInteger(n)||n<=0)return null;let r=typeof t.title==`string`&&t.title.trim()?t.title:typeof t.label==`string`&&t.label.trim()?t.label:void 0,i=typeof t.status==`string`&&t.status.trim()?t.status:void 0;return{id:n,meta:{...r?{title:r}:{},...i?{status:i}:{}}}}function C(e){let t=new Set;for(let n of e.matchAll(g)){let e=Number(n[1]);Number.isSafeInteger(e)&&e>0&&t.add(e)}return[...t].sort((e,t)=>e-t)}function w(e,t,n){let r=new Set(e),i=new Map;if(r.size===0)return i;for(let e of t){let t=S(e);t&&r.has(t.id)&&i.set(t.id,t.meta)}for(let e of n){let t=S(e);t&&r.has(t.id)&&i.set(t.id,t.meta)}return i}function T(e,t,n){if(e.length===0)return``;let r=w(e,t,n);return e.map(e=>{let t=r.get(e);return`${e}:${t?.title??``}:${t?.status??``}`}).join(`|`)}function E(e,t){let n=new Set;for(let r of e.matchAll(_)){let e=r[1],i=r[2];if(!e||!i)continue;let a=e.lastIndexOf(`#`),o=e.slice(0,a)||t;o&&n.add(`${o}#${i}`)}return[...n].sort()}function D(e,t){let n=new Set(e),r=new Map;if(n.size===0)return r;for(let e of Object.values(t)){let t=`${e.repo}#${e.externalKey}`;n.has(t)&&r.set(t,e)}return r}function O(e,t){if(e.length===0)return``;let n=D(e,t);return e.map(e=>{let t=n.get(e);return`${e}:${t?.id??``}:${t?.status??``}:${t?.updatedAt??``}:${t?.url??``}`}).join(`|`)}function k(e,t,n,r){let i=[],a=0;for(let n of e.matchAll(h)){let o=n[1],s=n[2],c=n[3],l=n[4],u=o||s||c||l,d=n.index??0;if(!u||d<a)continue;if(c){let t=Number(c.slice(1));Number.isSafeInteger(t)&&t>0&&(d>a&&i.push({text:e.slice(a,d)}),i.push({text:c,taskId:t}),a=d+c.length);continue}if(o){let n=o.lastIndexOf(`#`),s=o.slice(0,n)||t,c=o.slice(n+1),l=e.slice(d+o.length).match(/^(?:\/\d+)+/)?.[0]??``;if(s){d>a&&i.push({text:e.slice(a,d)});let t=r?.get(`${s}#${c}`);i.push({text:o,issueUrl:`https://github.com/${s}/issues/${c}`,...t?{issueRef:t}:{}}),l&&i.push({text:l}),a=d+o.length+l.length}else l&&(d>a&&i.push({text:e.slice(a,d)}),i.push({text:o+l}),a=d+o.length+l.length);continue}if(s){let t=s.replace(v,``),n=s.slice(t.length);d>a&&i.push({text:e.slice(a,d)}),i.push({text:t,url:t}),n&&i.push({text:n}),a=d+s.length;continue}if(!u.includes(`/`))continue;let f=u.replace(v,``),p=u.slice(f.length),m=f.match(/^(.*):(\d+)$/),h=m?Number(m[2]):void 0;m&&(f=m[1]),x(f)&&(d>a&&i.push({text:e.slice(a,d)}),i.push({text:f,path:f,line:Number.isFinite(h)?h:void 0}),p&&i.push({text:p}),a=d+u.length)}return a<e.length&&i.push({text:e.slice(a)}),i}function A({text:e,onOpenPath:t,onPreviewPath:n,onPreviewPathEnd:r,isPathLinkable:i,resolvePathTitle:a,issueRepo:c,onOpenTask:l,loadedTasksById:u,issueRefsByKey:d}){let f=(0,p.useMemo)(()=>k(e,c,u,d),[e,c,u,d]);return f.length===1&&!f[0]?.path&&!f[0]?.url&&!f[0]?.issueUrl&&!f[0]?.taskId?(0,m.jsx)(m.Fragment,{children:e}):(0,m.jsx)(m.Fragment,{children:f.map((e,c)=>{if(e.taskId!=null){if(l){let t=u?.get(e.taskId);return(0,m.jsx)(o,{id:e.taskId,title:t?.title,status:t?.status,compact:!0,onClick:()=>l(e.taskId)},`task-${e.taskId}-${c}`)}return(0,m.jsx)(`span`,{children:e.text},c)}if(e.issueUrl)return e.issueRef?(0,m.jsx)(s,{issueRef:e.issueRef,compact:!0},`${e.issueUrl}-${c}`):(0,m.jsx)(`a`,{href:e.issueUrl,target:`_blank`,rel:`noreferrer`,className:`font-medium underline decoration-dotted underline-offset-2`,onClick:e=>e.stopPropagation(),children:e.text},`${e.issueUrl}-${c}`);if(e.url)return(0,m.jsx)(`a`,{href:e.url,target:`_blank`,rel:`noreferrer`,className:`underline decoration-dotted underline-offset-2`,onClick:e=>e.stopPropagation(),children:e.text},`${e.url}-${c}`);if(e.path&&t){if(i&&!i(e.path,e.line))return(0,m.jsxs)(`span`,{children:[e.text,e.line?`:${e.line}`:``]},`${e.path}-${c}`);let o=a?.(e.path,e.line);return(0,m.jsxs)(`button`,{type:`button`,className:`chat-link rounded-sm font-mono underline decoration-dotted underline-offset-2`,onClick:n=>{n.stopPropagation(),t(e.path,e.line)},onMouseEnter:t=>n?.(e.path,e.line,t.currentTarget.getBoundingClientRect()),onFocus:t=>n?.(e.path,e.line,t.currentTarget.getBoundingClientRect()),onMouseLeave:()=>r?.(),onBlur:()=>r?.(),title:o?`Open ${o}`:`Open file`,children:[e.text,e.line?`:${e.line}`:``]},`${e.path}-${c}`)}return(0,m.jsx)(`span`,{children:e.text},c)})})}var j=({className:e,children:t})=>{let n=/language-([\w-]+)/.exec(e||``),r=String(t).replace(/\n$/,``);return n?(0,m.jsx)(c,{content:r,path:`.${n[1]}`,languageHint:n[1],compact:!0}):(0,m.jsx)(`code`,{className:e,children:t})},M=({href:e,children:t})=>(0,m.jsx)(`a`,{href:e,target:`_blank`,rel:`noreferrer`,onClick:e=>e.stopPropagation(),children:t});function N(e,t,n=!1){return p.Children.map(e,e=>{if(typeof e==`string`)return n?e:(0,m.jsx)(A,{text:e,onOpenPath:t.onOpenPath,onPreviewPath:t.onPreviewPath,onPreviewPathEnd:t.onPreviewPathEnd,isPathLinkable:t.isPathLinkable,resolvePathTitle:t.resolvePathTitle,issueRepo:t.issueRepo,onOpenTask:t.onOpenTask,loadedTasksById:t.loadedTasksById,issueRefsByKey:t.issueRefsByKey});if(!p.isValidElement(e))return e;let r=e.type;if(r===A||r===M||r===`a`)return e;let i=e.props;if(i?.children==null)return e;let a=n||r===j||r===`code`||r===`pre`;return p.cloneElement(e,void 0,N(i.children,t,a))})}function P({text:e,rawBody:n,className:r,onOpenPath:o,onPreviewPath:s,onPreviewPathEnd:c,isPathLinkable:d,resolvePathTitle:h,issueRepo:g,onPrimary:_,onOpenTask:v}){let y=(0,p.useMemo)(()=>a(n||``),[n]),x=F(y?y.text:e),S=i(e=>e.stats?.defaultIssueRepo),k=g??S,A=(0,p.useMemo)(()=>C(x),[x]),P=(0,p.useMemo)(()=>E(x,k),[x,k]),I=i(e=>T(A,e.tasks,e.deliverableTasks)),L=i(e=>e.openTaskDetailById),R=i(e=>O(P,e.issueRefsById)),z=(0,p.useMemo)(()=>{let e=i.getState();return w(A,e.tasks,e.deliverableTasks)},[A,I]),B=(0,p.useMemo)(()=>D(P,i.getState().issueRefsById),[P,R]),V=v??L,H=(0,p.useMemo)(()=>{let e={onOpenPath:o,onPreviewPath:s,onPreviewPathEnd:c,isPathLinkable:d,resolvePathTitle:h,issueRepo:k,onOpenTask:V,loadedTasksById:z,issueRefsByKey:B},t=t=>({children:n})=>(0,m.jsx)(t,{children:N(n,e)});return{p:t(`p`),li:t(`li`),h1:t(`h1`),h2:t(`h2`),h3:t(`h3`),h4:t(`h4`),h5:t(`h5`),h6:t(`h6`),td:t(`td`),th:t(`th`),blockquote:t(`blockquote`),a:M,code:j}},[o,s,c,d,h,k,V,z,B]),U=(0,p.useMemo)(()=>(0,m.jsx)(l,{remarkPlugins:[f],rehypePlugins:[[u,b]],components:H,children:x}),[H,x]);return(0,m.jsxs)(`div`,{className:r,children:[(0,m.jsx)(`div`,{className:t(`chat-markdown`,_&&`chat-markdown-on-primary`),children:U}),y&&y.meta.length>0&&(0,m.jsx)(`div`,{className:`mt-1.5 flex flex-wrap gap-1`,children:y.meta.map(([e,t])=>(0,m.jsxs)(`span`,{className:`inline-flex text-[10px] leading-tight bg-muted px-1.5 py-0.5 rounded text-muted-foreground font-mono`,children:[e,`: `,t]},e))})]})}function F(e){return e.includes(`\\n`)?e.replace(/\\n/g,`
2
2
  `):e}export{P as FormattedBody};
3
- //# sourceMappingURL=formatted-body-impl-DbdFmbwW.js.map
3
+ //# sourceMappingURL=formatted-body-impl-RmDxI5Ux.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatted-body-impl-DbdFmbwW.js","names":[],"sources":["../../dashboard/src/components/shared/formatted-body-impl.tsx"],"sourcesContent":["import React, { useMemo } from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport rehypeSanitize, { defaultSchema } from 'rehype-sanitize'\nimport remarkGfm from 'remark-gfm'\nimport { CodePreview } from './code-preview'\nimport { IssueRefChip } from './issue-ref-chip'\nimport { TaskChip } from './task-chip'\nimport { cn } from '@/lib/utils'\nimport { parseJsonBody } from '@/lib/display'\nimport { useRelayStore } from '@/store'\nimport type { IssueRef } from '@/lib/issue-types'\n\ntype LoadedTaskChip = { title?: string; status?: string }\n\nexport interface FormattedBodyProps {\n text: string\n rawBody?: string\n className?: string\n onOpenPath?: (path: string, line?: number) => void\n onPreviewPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewPathEnd?: () => void\n isPathLinkable?: (path: string, line?: number) => boolean\n resolvePathTitle?: (path: string, line?: number) => string | undefined\n /** Per-message tracker repo (`owner/repo`) for bare `#NNN` linkify (#533) — the originating\n * agent's project repo, resolved server-side. Falls back to the server-global default (#511). */\n issueRepo?: string\n /** Rendered inside a primary-background bubble (operator's outbound message). Flips link\n * color to primary-foreground so links stay legible against the accent bg (#612). */\n onPrimary?: boolean\n /** Override handler for clicking a resolved T<NNN> chip. Defaults to the store's\n * openTaskDetailById so callers rarely need to pass this explicitly. */\n onOpenTask?: (id: number) => void\n}\n\n// Issue-ref is FIRST so the path matcher can't swallow `owner/repo#NNN` as a file path.\n// URLs still win when they start earlier (leftmost-match), so a URL fragment like\n// `…/a#12` is never mis-linkified.\n// T<NNN> (group 3) is matched before the path group but only when NOT followed by `/`\n// (negative lookahead) so `T128/foo.ts` still routes to the file-path group (#873).\nconst LINK_RE = /((?:[A-Za-z0-9][\\w.-]*\\/[A-Za-z0-9][\\w.-]*)?#\\d+)\\b|(https?:\\/\\/[^\\s`'\")\\]]+)|(\\bT\\d+\\b(?!\\/))|((?:\\/[\\w.@+-][^\\s`'\")\\]]*|(?:\\.{1,2}\\/|[\\w.@+-]+\\/)[^\\s`'\")\\]]*))/g\nconst TASK_REF_RE = /\\bT(\\d+)\\b(?!\\/)/g\nconst ISSUE_REF_RE = /((?:[A-Za-z0-9][\\w.-]*\\/[A-Za-z0-9][\\w.-]*)?#(\\d+))\\b/g\nconst TRAILING_PUNCTUATION = /[.,;:!?]+$/\nconst KNOWN_PROJECT_DIRS = new Set(['src', 'app', 'apps', 'lib', 'server', 'client', 'dashboard', 'orchestrator', 'runner', 'sdk', 'codex', 'claude', 'public', 'docs', 'test', 'tests', 'scripts', 'config', 'configs'])\n\nconst markdownSchema = {\n ...defaultSchema,\n attributes: {\n ...defaultSchema.attributes,\n code: [\n ...(defaultSchema.attributes?.code || []),\n ['className', /^language-[\\w-]+$/],\n ],\n },\n}\n\nfunction looksLikeFilePath(path: string): boolean {\n if (path === '/api' || path.startsWith('/api/')) return false\n if (path.startsWith('/')) return true\n if (path.startsWith('./') || path.startsWith('../')) return true\n const segments = path.split('/').filter(Boolean)\n const first = segments[0] ?? ''\n const last = segments[segments.length - 1] ?? ''\n if (first.startsWith('.')) return true\n if (KNOWN_PROJECT_DIRS.has(first)) return true\n return last.includes('.')\n}\n\nfunction taskChipMeta(task: unknown): { id: number; meta: LoadedTaskChip } | null {\n if (!task || typeof task !== 'object') return null\n const record = task as Record<string, unknown>\n const id = typeof record.id === 'number' ? record.id : typeof record.id === 'string' ? Number(record.id) : NaN\n if (!Number.isSafeInteger(id) || id <= 0) return null\n const title = typeof record.title === 'string' && record.title.trim()\n ? record.title\n : typeof record.label === 'string' && record.label.trim()\n ? record.label\n : undefined\n const status = typeof record.status === 'string' && record.status.trim() ? record.status : undefined\n return { id, meta: { ...(title ? { title } : {}), ...(status ? { status } : {}) } }\n}\n\nfunction extractTaskRefIds(text: string): number[] {\n const ids = new Set<number>()\n for (const match of text.matchAll(TASK_REF_RE)) {\n const id = Number(match[1])\n if (Number.isSafeInteger(id) && id > 0) ids.add(id)\n }\n return [...ids].sort((a, b) => a - b)\n}\n\nfunction collectLoadedTasksById(ids: number[], tasks: unknown[], deliverableTasks: unknown[]): Map<number, LoadedTaskChip> {\n const wanted = new Set(ids)\n const map = new Map<number, LoadedTaskChip>()\n if (wanted.size === 0) return map\n for (const task of tasks) {\n const resolved = taskChipMeta(task)\n if (resolved && wanted.has(resolved.id)) map.set(resolved.id, resolved.meta)\n }\n for (const task of deliverableTasks) {\n const resolved = taskChipMeta(task)\n if (resolved && wanted.has(resolved.id)) map.set(resolved.id, resolved.meta)\n }\n return map\n}\n\nfunction taskMetaVersion(ids: number[], tasks: unknown[], deliverableTasks: unknown[]): string {\n if (ids.length === 0) return ''\n const loaded = collectLoadedTasksById(ids, tasks, deliverableTasks)\n return ids.map((id) => {\n const meta = loaded.get(id)\n return `${id}:${meta?.title ?? ''}:${meta?.status ?? ''}`\n }).join('|')\n}\n\nfunction extractIssueRefKeys(text: string, issueRepo?: string): string[] {\n const keys = new Set<string>()\n for (const match of text.matchAll(ISSUE_REF_RE)) {\n const raw = match[1]\n const num = match[2]\n if (!raw || !num) continue\n const hashIdx = raw.lastIndexOf('#')\n const repo = raw.slice(0, hashIdx) || issueRepo\n if (repo) keys.add(`${repo}#${num}`)\n }\n return [...keys].sort()\n}\n\nfunction collectIssueRefsByKey(keys: string[], issueRefsById: Record<string, IssueRef>): Map<string, IssueRef> {\n const wanted = new Set(keys)\n const map = new Map<string, IssueRef>()\n if (wanted.size === 0) return map\n for (const ref of Object.values(issueRefsById)) {\n const key = `${ref.repo}#${ref.externalKey}`\n if (wanted.has(key)) map.set(key, ref)\n }\n return map\n}\n\nfunction issueRefsVersion(keys: string[], issueRefsById: Record<string, IssueRef>): string {\n if (keys.length === 0) return ''\n const refs = collectIssueRefsByKey(keys, issueRefsById)\n return keys.map((key) => {\n const ref = refs.get(key)\n return `${key}:${ref?.id ?? ''}:${ref?.status ?? ''}:${ref?.updatedAt ?? ''}:${ref?.url ?? ''}`\n }).join('|')\n}\n\nexport function linkParts(\n text: string,\n issueRepo?: string,\n /** Optional loaded task metadata map/set. `T<id>` refs link by pattern even when absent. */\n _knownTaskIds?: { has: (id: number) => boolean },\n /** Map from `repo#externalKey` (e.g. `edimuj/agent-relay#888`) to the full IssueRef.\n * When provided and the ref resolves, the part gets `issueRef` for chip rendering. */\n issueRefsByKey?: Map<string, IssueRef>,\n): Array<{ text: string; path?: string; url?: string; line?: number; issueUrl?: string; taskId?: number; issueRef?: IssueRef }> {\n const parts: Array<{ text: string; path?: string; url?: string; line?: number; issueUrl?: string; taskId?: number; issueRef?: IssueRef }> = []\n let cursor = 0\n for (const match of text.matchAll(LINK_RE)) {\n const rawIssue = match[1]\n const rawUrl = match[2]\n const rawTask = match[3]\n const rawPath = match[4]\n const raw = rawIssue || rawUrl || rawTask || rawPath\n const index = match.index ?? 0\n if (!raw || index < cursor) continue\n\n if (rawTask) {\n const id = Number(rawTask.slice(1))\n if (Number.isSafeInteger(id) && id > 0) {\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: rawTask, taskId: id })\n cursor = index + rawTask.length\n }\n continue\n }\n\n if (rawIssue) {\n // `#NNN` (uses the configured default repo) or `owner/repo#NNN` (explicit repo).\n // With no resolvable repo we leave it as plain text — never emit a broken link.\n const hashIdx = rawIssue.lastIndexOf('#')\n const repo = rawIssue.slice(0, hashIdx) || issueRepo\n const num = rawIssue.slice(hashIdx + 1)\n // Slash-shorthand like `#508/509/513` (#531): the trailing `/509/513` is a run of\n // bare ref numbers, NOT a file path. Consume it as plain text so the path matcher\n // (which would otherwise claim `/509/...` as a phantom path link) can't reach it.\n const trailingRefs = text.slice(index + rawIssue.length).match(/^(?:\\/\\d+)+/)?.[0] ?? ''\n if (repo) {\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n const issueRef = issueRefsByKey?.get(`${repo}#${num}`)\n parts.push({ text: rawIssue, issueUrl: `https://github.com/${repo}/issues/${num}`, ...(issueRef ? { issueRef } : {}) })\n if (trailingRefs) parts.push({ text: trailingRefs })\n cursor = index + rawIssue.length + trailingRefs.length\n } else if (trailingRefs) {\n // No repo to link against, but still neutralize the slash run so it stays plain text.\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: rawIssue + trailingRefs })\n cursor = index + rawIssue.length + trailingRefs.length\n }\n continue\n }\n\n if (rawUrl) {\n const url = rawUrl.replace(TRAILING_PUNCTUATION, '')\n const trimmed = rawUrl.slice(url.length)\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: url, url })\n if (trimmed) parts.push({ text: trimmed })\n cursor = index + rawUrl.length\n continue\n }\n\n if (!raw.includes('/')) continue\n let path = raw.replace(TRAILING_PUNCTUATION, '')\n const trimmed = raw.slice(path.length)\n const lineMatch = path.match(/^(.*):(\\d+)$/)\n const line = lineMatch ? Number(lineMatch[2]) : undefined\n if (lineMatch) path = lineMatch[1]!\n if (!looksLikeFilePath(path)) continue\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: path, path, line: Number.isFinite(line) ? line : undefined })\n if (trimmed) parts.push({ text: trimmed })\n cursor = index + raw.length\n }\n if (cursor < text.length) parts.push({ text: text.slice(cursor) })\n return parts\n}\n\nfunction TextWithLinks({\n text,\n onOpenPath,\n onPreviewPath,\n onPreviewPathEnd,\n isPathLinkable,\n resolvePathTitle,\n issueRepo,\n onOpenTask,\n loadedTasksById,\n issueRefsByKey,\n}: {\n text: string\n onOpenPath?: (path: string, line?: number) => void\n onPreviewPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewPathEnd?: () => void\n isPathLinkable?: (path: string, line?: number) => boolean\n resolvePathTitle?: (path: string, line?: number) => string | undefined\n issueRepo?: string\n onOpenTask?: (id: number) => void\n loadedTasksById?: Map<number, LoadedTaskChip>\n issueRefsByKey?: Map<string, IssueRef>\n}) {\n const parts = useMemo(() => linkParts(text, issueRepo, loadedTasksById, issueRefsByKey), [text, issueRepo, loadedTasksById, issueRefsByKey])\n if (parts.length === 1 && !parts[0]?.path && !parts[0]?.url && !parts[0]?.issueUrl && !parts[0]?.taskId) return <>{text}</>\n return (\n <>\n {parts.map((part, index) => {\n if (part.taskId != null) {\n if (onOpenTask) {\n const t = loadedTasksById?.get(part.taskId)\n return (\n <TaskChip\n key={`task-${part.taskId}-${index}`}\n id={part.taskId}\n title={t?.title}\n status={t?.status}\n compact\n onClick={() => onOpenTask(part.taskId!)}\n />\n )\n }\n return <span key={index}>{part.text}</span>\n }\n if (part.issueUrl) {\n if (part.issueRef) {\n return <IssueRefChip key={`${part.issueUrl}-${index}`} issueRef={part.issueRef} compact />\n }\n return (\n <a\n key={`${part.issueUrl}-${index}`}\n href={part.issueUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"font-medium underline decoration-dotted underline-offset-2\"\n onClick={(e) => e.stopPropagation()}\n >\n {part.text}\n </a>\n )\n }\n if (part.url) {\n return (\n <a\n key={`${part.url}-${index}`}\n href={part.url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"underline decoration-dotted underline-offset-2\"\n onClick={(e) => e.stopPropagation()}\n >\n {part.text}\n </a>\n )\n }\n if (part.path && onOpenPath) {\n if (isPathLinkable && !isPathLinkable(part.path, part.line)) return <span key={`${part.path}-${index}`}>{part.text}{part.line ? `:${part.line}` : ''}</span>\n const resolvedTitle = resolvePathTitle?.(part.path, part.line)\n return (\n <button\n key={`${part.path}-${index}`}\n type=\"button\"\n className=\"chat-link rounded-sm font-mono underline decoration-dotted underline-offset-2\"\n onClick={(e) => { e.stopPropagation(); onOpenPath(part.path!, part.line) }}\n onMouseEnter={(e) => onPreviewPath?.(part.path!, part.line, e.currentTarget.getBoundingClientRect())}\n onFocus={(e) => onPreviewPath?.(part.path!, part.line, e.currentTarget.getBoundingClientRect())}\n onMouseLeave={() => onPreviewPathEnd?.()}\n onBlur={() => onPreviewPathEnd?.()}\n title={resolvedTitle ? `Open ${resolvedTitle}` : 'Open file'}\n >\n {part.text}{part.line ? `:${part.line}` : ''}\n </button>\n )\n }\n return <span key={index}>{part.text}</span>\n })}\n </>\n )\n}\n\ninterface LinkifyContext {\n onOpenPath?: (path: string, line?: number) => void\n onPreviewPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewPathEnd?: () => void\n isPathLinkable?: (path: string, line?: number) => boolean\n resolvePathTitle?: (path: string, line?: number) => string | undefined\n issueRepo?: string\n onOpenTask?: (id: number) => void\n loadedTasksById?: Map<number, LoadedTaskChip>\n issueRefsByKey?: Map<string, IssueRef>\n}\n\n// Inline code & fenced blocks (the `code`/`pre` overrides) — descend with `inCode`\n// set so `#NNN` inside backticks renders verbatim instead of linkifying.\nconst CodeComponent = ({ className: codeClass, children }: { className?: string; children?: React.ReactNode }) => {\n const match = /language-([\\w-]+)/.exec(codeClass || '')\n const code = String(children).replace(/\\n$/, '')\n if (match) {\n return <CodePreview content={code} path={`.${match[1]}`} languageHint={match[1]} compact />\n }\n return <code className={codeClass}>{children}</code>\n}\n\n// Markdown links — render the anchor and pass children through untouched. The\n// recursive walk skips this subtree so we never linkify a `#NNN` *inside* link\n// text (which would nest one <a> inside another). Text adjacent to a link still\n// linkifies via the enclosing block's walk.\nconst AnchorComponent = ({ href, children }: { href?: string; children?: React.ReactNode }) => (\n <a href={href} target=\"_blank\" rel=\"noreferrer\" onClick={(e) => e.stopPropagation()}>\n {children}\n </a>\n)\n\n// Recursively linkify every text leaf in a markdown subtree (#550). react-markdown\n// hands each block override its already-rendered React children, so nested inline\n// elements (strong/em/del/headings/table cells/…) arrive as element children with\n// their text strings buried one or more levels deep. We descend through them and\n// apply `linkParts` (via TextWithLinks) at every string leaf, keeping the linkify\n// rule in this ONE place. `inCode` suppresses linkify inside code/pre; anchors are\n// left whole to avoid nested links.\nfunction processTextChildren(children: React.ReactNode, ctx: LinkifyContext, inCode = false): React.ReactNode {\n return React.Children.map(children, (child) => {\n if (typeof child === 'string') {\n if (inCode) return child\n return (\n <TextWithLinks\n text={child}\n onOpenPath={ctx.onOpenPath}\n onPreviewPath={ctx.onPreviewPath}\n onPreviewPathEnd={ctx.onPreviewPathEnd}\n isPathLinkable={ctx.isPathLinkable}\n resolvePathTitle={ctx.resolvePathTitle}\n issueRepo={ctx.issueRepo}\n onOpenTask={ctx.onOpenTask}\n loadedTasksById={ctx.loadedTasksById}\n issueRefsByKey={ctx.issueRefsByKey}\n />\n )\n }\n if (!React.isValidElement(child)) return child\n const type = child.type\n // Already-linkified leaves carry their text in a prop, not children — leave whole.\n if (type === TextWithLinks) return child\n // Never linkify inside a link (no nested <a>); render the anchor subtree verbatim.\n if (type === AnchorComponent || type === 'a') return child\n const props = child.props as { children?: React.ReactNode }\n if (props?.children == null) return child\n const nextInCode = inCode || type === CodeComponent || type === 'code' || type === 'pre'\n return React.cloneElement(child, undefined, processTextChildren(props.children, ctx, nextInCode))\n })\n}\n\nexport function FormattedBody({ text, rawBody, className, onOpenPath, onPreviewPath, onPreviewPathEnd, isPathLinkable, resolvePathTitle, issueRepo: issueRepoProp, onPrimary, onOpenTask: onOpenTaskProp }: FormattedBodyProps) {\n const parsed = useMemo(() => parseJsonBody(rawBody || ''), [rawBody])\n const content = unescapeNewlines(parsed ? parsed.text : text)\n // Per-message tracker repo for bare #NNN linkify (#533): the originating agent's project\n // repo (`issueRepoProp`) wins; the server-global default (#511) is only a last-resort\n // fallback for messages with no resolvable project. undefined → #NNN stays plain text.\n const globalIssueRepo = useRelayStore((s) => s.stats?.defaultIssueRepo)\n const issueRepo = issueRepoProp ?? globalIssueRepo\n const taskRefIds = useMemo(() => extractTaskRefIds(content), [content])\n const issueRefKeys = useMemo(() => extractIssueRefKeys(content, issueRepo), [content, issueRepo])\n\n // T<NNN> linkify (#989): pattern matches render immediately; loaded task rows only hydrate\n // chip title/status. The selector returns a primitive version for refs in THIS message, so\n // unrelated task polling does not re-render every bubble.\n const storeTaskMetaVersion = useRelayStore((s) => taskMetaVersion(taskRefIds, s.tasks, s.deliverableTasks))\n const storeOpenTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n // #888: issue-ref chips use the same per-message primitive subscription.\n const storeIssueRefsVersion = useRelayStore((s) => issueRefsVersion(issueRefKeys, s.issueRefsById))\n\n const loadedTasksById = useMemo(() => {\n const state = useRelayStore.getState()\n return collectLoadedTasksById(taskRefIds, state.tasks, state.deliverableTasks)\n }, [taskRefIds, storeTaskMetaVersion])\n\n const issueRefsByKey = useMemo(() => {\n return collectIssueRefsByKey(issueRefKeys, useRelayStore.getState().issueRefsById)\n }, [issueRefKeys, storeIssueRefsVersion])\n\n const onOpenTask = onOpenTaskProp ?? storeOpenTaskDetailById\n\n const components = useMemo(() => {\n const ctx: LinkifyContext = { onOpenPath, onPreviewPath, onPreviewPathEnd, isPathLinkable, resolvePathTitle, issueRepo, onOpenTask, loadedTasksById, issueRefsByKey }\n // Every text-bearing block routes its children through the recursive walk, which\n // descends into nested inline elements (strong/em/del/code/links) on its own — so\n // these stay one-liners and the linkify rule lives only in processTextChildren.\n const block = (Tag: 'p' | 'li' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'td' | 'th' | 'blockquote') =>\n ({ children }: { children?: React.ReactNode }) => <Tag>{processTextChildren(children, ctx)}</Tag>\n return {\n p: block('p'),\n li: block('li'),\n h1: block('h1'),\n h2: block('h2'),\n h3: block('h3'),\n h4: block('h4'),\n h5: block('h5'),\n h6: block('h6'),\n td: block('td'),\n th: block('th'),\n blockquote: block('blockquote'),\n a: AnchorComponent,\n code: CodeComponent,\n }\n }, [onOpenPath, onPreviewPath, onPreviewPathEnd, isPathLinkable, resolvePathTitle, issueRepo, onOpenTask, loadedTasksById, issueRefsByKey])\n\n const markdown = useMemo(() => (\n <ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[[rehypeSanitize, markdownSchema]]} components={components}>\n {content}\n </ReactMarkdown>\n ), [components, content])\n\n return (\n <div className={className}>\n <div className={cn('chat-markdown', onPrimary && 'chat-markdown-on-primary')}>\n {markdown}\n </div>\n {parsed && parsed.meta.length > 0 && (\n <div className=\"mt-1.5 flex flex-wrap gap-1\">\n {parsed.meta.map(([k, v]) => (\n <span key={k} className=\"inline-flex text-[10px] leading-tight bg-muted px-1.5 py-0.5 rounded text-muted-foreground font-mono\">\n {k}: {v}\n </span>\n ))}\n </div>\n )}\n </div>\n )\n}\n\nfunction unescapeNewlines(text: string): string {\n if (!text.includes('\\\\n')) return text\n return text.replace(/\\\\n/g, '\\n')\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"formatted-body-impl-RmDxI5Ux.js","names":[],"sources":["../../dashboard/src/components/shared/formatted-body-impl.tsx"],"sourcesContent":["import React, { useMemo } from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport rehypeSanitize, { defaultSchema } from 'rehype-sanitize'\nimport remarkGfm from 'remark-gfm'\nimport { CodePreview } from './code-preview'\nimport { IssueRefChip } from './issue-ref-chip'\nimport { TaskChip } from './task-chip'\nimport { cn } from '@/lib/utils'\nimport { parseJsonBody } from '@/lib/display'\nimport { useRelayStore } from '@/store'\nimport type { IssueRef } from '@/lib/issue-types'\n\ntype LoadedTaskChip = { title?: string; status?: string }\n\nexport interface FormattedBodyProps {\n text: string\n rawBody?: string\n className?: string\n onOpenPath?: (path: string, line?: number) => void\n onPreviewPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewPathEnd?: () => void\n isPathLinkable?: (path: string, line?: number) => boolean\n resolvePathTitle?: (path: string, line?: number) => string | undefined\n /** Per-message tracker repo (`owner/repo`) for bare `#NNN` linkify (#533) — the originating\n * agent's project repo, resolved server-side. Falls back to the server-global default (#511). */\n issueRepo?: string\n /** Rendered inside a primary-background bubble (operator's outbound message). Flips link\n * color to primary-foreground so links stay legible against the accent bg (#612). */\n onPrimary?: boolean\n /** Override handler for clicking a resolved T<NNN> chip. Defaults to the store's\n * openTaskDetailById so callers rarely need to pass this explicitly. */\n onOpenTask?: (id: number) => void\n}\n\n// Issue-ref is FIRST so the path matcher can't swallow `owner/repo#NNN` as a file path.\n// URLs still win when they start earlier (leftmost-match), so a URL fragment like\n// `…/a#12` is never mis-linkified.\n// T<NNN> (group 3) is matched before the path group but only when NOT followed by `/`\n// (negative lookahead) so `T128/foo.ts` still routes to the file-path group (#873).\nconst LINK_RE = /((?:[A-Za-z0-9][\\w.-]*\\/[A-Za-z0-9][\\w.-]*)?#\\d+)\\b|(https?:\\/\\/[^\\s`'\")\\]]+)|(\\bT\\d+\\b(?!\\/))|((?:\\/[\\w.@+-][^\\s`'\")\\]]*|(?:\\.{1,2}\\/|[\\w.@+-]+\\/)[^\\s`'\")\\]]*))/g\nconst TASK_REF_RE = /\\bT(\\d+)\\b(?!\\/)/g\nconst ISSUE_REF_RE = /((?:[A-Za-z0-9][\\w.-]*\\/[A-Za-z0-9][\\w.-]*)?#(\\d+))\\b/g\nconst TRAILING_PUNCTUATION = /[.,;:!?]+$/\nconst KNOWN_PROJECT_DIRS = new Set(['src', 'app', 'apps', 'lib', 'server', 'client', 'dashboard', 'orchestrator', 'runner', 'sdk', 'codex', 'claude', 'public', 'docs', 'test', 'tests', 'scripts', 'config', 'configs'])\n\nconst markdownSchema = {\n ...defaultSchema,\n attributes: {\n ...defaultSchema.attributes,\n code: [\n ...(defaultSchema.attributes?.code || []),\n ['className', /^language-[\\w-]+$/],\n ],\n },\n}\n\nfunction looksLikeFilePath(path: string): boolean {\n if (path === '/api' || path.startsWith('/api/')) return false\n if (path.startsWith('/')) return true\n if (path.startsWith('./') || path.startsWith('../')) return true\n const segments = path.split('/').filter(Boolean)\n const first = segments[0] ?? ''\n const last = segments[segments.length - 1] ?? ''\n if (first.startsWith('.')) return true\n if (KNOWN_PROJECT_DIRS.has(first)) return true\n return last.includes('.')\n}\n\nfunction taskChipMeta(task: unknown): { id: number; meta: LoadedTaskChip } | null {\n if (!task || typeof task !== 'object') return null\n const record = task as Record<string, unknown>\n const id = typeof record.id === 'number' ? record.id : typeof record.id === 'string' ? Number(record.id) : NaN\n if (!Number.isSafeInteger(id) || id <= 0) return null\n const title = typeof record.title === 'string' && record.title.trim()\n ? record.title\n : typeof record.label === 'string' && record.label.trim()\n ? record.label\n : undefined\n const status = typeof record.status === 'string' && record.status.trim() ? record.status : undefined\n return { id, meta: { ...(title ? { title } : {}), ...(status ? { status } : {}) } }\n}\n\nfunction extractTaskRefIds(text: string): number[] {\n const ids = new Set<number>()\n for (const match of text.matchAll(TASK_REF_RE)) {\n const id = Number(match[1])\n if (Number.isSafeInteger(id) && id > 0) ids.add(id)\n }\n return [...ids].sort((a, b) => a - b)\n}\n\nfunction collectLoadedTasksById(ids: number[], tasks: unknown[], deliverableTasks: unknown[]): Map<number, LoadedTaskChip> {\n const wanted = new Set(ids)\n const map = new Map<number, LoadedTaskChip>()\n if (wanted.size === 0) return map\n for (const task of tasks) {\n const resolved = taskChipMeta(task)\n if (resolved && wanted.has(resolved.id)) map.set(resolved.id, resolved.meta)\n }\n for (const task of deliverableTasks) {\n const resolved = taskChipMeta(task)\n if (resolved && wanted.has(resolved.id)) map.set(resolved.id, resolved.meta)\n }\n return map\n}\n\nfunction taskMetaVersion(ids: number[], tasks: unknown[], deliverableTasks: unknown[]): string {\n if (ids.length === 0) return ''\n const loaded = collectLoadedTasksById(ids, tasks, deliverableTasks)\n return ids.map((id) => {\n const meta = loaded.get(id)\n return `${id}:${meta?.title ?? ''}:${meta?.status ?? ''}`\n }).join('|')\n}\n\nfunction extractIssueRefKeys(text: string, issueRepo?: string): string[] {\n const keys = new Set<string>()\n for (const match of text.matchAll(ISSUE_REF_RE)) {\n const raw = match[1]\n const num = match[2]\n if (!raw || !num) continue\n const hashIdx = raw.lastIndexOf('#')\n const repo = raw.slice(0, hashIdx) || issueRepo\n if (repo) keys.add(`${repo}#${num}`)\n }\n return [...keys].sort()\n}\n\nfunction collectIssueRefsByKey(keys: string[], issueRefsById: Record<string, IssueRef>): Map<string, IssueRef> {\n const wanted = new Set(keys)\n const map = new Map<string, IssueRef>()\n if (wanted.size === 0) return map\n for (const ref of Object.values(issueRefsById)) {\n const key = `${ref.repo}#${ref.externalKey}`\n if (wanted.has(key)) map.set(key, ref)\n }\n return map\n}\n\nfunction issueRefsVersion(keys: string[], issueRefsById: Record<string, IssueRef>): string {\n if (keys.length === 0) return ''\n const refs = collectIssueRefsByKey(keys, issueRefsById)\n return keys.map((key) => {\n const ref = refs.get(key)\n return `${key}:${ref?.id ?? ''}:${ref?.status ?? ''}:${ref?.updatedAt ?? ''}:${ref?.url ?? ''}`\n }).join('|')\n}\n\nexport function linkParts(\n text: string,\n issueRepo?: string,\n /** Optional loaded task metadata map/set. `T<id>` refs link by pattern even when absent. */\n _knownTaskIds?: { has: (id: number) => boolean },\n /** Map from `repo#externalKey` (e.g. `edimuj/agent-relay#888`) to the full IssueRef.\n * When provided and the ref resolves, the part gets `issueRef` for chip rendering. */\n issueRefsByKey?: Map<string, IssueRef>,\n): Array<{ text: string; path?: string; url?: string; line?: number; issueUrl?: string; taskId?: number; issueRef?: IssueRef }> {\n const parts: Array<{ text: string; path?: string; url?: string; line?: number; issueUrl?: string; taskId?: number; issueRef?: IssueRef }> = []\n let cursor = 0\n for (const match of text.matchAll(LINK_RE)) {\n const rawIssue = match[1]\n const rawUrl = match[2]\n const rawTask = match[3]\n const rawPath = match[4]\n const raw = rawIssue || rawUrl || rawTask || rawPath\n const index = match.index ?? 0\n if (!raw || index < cursor) continue\n\n if (rawTask) {\n const id = Number(rawTask.slice(1))\n if (Number.isSafeInteger(id) && id > 0) {\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: rawTask, taskId: id })\n cursor = index + rawTask.length\n }\n continue\n }\n\n if (rawIssue) {\n // `#NNN` (uses the configured default repo) or `owner/repo#NNN` (explicit repo).\n // With no resolvable repo we leave it as plain text — never emit a broken link.\n const hashIdx = rawIssue.lastIndexOf('#')\n const repo = rawIssue.slice(0, hashIdx) || issueRepo\n const num = rawIssue.slice(hashIdx + 1)\n // Slash-shorthand like `#508/509/513` (#531): the trailing `/509/513` is a run of\n // bare ref numbers, NOT a file path. Consume it as plain text so the path matcher\n // (which would otherwise claim `/509/...` as a phantom path link) can't reach it.\n const trailingRefs = text.slice(index + rawIssue.length).match(/^(?:\\/\\d+)+/)?.[0] ?? ''\n if (repo) {\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n const issueRef = issueRefsByKey?.get(`${repo}#${num}`)\n parts.push({ text: rawIssue, issueUrl: `https://github.com/${repo}/issues/${num}`, ...(issueRef ? { issueRef } : {}) })\n if (trailingRefs) parts.push({ text: trailingRefs })\n cursor = index + rawIssue.length + trailingRefs.length\n } else if (trailingRefs) {\n // No repo to link against, but still neutralize the slash run so it stays plain text.\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: rawIssue + trailingRefs })\n cursor = index + rawIssue.length + trailingRefs.length\n }\n continue\n }\n\n if (rawUrl) {\n const url = rawUrl.replace(TRAILING_PUNCTUATION, '')\n const trimmed = rawUrl.slice(url.length)\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: url, url })\n if (trimmed) parts.push({ text: trimmed })\n cursor = index + rawUrl.length\n continue\n }\n\n if (!raw.includes('/')) continue\n let path = raw.replace(TRAILING_PUNCTUATION, '')\n const trimmed = raw.slice(path.length)\n const lineMatch = path.match(/^(.*):(\\d+)$/)\n const line = lineMatch ? Number(lineMatch[2]) : undefined\n if (lineMatch) path = lineMatch[1]!\n if (!looksLikeFilePath(path)) continue\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: path, path, line: Number.isFinite(line) ? line : undefined })\n if (trimmed) parts.push({ text: trimmed })\n cursor = index + raw.length\n }\n if (cursor < text.length) parts.push({ text: text.slice(cursor) })\n return parts\n}\n\nfunction TextWithLinks({\n text,\n onOpenPath,\n onPreviewPath,\n onPreviewPathEnd,\n isPathLinkable,\n resolvePathTitle,\n issueRepo,\n onOpenTask,\n loadedTasksById,\n issueRefsByKey,\n}: {\n text: string\n onOpenPath?: (path: string, line?: number) => void\n onPreviewPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewPathEnd?: () => void\n isPathLinkable?: (path: string, line?: number) => boolean\n resolvePathTitle?: (path: string, line?: number) => string | undefined\n issueRepo?: string\n onOpenTask?: (id: number) => void\n loadedTasksById?: Map<number, LoadedTaskChip>\n issueRefsByKey?: Map<string, IssueRef>\n}) {\n const parts = useMemo(() => linkParts(text, issueRepo, loadedTasksById, issueRefsByKey), [text, issueRepo, loadedTasksById, issueRefsByKey])\n if (parts.length === 1 && !parts[0]?.path && !parts[0]?.url && !parts[0]?.issueUrl && !parts[0]?.taskId) return <>{text}</>\n return (\n <>\n {parts.map((part, index) => {\n if (part.taskId != null) {\n if (onOpenTask) {\n const t = loadedTasksById?.get(part.taskId)\n return (\n <TaskChip\n key={`task-${part.taskId}-${index}`}\n id={part.taskId}\n title={t?.title}\n status={t?.status}\n compact\n onClick={() => onOpenTask(part.taskId!)}\n />\n )\n }\n return <span key={index}>{part.text}</span>\n }\n if (part.issueUrl) {\n if (part.issueRef) {\n return <IssueRefChip key={`${part.issueUrl}-${index}`} issueRef={part.issueRef} compact />\n }\n return (\n <a\n key={`${part.issueUrl}-${index}`}\n href={part.issueUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"font-medium underline decoration-dotted underline-offset-2\"\n onClick={(e) => e.stopPropagation()}\n >\n {part.text}\n </a>\n )\n }\n if (part.url) {\n return (\n <a\n key={`${part.url}-${index}`}\n href={part.url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"underline decoration-dotted underline-offset-2\"\n onClick={(e) => e.stopPropagation()}\n >\n {part.text}\n </a>\n )\n }\n if (part.path && onOpenPath) {\n if (isPathLinkable && !isPathLinkable(part.path, part.line)) return <span key={`${part.path}-${index}`}>{part.text}{part.line ? `:${part.line}` : ''}</span>\n const resolvedTitle = resolvePathTitle?.(part.path, part.line)\n return (\n <button\n key={`${part.path}-${index}`}\n type=\"button\"\n className=\"chat-link rounded-sm font-mono underline decoration-dotted underline-offset-2\"\n onClick={(e) => { e.stopPropagation(); onOpenPath(part.path!, part.line) }}\n onMouseEnter={(e) => onPreviewPath?.(part.path!, part.line, e.currentTarget.getBoundingClientRect())}\n onFocus={(e) => onPreviewPath?.(part.path!, part.line, e.currentTarget.getBoundingClientRect())}\n onMouseLeave={() => onPreviewPathEnd?.()}\n onBlur={() => onPreviewPathEnd?.()}\n title={resolvedTitle ? `Open ${resolvedTitle}` : 'Open file'}\n >\n {part.text}{part.line ? `:${part.line}` : ''}\n </button>\n )\n }\n return <span key={index}>{part.text}</span>\n })}\n </>\n )\n}\n\ninterface LinkifyContext {\n onOpenPath?: (path: string, line?: number) => void\n onPreviewPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewPathEnd?: () => void\n isPathLinkable?: (path: string, line?: number) => boolean\n resolvePathTitle?: (path: string, line?: number) => string | undefined\n issueRepo?: string\n onOpenTask?: (id: number) => void\n loadedTasksById?: Map<number, LoadedTaskChip>\n issueRefsByKey?: Map<string, IssueRef>\n}\n\n// Inline code & fenced blocks (the `code`/`pre` overrides) — descend with `inCode`\n// set so `#NNN` inside backticks renders verbatim instead of linkifying.\nconst CodeComponent = ({ className: codeClass, children }: { className?: string; children?: React.ReactNode }) => {\n const match = /language-([\\w-]+)/.exec(codeClass || '')\n const code = String(children).replace(/\\n$/, '')\n if (match) {\n return <CodePreview content={code} path={`.${match[1]}`} languageHint={match[1]} compact />\n }\n return <code className={codeClass}>{children}</code>\n}\n\n// Markdown links — render the anchor and pass children through untouched. The\n// recursive walk skips this subtree so we never linkify a `#NNN` *inside* link\n// text (which would nest one <a> inside another). Text adjacent to a link still\n// linkifies via the enclosing block's walk.\nconst AnchorComponent = ({ href, children }: { href?: string; children?: React.ReactNode }) => (\n <a href={href} target=\"_blank\" rel=\"noreferrer\" onClick={(e) => e.stopPropagation()}>\n {children}\n </a>\n)\n\n// Recursively linkify every text leaf in a markdown subtree (#550). react-markdown\n// hands each block override its already-rendered React children, so nested inline\n// elements (strong/em/del/headings/table cells/…) arrive as element children with\n// their text strings buried one or more levels deep. We descend through them and\n// apply `linkParts` (via TextWithLinks) at every string leaf, keeping the linkify\n// rule in this ONE place. `inCode` suppresses linkify inside code/pre; anchors are\n// left whole to avoid nested links.\nfunction processTextChildren(children: React.ReactNode, ctx: LinkifyContext, inCode = false): React.ReactNode {\n return React.Children.map(children, (child) => {\n if (typeof child === 'string') {\n if (inCode) return child\n return (\n <TextWithLinks\n text={child}\n onOpenPath={ctx.onOpenPath}\n onPreviewPath={ctx.onPreviewPath}\n onPreviewPathEnd={ctx.onPreviewPathEnd}\n isPathLinkable={ctx.isPathLinkable}\n resolvePathTitle={ctx.resolvePathTitle}\n issueRepo={ctx.issueRepo}\n onOpenTask={ctx.onOpenTask}\n loadedTasksById={ctx.loadedTasksById}\n issueRefsByKey={ctx.issueRefsByKey}\n />\n )\n }\n if (!React.isValidElement(child)) return child\n const type = child.type\n // Already-linkified leaves carry their text in a prop, not children — leave whole.\n if (type === TextWithLinks) return child\n // Never linkify inside a link (no nested <a>); render the anchor subtree verbatim.\n if (type === AnchorComponent || type === 'a') return child\n const props = child.props as { children?: React.ReactNode }\n if (props?.children == null) return child\n const nextInCode = inCode || type === CodeComponent || type === 'code' || type === 'pre'\n return React.cloneElement(child, undefined, processTextChildren(props.children, ctx, nextInCode))\n })\n}\n\nexport function FormattedBody({ text, rawBody, className, onOpenPath, onPreviewPath, onPreviewPathEnd, isPathLinkable, resolvePathTitle, issueRepo: issueRepoProp, onPrimary, onOpenTask: onOpenTaskProp }: FormattedBodyProps) {\n const parsed = useMemo(() => parseJsonBody(rawBody || ''), [rawBody])\n const content = unescapeNewlines(parsed ? parsed.text : text)\n // Per-message tracker repo for bare #NNN linkify (#533): the originating agent's project\n // repo (`issueRepoProp`) wins; the server-global default (#511) is only a last-resort\n // fallback for messages with no resolvable project. undefined → #NNN stays plain text.\n const globalIssueRepo = useRelayStore((s) => s.stats?.defaultIssueRepo)\n const issueRepo = issueRepoProp ?? globalIssueRepo\n const taskRefIds = useMemo(() => extractTaskRefIds(content), [content])\n const issueRefKeys = useMemo(() => extractIssueRefKeys(content, issueRepo), [content, issueRepo])\n\n // T<NNN> linkify (#989): pattern matches render immediately; loaded task rows only hydrate\n // chip title/status. The selector returns a primitive version for refs in THIS message, so\n // unrelated task polling does not re-render every bubble.\n const storeTaskMetaVersion = useRelayStore((s) => taskMetaVersion(taskRefIds, s.tasks, s.deliverableTasks))\n const storeOpenTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n // #888: issue-ref chips use the same per-message primitive subscription.\n const storeIssueRefsVersion = useRelayStore((s) => issueRefsVersion(issueRefKeys, s.issueRefsById))\n\n const loadedTasksById = useMemo(() => {\n const state = useRelayStore.getState()\n return collectLoadedTasksById(taskRefIds, state.tasks, state.deliverableTasks)\n }, [taskRefIds, storeTaskMetaVersion])\n\n const issueRefsByKey = useMemo(() => {\n return collectIssueRefsByKey(issueRefKeys, useRelayStore.getState().issueRefsById)\n }, [issueRefKeys, storeIssueRefsVersion])\n\n const onOpenTask = onOpenTaskProp ?? storeOpenTaskDetailById\n\n const components = useMemo(() => {\n const ctx: LinkifyContext = { onOpenPath, onPreviewPath, onPreviewPathEnd, isPathLinkable, resolvePathTitle, issueRepo, onOpenTask, loadedTasksById, issueRefsByKey }\n // Every text-bearing block routes its children through the recursive walk, which\n // descends into nested inline elements (strong/em/del/code/links) on its own — so\n // these stay one-liners and the linkify rule lives only in processTextChildren.\n const block = (Tag: 'p' | 'li' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'td' | 'th' | 'blockquote') =>\n ({ children }: { children?: React.ReactNode }) => <Tag>{processTextChildren(children, ctx)}</Tag>\n return {\n p: block('p'),\n li: block('li'),\n h1: block('h1'),\n h2: block('h2'),\n h3: block('h3'),\n h4: block('h4'),\n h5: block('h5'),\n h6: block('h6'),\n td: block('td'),\n th: block('th'),\n blockquote: block('blockquote'),\n a: AnchorComponent,\n code: CodeComponent,\n }\n }, [onOpenPath, onPreviewPath, onPreviewPathEnd, isPathLinkable, resolvePathTitle, issueRepo, onOpenTask, loadedTasksById, issueRefsByKey])\n\n const markdown = useMemo(() => (\n <ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[[rehypeSanitize, markdownSchema]]} components={components}>\n {content}\n </ReactMarkdown>\n ), [components, content])\n\n return (\n <div className={className}>\n <div className={cn('chat-markdown', onPrimary && 'chat-markdown-on-primary')}>\n {markdown}\n </div>\n {parsed && parsed.meta.length > 0 && (\n <div className=\"mt-1.5 flex flex-wrap gap-1\">\n {parsed.meta.map(([k, v]) => (\n <span key={k} className=\"inline-flex text-[10px] leading-tight bg-muted px-1.5 py-0.5 rounded text-muted-foreground font-mono\">\n {k}: {v}\n </span>\n ))}\n </div>\n )}\n </div>\n )\n}\n\nfunction unescapeNewlines(text: string): string {\n if (!text.includes('\\\\n')) return text\n return text.replace(/\\\\n/g, '\\n')\n}\n"],"mappings":"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"}