agent-relay-server 0.42.0 → 0.44.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.
- package/docs/openapi.json +85 -1
- package/package.json +2 -2
- package/public/assets/{activity-G6l1nOLj.js → activity-Dkzmef0Z.js} +2 -2
- package/public/assets/{activity-G6l1nOLj.js.map → activity-Dkzmef0Z.js.map} +1 -1
- package/public/assets/{agent-profiles-BWKpVoNw.js → agent-profiles-DQYHMAyu.js} +2 -2
- package/public/assets/{agent-profiles-BWKpVoNw.js.map → agent-profiles-DQYHMAyu.js.map} +1 -1
- package/public/assets/{agents-DBuCRS5A.js → agents-atr6HZ1e.js} +2 -2
- package/public/assets/{agents-DBuCRS5A.js.map → agents-atr6HZ1e.js.map} +1 -1
- package/public/assets/{analytics-BhwMtd0_.js → analytics-azFfAy4R.js} +2 -2
- package/public/assets/{analytics-BhwMtd0_.js.map → analytics-azFfAy4R.js.map} +1 -1
- package/public/assets/{automation-B7jnsYhH.js → automation-Bk4umHIm.js} +2 -2
- package/public/assets/{automation-B7jnsYhH.js.map → automation-Bk4umHIm.js.map} +1 -1
- package/public/assets/{branch-state-badge-dN2mQ2gs.js → branch-state-badge-DymObp8d.js} +2 -2
- package/public/assets/{branch-state-badge-dN2mQ2gs.js.map → branch-state-badge-DymObp8d.js.map} +1 -1
- package/public/assets/channels-B_0wDy0s.js +2 -0
- package/public/assets/{channels-CQy-JL7o.js.map → channels-B_0wDy0s.js.map} +1 -1
- package/public/assets/chat-DxV4MyYF.js +2 -0
- package/public/assets/chat-DxV4MyYF.js.map +1 -0
- package/public/assets/{connectors-ByNxzq2k.js → connectors-DLqYF2_3.js} +2 -2
- package/public/assets/{connectors-ByNxzq2k.js.map → connectors-DLqYF2_3.js.map} +1 -1
- package/public/assets/display-ConJ9cJB.js.map +1 -1
- package/public/assets/{formatted-body-impl-BXuR2gfL.js → formatted-body-impl-LK0QcFLC.js} +2 -2
- package/public/assets/{formatted-body-impl-BXuR2gfL.js.map → formatted-body-impl-LK0QcFLC.js.map} +1 -1
- package/public/assets/{index-CAnJGSBl.js → index-CT10XH5Y.js} +8 -8
- package/public/assets/{index-CAnJGSBl.js.map → index-CT10XH5Y.js.map} +1 -1
- package/public/assets/index-Ryvn1efN.css +2 -0
- package/public/assets/{integrations-BBmL-PnQ.js → integrations-CdeQuRSQ.js} +2 -2
- package/public/assets/{integrations-BBmL-PnQ.js.map → integrations-CdeQuRSQ.js.map} +1 -1
- package/public/assets/{maintenance-B1bX9ety.js → maintenance-DbqSE_ps.js} +2 -2
- package/public/assets/{maintenance-B1bX9ety.js.map → maintenance-DbqSE_ps.js.map} +1 -1
- package/public/assets/{managed-agents-D12-nGKd.js → managed-agents-BxhRiBVn.js} +2 -2
- package/public/assets/{managed-agents-D12-nGKd.js.map → managed-agents-BxhRiBVn.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-BWd3swt8.js → markdown-preview-impl-Dz2Spmat.js} +2 -2
- package/public/assets/{markdown-preview-impl-BWd3swt8.js.map → markdown-preview-impl-Dz2Spmat.js.map} +1 -1
- package/public/assets/{memory-4ERBFJBZ.js → memory-BTjUKTyA.js} +2 -2
- package/public/assets/{memory-4ERBFJBZ.js.map → memory-BTjUKTyA.js.map} +1 -1
- package/public/assets/{messages-B3ZES72c.js → messages-C8z05Y3O.js} +2 -2
- package/public/assets/{messages-B3ZES72c.js.map → messages-C8z05Y3O.js.map} +1 -1
- package/public/assets/{orchestrators-qKPntM-a.js → orchestrators-DQqtYryc.js} +2 -2
- package/public/assets/{orchestrators-qKPntM-a.js.map → orchestrators-DQqtYryc.js.map} +1 -1
- package/public/assets/{overview-ekmeyPaO.js → overview-CGYu6qQr.js} +2 -2
- package/public/assets/{overview-ekmeyPaO.js.map → overview-CGYu6qQr.js.map} +1 -1
- package/public/assets/pairs-BgqXzCto.js +2 -0
- package/public/assets/{pairs-B2QdTF0M.js.map → pairs-BgqXzCto.js.map} +1 -1
- package/public/assets/{security-WkMkEhb7.js → security-BqfRVw-p.js} +2 -2
- package/public/assets/{security-WkMkEhb7.js.map → security-BqfRVw-p.js.map} +1 -1
- package/public/assets/{settings-C5aXX3od.js → settings-63whklIq.js} +3 -3
- package/public/assets/{settings-C5aXX3od.js.map → settings-63whklIq.js.map} +1 -1
- package/public/assets/store-B6qT5_de.js +9 -0
- package/public/assets/store-B6qT5_de.js.map +1 -0
- package/public/assets/tasks-DiZ_4TYW.js +2 -0
- package/public/assets/{tasks-ppuVeXkO.js.map → tasks-DiZ_4TYW.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-DXycIJKe.js → terminal-viewer-impl-D3T1F77z.js} +2 -2
- package/public/assets/{terminal-viewer-impl-DXycIJKe.js.map → terminal-viewer-impl-D3T1F77z.js.map} +1 -1
- package/public/assets/work-queue-C1p2Hybt.js +2 -0
- package/public/assets/{work-queue-C8UzeekT.js.map → work-queue-C1p2Hybt.js.map} +1 -1
- package/public/assets/{workspaces-BD1P6J21.js → workspaces-DqVr0uN7.js} +2 -2
- package/public/assets/{workspaces-BD1P6J21.js.map → workspaces-DqVr0uN7.js.map} +1 -1
- package/public/index.html +3 -3
- package/src/agent-ref.ts +7 -4
- package/src/channel-target.ts +1 -0
- package/src/db/agent-search.ts +6 -0
- package/src/db/agents.ts +11 -2
- package/src/db/channels.ts +2 -1
- package/src/db/delivery.ts +4 -1
- package/src/db/index.ts +1 -0
- package/src/db/mappers.ts +1 -1
- package/src/db/message-reads.ts +10 -1
- package/src/db/migrations.ts +54 -2
- package/src/db/plans.ts +17 -2
- package/src/db/schema.ts +24 -3
- package/src/db/teams.ts +151 -0
- package/src/mcp-plan-tools.ts +7 -3
- package/src/mcp-team-tools.ts +87 -0
- package/src/mcp.ts +6 -1
- package/src/routes/index.ts +4 -0
- package/src/routes/integrations.ts +2 -1
- package/src/routes/messages.ts +1 -1
- package/src/routes/plans.ts +9 -3
- package/src/routes/teams.ts +60 -0
- package/src/runtime-tokens.ts +9 -3
- package/src/security.ts +7 -1
- package/src/services/plan-graph.ts +17 -3
- package/src/services/register-agent.ts +3 -1
- package/src/services/spawn-agent.ts +4 -1
- package/src/services/team.ts +69 -0
- package/src/token-db.ts +3 -3
- package/src/validation.ts +2 -0
- package/public/assets/channels-CQy-JL7o.js +0 -2
- package/public/assets/chat-KaK5_x65.js +0 -2
- package/public/assets/chat-KaK5_x65.js.map +0 -1
- package/public/assets/index-DzRKGPXS.css +0 -2
- package/public/assets/pairs-B2QdTF0M.js +0 -2
- package/public/assets/store-CtKmqMfx.js +0 -9
- package/public/assets/store-CtKmqMfx.js.map +0 -1
- package/public/assets/tasks-ppuVeXkO.js +0 -2
- package/public/assets/work-queue-C8UzeekT.js +0 -2
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{Qt as e,at as t,i as n,jn as r,ln as i}from"./lucide-react-ChOGD4vc.js";import{o as a,t as o}from"./store-B6qT5_de.js";import{H as s,ct as c,d as l}from"./display-ConJ9cJB.js";import{t as u}from"./badge-CFVr4fG9.js";import{t as d}from"./button-BuTu0MHx.js";import{L as f,U as p,s as m}from"./index-CT10XH5Y.js";import{n as h,t as g}from"./card-C3ErQ372.js";var _=r(),v={open:`bg-blue-500/10 text-blue-400 border-blue-500/20`,blocked:`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`,claimed:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,in_progress:`bg-purple-500/10 text-purple-400 border-purple-500/20`};function y(){let e=a(),n=o(e=>e.agentsById),r=o(e=>e.compose),i=o(e=>e.set),s=o(e=>e.doClaimTask),c=o(e=>e.doClaim),d=o(e=>e.openTaskEvents),h=o(e=>e.showError),g=o(e=>e.openConfirm),v=o(e=>e.doDeleteMessage),y=o(e=>e.doUpdateTaskStatus),x=f(),S=p(),C=S.filter(e=>e.claimable).length;function w(e){if(!r.from){h(`Validation`,`Select a "Claim as" agent first.`);return}e.sourceType===`task`&&e.task?s(e.task.id):e.sourceType===`message`&&e.message&&c(e.message.id)}function T(e){g(`Cancel Item`,`Cancel "${e.title||(e.sourceType===`task`?`Task #${e.id}`:`Message #${e.id}`)}"? This cannot be undone.`,()=>{e.sourceType===`task`&&e.task?y(e.task,`canceled`):e.sourceType===`message`&&e.message&&v(e.message.id)})}function E(e){e.task&&d(e.task)}return(0,_.jsxs)(`div`,{className:`space-y-4`,children:[(0,_.jsxs)(`div`,{className:`flex items-center justify-between flex-wrap gap-2`,children:[(0,_.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,_.jsx)(t,{className:`w-5 h-5 text-muted-foreground`}),(0,_.jsx)(`h2`,{className:`text-lg font-semibold`,children:`Work Queue`}),C>0&&(0,_.jsxs)(u,{className:`bg-orange-500/20 text-orange-400 border-orange-500/30 border`,children:[C,` claimable`]})]}),(0,_.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,_.jsx)(`label`,{className:`text-xs text-muted-foreground shrink-0`,children:`Claim as`}),(0,_.jsxs)(`select`,{className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm`,value:r.from,onChange:e=>i({compose:{...r,from:e.target.value}}),children:[(0,_.jsx)(`option`,{value:``,children:`Select agent...`}),x.map(e=>(0,_.jsx)(`option`,{value:e.id,children:l(e)},e.id))]})]})]}),(0,_.jsx)(m,{className:`h-[calc(100dvh-11rem)]`,children:(0,_.jsxs)(`div`,{className:`space-y-2 pr-2`,children:[S.length===0&&(0,_.jsx)(`div`,{className:`text-center text-muted-foreground py-16 text-sm`,children:`Work queue is empty`}),S.map(t=>(0,_.jsx)(b,{item:t,now:e,agentName:e=>n[e]?l(n[e]):e.slice(-10),onClaim:()=>w(t),onCancel:()=>T(t),onEvents:t.task?()=>E(t):void 0},t.id))]})})]})}function b({item:t,now:r,agentName:a,onClaim:o,onCancel:l,onEvents:f}){let p=c[t.severity]||c.info,m=v[t.status]||`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`;return(0,_.jsx)(g,{className:t.claimable?`ring-1 ring-orange-500/30`:``,children:(0,_.jsx)(h,{className:`p-3`,children:(0,_.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,_.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,_.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap mb-1`,children:[(0,_.jsx)(`span`,{className:`font-medium text-sm truncate`,children:t.title}),(0,_.jsx)(u,{variant:`outline`,className:`text-xs px-1.5 py-0 border ${p}`,children:t.severity}),(0,_.jsx)(u,{variant:`outline`,className:`text-xs px-1.5 py-0 border ${m}`,children:t.status}),t.sourceType===`message`&&(0,_.jsx)(u,{variant:`outline`,className:`text-xs px-1.5 py-0 border border-zinc-500/20 text-zinc-400`,children:`msg`})]}),t.body&&(0,_.jsx)(`p`,{className:`text-xs text-muted-foreground line-clamp-2 mb-2`,children:t.body}),(0,_.jsxs)(`div`,{className:`flex items-center gap-3 text-xs text-muted-foreground flex-wrap`,children:[t.owner&&(0,_.jsxs)(`span`,{title:`Owner`,children:[(0,_.jsx)(e,{className:`w-3 h-3 inline mr-1`}),a(t.owner)]}),t.source&&(0,_.jsx)(`span`,{className:`font-mono`,children:t.source}),(0,_.jsxs)(`span`,{title:String(t.updatedAt),children:[(0,_.jsx)(i,{className:`w-3 h-3 inline mr-1`}),s(r,t.updatedAt)]})]})]}),(0,_.jsxs)(`div`,{className:`flex flex-col gap-1 shrink-0`,children:[t.claimable&&(0,_.jsxs)(d,{size:`sm`,className:`h-7 text-xs bg-orange-600 hover:bg-orange-700 text-white`,onClick:o,children:[(0,_.jsx)(e,{className:`w-3.5 h-3.5 mr-1`}),` Claim`]}),f&&(0,_.jsx)(d,{size:`sm`,variant:`outline`,className:`h-7 text-xs`,onClick:f,children:`Events`}),(0,_.jsxs)(d,{size:`sm`,variant:`outline`,className:`h-7 text-xs text-red-400 hover:text-red-300 hover:bg-red-500/10 border-red-500/20`,onClick:l,children:[(0,_.jsx)(n,{className:`w-3.5 h-3.5 mr-1`}),` Cancel`]})]})]})})})}export{y as WorkQueueView};
|
|
2
|
+
//# sourceMappingURL=work-queue-C1p2Hybt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-queue-C8UzeekT.js","names":[],"sources":["../../dashboard/src/components/views/work-queue.tsx"],"sourcesContent":["import { useRelayStore, useNow } from '@/store'\nimport { useWorkQueueItems, useComposeAgents } from '@/hooks/use-selectors'\nimport { Card, CardContent } from '@/components/ui/card'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { ScrollArea } from '@/components/ui/scroll-area'\nimport { ListChecks, CheckCircle2, Calendar, X } from 'lucide-react'\nimport { displayName, timeAgo } from '@/lib/display'\nimport { SEVERITY_COLORS } from '@/lib/constants'\nimport type { WorkQueueItem } from '@/types'\n\nconst STATUS_COLORS: Record<string, string> = {\n open: 'bg-blue-500/10 text-blue-400 border-blue-500/20',\n blocked: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n claimed: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n in_progress: 'bg-purple-500/10 text-purple-400 border-purple-500/20',\n}\n\nexport function WorkQueueView() {\n const now = useNow()\n const agentsById = useRelayStore((s) => s.agentsById)\n const compose = useRelayStore((s) => s.compose)\n const set = useRelayStore((s) => s.set)\n const doClaimTask = useRelayStore((s) => s.doClaimTask)\n const doClaim = useRelayStore((s) => s.doClaim)\n const openTaskEvents = useRelayStore((s) => s.openTaskEvents)\n const showError = useRelayStore((s) => s.showError)\n const openConfirm = useRelayStore((s) => s.openConfirm)\n const doDeleteMessage = useRelayStore((s) => s.doDeleteMessage)\n const doUpdateTaskStatus = useRelayStore((s) => s.doUpdateTaskStatus)\n\n const composeAgents = useComposeAgents()\n const items = useWorkQueueItems()\n\n const claimableCount = items.filter((i) => i.claimable).length\n\n function handleClaim(item: WorkQueueItem) {\n if (!compose.from) { showError('Validation', 'Select a \"Claim as\" agent first.'); return }\n if (item.sourceType === 'task' && item.task) doClaimTask(item.task.id)\n else if (item.sourceType === 'message' && item.message) doClaim(item.message.id)\n }\n\n function handleCancel(item: WorkQueueItem) {\n const label = item.title || (item.sourceType === 'task' ? `Task #${item.id}` : `Message #${item.id}`)\n openConfirm('Cancel Item', `Cancel \"${label}\"? This cannot be undone.`, () => {\n if (item.sourceType === 'task' && item.task) doUpdateTaskStatus(item.task, 'canceled')\n else if (item.sourceType === 'message' && item.message) doDeleteMessage(item.message.id)\n })\n }\n\n function handleEvents(item: WorkQueueItem) {\n if (item.task) openTaskEvents(item.task)\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n <ListChecks className=\"w-5 h-5 text-muted-foreground\" />\n <h2 className=\"text-lg font-semibold\">Work Queue</h2>\n {claimableCount > 0 && (\n <Badge className=\"bg-orange-500/20 text-orange-400 border-orange-500/30 border\">\n {claimableCount} claimable\n </Badge>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n <label className=\"text-xs text-muted-foreground shrink-0\">Claim as</label>\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={compose.from}\n onChange={(e) => set({ compose: { ...compose, from: e.target.value } })}\n >\n <option value=\"\">Select agent...</option>\n {composeAgents.map((a) => <option key={a.id} value={a.id}>{displayName(a)}</option>)}\n </select>\n </div>\n </div>\n\n <ScrollArea className=\"h-[calc(100dvh-11rem)]\">\n <div className=\"space-y-2 pr-2\">\n {items.length === 0 && (\n <div className=\"text-center text-muted-foreground py-16 text-sm\">Work queue is empty</div>\n )}\n {items.map((item) => (\n <WorkQueueCard\n key={item.id}\n item={item}\n now={now}\n agentName={(id) => agentsById[id] ? displayName(agentsById[id]) : id.slice(-10)}\n onClaim={() => handleClaim(item)}\n onCancel={() => handleCancel(item)}\n onEvents={item.task ? () => handleEvents(item) : undefined}\n />\n ))}\n </div>\n </ScrollArea>\n </div>\n )\n}\n\nfunction WorkQueueCard({\n item, now, agentName, onClaim, onCancel, onEvents,\n}: {\n item: WorkQueueItem\n now: number\n agentName: (id: string) => string\n onClaim: () => void\n onCancel: () => void\n onEvents?: () => void\n}) {\n const severityClass = SEVERITY_COLORS[item.severity] || SEVERITY_COLORS.info\n const statusClass = STATUS_COLORS[item.status] || 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'\n\n return (\n <Card className={item.claimable ? 'ring-1 ring-orange-500/30' : ''}>\n <CardContent className=\"p-3\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap mb-1\">\n <span className=\"font-medium text-sm truncate\">{item.title}</span>\n <Badge variant=\"outline\" className={`text-xs px-1.5 py-0 border ${severityClass}`}>{item.severity}</Badge>\n <Badge variant=\"outline\" className={`text-xs px-1.5 py-0 border ${statusClass}`}>{item.status}</Badge>\n {item.sourceType === 'message' && (\n <Badge variant=\"outline\" className=\"text-xs px-1.5 py-0 border border-zinc-500/20 text-zinc-400\">msg</Badge>\n )}\n </div>\n {item.body && (\n <p className=\"text-xs text-muted-foreground line-clamp-2 mb-2\">{item.body}</p>\n )}\n <div className=\"flex items-center gap-3 text-xs text-muted-foreground flex-wrap\">\n {item.owner && (\n <span title=\"Owner\">\n <CheckCircle2 className=\"w-3 h-3 inline mr-1\" />{agentName(item.owner)}\n </span>\n )}\n {item.source && (\n <span className=\"font-mono\">{item.source}</span>\n )}\n <span title={String(item.updatedAt)}>\n <Calendar className=\"w-3 h-3 inline mr-1\" />{timeAgo(now, item.updatedAt)}\n </span>\n </div>\n </div>\n <div className=\"flex flex-col gap-1 shrink-0\">\n {item.claimable && (\n <Button size=\"sm\" className=\"h-7 text-xs bg-orange-600 hover:bg-orange-700 text-white\" onClick={onClaim}>\n <CheckCircle2 className=\"w-3.5 h-3.5 mr-1\" /> Claim\n </Button>\n )}\n {onEvents && (\n <Button size=\"sm\" variant=\"outline\" className=\"h-7 text-xs\" onClick={onEvents}>\n Events\n </Button>\n )}\n <Button size=\"sm\" variant=\"outline\" className=\"h-7 text-xs text-red-400 hover:text-red-300 hover:bg-red-500/10 border-red-500/20\" onClick={onCancel}>\n <X className=\"w-3.5 h-3.5 mr-1\" /> Cancel\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n )\n}\n"],"mappings":"iXAWM,EAAwC,CAC5C,KAAM,kDACN,QAAS,wDACT,QAAS,2DACT,YAAa,wDACd,CAED,SAAgB,GAAgB,CAC9B,IAAM,EAAM,GAAQ,CACd,EAAa,EAAe,GAAM,EAAE,WAAW,CAC/C,EAAU,EAAe,GAAM,EAAE,QAAQ,CACzC,EAAM,EAAe,GAAM,EAAE,IAAI,CACjC,EAAc,EAAe,GAAM,EAAE,YAAY,CACjD,EAAU,EAAe,GAAM,EAAE,QAAQ,CACzC,EAAiB,EAAe,GAAM,EAAE,eAAe,CACvD,EAAY,EAAe,GAAM,EAAE,UAAU,CAC7C,EAAc,EAAe,GAAM,EAAE,YAAY,CACjD,EAAkB,EAAe,GAAM,EAAE,gBAAgB,CACzD,EAAqB,EAAe,GAAM,EAAE,mBAAmB,CAE/D,EAAgB,GAAkB,CAClC,EAAQ,GAAmB,CAE3B,EAAiB,EAAM,OAAQ,GAAM,EAAE,UAAU,CAAC,OAExD,SAAS,EAAY,EAAqB,CACxC,GAAI,CAAC,EAAQ,KAAM,CAAE,EAAU,aAAc,mCAAmC,CAAE,OAC9E,EAAK,aAAe,QAAU,EAAK,KAAM,EAAY,EAAK,KAAK,GAAG,CAC7D,EAAK,aAAe,WAAa,EAAK,SAAS,EAAQ,EAAK,QAAQ,GAAG,CAGlF,SAAS,EAAa,EAAqB,CAEzC,EAAY,cAAe,WADb,EAAK,QAAU,EAAK,aAAe,OAAS,SAAS,EAAK,KAAO,YAAY,EAAK,MACpD,+BAAkC,CACxE,EAAK,aAAe,QAAU,EAAK,KAAM,EAAmB,EAAK,KAAM,WAAW,CAC7E,EAAK,aAAe,WAAa,EAAK,SAAS,EAAgB,EAAK,QAAQ,GAAG,EACxF,CAGJ,SAAS,EAAa,EAAqB,CACrC,EAAK,MAAM,EAAe,EAAK,KAAK,CAG1C,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,qBAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6DAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mCAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAY,UAAU,gCAAkC,CAAA,EACxD,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,iCAAwB,aAAe,CAAA,CACpD,EAAiB,IAChB,EAAA,EAAA,MAAC,EAAD,CAAO,UAAU,wEAAjB,CACG,EAAe,aACV,GAEN,IACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mCAAf,EACE,EAAA,EAAA,KAAC,QAAD,CAAO,UAAU,kDAAyC,WAAgB,CAAA,EAC1E,EAAA,EAAA,MAAC,SAAD,CACE,UAAU,mEACV,MAAO,EAAQ,KACf,SAAW,GAAM,EAAI,CAAE,QAAS,CAAE,GAAG,EAAS,KAAM,EAAE,OAAO,MAAO,CAAE,CAAC,UAHzE,EAKE,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,YAAG,kBAAwB,CAAA,CACxC,EAAc,IAAK,IAAM,EAAA,EAAA,KAAC,SAAD,CAAmB,MAAO,EAAE,YAAK,EAAY,EAAE,CAAU,CAA5C,EAAE,GAA0C,CAAC,CAC7E,GACL,GACF,IAEN,EAAA,EAAA,KAAC,EAAD,CAAY,UAAU,mCACpB,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,0BAAf,CACG,EAAM,SAAW,IAChB,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,2DAAkD,sBAAyB,CAAA,CAE3F,EAAM,IAAK,IACV,EAAA,EAAA,KAAC,EAAD,CAEQ,OACD,MACL,UAAY,GAAO,EAAW,GAAM,EAAY,EAAW,GAAI,CAAG,EAAG,MAAM,IAAI,CAC/E,YAAe,EAAY,EAAK,CAChC,aAAgB,EAAa,EAAK,CAClC,SAAU,EAAK,SAAa,EAAa,EAAK,CAAG,IAAA,GACjD,CAPK,EAAK,GAOV,CACF,CACE,GACK,CAAA,CACT,GAIV,SAAS,EAAc,CACrB,OAAM,MAAK,YAAW,UAAS,WAAU,YAQxC,CACD,IAAM,EAAgB,EAAgB,EAAK,WAAa,EAAgB,KAClE,EAAc,EAAc,EAAK,SAAW,kDAElD,OACE,EAAA,EAAA,KAAC,EAAD,CAAM,UAAW,EAAK,UAAY,4BAA8B,aAC9D,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,gBACrB,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,kCAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,0BAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,kDAAf,EACE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,wCAAgC,EAAK,MAAa,CAAA,EAClE,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAW,8BAA8B,aAAkB,EAAK,SAAiB,CAAA,EAC1G,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAW,8BAA8B,aAAgB,EAAK,OAAe,CAAA,CACrG,EAAK,aAAe,YACnB,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAU,uEAA8D,MAAW,CAAA,CAE1G,GACL,EAAK,OACJ,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,2DAAmD,EAAK,KAAS,CAAA,EAEhF,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,2EAAf,CACG,EAAK,QACJ,EAAA,EAAA,MAAC,OAAD,CAAM,MAAM,iBAAZ,EACE,EAAA,EAAA,KAAC,EAAD,CAAc,UAAU,sBAAwB,CAAA,CAAC,EAAU,EAAK,MAAM,CACjE,GAER,EAAK,SACJ,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,qBAAa,EAAK,OAAc,CAAA,EAElD,EAAA,EAAA,MAAC,OAAD,CAAM,MAAO,OAAO,EAAK,UAAU,UAAnC,EACE,EAAA,EAAA,KAAC,EAAD,CAAU,UAAU,sBAAwB,CAAA,CAAC,EAAQ,EAAK,EAAK,UAAU,CACpE,GACH,GACF,IACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,wCAAf,CACG,EAAK,YACJ,EAAA,EAAA,MAAC,EAAD,CAAQ,KAAK,KAAK,UAAU,2DAA2D,QAAS,WAAhG,EACE,EAAA,EAAA,KAAC,EAAD,CAAc,UAAU,mBAAqB,CAAA,CAAA,SACtC,GAEV,IACC,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,KAAK,QAAQ,UAAU,UAAU,cAAc,QAAS,WAAU,SAEtE,CAAA,EAEX,EAAA,EAAA,MAAC,EAAD,CAAQ,KAAK,KAAK,QAAQ,UAAU,UAAU,oFAAoF,QAAS,WAA3I,EACE,EAAA,EAAA,KAAC,EAAD,CAAG,UAAU,mBAAqB,CAAA,CAAA,UAC3B,GACL,GACF,GACM,CAAA,CACT,CAAA"}
|
|
1
|
+
{"version":3,"file":"work-queue-C1p2Hybt.js","names":[],"sources":["../../dashboard/src/components/views/work-queue.tsx"],"sourcesContent":["import { useRelayStore, useNow } from '@/store'\nimport { useWorkQueueItems, useComposeAgents } from '@/hooks/use-selectors'\nimport { Card, CardContent } from '@/components/ui/card'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { ScrollArea } from '@/components/ui/scroll-area'\nimport { ListChecks, CheckCircle2, Calendar, X } from 'lucide-react'\nimport { displayName, timeAgo } from '@/lib/display'\nimport { SEVERITY_COLORS } from '@/lib/constants'\nimport type { WorkQueueItem } from '@/types'\n\nconst STATUS_COLORS: Record<string, string> = {\n open: 'bg-blue-500/10 text-blue-400 border-blue-500/20',\n blocked: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n claimed: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n in_progress: 'bg-purple-500/10 text-purple-400 border-purple-500/20',\n}\n\nexport function WorkQueueView() {\n const now = useNow()\n const agentsById = useRelayStore((s) => s.agentsById)\n const compose = useRelayStore((s) => s.compose)\n const set = useRelayStore((s) => s.set)\n const doClaimTask = useRelayStore((s) => s.doClaimTask)\n const doClaim = useRelayStore((s) => s.doClaim)\n const openTaskEvents = useRelayStore((s) => s.openTaskEvents)\n const showError = useRelayStore((s) => s.showError)\n const openConfirm = useRelayStore((s) => s.openConfirm)\n const doDeleteMessage = useRelayStore((s) => s.doDeleteMessage)\n const doUpdateTaskStatus = useRelayStore((s) => s.doUpdateTaskStatus)\n\n const composeAgents = useComposeAgents()\n const items = useWorkQueueItems()\n\n const claimableCount = items.filter((i) => i.claimable).length\n\n function handleClaim(item: WorkQueueItem) {\n if (!compose.from) { showError('Validation', 'Select a \"Claim as\" agent first.'); return }\n if (item.sourceType === 'task' && item.task) doClaimTask(item.task.id)\n else if (item.sourceType === 'message' && item.message) doClaim(item.message.id)\n }\n\n function handleCancel(item: WorkQueueItem) {\n const label = item.title || (item.sourceType === 'task' ? `Task #${item.id}` : `Message #${item.id}`)\n openConfirm('Cancel Item', `Cancel \"${label}\"? This cannot be undone.`, () => {\n if (item.sourceType === 'task' && item.task) doUpdateTaskStatus(item.task, 'canceled')\n else if (item.sourceType === 'message' && item.message) doDeleteMessage(item.message.id)\n })\n }\n\n function handleEvents(item: WorkQueueItem) {\n if (item.task) openTaskEvents(item.task)\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n <ListChecks className=\"w-5 h-5 text-muted-foreground\" />\n <h2 className=\"text-lg font-semibold\">Work Queue</h2>\n {claimableCount > 0 && (\n <Badge className=\"bg-orange-500/20 text-orange-400 border-orange-500/30 border\">\n {claimableCount} claimable\n </Badge>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n <label className=\"text-xs text-muted-foreground shrink-0\">Claim as</label>\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={compose.from}\n onChange={(e) => set({ compose: { ...compose, from: e.target.value } })}\n >\n <option value=\"\">Select agent...</option>\n {composeAgents.map((a) => <option key={a.id} value={a.id}>{displayName(a)}</option>)}\n </select>\n </div>\n </div>\n\n <ScrollArea className=\"h-[calc(100dvh-11rem)]\">\n <div className=\"space-y-2 pr-2\">\n {items.length === 0 && (\n <div className=\"text-center text-muted-foreground py-16 text-sm\">Work queue is empty</div>\n )}\n {items.map((item) => (\n <WorkQueueCard\n key={item.id}\n item={item}\n now={now}\n agentName={(id) => agentsById[id] ? displayName(agentsById[id]) : id.slice(-10)}\n onClaim={() => handleClaim(item)}\n onCancel={() => handleCancel(item)}\n onEvents={item.task ? () => handleEvents(item) : undefined}\n />\n ))}\n </div>\n </ScrollArea>\n </div>\n )\n}\n\nfunction WorkQueueCard({\n item, now, agentName, onClaim, onCancel, onEvents,\n}: {\n item: WorkQueueItem\n now: number\n agentName: (id: string) => string\n onClaim: () => void\n onCancel: () => void\n onEvents?: () => void\n}) {\n const severityClass = SEVERITY_COLORS[item.severity] || SEVERITY_COLORS.info\n const statusClass = STATUS_COLORS[item.status] || 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'\n\n return (\n <Card className={item.claimable ? 'ring-1 ring-orange-500/30' : ''}>\n <CardContent className=\"p-3\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap mb-1\">\n <span className=\"font-medium text-sm truncate\">{item.title}</span>\n <Badge variant=\"outline\" className={`text-xs px-1.5 py-0 border ${severityClass}`}>{item.severity}</Badge>\n <Badge variant=\"outline\" className={`text-xs px-1.5 py-0 border ${statusClass}`}>{item.status}</Badge>\n {item.sourceType === 'message' && (\n <Badge variant=\"outline\" className=\"text-xs px-1.5 py-0 border border-zinc-500/20 text-zinc-400\">msg</Badge>\n )}\n </div>\n {item.body && (\n <p className=\"text-xs text-muted-foreground line-clamp-2 mb-2\">{item.body}</p>\n )}\n <div className=\"flex items-center gap-3 text-xs text-muted-foreground flex-wrap\">\n {item.owner && (\n <span title=\"Owner\">\n <CheckCircle2 className=\"w-3 h-3 inline mr-1\" />{agentName(item.owner)}\n </span>\n )}\n {item.source && (\n <span className=\"font-mono\">{item.source}</span>\n )}\n <span title={String(item.updatedAt)}>\n <Calendar className=\"w-3 h-3 inline mr-1\" />{timeAgo(now, item.updatedAt)}\n </span>\n </div>\n </div>\n <div className=\"flex flex-col gap-1 shrink-0\">\n {item.claimable && (\n <Button size=\"sm\" className=\"h-7 text-xs bg-orange-600 hover:bg-orange-700 text-white\" onClick={onClaim}>\n <CheckCircle2 className=\"w-3.5 h-3.5 mr-1\" /> Claim\n </Button>\n )}\n {onEvents && (\n <Button size=\"sm\" variant=\"outline\" className=\"h-7 text-xs\" onClick={onEvents}>\n Events\n </Button>\n )}\n <Button size=\"sm\" variant=\"outline\" className=\"h-7 text-xs text-red-400 hover:text-red-300 hover:bg-red-500/10 border-red-500/20\" onClick={onCancel}>\n <X className=\"w-3.5 h-3.5 mr-1\" /> Cancel\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{At as t,Dn as n,Dt as r,Et as i,Gt as a,M as o,Mt as s,Nt as c,Un as l,W as u,an as d,bt as f,i as p,in as m,jn as h,m as g,nn as _,p as v,vt as y,wn as b,xt as x,yn as S}from"./lucide-react-ChOGD4vc.js";import{a as C,t as w}from"./store-CtKmqMfx.js";import{H as T,d as E,q as D}from"./display-ConJ9cJB.js";import{t as O}from"./badge-CFVr4fG9.js";import{t as k}from"./button-BuTu0MHx.js";import{t as ee}from"./copy-button-BOj6uOlU.js";import{i as A,n as j,r as M,t as N}from"./card-C3ErQ372.js";var P=e(l(),1),F=h(),I=new Set([`active`,`ready`,`conflict`,`review_requested`,`merge_planned`,`cleanup_requested`]),L=new Set([`active`,`ready`,`conflict`,`review_requested`,`merge_planned`]),R=new Set([`active`,`ready`,`review_requested`,`merge_planned`,`conflict`]),z={active:`bg-blue-500/10 text-blue-400 border-blue-500/20`,ready:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,conflict:`bg-red-500/10 text-red-400 border-red-500/20`,review_requested:`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`,merge_planned:`bg-indigo-500/10 text-indigo-400 border-indigo-500/20`,merged:`bg-violet-500/10 text-violet-400 border-violet-500/20`,abandoned:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`,cleanup_requested:`bg-orange-500/10 text-orange-400 border-orange-500/20`,cleaned:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`},B={active:`active`,ready:`ready`,conflict:`conflict`,review_requested:`review`,merge_planned:`merge planned`,merged:`merged`,abandoned:`abandoned`,cleanup_requested:`cleanup`,cleaned:`cleaned`},V={conflict:0,ready:1,review_requested:2,merge_planned:3,active:4,cleanup_requested:5,merged:6,abandoned:7,cleaned:8},H=new Set(D);function U(e){let t=e.split(`/`).filter(Boolean);return t.length<=3?e||`-`:`/${t.slice(-3).join(`/`)}`}function W(e,t){return e?E(e):t||`-`}function G(e){return z[e]||`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`}function K(e,t){let n=(V[e.status]??99)-(V[t.status]??99);return n===0?Number(t.updatedAt||0)-Number(e.updatedAt||0):n}function q(e){let t=new Map;for(let n of e){let e=n.repoRoot||n.sourceCwd||`unknown repo`;t.set(e,[...t.get(e)||[],n])}return[...t.entries()].map(([e,t])=>({repoRoot:e,workspaces:t.sort(K)})).sort((e,t)=>{let n=e.workspaces.some(e=>I.has(e.status));return n===t.workspaces.some(e=>I.has(e.status))?e.repoRoot.localeCompare(t.repoRoot):n?-1:1})}function J(e,t){return t===`all`?e:t===`cleaned`?e.filter(e=>H.has(e.status)):e.filter(e=>!H.has(e.status))}function Y({workspace:e,expanded:t,onToggleDetails:n}){let a=w(e=>e.workspaceAction),o=w(e=>e.purgeWorkspace),s=w(e=>e.fetchWorkspaceMergePreview),l=w(e=>e.openFilesAt),u=e.status===`cleaned`||e.status===`merged`||e.status===`abandoned`,f=u||e.status===`cleanup_requested`,h=e.worktreePath||e.sourceCwd||e.repoRoot,v=w(t=>t.workspaceGitState[e.id]),b=!!v&&v.available!==!1&&v.landed===!0,x=e.mode===`isolated`&&!!e.worktreePath&&R.has(e.status)&&!b;async function S(){await s(e.id),await a(e.id,`merge`)}return(0,F.jsxs)(`div`,{className:`flex flex-wrap justify-end gap-1.5`,children:[(0,F.jsx)(k,{size:`icon-sm`,variant:`ghost`,title:t?`Hide details`:`Show diff & timeline`,onClick:n,children:t?(0,F.jsx)(m,{className:`h-3.5 w-3.5`}):(0,F.jsx)(_,{className:`h-3.5 w-3.5`})}),(0,F.jsx)(k,{size:`icon-sm`,variant:`ghost`,title:`Open workspace`,disabled:!h||e.status===`cleaned`,onClick:()=>void l({path:h}),children:(0,F.jsx)(i,{className:`h-3.5 w-3.5`})}),(0,F.jsx)(ee,{value:h,label:`Copy path`,copiedLabel:`Copied path`,size:`icon-sm`,variant:`ghost`,disabled:!h}),(0,F.jsx)(k,{size:`icon-sm`,variant:`outline`,title:`Mark ready`,disabled:f||e.status===`ready`,onClick:()=>void a(e.id,`ready`),children:(0,F.jsx)(d,{className:`h-3.5 w-3.5`})}),(0,F.jsx)(k,{size:`icon-sm`,variant:`outline`,title:`Request review`,disabled:f||e.status===`review_requested`,onClick:()=>void a(e.id,`request-review`),children:(0,F.jsx)(c,{className:`h-3.5 w-3.5`})}),(0,F.jsx)(k,{size:`icon-sm`,variant:`outline`,title:`Mark merge planned`,disabled:f||e.status===`merge_planned`,onClick:()=>void a(e.id,`merge-plan`),children:(0,F.jsx)(r,{className:`h-3.5 w-3.5`})}),(0,F.jsx)(k,{size:`icon-sm`,variant:`default`,title:b?`Already merged into base — nothing to land`:`Merge & land work`,disabled:!x,onClick:()=>void S(),children:(0,F.jsx)(y,{className:`h-3.5 w-3.5`})}),(0,F.jsx)(k,{size:`icon-sm`,variant:`outline`,title:`Request cleanup (removes the worktree on the host)`,disabled:e.status===`cleaned`||e.status===`cleanup_requested`,onClick:()=>void a(e.id,`cleanup`),children:(0,F.jsx)(g,{className:`h-3.5 w-3.5`})}),u&&(0,F.jsx)(k,{size:`icon-sm`,variant:`ghost`,title:`Purge record (clears the row, does not touch disk)`,onClick:()=>void o(e.id),children:(0,F.jsx)(p,{className:`h-3.5 w-3.5`})})]})}function X({workspace:e,unmerged:t}){let n=w(t=>t.workspaceMergePreview[e.id]),r=w(e=>e.fetchWorkspaceMergePreview),i=t>0&&R.has(e.status);return(0,P.useEffect)(()=>{i&&n===void 0&&r(e.id)},[i,n,e.id,r]),!i||n===void 0||n.available===!1?null:n.conflict?(0,F.jsxs)(`span`,{className:`flex items-center gap-0.5 text-amber-400`,title:`Merging into ${n.baseRef||`base`} would conflict — resolve before merging`,children:[(0,F.jsx)(v,{className:`h-3 w-3`}),`conflict`]}):n.conflict===!1?(0,F.jsxs)(`span`,{className:`flex items-center gap-0.5 text-emerald-400`,title:`Clean to merge via ${n.strategy===`pr`?`PR`:`rebase + fast-forward`} into ${n.baseRef||`base`}`,children:[(0,F.jsx)(d,{className:`h-3 w-3`}),n.strategy===`pr`?`PR ready`:`clean`]}):null}function Z({workspace:e}){let n=C(),r=w(t=>t.workspaceGitState[e.id]),i=w(e=>e.fetchWorkspaceGitState),a=e.mode===`isolated`&&!!e.worktreePath&&L.has(e.status);if((0,P.useEffect)(()=>{a&&r===void 0&&i(e.id)},[a,r,e.id,i]),!a)return null;let s=(0,F.jsx)(k,{size:`icon-sm`,variant:`ghost`,className:`h-5 w-5`,title:`Refresh git state`,onClick:()=>void i(e.id),children:(0,F.jsx)(o,{className:`h-3 w-3`})});if(r===void 0)return(0,F.jsx)(`div`,{className:`flex items-center gap-1 text-[11px] text-muted-foreground`,children:`Loading git state…`});if(r.available===!1)return(0,F.jsxs)(`div`,{className:`flex items-center gap-1 text-[11px] text-muted-foreground`,title:r.reason,children:[(0,F.jsx)(`span`,{children:`git state unavailable`}),s]});if(r.missing)return(0,F.jsxs)(`div`,{className:`flex items-center gap-1 text-[11px] text-muted-foreground`,children:[`worktree gone `,s]});if(r.error)return(0,F.jsxs)(`div`,{className:`flex items-center gap-1 text-[11px] text-red-400`,title:r.error,children:[(0,F.jsx)(`span`,{children:`git error`}),s]});let c=r.ahead??0,l=r.landed===!0,u=l?0:r.unmergedAhead??c,p=r.behind??0,m=r.dirtyCount??0,h=u===0&&m===0&&!l,g=r.lastCommit;return(0,F.jsxs)(`div`,{className:`space-y-0.5`,children:[(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[11px]`,children:[h?(0,F.jsx)(`span`,{className:`text-muted-foreground`,children:`no unmerged work`}):(0,F.jsxs)(F.Fragment,{children:[l?(0,F.jsxs)(`span`,{className:`flex items-center gap-0.5 text-emerald-400`,title:`Work already landed in ${r.baseRef||`base`} via squash/cherry-pick — safe to clean up`,children:[(0,F.jsx)(d,{className:`h-3 w-3`}),`landed`]}):(0,F.jsxs)(`span`,{className:u>0?`flex items-center text-emerald-400`:`flex items-center text-muted-foreground`,title:c===u?`${u} commit(s) ahead of base`:`${u} unmerged commit(s) (${c} ahead of base)`,children:[(0,F.jsx)(S,{className:`h-3 w-3`}),u]}),(0,F.jsxs)(`span`,{className:p>0?`flex items-center text-amber-400`:`flex items-center text-muted-foreground`,title:`${p} commit(s) behind base`,children:[(0,F.jsx)(b,{className:`h-3 w-3`}),p]}),m>0&&(0,F.jsxs)(`span`,{className:`flex items-center text-orange-400`,title:`${m} uncommitted change(s)`,children:[(0,F.jsx)(t,{className:`h-3 w-3`}),m]}),!l&&(0,F.jsx)(X,{workspace:e,unmerged:u})]}),s]}),g&&(0,F.jsxs)(`div`,{className:`flex min-w-0 items-center gap-1 text-[11px] text-muted-foreground`,title:`${g.sha.slice(0,8)} — ${g.message}`,children:[(0,F.jsx)(f,{className:`h-3 w-3 shrink-0`}),(0,F.jsx)(`span`,{className:`truncate`,children:g.message||g.sha.slice(0,8)}),g.at?(0,F.jsxs)(`span`,{className:`shrink-0 opacity-70`,children:[`· `,T(n,g.at)]}):null]})]})}function Q({workspace:e}){let t=C(),n=typeof e.metadata?.mergedAt==`number`?e.metadata.mergedAt:void 0,r=[{label:`created`,at:e.createdAt},{label:`ready`,at:e.readyAt},{label:`merged`,at:n},{label:`cleaned`,at:e.cleanedAt}].filter(e=>typeof e.at==`number`&&e.at>0);return(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-muted-foreground`,children:[(0,F.jsxs)(`span`,{className:`flex items-center gap-1 font-medium text-foreground`,children:[(0,F.jsx)(a,{className:`h-3 w-3`}),`Timeline`]}),r.map((e,n)=>(0,F.jsxs)(`span`,{className:`flex items-center gap-1`,children:[n>0&&(0,F.jsx)(`span`,{className:`opacity-40`,children:`→`}),(0,F.jsx)(`span`,{className:`text-foreground`,children:e.label}),(0,F.jsx)(`span`,{title:new Date(e.at).toLocaleString(),children:T(t,e.at)})]},e.label))]})}function te(e){return e.startsWith(`+`)&&!e.startsWith(`+++`)?`text-emerald-400`:e.startsWith(`-`)&&!e.startsWith(`---`)?`text-red-400`:e.startsWith(`@@`)?`text-cyan-400`:e.startsWith(`diff `)||e.startsWith(`index `)||e.startsWith(`+++`)||e.startsWith(`---`)?`text-muted-foreground`:``}function ne({workspace:e}){let t=w(t=>t.workspaceDiff[e.id]),n=w(e=>e.fetchWorkspaceDiff),r=e.mode===`isolated`&&!!e.worktreePath;if((0,P.useEffect)(()=>{r&&t===void 0&&n(e.id)},[r,t,e.id,n]),!r)return null;if(t===void 0)return(0,F.jsx)(`div`,{className:`text-[11px] text-muted-foreground`,children:`Loading diff…`});if(t.available===!1)return(0,F.jsxs)(`div`,{className:`text-[11px] text-muted-foreground`,children:[`Diff unavailable`,t.reason?`: ${t.reason}`:``]});if(t.missing)return(0,F.jsx)(`div`,{className:`text-[11px] text-muted-foreground`,children:`Worktree no longer on disk`});if(t.error)return(0,F.jsxs)(`div`,{className:`text-[11px] text-red-400`,children:[`Diff error: `,t.error]});if(!t.files.length)return(0,F.jsxs)(`div`,{className:`text-[11px] text-muted-foreground`,children:[`No committed changes against `,t.baseRef||`base`]});let i=(0,F.jsx)(k,{size:`icon-sm`,variant:`ghost`,className:`h-5 w-5`,title:`Refresh diff`,onClick:()=>void n(e.id),children:(0,F.jsx)(o,{className:`h-3 w-3`})});return(0,F.jsxs)(`div`,{className:`space-y-1`,children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2 text-[11px] font-medium`,children:[(0,F.jsx)(s,{className:`h-3 w-3`}),(0,F.jsxs)(`span`,{children:[t.files.length,` file`,t.files.length===1?``:`s`,` vs `,t.baseRef||`base`]}),i]}),(0,F.jsx)(`ul`,{className:`space-y-0.5 text-[11px]`,children:t.files.map(e=>(0,F.jsxs)(`li`,{className:`flex items-center gap-2 font-mono`,children:[(0,F.jsx)(`span`,{className:`truncate`,title:e.path,children:e.path}),e.binary?(0,F.jsx)(`span`,{className:`shrink-0 text-muted-foreground`,children:`binary`}):(0,F.jsxs)(`span`,{className:`shrink-0`,children:[(0,F.jsxs)(`span`,{className:`text-emerald-400`,children:[`+`,e.additions??0]}),` `,(0,F.jsxs)(`span`,{className:`text-red-400`,children:[`−`,e.deletions??0]})]})]},e.path))}),t.patch&&(0,F.jsxs)(`pre`,{className:`max-h-80 overflow-auto rounded border border-border bg-muted/30 p-2 text-[10.5px] leading-relaxed`,children:[t.patch.split(`
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{At as t,Dn as n,Dt as r,Et as i,Gt as a,M as o,Mt as s,Nt as c,Un as l,W as u,an as d,bt as f,i as p,in as m,jn as h,m as g,nn as _,p as v,vt as y,wn as b,xt as x,yn as S}from"./lucide-react-ChOGD4vc.js";import{o as C,t as w}from"./store-B6qT5_de.js";import{H as T,d as E,q as D}from"./display-ConJ9cJB.js";import{t as O}from"./badge-CFVr4fG9.js";import{t as k}from"./button-BuTu0MHx.js";import{t as ee}from"./copy-button-BOj6uOlU.js";import{i as A,n as j,r as M,t as N}from"./card-C3ErQ372.js";var P=e(l(),1),F=h(),I=new Set([`active`,`ready`,`conflict`,`review_requested`,`merge_planned`,`cleanup_requested`]),L=new Set([`active`,`ready`,`conflict`,`review_requested`,`merge_planned`]),R=new Set([`active`,`ready`,`review_requested`,`merge_planned`,`conflict`]),z={active:`bg-blue-500/10 text-blue-400 border-blue-500/20`,ready:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,conflict:`bg-red-500/10 text-red-400 border-red-500/20`,review_requested:`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`,merge_planned:`bg-indigo-500/10 text-indigo-400 border-indigo-500/20`,merged:`bg-violet-500/10 text-violet-400 border-violet-500/20`,abandoned:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`,cleanup_requested:`bg-orange-500/10 text-orange-400 border-orange-500/20`,cleaned:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`},B={active:`active`,ready:`ready`,conflict:`conflict`,review_requested:`review`,merge_planned:`merge planned`,merged:`merged`,abandoned:`abandoned`,cleanup_requested:`cleanup`,cleaned:`cleaned`},V={conflict:0,ready:1,review_requested:2,merge_planned:3,active:4,cleanup_requested:5,merged:6,abandoned:7,cleaned:8},H=new Set(D);function U(e){let t=e.split(`/`).filter(Boolean);return t.length<=3?e||`-`:`/${t.slice(-3).join(`/`)}`}function W(e,t){return e?E(e):t||`-`}function G(e){return z[e]||`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`}function K(e,t){let n=(V[e.status]??99)-(V[t.status]??99);return n===0?Number(t.updatedAt||0)-Number(e.updatedAt||0):n}function q(e){let t=new Map;for(let n of e){let e=n.repoRoot||n.sourceCwd||`unknown repo`;t.set(e,[...t.get(e)||[],n])}return[...t.entries()].map(([e,t])=>({repoRoot:e,workspaces:t.sort(K)})).sort((e,t)=>{let n=e.workspaces.some(e=>I.has(e.status));return n===t.workspaces.some(e=>I.has(e.status))?e.repoRoot.localeCompare(t.repoRoot):n?-1:1})}function J(e,t){return t===`all`?e:t===`cleaned`?e.filter(e=>H.has(e.status)):e.filter(e=>!H.has(e.status))}function Y({workspace:e,expanded:t,onToggleDetails:n}){let a=w(e=>e.workspaceAction),o=w(e=>e.purgeWorkspace),s=w(e=>e.fetchWorkspaceMergePreview),l=w(e=>e.openFilesAt),u=e.status===`cleaned`||e.status===`merged`||e.status===`abandoned`,f=u||e.status===`cleanup_requested`,h=e.worktreePath||e.sourceCwd||e.repoRoot,v=w(t=>t.workspaceGitState[e.id]),b=!!v&&v.available!==!1&&v.landed===!0,x=e.mode===`isolated`&&!!e.worktreePath&&R.has(e.status)&&!b;async function S(){await s(e.id),await a(e.id,`merge`)}return(0,F.jsxs)(`div`,{className:`flex flex-wrap justify-end gap-1.5`,children:[(0,F.jsx)(k,{size:`icon-sm`,variant:`ghost`,title:t?`Hide details`:`Show diff & timeline`,onClick:n,children:t?(0,F.jsx)(m,{className:`h-3.5 w-3.5`}):(0,F.jsx)(_,{className:`h-3.5 w-3.5`})}),(0,F.jsx)(k,{size:`icon-sm`,variant:`ghost`,title:`Open workspace`,disabled:!h||e.status===`cleaned`,onClick:()=>void l({path:h}),children:(0,F.jsx)(i,{className:`h-3.5 w-3.5`})}),(0,F.jsx)(ee,{value:h,label:`Copy path`,copiedLabel:`Copied path`,size:`icon-sm`,variant:`ghost`,disabled:!h}),(0,F.jsx)(k,{size:`icon-sm`,variant:`outline`,title:`Mark ready`,disabled:f||e.status===`ready`,onClick:()=>void a(e.id,`ready`),children:(0,F.jsx)(d,{className:`h-3.5 w-3.5`})}),(0,F.jsx)(k,{size:`icon-sm`,variant:`outline`,title:`Request review`,disabled:f||e.status===`review_requested`,onClick:()=>void a(e.id,`request-review`),children:(0,F.jsx)(c,{className:`h-3.5 w-3.5`})}),(0,F.jsx)(k,{size:`icon-sm`,variant:`outline`,title:`Mark merge planned`,disabled:f||e.status===`merge_planned`,onClick:()=>void a(e.id,`merge-plan`),children:(0,F.jsx)(r,{className:`h-3.5 w-3.5`})}),(0,F.jsx)(k,{size:`icon-sm`,variant:`default`,title:b?`Already merged into base — nothing to land`:`Merge & land work`,disabled:!x,onClick:()=>void S(),children:(0,F.jsx)(y,{className:`h-3.5 w-3.5`})}),(0,F.jsx)(k,{size:`icon-sm`,variant:`outline`,title:`Request cleanup (removes the worktree on the host)`,disabled:e.status===`cleaned`||e.status===`cleanup_requested`,onClick:()=>void a(e.id,`cleanup`),children:(0,F.jsx)(g,{className:`h-3.5 w-3.5`})}),u&&(0,F.jsx)(k,{size:`icon-sm`,variant:`ghost`,title:`Purge record (clears the row, does not touch disk)`,onClick:()=>void o(e.id),children:(0,F.jsx)(p,{className:`h-3.5 w-3.5`})})]})}function X({workspace:e,unmerged:t}){let n=w(t=>t.workspaceMergePreview[e.id]),r=w(e=>e.fetchWorkspaceMergePreview),i=t>0&&R.has(e.status);return(0,P.useEffect)(()=>{i&&n===void 0&&r(e.id)},[i,n,e.id,r]),!i||n===void 0||n.available===!1?null:n.conflict?(0,F.jsxs)(`span`,{className:`flex items-center gap-0.5 text-amber-400`,title:`Merging into ${n.baseRef||`base`} would conflict — resolve before merging`,children:[(0,F.jsx)(v,{className:`h-3 w-3`}),`conflict`]}):n.conflict===!1?(0,F.jsxs)(`span`,{className:`flex items-center gap-0.5 text-emerald-400`,title:`Clean to merge via ${n.strategy===`pr`?`PR`:`rebase + fast-forward`} into ${n.baseRef||`base`}`,children:[(0,F.jsx)(d,{className:`h-3 w-3`}),n.strategy===`pr`?`PR ready`:`clean`]}):null}function Z({workspace:e}){let n=C(),r=w(t=>t.workspaceGitState[e.id]),i=w(e=>e.fetchWorkspaceGitState),a=e.mode===`isolated`&&!!e.worktreePath&&L.has(e.status);if((0,P.useEffect)(()=>{a&&r===void 0&&i(e.id)},[a,r,e.id,i]),!a)return null;let s=(0,F.jsx)(k,{size:`icon-sm`,variant:`ghost`,className:`h-5 w-5`,title:`Refresh git state`,onClick:()=>void i(e.id),children:(0,F.jsx)(o,{className:`h-3 w-3`})});if(r===void 0)return(0,F.jsx)(`div`,{className:`flex items-center gap-1 text-[11px] text-muted-foreground`,children:`Loading git state…`});if(r.available===!1)return(0,F.jsxs)(`div`,{className:`flex items-center gap-1 text-[11px] text-muted-foreground`,title:r.reason,children:[(0,F.jsx)(`span`,{children:`git state unavailable`}),s]});if(r.missing)return(0,F.jsxs)(`div`,{className:`flex items-center gap-1 text-[11px] text-muted-foreground`,children:[`worktree gone `,s]});if(r.error)return(0,F.jsxs)(`div`,{className:`flex items-center gap-1 text-[11px] text-red-400`,title:r.error,children:[(0,F.jsx)(`span`,{children:`git error`}),s]});let c=r.ahead??0,l=r.landed===!0,u=l?0:r.unmergedAhead??c,p=r.behind??0,m=r.dirtyCount??0,h=u===0&&m===0&&!l,g=r.lastCommit;return(0,F.jsxs)(`div`,{className:`space-y-0.5`,children:[(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[11px]`,children:[h?(0,F.jsx)(`span`,{className:`text-muted-foreground`,children:`no unmerged work`}):(0,F.jsxs)(F.Fragment,{children:[l?(0,F.jsxs)(`span`,{className:`flex items-center gap-0.5 text-emerald-400`,title:`Work already landed in ${r.baseRef||`base`} via squash/cherry-pick — safe to clean up`,children:[(0,F.jsx)(d,{className:`h-3 w-3`}),`landed`]}):(0,F.jsxs)(`span`,{className:u>0?`flex items-center text-emerald-400`:`flex items-center text-muted-foreground`,title:c===u?`${u} commit(s) ahead of base`:`${u} unmerged commit(s) (${c} ahead of base)`,children:[(0,F.jsx)(S,{className:`h-3 w-3`}),u]}),(0,F.jsxs)(`span`,{className:p>0?`flex items-center text-amber-400`:`flex items-center text-muted-foreground`,title:`${p} commit(s) behind base`,children:[(0,F.jsx)(b,{className:`h-3 w-3`}),p]}),m>0&&(0,F.jsxs)(`span`,{className:`flex items-center text-orange-400`,title:`${m} uncommitted change(s)`,children:[(0,F.jsx)(t,{className:`h-3 w-3`}),m]}),!l&&(0,F.jsx)(X,{workspace:e,unmerged:u})]}),s]}),g&&(0,F.jsxs)(`div`,{className:`flex min-w-0 items-center gap-1 text-[11px] text-muted-foreground`,title:`${g.sha.slice(0,8)} — ${g.message}`,children:[(0,F.jsx)(f,{className:`h-3 w-3 shrink-0`}),(0,F.jsx)(`span`,{className:`truncate`,children:g.message||g.sha.slice(0,8)}),g.at?(0,F.jsxs)(`span`,{className:`shrink-0 opacity-70`,children:[`· `,T(n,g.at)]}):null]})]})}function Q({workspace:e}){let t=C(),n=typeof e.metadata?.mergedAt==`number`?e.metadata.mergedAt:void 0,r=[{label:`created`,at:e.createdAt},{label:`ready`,at:e.readyAt},{label:`merged`,at:n},{label:`cleaned`,at:e.cleanedAt}].filter(e=>typeof e.at==`number`&&e.at>0);return(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-muted-foreground`,children:[(0,F.jsxs)(`span`,{className:`flex items-center gap-1 font-medium text-foreground`,children:[(0,F.jsx)(a,{className:`h-3 w-3`}),`Timeline`]}),r.map((e,n)=>(0,F.jsxs)(`span`,{className:`flex items-center gap-1`,children:[n>0&&(0,F.jsx)(`span`,{className:`opacity-40`,children:`→`}),(0,F.jsx)(`span`,{className:`text-foreground`,children:e.label}),(0,F.jsx)(`span`,{title:new Date(e.at).toLocaleString(),children:T(t,e.at)})]},e.label))]})}function te(e){return e.startsWith(`+`)&&!e.startsWith(`+++`)?`text-emerald-400`:e.startsWith(`-`)&&!e.startsWith(`---`)?`text-red-400`:e.startsWith(`@@`)?`text-cyan-400`:e.startsWith(`diff `)||e.startsWith(`index `)||e.startsWith(`+++`)||e.startsWith(`---`)?`text-muted-foreground`:``}function ne({workspace:e}){let t=w(t=>t.workspaceDiff[e.id]),n=w(e=>e.fetchWorkspaceDiff),r=e.mode===`isolated`&&!!e.worktreePath;if((0,P.useEffect)(()=>{r&&t===void 0&&n(e.id)},[r,t,e.id,n]),!r)return null;if(t===void 0)return(0,F.jsx)(`div`,{className:`text-[11px] text-muted-foreground`,children:`Loading diff…`});if(t.available===!1)return(0,F.jsxs)(`div`,{className:`text-[11px] text-muted-foreground`,children:[`Diff unavailable`,t.reason?`: ${t.reason}`:``]});if(t.missing)return(0,F.jsx)(`div`,{className:`text-[11px] text-muted-foreground`,children:`Worktree no longer on disk`});if(t.error)return(0,F.jsxs)(`div`,{className:`text-[11px] text-red-400`,children:[`Diff error: `,t.error]});if(!t.files.length)return(0,F.jsxs)(`div`,{className:`text-[11px] text-muted-foreground`,children:[`No committed changes against `,t.baseRef||`base`]});let i=(0,F.jsx)(k,{size:`icon-sm`,variant:`ghost`,className:`h-5 w-5`,title:`Refresh diff`,onClick:()=>void n(e.id),children:(0,F.jsx)(o,{className:`h-3 w-3`})});return(0,F.jsxs)(`div`,{className:`space-y-1`,children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2 text-[11px] font-medium`,children:[(0,F.jsx)(s,{className:`h-3 w-3`}),(0,F.jsxs)(`span`,{children:[t.files.length,` file`,t.files.length===1?``:`s`,` vs `,t.baseRef||`base`]}),i]}),(0,F.jsx)(`ul`,{className:`space-y-0.5 text-[11px]`,children:t.files.map(e=>(0,F.jsxs)(`li`,{className:`flex items-center gap-2 font-mono`,children:[(0,F.jsx)(`span`,{className:`truncate`,title:e.path,children:e.path}),e.binary?(0,F.jsx)(`span`,{className:`shrink-0 text-muted-foreground`,children:`binary`}):(0,F.jsxs)(`span`,{className:`shrink-0`,children:[(0,F.jsxs)(`span`,{className:`text-emerald-400`,children:[`+`,e.additions??0]}),` `,(0,F.jsxs)(`span`,{className:`text-red-400`,children:[`−`,e.deletions??0]})]})]},e.path))}),t.patch&&(0,F.jsxs)(`pre`,{className:`max-h-80 overflow-auto rounded border border-border bg-muted/30 p-2 text-[10.5px] leading-relaxed`,children:[t.patch.split(`
|
|
2
2
|
`).map((e,t)=>(0,F.jsx)(`div`,{className:te(e),children:e||` `},t)),t.truncated&&(0,F.jsx)(`div`,{className:`mt-1 text-muted-foreground`,children:`… patch truncated`})]})]})}function $({workspace:e}){return(0,F.jsxs)(`div`,{className:`space-y-3 border-t border-dashed border-border bg-muted/20 px-3 py-3`,children:[(0,F.jsx)(Q,{workspace:e}),(0,F.jsx)(ne,{workspace:e})]})}function re(){let e=w(e=>e.workspaceOrphans),t=w(e=>e.fetchWorkspaceOrphans),r=w(e=>e.reclaimWorkspaceOrphan);return(0,P.useEffect)(()=>{t()},[t]),e.length?(0,F.jsxs)(N,{className:`border-amber-500/30`,children:[(0,F.jsxs)(M,{className:`px-4 py-3`,children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,F.jsx)(u,{className:`h-4 w-4 text-amber-400`}),(0,F.jsx)(A,{className:`text-sm font-medium`,children:`Orphaned worktrees`}),(0,F.jsx)(O,{variant:`outline`,className:`border-amber-500/30 text-amber-400 text-[10px]`,children:e.length}),(0,F.jsx)(k,{size:`icon-sm`,variant:`ghost`,className:`ml-auto h-6 w-6`,title:`Rescan`,onClick:()=>void t(),children:(0,F.jsx)(o,{className:`h-3.5 w-3.5`})})]}),(0,F.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Agent worktrees on disk with no live workspace. Reclaim removes them from the host.`})]}),(0,F.jsx)(j,{className:`p-0`,children:e.map(e=>(0,F.jsxs)(`div`,{className:`flex items-center gap-3 border-t border-border px-4 py-2 text-xs`,children:[(0,F.jsx)(x,{className:`h-3.5 w-3.5 shrink-0 text-muted-foreground`}),(0,F.jsx)(`span`,{className:`truncate font-medium`,title:e.branch,children:e.branch||`(detached)`}),(0,F.jsx)(`span`,{className:`truncate font-mono text-muted-foreground`,title:e.worktreePath,children:U(e.worktreePath)}),e.hadTerminalRow&&(0,F.jsx)(O,{variant:`outline`,className:`text-[10px]`,children:`cleanup failed`}),(0,F.jsxs)(k,{size:`sm`,variant:`outline`,className:`ml-auto h-7 text-xs`,onClick:()=>void r(e),children:[(0,F.jsx)(n,{className:`mr-1 h-3.5 w-3.5`}),`Reclaim`]})]},e.worktreePath))})]}):null}function ie(e){let t=e.match(/\/pull\/(\d+)/);return t?`PR #${t[1]}`:`View PR`}function ae({workspace:e}){let t=e.metadata?.mergeResult,n=t?.prUrl,r=t?.error||e.metadata?.mergeError;return!n&&!r?null:(0,F.jsxs)(F.Fragment,{children:[n&&(0,F.jsxs)(`a`,{href:n,target:`_blank`,rel:`noreferrer`,title:n,className:`inline-flex items-center gap-0.5 rounded border border-violet-500/30 px-1.5 py-0 text-[10px] text-violet-400 hover:text-violet-300`,children:[(0,F.jsx)(y,{className:`h-2.5 w-2.5`}),ie(n)]}),r&&(0,F.jsxs)(O,{variant:`outline`,className:`max-w-[16rem] border-amber-500/30 text-amber-400 text-[10px]`,title:r,children:[(0,F.jsx)(v,{className:`mr-0.5 h-2.5 w-2.5 shrink-0`}),(0,F.jsxs)(`span`,{className:`truncate`,children:[`merge: `,r]})]})]})}function oe({workspace:e}){let t=C(),[n,r]=(0,P.useState)(!1),i=w(e=>e.agentsById),a=w(t=>t.workspaceFocusId===e.id),o=e.ownerAgentId?i[e.ownerAgentId]:void 0,s=e.stewardAgentId?i[e.stewardAgentId]:void 0,c=e.worktreePath||e.sourceCwd||e.repoRoot,l=(0,P.useRef)(null);return(0,P.useEffect)(()=>{a&&l.current?.scrollIntoView({behavior:`smooth`,block:`center`})},[a]),(0,F.jsxs)(`div`,{ref:l,className:`border-t border-border ${a?`bg-primary/5 ring-1 ring-inset ring-primary/40`:``}`,children:[(0,F.jsxs)(`div`,{className:`grid gap-3 px-3 py-3 text-sm lg:grid-cols-[minmax(180px,1.2fr)_minmax(220px,1.5fr)_minmax(160px,1fr)_minmax(170px,auto)]`,children:[(0,F.jsxs)(`div`,{className:`min-w-0 space-y-1`,children:[(0,F.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,F.jsx)(x,{className:`h-3.5 w-3.5 shrink-0 text-muted-foreground`}),(0,F.jsx)(`span`,{className:`truncate font-medium`,title:e.branch||e.id,children:e.branch||e.id})]}),(0,F.jsxs)(`div`,{className:`flex flex-wrap gap-1`,children:[(0,F.jsx)(O,{variant:`outline`,className:`text-[10px] ${e.mode===`isolated`?`border-sky-500/30 text-sky-400`:`border-zinc-500/30 text-zinc-400`}`,children:e.mode}),e.requestedMode&&e.requestedMode!==e.mode&&(0,F.jsxs)(O,{variant:`outline`,className:`text-[10px]`,children:[`requested `,e.requestedMode]}),(0,F.jsx)(O,{variant:`outline`,className:`text-[10px] ${G(e.status)}`,children:B[e.status]||e.status}),(0,F.jsx)(ae,{workspace:e})]})]}),(0,F.jsxs)(`div`,{className:`min-w-0 space-y-1`,children:[(0,F.jsx)(`div`,{className:`truncate font-mono text-xs`,title:c,children:U(c)}),(0,F.jsx)(`div`,{className:`truncate text-xs text-muted-foreground`,title:e.sourceCwd,children:e.sourceCwd}),(0,F.jsx)(Z,{workspace:e})]}),(0,F.jsxs)(`div`,{className:`grid grid-cols-[4.5rem_minmax(0,1fr)] gap-x-2 gap-y-1 text-xs`,children:[(0,F.jsx)(`span`,{className:`text-muted-foreground`,children:`Owner`}),(0,F.jsx)(`span`,{className:`truncate`,title:e.ownerAgentId,children:W(o,e.ownerPolicyName||e.ownerAgentId)}),(0,F.jsx)(`span`,{className:`text-muted-foreground`,children:`Steward`}),(0,F.jsx)(`span`,{className:`truncate`,title:e.stewardAgentId,children:W(s,e.stewardAgentId)}),(0,F.jsx)(`span`,{className:`text-muted-foreground`,children:`Updated`}),(0,F.jsx)(`span`,{children:T(t,e.updatedAt)}),e.baseRef&&(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`span`,{className:`text-muted-foreground`,children:`Base`}),(0,F.jsx)(`span`,{className:`truncate`,title:e.baseSha,children:e.baseRef})]})]}),(0,F.jsx)(Y,{workspace:e,expanded:n,onToggleDetails:()=>r(e=>!e)})]}),n&&(0,F.jsx)($,{workspace:e})]})}function se({repoRoot:e,workspaces:t}){let n=t.filter(e=>I.has(e.status)).length,r=t.filter(e=>e.status===`ready`||e.status===`review_requested`||e.status===`merge_planned`).length,i=t.filter(e=>e.status===`conflict`).length;return(0,F.jsxs)(N,{children:[(0,F.jsx)(M,{className:`px-4 py-3`,children:(0,F.jsx)(`div`,{className:`flex min-w-0 items-start gap-3`,children:(0,F.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,F.jsx)(A,{className:`truncate text-sm font-medium`,title:e,children:e}),(0,F.jsxs)(`div`,{className:`mt-1 flex flex-wrap gap-1.5`,children:[(0,F.jsxs)(O,{variant:`outline`,className:`text-[10px]`,children:[t.length,` total`]}),(0,F.jsxs)(O,{variant:`outline`,className:`text-[10px]`,children:[n,` live`]}),r>0&&(0,F.jsxs)(O,{variant:`outline`,className:`border-emerald-500/20 text-emerald-400 text-[10px]`,children:[r,` merge signal`]}),i>0&&(0,F.jsxs)(O,{variant:`outline`,className:`border-red-500/20 text-red-400 text-[10px]`,children:[i,` conflict`]})]})]})})}),(0,F.jsx)(j,{className:`p-0`,children:t.map(e=>(0,F.jsx)(oe,{workspace:e},e.id))})]})}function ce(){let[e,t]=(0,P.useState)(`active`),n=w(e=>e.workspaces),r=w(e=>e.set),i=w(e=>e.workspaceFocusId);(0,P.useEffect)(()=>{i&&t(`all`)},[i]),(0,P.useEffect)(()=>()=>{r({workspaceFocusId:null})},[r]);let a=(0,P.useMemo)(()=>J(n,e),[n,e]),o=(0,P.useMemo)(()=>q(a),[a]),s=n.filter(e=>I.has(e.status)).length,c=n.filter(e=>e.mode===`isolated`).length,l=n.filter(e=>e.status===`ready`||e.status===`review_requested`||e.status===`merge_planned`).length,u=n.filter(e=>e.status===`conflict`).length,d=n.filter(e=>H.has(e.status)).length,f=[{key:`active`,label:`Active`,count:n.length-d},{key:`cleaned`,label:`Done`,count:d},{key:`all`,label:`All`,count:n.length}];return(0,F.jsxs)(`div`,{className:`space-y-4`,children:[(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`h1`,{className:`text-2xl font-semibold`,children:`Workspaces`}),(0,F.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Agent worktrees, ownership, and merge readiness.`})]}),(0,F.jsxs)(`div`,{className:`ml-auto flex flex-wrap gap-1.5`,children:[(0,F.jsxs)(O,{variant:`outline`,children:[c,` isolated`]}),(0,F.jsxs)(O,{variant:`outline`,children:[s,` live`]}),(0,F.jsxs)(O,{variant:`outline`,children:[l,` ready`]}),u>0&&(0,F.jsxs)(O,{variant:`outline`,className:`border-red-500/20 text-red-400`,children:[u,` conflict`]})]})]}),(0,F.jsx)(re,{}),(0,F.jsx)(`div`,{className:`flex flex-wrap items-center gap-2`,children:(0,F.jsx)(`div`,{className:`flex rounded-md border border-border bg-card p-1`,children:f.map(n=>(0,F.jsxs)(k,{size:`sm`,variant:e===n.key?`secondary`:`ghost`,className:`h-7 text-xs`,onClick:()=>t(n.key),children:[n.label,(0,F.jsx)(`span`,{className:`ml-1 text-muted-foreground`,children:n.count})]},n.key))})}),o.length===0?(0,F.jsxs)(`div`,{className:`rounded-md border border-border px-3 py-12 text-center text-sm text-muted-foreground`,children:[`No `,e===`all`?``:e,` workspaces`]}):(0,F.jsx)(`div`,{className:`space-y-3`,children:o.map(e=>(0,F.jsx)(se,{repoRoot:e.repoRoot,workspaces:e.workspaces},e.repoRoot))})]})}export{ce as WorkspacesView};
|
|
3
|
-
//# sourceMappingURL=workspaces-
|
|
3
|
+
//# sourceMappingURL=workspaces-DqVr0uN7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaces-BD1P6J21.js","names":[],"sources":["../../dashboard/src/components/views/workspaces.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react'\nimport { ArchiveRestore, ArrowDown, ArrowUp, Check, ChevronDown, ChevronRight, Clock, Eye, FileDiff, FilePen, Flag, FolderOpen, GitBranch, GitCommitHorizontal, GitMerge, PackageOpen, RefreshCw, TriangleAlert, Trash2, X } from 'lucide-react'\nimport { TERMINAL_WORKSPACE_STATUS_VALUES } from 'agent-relay-sdk/types'\nimport { useRelayStore, useNow } from '@/store'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { CopyButton } from '@/components/shared/copy-button'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { displayName, timeAgo } from '@/lib/display'\nimport type { Agent, WorkspaceOrphan, WorkspaceRecord, WorkspaceStatus } from '@/types'\n\nconst LIVE_STATUSES = new Set<WorkspaceStatus>(['active', 'ready', 'conflict', 'review_requested', 'merge_planned', 'cleanup_requested'])\nconst GIT_STATE_STATUSES = new Set<WorkspaceStatus>(['active', 'ready', 'conflict', 'review_requested', 'merge_planned'])\nconst MERGEABLE_STATUSES = new Set<WorkspaceStatus>(['active', 'ready', 'review_requested', 'merge_planned', 'conflict'])\n\ntype WorkspaceFilter = 'active' | 'cleaned' | 'all'\n\nconst STATUS_CLASS: Record<WorkspaceStatus, string> = {\n active: 'bg-blue-500/10 text-blue-400 border-blue-500/20',\n ready: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n conflict: 'bg-red-500/10 text-red-400 border-red-500/20',\n review_requested: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n merge_planned: 'bg-indigo-500/10 text-indigo-400 border-indigo-500/20',\n merged: 'bg-violet-500/10 text-violet-400 border-violet-500/20',\n abandoned: 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20',\n cleanup_requested: 'bg-orange-500/10 text-orange-400 border-orange-500/20',\n cleaned: 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20',\n}\n\nconst STATUS_LABEL: Record<WorkspaceStatus, string> = {\n active: 'active',\n ready: 'ready',\n conflict: 'conflict',\n review_requested: 'review',\n merge_planned: 'merge planned',\n merged: 'merged',\n abandoned: 'abandoned',\n cleanup_requested: 'cleanup',\n cleaned: 'cleaned',\n}\n\nconst STATUS_ORDER: Record<WorkspaceStatus, number> = {\n conflict: 0,\n ready: 1,\n review_requested: 2,\n merge_planned: 3,\n active: 4,\n cleanup_requested: 5,\n merged: 6,\n abandoned: 7,\n cleaned: 8,\n}\n\nconst TERMINAL_STATUSES = new Set<WorkspaceStatus>(TERMINAL_WORKSPACE_STATUS_VALUES)\n\nfunction shortPath(path: string): string {\n const parts = path.split('/').filter(Boolean)\n if (parts.length <= 3) return path || '-'\n return `/${parts.slice(-3).join('/')}`\n}\n\nfunction agentLabel(agent: Agent | undefined, fallback: string | undefined): string {\n if (agent) return displayName(agent)\n return fallback || '-'\n}\n\nfunction statusTone(status: WorkspaceStatus): string {\n return STATUS_CLASS[status] || 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'\n}\n\nfunction workspaceSort(a: WorkspaceRecord, b: WorkspaceRecord): number {\n const status = (STATUS_ORDER[a.status] ?? 99) - (STATUS_ORDER[b.status] ?? 99)\n if (status !== 0) return status\n return Number(b.updatedAt || 0) - Number(a.updatedAt || 0)\n}\n\nfunction groupWorkspaces(workspaces: WorkspaceRecord[]): Array<{ repoRoot: string; workspaces: WorkspaceRecord[] }> {\n const grouped = new Map<string, WorkspaceRecord[]>()\n for (const workspace of workspaces) {\n const key = workspace.repoRoot || workspace.sourceCwd || 'unknown repo'\n grouped.set(key, [...(grouped.get(key) || []), workspace])\n }\n return [...grouped.entries()]\n .map(([repoRoot, items]) => ({ repoRoot, workspaces: items.sort(workspaceSort) }))\n .sort((a, b) => {\n const liveA = a.workspaces.some((item) => LIVE_STATUSES.has(item.status))\n const liveB = b.workspaces.some((item) => LIVE_STATUSES.has(item.status))\n if (liveA !== liveB) return liveA ? -1 : 1\n return a.repoRoot.localeCompare(b.repoRoot)\n })\n}\n\nfunction filterWorkspaces(workspaces: WorkspaceRecord[], filter: WorkspaceFilter): WorkspaceRecord[] {\n if (filter === 'all') return workspaces\n if (filter === 'cleaned') return workspaces.filter((workspace) => TERMINAL_STATUSES.has(workspace.status))\n return workspaces.filter((workspace) => !TERMINAL_STATUSES.has(workspace.status))\n}\n\nfunction WorkspaceActions({ workspace, expanded, onToggleDetails }: { workspace: WorkspaceRecord; expanded: boolean; onToggleDetails: () => void }) {\n const workspaceAction = useRelayStore((s) => s.workspaceAction)\n const purgeWorkspace = useRelayStore((s) => s.purgeWorkspace)\n const fetchWorkspaceMergePreview = useRelayStore((s) => s.fetchWorkspaceMergePreview)\n const openFilesAt = useRelayStore((s) => s.openFilesAt)\n const terminal = workspace.status === 'cleaned' || workspace.status === 'merged' || workspace.status === 'abandoned'\n const disabled = terminal || workspace.status === 'cleanup_requested'\n const openPath = workspace.worktreePath || workspace.sourceCwd || workspace.repoRoot\n // Already-landed work (squash/cherry-pick) has nothing left to merge — a merge\n // would open a duplicate/no-op PR, so disable the button.\n const gitState = useRelayStore((s) => s.workspaceGitState[workspace.id])\n const landed = !!gitState && gitState.available !== false && gitState.landed === true\n const mergeable = workspace.mode === 'isolated' && Boolean(workspace.worktreePath) && MERGEABLE_STATUSES.has(workspace.status) && !landed\n\n // Refresh the preview so the confirm dialog states the strategy/outcome, then\n // dispatch the real merge command.\n async function merge() {\n await fetchWorkspaceMergePreview(workspace.id)\n await workspaceAction(workspace.id, 'merge')\n }\n\n return (\n <div className=\"flex flex-wrap justify-end gap-1.5\">\n <Button size=\"icon-sm\" variant=\"ghost\" title={expanded ? 'Hide details' : 'Show diff & timeline'} onClick={onToggleDetails}>\n {expanded ? <ChevronDown className=\"h-3.5 w-3.5\" /> : <ChevronRight className=\"h-3.5 w-3.5\" />}\n </Button>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n title=\"Open workspace\"\n disabled={!openPath || workspace.status === 'cleaned'}\n onClick={() => void openFilesAt({ path: openPath })}\n >\n <FolderOpen className=\"h-3.5 w-3.5\" />\n </Button>\n <CopyButton value={openPath} label=\"Copy path\" copiedLabel=\"Copied path\" size=\"icon-sm\" variant=\"ghost\" disabled={!openPath} />\n <Button size=\"icon-sm\" variant=\"outline\" title=\"Mark ready\" disabled={disabled || workspace.status === 'ready'} onClick={() => void workspaceAction(workspace.id, 'ready')}>\n <Check className=\"h-3.5 w-3.5\" />\n </Button>\n <Button size=\"icon-sm\" variant=\"outline\" title=\"Request review\" disabled={disabled || workspace.status === 'review_requested'} onClick={() => void workspaceAction(workspace.id, 'request-review')}>\n <Eye className=\"h-3.5 w-3.5\" />\n </Button>\n <Button size=\"icon-sm\" variant=\"outline\" title=\"Mark merge planned\" disabled={disabled || workspace.status === 'merge_planned'} onClick={() => void workspaceAction(workspace.id, 'merge-plan')}>\n <Flag className=\"h-3.5 w-3.5\" />\n </Button>\n <Button size=\"icon-sm\" variant=\"default\" title={landed ? 'Already merged into base — nothing to land' : 'Merge & land work'} disabled={!mergeable} onClick={() => void merge()}>\n <GitMerge className=\"h-3.5 w-3.5\" />\n </Button>\n <Button size=\"icon-sm\" variant=\"outline\" title=\"Request cleanup (removes the worktree on the host)\" disabled={workspace.status === 'cleaned' || workspace.status === 'cleanup_requested'} onClick={() => void workspaceAction(workspace.id, 'cleanup')}>\n <Trash2 className=\"h-3.5 w-3.5\" />\n </Button>\n {terminal && (\n <Button size=\"icon-sm\" variant=\"ghost\" title=\"Purge record (clears the row, does not touch disk)\" onClick={() => void purgeWorkspace(workspace.id)}>\n <X className=\"h-3.5 w-3.5\" />\n </Button>\n )}\n </div>\n )\n}\n\n// Pre-merge conflict hint, shown next to git state when the worktree has work to\n// land. Lazily fetched from the owning host (an external sync), so an effect is\n// the right tool. Reports clean-ff vs would-conflict before the user merges.\nfunction MergePreviewHint({ workspace, unmerged }: { workspace: WorkspaceRecord; unmerged: number }) {\n const preview = useRelayStore((s) => s.workspaceMergePreview[workspace.id])\n const fetchWorkspaceMergePreview = useRelayStore((s) => s.fetchWorkspaceMergePreview)\n const eligible = unmerged > 0 && MERGEABLE_STATUSES.has(workspace.status)\n\n useEffect(() => {\n if (eligible && preview === undefined) void fetchWorkspaceMergePreview(workspace.id)\n }, [eligible, preview, workspace.id, fetchWorkspaceMergePreview])\n\n if (!eligible || preview === undefined || preview.available === false) return null\n if (preview.conflict) {\n return (\n <span className=\"flex items-center gap-0.5 text-amber-400\" title={`Merging into ${preview.baseRef || 'base'} would conflict — resolve before merging`}>\n <TriangleAlert className=\"h-3 w-3\" />conflict\n </span>\n )\n }\n if (preview.conflict === false) {\n return (\n <span className=\"flex items-center gap-0.5 text-emerald-400\" title={`Clean to merge via ${preview.strategy === 'pr' ? 'PR' : 'rebase + fast-forward'} into ${preview.baseRef || 'base'}`}>\n <Check className=\"h-3 w-3\" />{preview.strategy === 'pr' ? 'PR ready' : 'clean'}\n </span>\n )\n }\n return null\n}\n\n// Live git state of the worktree, fetched lazily from the owning host. This is\n// an external-system sync (host git over the orchestrator proxy), so an effect\n// is the right tool — it does not derive state from props.\nfunction GitState({ workspace }: { workspace: WorkspaceRecord }) {\n const now = useNow()\n const gitState = useRelayStore((s) => s.workspaceGitState[workspace.id])\n const fetchWorkspaceGitState = useRelayStore((s) => s.fetchWorkspaceGitState)\n const eligible = workspace.mode === 'isolated' && Boolean(workspace.worktreePath) && GIT_STATE_STATUSES.has(workspace.status)\n\n useEffect(() => {\n if (eligible && gitState === undefined) void fetchWorkspaceGitState(workspace.id)\n }, [eligible, gitState, workspace.id, fetchWorkspaceGitState])\n\n if (!eligible) return null\n\n const refresh = (\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n className=\"h-5 w-5\"\n title=\"Refresh git state\"\n onClick={() => void fetchWorkspaceGitState(workspace.id)}\n >\n <RefreshCw className=\"h-3 w-3\" />\n </Button>\n )\n\n if (gitState === undefined) {\n return <div className=\"flex items-center gap-1 text-[11px] text-muted-foreground\">Loading git state…</div>\n }\n if (gitState.available === false) {\n return (\n <div className=\"flex items-center gap-1 text-[11px] text-muted-foreground\" title={gitState.reason}>\n <span>git state unavailable</span>\n {refresh}\n </div>\n )\n }\n if (gitState.missing) {\n return <div className=\"flex items-center gap-1 text-[11px] text-muted-foreground\">worktree gone {refresh}</div>\n }\n if (gitState.error) {\n return (\n <div className=\"flex items-center gap-1 text-[11px] text-red-400\" title={gitState.error}>\n <span>git error</span>\n {refresh}\n </div>\n )\n }\n\n const ahead = gitState.ahead ?? 0\n // Work squash/cherry-pick-merged into base leaves `ahead` positive but is\n // effectively landed — surface the unmerged count, not the raw ahead.\n const landed = gitState.landed === true\n const unmerged = landed ? 0 : (gitState.unmergedAhead ?? ahead)\n const behind = gitState.behind ?? 0\n const dirty = gitState.dirtyCount ?? 0\n const clean = unmerged === 0 && dirty === 0 && !landed\n const commit = gitState.lastCommit\n\n return (\n <div className=\"space-y-0.5\">\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[11px]\">\n {clean ? (\n <span className=\"text-muted-foreground\">no unmerged work</span>\n ) : (\n <>\n {landed ? (\n <span className=\"flex items-center gap-0.5 text-emerald-400\" title={`Work already landed in ${gitState.baseRef || 'base'} via squash/cherry-pick — safe to clean up`}>\n <Check className=\"h-3 w-3\" />landed\n </span>\n ) : (\n <span className={unmerged > 0 ? 'flex items-center text-emerald-400' : 'flex items-center text-muted-foreground'} title={ahead !== unmerged ? `${unmerged} unmerged commit(s) (${ahead} ahead of base)` : `${unmerged} commit(s) ahead of base`}>\n <ArrowUp className=\"h-3 w-3\" />{unmerged}\n </span>\n )}\n <span className={behind > 0 ? 'flex items-center text-amber-400' : 'flex items-center text-muted-foreground'} title={`${behind} commit(s) behind base`}>\n <ArrowDown className=\"h-3 w-3\" />{behind}\n </span>\n {dirty > 0 && (\n <span className=\"flex items-center text-orange-400\" title={`${dirty} uncommitted change(s)`}>\n <FilePen className=\"h-3 w-3\" />{dirty}\n </span>\n )}\n {!landed && <MergePreviewHint workspace={workspace} unmerged={unmerged} />}\n </>\n )}\n {refresh}\n </div>\n {commit && (\n <div className=\"flex min-w-0 items-center gap-1 text-[11px] text-muted-foreground\" title={`${commit.sha.slice(0, 8)} — ${commit.message}`}>\n <GitCommitHorizontal className=\"h-3 w-3 shrink-0\" />\n <span className=\"truncate\">{commit.message || commit.sha.slice(0, 8)}</span>\n {commit.at ? <span className=\"shrink-0 opacity-70\">· {timeAgo(now, commit.at)}</span> : null}\n </div>\n )}\n </div>\n )\n}\n\n// Lifecycle timeline from the timestamps the relay already records:\n// created → ready → merged → cleaned. Surfaces history without a drawer.\nfunction WorkspaceTimeline({ workspace }: { workspace: WorkspaceRecord }) {\n const now = useNow()\n const mergedAt = typeof workspace.metadata?.mergedAt === 'number' ? (workspace.metadata.mergedAt as number) : undefined\n const steps = [\n { label: 'created', at: workspace.createdAt },\n { label: 'ready', at: workspace.readyAt },\n { label: 'merged', at: mergedAt },\n { label: 'cleaned', at: workspace.cleanedAt },\n ].filter((step): step is { label: string; at: number } => typeof step.at === 'number' && step.at > 0)\n\n return (\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-muted-foreground\">\n <span className=\"flex items-center gap-1 font-medium text-foreground\"><Clock className=\"h-3 w-3\" />Timeline</span>\n {steps.map((step, i) => (\n <span key={step.label} className=\"flex items-center gap-1\">\n {i > 0 && <span className=\"opacity-40\">→</span>}\n <span className=\"text-foreground\">{step.label}</span>\n <span title={new Date(step.at).toLocaleString()}>{timeAgo(now, step.at)}</span>\n </span>\n ))}\n </div>\n )\n}\n\nfunction diffLineClass(line: string): string {\n if (line.startsWith('+') && !line.startsWith('+++')) return 'text-emerald-400'\n if (line.startsWith('-') && !line.startsWith('---')) return 'text-red-400'\n if (line.startsWith('@@')) return 'text-cyan-400'\n if (line.startsWith('diff ') || line.startsWith('index ') || line.startsWith('+++') || line.startsWith('---')) return 'text-muted-foreground'\n return ''\n}\n\n// Diff of committed work against base, lazily fetched from the owning host.\nfunction WorkspaceDiffView({ workspace }: { workspace: WorkspaceRecord }) {\n const diff = useRelayStore((s) => s.workspaceDiff[workspace.id])\n const fetchWorkspaceDiff = useRelayStore((s) => s.fetchWorkspaceDiff)\n const eligible = workspace.mode === 'isolated' && Boolean(workspace.worktreePath)\n\n useEffect(() => {\n if (eligible && diff === undefined) void fetchWorkspaceDiff(workspace.id)\n }, [eligible, diff, workspace.id, fetchWorkspaceDiff])\n\n if (!eligible) return null\n if (diff === undefined) return <div className=\"text-[11px] text-muted-foreground\">Loading diff…</div>\n if (diff.available === false) return <div className=\"text-[11px] text-muted-foreground\">Diff unavailable{diff.reason ? `: ${diff.reason}` : ''}</div>\n if (diff.missing) return <div className=\"text-[11px] text-muted-foreground\">Worktree no longer on disk</div>\n if (diff.error) return <div className=\"text-[11px] text-red-400\">Diff error: {diff.error}</div>\n if (!diff.files.length) return <div className=\"text-[11px] text-muted-foreground\">No committed changes against {diff.baseRef || 'base'}</div>\n\n const refresh = (\n <Button size=\"icon-sm\" variant=\"ghost\" className=\"h-5 w-5\" title=\"Refresh diff\" onClick={() => void fetchWorkspaceDiff(workspace.id)}>\n <RefreshCw className=\"h-3 w-3\" />\n </Button>\n )\n\n return (\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2 text-[11px] font-medium\">\n <FileDiff className=\"h-3 w-3\" />\n <span>{diff.files.length} file{diff.files.length === 1 ? '' : 's'} vs {diff.baseRef || 'base'}</span>\n {refresh}\n </div>\n <ul className=\"space-y-0.5 text-[11px]\">\n {diff.files.map((file) => (\n <li key={file.path} className=\"flex items-center gap-2 font-mono\">\n <span className=\"truncate\" title={file.path}>{file.path}</span>\n {file.binary ? (\n <span className=\"shrink-0 text-muted-foreground\">binary</span>\n ) : (\n <span className=\"shrink-0\">\n <span className=\"text-emerald-400\">+{file.additions ?? 0}</span>{' '}\n <span className=\"text-red-400\">−{file.deletions ?? 0}</span>\n </span>\n )}\n </li>\n ))}\n </ul>\n {diff.patch && (\n <pre className=\"max-h-80 overflow-auto rounded border border-border bg-muted/30 p-2 text-[10.5px] leading-relaxed\">\n {diff.patch.split('\\n').map((line, i) => (\n <div key={i} className={diffLineClass(line)}>{line || ' '}</div>\n ))}\n {diff.truncated && <div className=\"mt-1 text-muted-foreground\">… patch truncated</div>}\n </pre>\n )}\n </div>\n )\n}\n\nfunction WorkspaceDetails({ workspace }: { workspace: WorkspaceRecord }) {\n return (\n <div className=\"space-y-3 border-t border-dashed border-border bg-muted/20 px-3 py-3\">\n <WorkspaceTimeline workspace={workspace} />\n <WorkspaceDiffView workspace={workspace} />\n </div>\n )\n}\n\n// Worktrees on disk with no live workspace row — reclaimable in one click.\nfunction OrphanPanel() {\n const orphans = useRelayStore((s) => s.workspaceOrphans)\n const fetchWorkspaceOrphans = useRelayStore((s) => s.fetchWorkspaceOrphans)\n const reclaimWorkspaceOrphan = useRelayStore((s) => s.reclaimWorkspaceOrphan)\n\n useEffect(() => {\n void fetchWorkspaceOrphans()\n }, [fetchWorkspaceOrphans])\n\n if (!orphans.length) return null\n\n return (\n <Card className=\"border-amber-500/30\">\n <CardHeader className=\"px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n <PackageOpen className=\"h-4 w-4 text-amber-400\" />\n <CardTitle className=\"text-sm font-medium\">Orphaned worktrees</CardTitle>\n <Badge variant=\"outline\" className=\"border-amber-500/30 text-amber-400 text-[10px]\">{orphans.length}</Badge>\n <Button size=\"icon-sm\" variant=\"ghost\" className=\"ml-auto h-6 w-6\" title=\"Rescan\" onClick={() => void fetchWorkspaceOrphans()}>\n <RefreshCw className=\"h-3.5 w-3.5\" />\n </Button>\n </div>\n <p className=\"mt-1 text-xs text-muted-foreground\">Agent worktrees on disk with no live workspace. Reclaim removes them from the host.</p>\n </CardHeader>\n <CardContent className=\"p-0\">\n {orphans.map((orphan) => (\n <div key={orphan.worktreePath} className=\"flex items-center gap-3 border-t border-border px-4 py-2 text-xs\">\n <GitBranch className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate font-medium\" title={orphan.branch}>{orphan.branch || '(detached)'}</span>\n <span className=\"truncate font-mono text-muted-foreground\" title={orphan.worktreePath}>{shortPath(orphan.worktreePath)}</span>\n {orphan.hadTerminalRow && <Badge variant=\"outline\" className=\"text-[10px]\">cleanup failed</Badge>}\n <Button size=\"sm\" variant=\"outline\" className=\"ml-auto h-7 text-xs\" onClick={() => void reclaimWorkspaceOrphan(orphan)}>\n <ArchiveRestore className=\"mr-1 h-3.5 w-3.5\" />Reclaim\n </Button>\n </div>\n ))}\n </CardContent>\n </Card>\n )\n}\n\n// A `pr`-strategy merge lands as an opened PR, not a local fast-forward, so the\n// outcome only shows up here — surface the PR link and any error the orchestrator\n// returned (which the command layer keeps in result even when status=succeeded).\nfunction prLabel(url: string): string {\n const match = url.match(/\\/pull\\/(\\d+)/)\n return match ? `PR #${match[1]}` : 'View PR'\n}\n\nfunction MergeOutcome({ workspace }: { workspace: WorkspaceRecord }) {\n const result = workspace.metadata?.mergeResult as { prUrl?: string; error?: string } | undefined\n const prUrl = result?.prUrl\n const error = result?.error || (workspace.metadata?.mergeError as string | undefined)\n if (!prUrl && !error) return null\n return (\n <>\n {prUrl && (\n <a\n href={prUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n title={prUrl}\n className=\"inline-flex items-center gap-0.5 rounded border border-violet-500/30 px-1.5 py-0 text-[10px] text-violet-400 hover:text-violet-300\"\n >\n <GitMerge className=\"h-2.5 w-2.5\" />{prLabel(prUrl)}\n </a>\n )}\n {error && (\n <Badge variant=\"outline\" className=\"max-w-[16rem] border-amber-500/30 text-amber-400 text-[10px]\" title={error}>\n <TriangleAlert className=\"mr-0.5 h-2.5 w-2.5 shrink-0\" /><span className=\"truncate\">merge: {error}</span>\n </Badge>\n )}\n </>\n )\n}\n\nfunction WorkspaceRow({ workspace }: { workspace: WorkspaceRecord }) {\n const now = useNow()\n const [expanded, setExpanded] = useState(false)\n const agentsById = useRelayStore((s) => s.agentsById)\n const focused = useRelayStore((s) => s.workspaceFocusId === workspace.id)\n const owner = workspace.ownerAgentId ? agentsById[workspace.ownerAgentId] : undefined\n const steward = workspace.stewardAgentId ? agentsById[workspace.stewardAgentId] : undefined\n const path = workspace.worktreePath || workspace.sourceCwd || workspace.repoRoot\n const rowRef = useRef<HTMLDivElement>(null)\n\n // Click-through landing (#236): scroll the targeted row into view once it mounts.\n useEffect(() => {\n if (focused) rowRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' })\n }, [focused])\n\n return (\n <div ref={rowRef} className={`border-t border-border ${focused ? 'bg-primary/5 ring-1 ring-inset ring-primary/40' : ''}`}>\n <div className=\"grid gap-3 px-3 py-3 text-sm lg:grid-cols-[minmax(180px,1.2fr)_minmax(220px,1.5fr)_minmax(160px,1fr)_minmax(170px,auto)]\">\n <div className=\"min-w-0 space-y-1\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <GitBranch className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate font-medium\" title={workspace.branch || workspace.id}>{workspace.branch || workspace.id}</span>\n </div>\n <div className=\"flex flex-wrap gap-1\">\n <Badge variant=\"outline\" className={`text-[10px] ${workspace.mode === 'isolated' ? 'border-sky-500/30 text-sky-400' : 'border-zinc-500/30 text-zinc-400'}`}>\n {workspace.mode}\n </Badge>\n {workspace.requestedMode && workspace.requestedMode !== workspace.mode && (\n <Badge variant=\"outline\" className=\"text-[10px]\">requested {workspace.requestedMode}</Badge>\n )}\n <Badge variant=\"outline\" className={`text-[10px] ${statusTone(workspace.status)}`}>{STATUS_LABEL[workspace.status] || workspace.status}</Badge>\n <MergeOutcome workspace={workspace} />\n </div>\n </div>\n\n <div className=\"min-w-0 space-y-1\">\n <div className=\"truncate font-mono text-xs\" title={path}>{shortPath(path)}</div>\n <div className=\"truncate text-xs text-muted-foreground\" title={workspace.sourceCwd}>{workspace.sourceCwd}</div>\n <GitState workspace={workspace} />\n </div>\n\n <div className=\"grid grid-cols-[4.5rem_minmax(0,1fr)] gap-x-2 gap-y-1 text-xs\">\n <span className=\"text-muted-foreground\">Owner</span>\n <span className=\"truncate\" title={workspace.ownerAgentId}>{agentLabel(owner, workspace.ownerPolicyName || workspace.ownerAgentId)}</span>\n <span className=\"text-muted-foreground\">Steward</span>\n <span className=\"truncate\" title={workspace.stewardAgentId}>{agentLabel(steward, workspace.stewardAgentId)}</span>\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{timeAgo(now, workspace.updatedAt)}</span>\n {workspace.baseRef && (\n <>\n <span className=\"text-muted-foreground\">Base</span>\n <span className=\"truncate\" title={workspace.baseSha}>{workspace.baseRef}</span>\n </>\n )}\n </div>\n\n <WorkspaceActions workspace={workspace} expanded={expanded} onToggleDetails={() => setExpanded((value) => !value)} />\n </div>\n {expanded && <WorkspaceDetails workspace={workspace} />}\n </div>\n )\n}\n\nfunction RepoGroup({ repoRoot, workspaces }: { repoRoot: string; workspaces: WorkspaceRecord[] }) {\n const liveCount = workspaces.filter((item) => LIVE_STATUSES.has(item.status)).length\n const readyCount = workspaces.filter((item) => item.status === 'ready' || item.status === 'review_requested' || item.status === 'merge_planned').length\n const conflictCount = workspaces.filter((item) => item.status === 'conflict').length\n\n return (\n <Card>\n <CardHeader className=\"px-4 py-3\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <div className=\"min-w-0 flex-1\">\n <CardTitle className=\"truncate text-sm font-medium\" title={repoRoot}>{repoRoot}</CardTitle>\n <div className=\"mt-1 flex flex-wrap gap-1.5\">\n <Badge variant=\"outline\" className=\"text-[10px]\">{workspaces.length} total</Badge>\n <Badge variant=\"outline\" className=\"text-[10px]\">{liveCount} live</Badge>\n {readyCount > 0 && <Badge variant=\"outline\" className=\"border-emerald-500/20 text-emerald-400 text-[10px]\">{readyCount} merge signal</Badge>}\n {conflictCount > 0 && <Badge variant=\"outline\" className=\"border-red-500/20 text-red-400 text-[10px]\">{conflictCount} conflict</Badge>}\n </div>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"p-0\">\n {workspaces.map((workspace) => (\n <WorkspaceRow key={workspace.id} workspace={workspace} />\n ))}\n </CardContent>\n </Card>\n )\n}\n\nexport function WorkspacesView() {\n const [filter, setFilter] = useState<WorkspaceFilter>('active')\n const workspaces = useRelayStore((s) => s.workspaces)\n const set = useRelayStore((s) => s.set)\n const workspaceFocusId = useRelayStore((s) => s.workspaceFocusId)\n\n // Badge click-through (#236): a focused workspace may be in any state, so widen\n // the filter to 'all' to guarantee its row renders. Clear the focus when leaving\n // the panel so the highlight doesn't linger on the next visit.\n useEffect(() => {\n if (workspaceFocusId) setFilter('all')\n }, [workspaceFocusId])\n useEffect(() => () => { set({ workspaceFocusId: null }) }, [set])\n\n const visibleWorkspaces = useMemo(() => filterWorkspaces(workspaces, filter), [workspaces, filter])\n const grouped = useMemo(() => groupWorkspaces(visibleWorkspaces), [visibleWorkspaces])\n const liveCount = workspaces.filter((item) => LIVE_STATUSES.has(item.status)).length\n const isolatedCount = workspaces.filter((item) => item.mode === 'isolated').length\n const readyCount = workspaces.filter((item) => item.status === 'ready' || item.status === 'review_requested' || item.status === 'merge_planned').length\n const conflictCount = workspaces.filter((item) => item.status === 'conflict').length\n const doneCount = workspaces.filter((item) => TERMINAL_STATUSES.has(item.status)).length\n const activeCount = workspaces.length - doneCount\n const filters: Array<{ key: WorkspaceFilter; label: string; count: number }> = [\n { key: 'active', label: 'Active', count: activeCount },\n { key: 'cleaned', label: 'Done', count: doneCount },\n { key: 'all', label: 'All', count: workspaces.length },\n ]\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Workspaces</h1>\n <p className=\"text-sm text-muted-foreground\">Agent worktrees, ownership, and merge readiness.</p>\n </div>\n <div className=\"ml-auto flex flex-wrap gap-1.5\">\n <Badge variant=\"outline\">{isolatedCount} isolated</Badge>\n <Badge variant=\"outline\">{liveCount} live</Badge>\n <Badge variant=\"outline\">{readyCount} ready</Badge>\n {conflictCount > 0 && <Badge variant=\"outline\" className=\"border-red-500/20 text-red-400\">{conflictCount} conflict</Badge>}\n </div>\n </div>\n\n <OrphanPanel />\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <div className=\"flex rounded-md border border-border bg-card p-1\">\n {filters.map((item) => (\n <Button\n key={item.key}\n size=\"sm\"\n variant={filter === item.key ? 'secondary' : 'ghost'}\n className=\"h-7 text-xs\"\n onClick={() => setFilter(item.key)}\n >\n {item.label}\n <span className=\"ml-1 text-muted-foreground\">{item.count}</span>\n </Button>\n ))}\n </div>\n </div>\n\n {grouped.length === 0 ? (\n <div className=\"rounded-md border border-border px-3 py-12 text-center text-sm text-muted-foreground\">\n No {filter === 'all' ? '' : filter} workspaces\n </div>\n ) : (\n <div className=\"space-y-3\">\n {grouped.map((group) => (\n <RepoGroup key={group.repoRoot} repoRoot={group.repoRoot} workspaces={group.workspaces} />\n ))}\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"workspaces-DqVr0uN7.js","names":[],"sources":["../../dashboard/src/components/views/workspaces.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react'\nimport { ArchiveRestore, ArrowDown, ArrowUp, Check, ChevronDown, ChevronRight, Clock, Eye, FileDiff, FilePen, Flag, FolderOpen, GitBranch, GitCommitHorizontal, GitMerge, PackageOpen, RefreshCw, TriangleAlert, Trash2, X } from 'lucide-react'\nimport { TERMINAL_WORKSPACE_STATUS_VALUES } from 'agent-relay-sdk/types'\nimport { useRelayStore, useNow } from '@/store'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { CopyButton } from '@/components/shared/copy-button'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { displayName, timeAgo } from '@/lib/display'\nimport type { Agent, WorkspaceOrphan, WorkspaceRecord, WorkspaceStatus } from '@/types'\n\nconst LIVE_STATUSES = new Set<WorkspaceStatus>(['active', 'ready', 'conflict', 'review_requested', 'merge_planned', 'cleanup_requested'])\nconst GIT_STATE_STATUSES = new Set<WorkspaceStatus>(['active', 'ready', 'conflict', 'review_requested', 'merge_planned'])\nconst MERGEABLE_STATUSES = new Set<WorkspaceStatus>(['active', 'ready', 'review_requested', 'merge_planned', 'conflict'])\n\ntype WorkspaceFilter = 'active' | 'cleaned' | 'all'\n\nconst STATUS_CLASS: Record<WorkspaceStatus, string> = {\n active: 'bg-blue-500/10 text-blue-400 border-blue-500/20',\n ready: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n conflict: 'bg-red-500/10 text-red-400 border-red-500/20',\n review_requested: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n merge_planned: 'bg-indigo-500/10 text-indigo-400 border-indigo-500/20',\n merged: 'bg-violet-500/10 text-violet-400 border-violet-500/20',\n abandoned: 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20',\n cleanup_requested: 'bg-orange-500/10 text-orange-400 border-orange-500/20',\n cleaned: 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20',\n}\n\nconst STATUS_LABEL: Record<WorkspaceStatus, string> = {\n active: 'active',\n ready: 'ready',\n conflict: 'conflict',\n review_requested: 'review',\n merge_planned: 'merge planned',\n merged: 'merged',\n abandoned: 'abandoned',\n cleanup_requested: 'cleanup',\n cleaned: 'cleaned',\n}\n\nconst STATUS_ORDER: Record<WorkspaceStatus, number> = {\n conflict: 0,\n ready: 1,\n review_requested: 2,\n merge_planned: 3,\n active: 4,\n cleanup_requested: 5,\n merged: 6,\n abandoned: 7,\n cleaned: 8,\n}\n\nconst TERMINAL_STATUSES = new Set<WorkspaceStatus>(TERMINAL_WORKSPACE_STATUS_VALUES)\n\nfunction shortPath(path: string): string {\n const parts = path.split('/').filter(Boolean)\n if (parts.length <= 3) return path || '-'\n return `/${parts.slice(-3).join('/')}`\n}\n\nfunction agentLabel(agent: Agent | undefined, fallback: string | undefined): string {\n if (agent) return displayName(agent)\n return fallback || '-'\n}\n\nfunction statusTone(status: WorkspaceStatus): string {\n return STATUS_CLASS[status] || 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'\n}\n\nfunction workspaceSort(a: WorkspaceRecord, b: WorkspaceRecord): number {\n const status = (STATUS_ORDER[a.status] ?? 99) - (STATUS_ORDER[b.status] ?? 99)\n if (status !== 0) return status\n return Number(b.updatedAt || 0) - Number(a.updatedAt || 0)\n}\n\nfunction groupWorkspaces(workspaces: WorkspaceRecord[]): Array<{ repoRoot: string; workspaces: WorkspaceRecord[] }> {\n const grouped = new Map<string, WorkspaceRecord[]>()\n for (const workspace of workspaces) {\n const key = workspace.repoRoot || workspace.sourceCwd || 'unknown repo'\n grouped.set(key, [...(grouped.get(key) || []), workspace])\n }\n return [...grouped.entries()]\n .map(([repoRoot, items]) => ({ repoRoot, workspaces: items.sort(workspaceSort) }))\n .sort((a, b) => {\n const liveA = a.workspaces.some((item) => LIVE_STATUSES.has(item.status))\n const liveB = b.workspaces.some((item) => LIVE_STATUSES.has(item.status))\n if (liveA !== liveB) return liveA ? -1 : 1\n return a.repoRoot.localeCompare(b.repoRoot)\n })\n}\n\nfunction filterWorkspaces(workspaces: WorkspaceRecord[], filter: WorkspaceFilter): WorkspaceRecord[] {\n if (filter === 'all') return workspaces\n if (filter === 'cleaned') return workspaces.filter((workspace) => TERMINAL_STATUSES.has(workspace.status))\n return workspaces.filter((workspace) => !TERMINAL_STATUSES.has(workspace.status))\n}\n\nfunction WorkspaceActions({ workspace, expanded, onToggleDetails }: { workspace: WorkspaceRecord; expanded: boolean; onToggleDetails: () => void }) {\n const workspaceAction = useRelayStore((s) => s.workspaceAction)\n const purgeWorkspace = useRelayStore((s) => s.purgeWorkspace)\n const fetchWorkspaceMergePreview = useRelayStore((s) => s.fetchWorkspaceMergePreview)\n const openFilesAt = useRelayStore((s) => s.openFilesAt)\n const terminal = workspace.status === 'cleaned' || workspace.status === 'merged' || workspace.status === 'abandoned'\n const disabled = terminal || workspace.status === 'cleanup_requested'\n const openPath = workspace.worktreePath || workspace.sourceCwd || workspace.repoRoot\n // Already-landed work (squash/cherry-pick) has nothing left to merge — a merge\n // would open a duplicate/no-op PR, so disable the button.\n const gitState = useRelayStore((s) => s.workspaceGitState[workspace.id])\n const landed = !!gitState && gitState.available !== false && gitState.landed === true\n const mergeable = workspace.mode === 'isolated' && Boolean(workspace.worktreePath) && MERGEABLE_STATUSES.has(workspace.status) && !landed\n\n // Refresh the preview so the confirm dialog states the strategy/outcome, then\n // dispatch the real merge command.\n async function merge() {\n await fetchWorkspaceMergePreview(workspace.id)\n await workspaceAction(workspace.id, 'merge')\n }\n\n return (\n <div className=\"flex flex-wrap justify-end gap-1.5\">\n <Button size=\"icon-sm\" variant=\"ghost\" title={expanded ? 'Hide details' : 'Show diff & timeline'} onClick={onToggleDetails}>\n {expanded ? <ChevronDown className=\"h-3.5 w-3.5\" /> : <ChevronRight className=\"h-3.5 w-3.5\" />}\n </Button>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n title=\"Open workspace\"\n disabled={!openPath || workspace.status === 'cleaned'}\n onClick={() => void openFilesAt({ path: openPath })}\n >\n <FolderOpen className=\"h-3.5 w-3.5\" />\n </Button>\n <CopyButton value={openPath} label=\"Copy path\" copiedLabel=\"Copied path\" size=\"icon-sm\" variant=\"ghost\" disabled={!openPath} />\n <Button size=\"icon-sm\" variant=\"outline\" title=\"Mark ready\" disabled={disabled || workspace.status === 'ready'} onClick={() => void workspaceAction(workspace.id, 'ready')}>\n <Check className=\"h-3.5 w-3.5\" />\n </Button>\n <Button size=\"icon-sm\" variant=\"outline\" title=\"Request review\" disabled={disabled || workspace.status === 'review_requested'} onClick={() => void workspaceAction(workspace.id, 'request-review')}>\n <Eye className=\"h-3.5 w-3.5\" />\n </Button>\n <Button size=\"icon-sm\" variant=\"outline\" title=\"Mark merge planned\" disabled={disabled || workspace.status === 'merge_planned'} onClick={() => void workspaceAction(workspace.id, 'merge-plan')}>\n <Flag className=\"h-3.5 w-3.5\" />\n </Button>\n <Button size=\"icon-sm\" variant=\"default\" title={landed ? 'Already merged into base — nothing to land' : 'Merge & land work'} disabled={!mergeable} onClick={() => void merge()}>\n <GitMerge className=\"h-3.5 w-3.5\" />\n </Button>\n <Button size=\"icon-sm\" variant=\"outline\" title=\"Request cleanup (removes the worktree on the host)\" disabled={workspace.status === 'cleaned' || workspace.status === 'cleanup_requested'} onClick={() => void workspaceAction(workspace.id, 'cleanup')}>\n <Trash2 className=\"h-3.5 w-3.5\" />\n </Button>\n {terminal && (\n <Button size=\"icon-sm\" variant=\"ghost\" title=\"Purge record (clears the row, does not touch disk)\" onClick={() => void purgeWorkspace(workspace.id)}>\n <X className=\"h-3.5 w-3.5\" />\n </Button>\n )}\n </div>\n )\n}\n\n// Pre-merge conflict hint, shown next to git state when the worktree has work to\n// land. Lazily fetched from the owning host (an external sync), so an effect is\n// the right tool. Reports clean-ff vs would-conflict before the user merges.\nfunction MergePreviewHint({ workspace, unmerged }: { workspace: WorkspaceRecord; unmerged: number }) {\n const preview = useRelayStore((s) => s.workspaceMergePreview[workspace.id])\n const fetchWorkspaceMergePreview = useRelayStore((s) => s.fetchWorkspaceMergePreview)\n const eligible = unmerged > 0 && MERGEABLE_STATUSES.has(workspace.status)\n\n useEffect(() => {\n if (eligible && preview === undefined) void fetchWorkspaceMergePreview(workspace.id)\n }, [eligible, preview, workspace.id, fetchWorkspaceMergePreview])\n\n if (!eligible || preview === undefined || preview.available === false) return null\n if (preview.conflict) {\n return (\n <span className=\"flex items-center gap-0.5 text-amber-400\" title={`Merging into ${preview.baseRef || 'base'} would conflict — resolve before merging`}>\n <TriangleAlert className=\"h-3 w-3\" />conflict\n </span>\n )\n }\n if (preview.conflict === false) {\n return (\n <span className=\"flex items-center gap-0.5 text-emerald-400\" title={`Clean to merge via ${preview.strategy === 'pr' ? 'PR' : 'rebase + fast-forward'} into ${preview.baseRef || 'base'}`}>\n <Check className=\"h-3 w-3\" />{preview.strategy === 'pr' ? 'PR ready' : 'clean'}\n </span>\n )\n }\n return null\n}\n\n// Live git state of the worktree, fetched lazily from the owning host. This is\n// an external-system sync (host git over the orchestrator proxy), so an effect\n// is the right tool — it does not derive state from props.\nfunction GitState({ workspace }: { workspace: WorkspaceRecord }) {\n const now = useNow()\n const gitState = useRelayStore((s) => s.workspaceGitState[workspace.id])\n const fetchWorkspaceGitState = useRelayStore((s) => s.fetchWorkspaceGitState)\n const eligible = workspace.mode === 'isolated' && Boolean(workspace.worktreePath) && GIT_STATE_STATUSES.has(workspace.status)\n\n useEffect(() => {\n if (eligible && gitState === undefined) void fetchWorkspaceGitState(workspace.id)\n }, [eligible, gitState, workspace.id, fetchWorkspaceGitState])\n\n if (!eligible) return null\n\n const refresh = (\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n className=\"h-5 w-5\"\n title=\"Refresh git state\"\n onClick={() => void fetchWorkspaceGitState(workspace.id)}\n >\n <RefreshCw className=\"h-3 w-3\" />\n </Button>\n )\n\n if (gitState === undefined) {\n return <div className=\"flex items-center gap-1 text-[11px] text-muted-foreground\">Loading git state…</div>\n }\n if (gitState.available === false) {\n return (\n <div className=\"flex items-center gap-1 text-[11px] text-muted-foreground\" title={gitState.reason}>\n <span>git state unavailable</span>\n {refresh}\n </div>\n )\n }\n if (gitState.missing) {\n return <div className=\"flex items-center gap-1 text-[11px] text-muted-foreground\">worktree gone {refresh}</div>\n }\n if (gitState.error) {\n return (\n <div className=\"flex items-center gap-1 text-[11px] text-red-400\" title={gitState.error}>\n <span>git error</span>\n {refresh}\n </div>\n )\n }\n\n const ahead = gitState.ahead ?? 0\n // Work squash/cherry-pick-merged into base leaves `ahead` positive but is\n // effectively landed — surface the unmerged count, not the raw ahead.\n const landed = gitState.landed === true\n const unmerged = landed ? 0 : (gitState.unmergedAhead ?? ahead)\n const behind = gitState.behind ?? 0\n const dirty = gitState.dirtyCount ?? 0\n const clean = unmerged === 0 && dirty === 0 && !landed\n const commit = gitState.lastCommit\n\n return (\n <div className=\"space-y-0.5\">\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[11px]\">\n {clean ? (\n <span className=\"text-muted-foreground\">no unmerged work</span>\n ) : (\n <>\n {landed ? (\n <span className=\"flex items-center gap-0.5 text-emerald-400\" title={`Work already landed in ${gitState.baseRef || 'base'} via squash/cherry-pick — safe to clean up`}>\n <Check className=\"h-3 w-3\" />landed\n </span>\n ) : (\n <span className={unmerged > 0 ? 'flex items-center text-emerald-400' : 'flex items-center text-muted-foreground'} title={ahead !== unmerged ? `${unmerged} unmerged commit(s) (${ahead} ahead of base)` : `${unmerged} commit(s) ahead of base`}>\n <ArrowUp className=\"h-3 w-3\" />{unmerged}\n </span>\n )}\n <span className={behind > 0 ? 'flex items-center text-amber-400' : 'flex items-center text-muted-foreground'} title={`${behind} commit(s) behind base`}>\n <ArrowDown className=\"h-3 w-3\" />{behind}\n </span>\n {dirty > 0 && (\n <span className=\"flex items-center text-orange-400\" title={`${dirty} uncommitted change(s)`}>\n <FilePen className=\"h-3 w-3\" />{dirty}\n </span>\n )}\n {!landed && <MergePreviewHint workspace={workspace} unmerged={unmerged} />}\n </>\n )}\n {refresh}\n </div>\n {commit && (\n <div className=\"flex min-w-0 items-center gap-1 text-[11px] text-muted-foreground\" title={`${commit.sha.slice(0, 8)} — ${commit.message}`}>\n <GitCommitHorizontal className=\"h-3 w-3 shrink-0\" />\n <span className=\"truncate\">{commit.message || commit.sha.slice(0, 8)}</span>\n {commit.at ? <span className=\"shrink-0 opacity-70\">· {timeAgo(now, commit.at)}</span> : null}\n </div>\n )}\n </div>\n )\n}\n\n// Lifecycle timeline from the timestamps the relay already records:\n// created → ready → merged → cleaned. Surfaces history without a drawer.\nfunction WorkspaceTimeline({ workspace }: { workspace: WorkspaceRecord }) {\n const now = useNow()\n const mergedAt = typeof workspace.metadata?.mergedAt === 'number' ? (workspace.metadata.mergedAt as number) : undefined\n const steps = [\n { label: 'created', at: workspace.createdAt },\n { label: 'ready', at: workspace.readyAt },\n { label: 'merged', at: mergedAt },\n { label: 'cleaned', at: workspace.cleanedAt },\n ].filter((step): step is { label: string; at: number } => typeof step.at === 'number' && step.at > 0)\n\n return (\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-muted-foreground\">\n <span className=\"flex items-center gap-1 font-medium text-foreground\"><Clock className=\"h-3 w-3\" />Timeline</span>\n {steps.map((step, i) => (\n <span key={step.label} className=\"flex items-center gap-1\">\n {i > 0 && <span className=\"opacity-40\">→</span>}\n <span className=\"text-foreground\">{step.label}</span>\n <span title={new Date(step.at).toLocaleString()}>{timeAgo(now, step.at)}</span>\n </span>\n ))}\n </div>\n )\n}\n\nfunction diffLineClass(line: string): string {\n if (line.startsWith('+') && !line.startsWith('+++')) return 'text-emerald-400'\n if (line.startsWith('-') && !line.startsWith('---')) return 'text-red-400'\n if (line.startsWith('@@')) return 'text-cyan-400'\n if (line.startsWith('diff ') || line.startsWith('index ') || line.startsWith('+++') || line.startsWith('---')) return 'text-muted-foreground'\n return ''\n}\n\n// Diff of committed work against base, lazily fetched from the owning host.\nfunction WorkspaceDiffView({ workspace }: { workspace: WorkspaceRecord }) {\n const diff = useRelayStore((s) => s.workspaceDiff[workspace.id])\n const fetchWorkspaceDiff = useRelayStore((s) => s.fetchWorkspaceDiff)\n const eligible = workspace.mode === 'isolated' && Boolean(workspace.worktreePath)\n\n useEffect(() => {\n if (eligible && diff === undefined) void fetchWorkspaceDiff(workspace.id)\n }, [eligible, diff, workspace.id, fetchWorkspaceDiff])\n\n if (!eligible) return null\n if (diff === undefined) return <div className=\"text-[11px] text-muted-foreground\">Loading diff…</div>\n if (diff.available === false) return <div className=\"text-[11px] text-muted-foreground\">Diff unavailable{diff.reason ? `: ${diff.reason}` : ''}</div>\n if (diff.missing) return <div className=\"text-[11px] text-muted-foreground\">Worktree no longer on disk</div>\n if (diff.error) return <div className=\"text-[11px] text-red-400\">Diff error: {diff.error}</div>\n if (!diff.files.length) return <div className=\"text-[11px] text-muted-foreground\">No committed changes against {diff.baseRef || 'base'}</div>\n\n const refresh = (\n <Button size=\"icon-sm\" variant=\"ghost\" className=\"h-5 w-5\" title=\"Refresh diff\" onClick={() => void fetchWorkspaceDiff(workspace.id)}>\n <RefreshCw className=\"h-3 w-3\" />\n </Button>\n )\n\n return (\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2 text-[11px] font-medium\">\n <FileDiff className=\"h-3 w-3\" />\n <span>{diff.files.length} file{diff.files.length === 1 ? '' : 's'} vs {diff.baseRef || 'base'}</span>\n {refresh}\n </div>\n <ul className=\"space-y-0.5 text-[11px]\">\n {diff.files.map((file) => (\n <li key={file.path} className=\"flex items-center gap-2 font-mono\">\n <span className=\"truncate\" title={file.path}>{file.path}</span>\n {file.binary ? (\n <span className=\"shrink-0 text-muted-foreground\">binary</span>\n ) : (\n <span className=\"shrink-0\">\n <span className=\"text-emerald-400\">+{file.additions ?? 0}</span>{' '}\n <span className=\"text-red-400\">−{file.deletions ?? 0}</span>\n </span>\n )}\n </li>\n ))}\n </ul>\n {diff.patch && (\n <pre className=\"max-h-80 overflow-auto rounded border border-border bg-muted/30 p-2 text-[10.5px] leading-relaxed\">\n {diff.patch.split('\\n').map((line, i) => (\n <div key={i} className={diffLineClass(line)}>{line || ' '}</div>\n ))}\n {diff.truncated && <div className=\"mt-1 text-muted-foreground\">… patch truncated</div>}\n </pre>\n )}\n </div>\n )\n}\n\nfunction WorkspaceDetails({ workspace }: { workspace: WorkspaceRecord }) {\n return (\n <div className=\"space-y-3 border-t border-dashed border-border bg-muted/20 px-3 py-3\">\n <WorkspaceTimeline workspace={workspace} />\n <WorkspaceDiffView workspace={workspace} />\n </div>\n )\n}\n\n// Worktrees on disk with no live workspace row — reclaimable in one click.\nfunction OrphanPanel() {\n const orphans = useRelayStore((s) => s.workspaceOrphans)\n const fetchWorkspaceOrphans = useRelayStore((s) => s.fetchWorkspaceOrphans)\n const reclaimWorkspaceOrphan = useRelayStore((s) => s.reclaimWorkspaceOrphan)\n\n useEffect(() => {\n void fetchWorkspaceOrphans()\n }, [fetchWorkspaceOrphans])\n\n if (!orphans.length) return null\n\n return (\n <Card className=\"border-amber-500/30\">\n <CardHeader className=\"px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n <PackageOpen className=\"h-4 w-4 text-amber-400\" />\n <CardTitle className=\"text-sm font-medium\">Orphaned worktrees</CardTitle>\n <Badge variant=\"outline\" className=\"border-amber-500/30 text-amber-400 text-[10px]\">{orphans.length}</Badge>\n <Button size=\"icon-sm\" variant=\"ghost\" className=\"ml-auto h-6 w-6\" title=\"Rescan\" onClick={() => void fetchWorkspaceOrphans()}>\n <RefreshCw className=\"h-3.5 w-3.5\" />\n </Button>\n </div>\n <p className=\"mt-1 text-xs text-muted-foreground\">Agent worktrees on disk with no live workspace. Reclaim removes them from the host.</p>\n </CardHeader>\n <CardContent className=\"p-0\">\n {orphans.map((orphan) => (\n <div key={orphan.worktreePath} className=\"flex items-center gap-3 border-t border-border px-4 py-2 text-xs\">\n <GitBranch className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate font-medium\" title={orphan.branch}>{orphan.branch || '(detached)'}</span>\n <span className=\"truncate font-mono text-muted-foreground\" title={orphan.worktreePath}>{shortPath(orphan.worktreePath)}</span>\n {orphan.hadTerminalRow && <Badge variant=\"outline\" className=\"text-[10px]\">cleanup failed</Badge>}\n <Button size=\"sm\" variant=\"outline\" className=\"ml-auto h-7 text-xs\" onClick={() => void reclaimWorkspaceOrphan(orphan)}>\n <ArchiveRestore className=\"mr-1 h-3.5 w-3.5\" />Reclaim\n </Button>\n </div>\n ))}\n </CardContent>\n </Card>\n )\n}\n\n// A `pr`-strategy merge lands as an opened PR, not a local fast-forward, so the\n// outcome only shows up here — surface the PR link and any error the orchestrator\n// returned (which the command layer keeps in result even when status=succeeded).\nfunction prLabel(url: string): string {\n const match = url.match(/\\/pull\\/(\\d+)/)\n return match ? `PR #${match[1]}` : 'View PR'\n}\n\nfunction MergeOutcome({ workspace }: { workspace: WorkspaceRecord }) {\n const result = workspace.metadata?.mergeResult as { prUrl?: string; error?: string } | undefined\n const prUrl = result?.prUrl\n const error = result?.error || (workspace.metadata?.mergeError as string | undefined)\n if (!prUrl && !error) return null\n return (\n <>\n {prUrl && (\n <a\n href={prUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n title={prUrl}\n className=\"inline-flex items-center gap-0.5 rounded border border-violet-500/30 px-1.5 py-0 text-[10px] text-violet-400 hover:text-violet-300\"\n >\n <GitMerge className=\"h-2.5 w-2.5\" />{prLabel(prUrl)}\n </a>\n )}\n {error && (\n <Badge variant=\"outline\" className=\"max-w-[16rem] border-amber-500/30 text-amber-400 text-[10px]\" title={error}>\n <TriangleAlert className=\"mr-0.5 h-2.5 w-2.5 shrink-0\" /><span className=\"truncate\">merge: {error}</span>\n </Badge>\n )}\n </>\n )\n}\n\nfunction WorkspaceRow({ workspace }: { workspace: WorkspaceRecord }) {\n const now = useNow()\n const [expanded, setExpanded] = useState(false)\n const agentsById = useRelayStore((s) => s.agentsById)\n const focused = useRelayStore((s) => s.workspaceFocusId === workspace.id)\n const owner = workspace.ownerAgentId ? agentsById[workspace.ownerAgentId] : undefined\n const steward = workspace.stewardAgentId ? agentsById[workspace.stewardAgentId] : undefined\n const path = workspace.worktreePath || workspace.sourceCwd || workspace.repoRoot\n const rowRef = useRef<HTMLDivElement>(null)\n\n // Click-through landing (#236): scroll the targeted row into view once it mounts.\n useEffect(() => {\n if (focused) rowRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' })\n }, [focused])\n\n return (\n <div ref={rowRef} className={`border-t border-border ${focused ? 'bg-primary/5 ring-1 ring-inset ring-primary/40' : ''}`}>\n <div className=\"grid gap-3 px-3 py-3 text-sm lg:grid-cols-[minmax(180px,1.2fr)_minmax(220px,1.5fr)_minmax(160px,1fr)_minmax(170px,auto)]\">\n <div className=\"min-w-0 space-y-1\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <GitBranch className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate font-medium\" title={workspace.branch || workspace.id}>{workspace.branch || workspace.id}</span>\n </div>\n <div className=\"flex flex-wrap gap-1\">\n <Badge variant=\"outline\" className={`text-[10px] ${workspace.mode === 'isolated' ? 'border-sky-500/30 text-sky-400' : 'border-zinc-500/30 text-zinc-400'}`}>\n {workspace.mode}\n </Badge>\n {workspace.requestedMode && workspace.requestedMode !== workspace.mode && (\n <Badge variant=\"outline\" className=\"text-[10px]\">requested {workspace.requestedMode}</Badge>\n )}\n <Badge variant=\"outline\" className={`text-[10px] ${statusTone(workspace.status)}`}>{STATUS_LABEL[workspace.status] || workspace.status}</Badge>\n <MergeOutcome workspace={workspace} />\n </div>\n </div>\n\n <div className=\"min-w-0 space-y-1\">\n <div className=\"truncate font-mono text-xs\" title={path}>{shortPath(path)}</div>\n <div className=\"truncate text-xs text-muted-foreground\" title={workspace.sourceCwd}>{workspace.sourceCwd}</div>\n <GitState workspace={workspace} />\n </div>\n\n <div className=\"grid grid-cols-[4.5rem_minmax(0,1fr)] gap-x-2 gap-y-1 text-xs\">\n <span className=\"text-muted-foreground\">Owner</span>\n <span className=\"truncate\" title={workspace.ownerAgentId}>{agentLabel(owner, workspace.ownerPolicyName || workspace.ownerAgentId)}</span>\n <span className=\"text-muted-foreground\">Steward</span>\n <span className=\"truncate\" title={workspace.stewardAgentId}>{agentLabel(steward, workspace.stewardAgentId)}</span>\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{timeAgo(now, workspace.updatedAt)}</span>\n {workspace.baseRef && (\n <>\n <span className=\"text-muted-foreground\">Base</span>\n <span className=\"truncate\" title={workspace.baseSha}>{workspace.baseRef}</span>\n </>\n )}\n </div>\n\n <WorkspaceActions workspace={workspace} expanded={expanded} onToggleDetails={() => setExpanded((value) => !value)} />\n </div>\n {expanded && <WorkspaceDetails workspace={workspace} />}\n </div>\n )\n}\n\nfunction RepoGroup({ repoRoot, workspaces }: { repoRoot: string; workspaces: WorkspaceRecord[] }) {\n const liveCount = workspaces.filter((item) => LIVE_STATUSES.has(item.status)).length\n const readyCount = workspaces.filter((item) => item.status === 'ready' || item.status === 'review_requested' || item.status === 'merge_planned').length\n const conflictCount = workspaces.filter((item) => item.status === 'conflict').length\n\n return (\n <Card>\n <CardHeader className=\"px-4 py-3\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <div className=\"min-w-0 flex-1\">\n <CardTitle className=\"truncate text-sm font-medium\" title={repoRoot}>{repoRoot}</CardTitle>\n <div className=\"mt-1 flex flex-wrap gap-1.5\">\n <Badge variant=\"outline\" className=\"text-[10px]\">{workspaces.length} total</Badge>\n <Badge variant=\"outline\" className=\"text-[10px]\">{liveCount} live</Badge>\n {readyCount > 0 && <Badge variant=\"outline\" className=\"border-emerald-500/20 text-emerald-400 text-[10px]\">{readyCount} merge signal</Badge>}\n {conflictCount > 0 && <Badge variant=\"outline\" className=\"border-red-500/20 text-red-400 text-[10px]\">{conflictCount} conflict</Badge>}\n </div>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"p-0\">\n {workspaces.map((workspace) => (\n <WorkspaceRow key={workspace.id} workspace={workspace} />\n ))}\n </CardContent>\n </Card>\n )\n}\n\nexport function WorkspacesView() {\n const [filter, setFilter] = useState<WorkspaceFilter>('active')\n const workspaces = useRelayStore((s) => s.workspaces)\n const set = useRelayStore((s) => s.set)\n const workspaceFocusId = useRelayStore((s) => s.workspaceFocusId)\n\n // Badge click-through (#236): a focused workspace may be in any state, so widen\n // the filter to 'all' to guarantee its row renders. Clear the focus when leaving\n // the panel so the highlight doesn't linger on the next visit.\n useEffect(() => {\n if (workspaceFocusId) setFilter('all')\n }, [workspaceFocusId])\n useEffect(() => () => { set({ workspaceFocusId: null }) }, [set])\n\n const visibleWorkspaces = useMemo(() => filterWorkspaces(workspaces, filter), [workspaces, filter])\n const grouped = useMemo(() => groupWorkspaces(visibleWorkspaces), [visibleWorkspaces])\n const liveCount = workspaces.filter((item) => LIVE_STATUSES.has(item.status)).length\n const isolatedCount = workspaces.filter((item) => item.mode === 'isolated').length\n const readyCount = workspaces.filter((item) => item.status === 'ready' || item.status === 'review_requested' || item.status === 'merge_planned').length\n const conflictCount = workspaces.filter((item) => item.status === 'conflict').length\n const doneCount = workspaces.filter((item) => TERMINAL_STATUSES.has(item.status)).length\n const activeCount = workspaces.length - doneCount\n const filters: Array<{ key: WorkspaceFilter; label: string; count: number }> = [\n { key: 'active', label: 'Active', count: activeCount },\n { key: 'cleaned', label: 'Done', count: doneCount },\n { key: 'all', label: 'All', count: workspaces.length },\n ]\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Workspaces</h1>\n <p className=\"text-sm text-muted-foreground\">Agent worktrees, ownership, and merge readiness.</p>\n </div>\n <div className=\"ml-auto flex flex-wrap gap-1.5\">\n <Badge variant=\"outline\">{isolatedCount} isolated</Badge>\n <Badge variant=\"outline\">{liveCount} live</Badge>\n <Badge variant=\"outline\">{readyCount} ready</Badge>\n {conflictCount > 0 && <Badge variant=\"outline\" className=\"border-red-500/20 text-red-400\">{conflictCount} conflict</Badge>}\n </div>\n </div>\n\n <OrphanPanel />\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <div className=\"flex rounded-md border border-border bg-card p-1\">\n {filters.map((item) => (\n <Button\n key={item.key}\n size=\"sm\"\n variant={filter === item.key ? 'secondary' : 'ghost'}\n className=\"h-7 text-xs\"\n onClick={() => setFilter(item.key)}\n >\n {item.label}\n <span className=\"ml-1 text-muted-foreground\">{item.count}</span>\n </Button>\n ))}\n </div>\n </div>\n\n {grouped.length === 0 ? (\n <div className=\"rounded-md border border-border px-3 py-12 text-center text-sm text-muted-foreground\">\n No {filter === 'all' ? '' : filter} workspaces\n </div>\n ) : (\n <div className=\"space-y-3\">\n {grouped.map((group) => (\n <RepoGroup key={group.repoRoot} repoRoot={group.repoRoot} workspaces={group.workspaces} />\n ))}\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
package/public/index.html
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='6' fill='%2309090b'/%3E%3Ccircle cx='16' cy='16' r='4.5' fill='%2358a6ff'/%3E%3Ccircle cx='6' cy='8' r='2.5' fill='%233fb950'/%3E%3Ccircle cx='26' cy='8' r='2.5' fill='%233fb950'/%3E%3Ccircle cx='6' cy='24' r='2.5' fill='%233fb950'/%3E%3Ccircle cx='26' cy='24' r='2.5' fill='%233fb950'/%3E%3Cline x1='8' y1='9.5' x2='13' y2='14' stroke='%2330363d' stroke-width='1.5'/%3E%3Cline x1='24' y1='9.5' x2='19' y2='14' stroke='%2330363d' stroke-width='1.5'/%3E%3Cline x1='8' y1='22.5' x2='13' y2='18' stroke='%2330363d' stroke-width='1.5'/%3E%3Cline x1='24' y1='22.5' x2='19' y2='18' stroke='%2330363d' stroke-width='1.5'/%3E%3C/svg%3E">
|
|
13
13
|
<link rel="manifest" href="manifest.webmanifest">
|
|
14
14
|
<link rel="apple-touch-icon" href="icons/agent-relay-192.png">
|
|
15
|
-
<script type="module" crossorigin src="./assets/index-
|
|
15
|
+
<script type="module" crossorigin src="./assets/index-CT10XH5Y.js"></script>
|
|
16
16
|
<link rel="modulepreload" crossorigin href="./assets/chunk-CilyBKbf.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-CDM1EFcn.js">
|
|
18
18
|
<link rel="modulepreload" crossorigin href="./assets/lucide-react-ChOGD4vc.js">
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
<link rel="modulepreload" crossorigin href="./assets/dist-BiVJ8IWp.js">
|
|
32
32
|
<link rel="modulepreload" crossorigin href="./assets/switch-Bp64beKV.js">
|
|
33
33
|
<link rel="modulepreload" crossorigin href="./assets/display-ConJ9cJB.js">
|
|
34
|
-
<link rel="modulepreload" crossorigin href="./assets/store-
|
|
34
|
+
<link rel="modulepreload" crossorigin href="./assets/store-B6qT5_de.js">
|
|
35
35
|
<link rel="modulepreload" crossorigin href="./assets/button-BuTu0MHx.js">
|
|
36
36
|
<link rel="modulepreload" crossorigin href="./assets/copy-button-BOj6uOlU.js">
|
|
37
37
|
<link rel="modulepreload" crossorigin href="./assets/badge-CFVr4fG9.js">
|
|
38
38
|
<link rel="modulepreload" crossorigin href="./assets/input-CawU8wfc.js">
|
|
39
39
|
<link rel="modulepreload" crossorigin href="./assets/themes-mW9zZP4O.js">
|
|
40
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
40
|
+
<link rel="stylesheet" crossorigin href="./assets/index-Ryvn1efN.css">
|
|
41
41
|
</head>
|
|
42
42
|
<body class="bg-background text-foreground antialiased">
|
|
43
43
|
<div id="root"></div>
|
package/src/agent-ref.ts
CHANGED
|
@@ -32,13 +32,14 @@ export function isAgentOnline(agent: AgentCard, now: number = Date.now()): boole
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
interface Selector {
|
|
35
|
-
field: "id" | "label" | "name" | "tag" | "capability" | "machine" | "rig";
|
|
35
|
+
field: "id" | "label" | "name" | "tag" | "capability" | "machine" | "rig" | "team";
|
|
36
36
|
value: string;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const SELECTOR_PREFIXES: Array<[string, Selector["field"]]> = [
|
|
40
40
|
["id:", "id"],
|
|
41
41
|
["label:", "label"],
|
|
42
|
+
["team:", "team"],
|
|
42
43
|
["name:", "name"],
|
|
43
44
|
["tag:", "tag"],
|
|
44
45
|
["capability:", "capability"],
|
|
@@ -63,6 +64,7 @@ function matchesSelector(agent: AgentCard, selector: Selector): boolean {
|
|
|
63
64
|
case "capability": return agent.capabilities.includes(selector.value);
|
|
64
65
|
case "machine": return agent.machine === selector.value;
|
|
65
66
|
case "rig": return agent.rig === selector.value;
|
|
67
|
+
case "team": return agent.teamId === selector.value;
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
|
|
@@ -152,6 +154,7 @@ export function targetMatchesAgent(target: string, agentId: string, agent?: Agen
|
|
|
152
154
|
if (target.startsWith("tag:") && agent.tags.includes(target.slice(4))) return true;
|
|
153
155
|
if (target.startsWith("cap:") && agent.capabilities.includes(target.slice(4))) return true;
|
|
154
156
|
if (target.startsWith("label:") && agent.label === target.slice(6)) return true;
|
|
157
|
+
if (target.startsWith("team:") && agent.teamId === target.slice(5)) return true;
|
|
155
158
|
return false;
|
|
156
159
|
}
|
|
157
160
|
|
|
@@ -184,7 +187,7 @@ type SendPlan =
|
|
|
184
187
|
| { kind: "not_found"; message: string }
|
|
185
188
|
| { kind: "ambiguous"; message: string; candidates: AgentCard[] };
|
|
186
189
|
|
|
187
|
-
const FANOUT_PREFIXES = ["tag:", "cap:", "capability:", "label:"];
|
|
190
|
+
const FANOUT_PREFIXES = ["tag:", "cap:", "capability:", "label:", "team:"];
|
|
188
191
|
const PASSTHROUGH_PREFIXES = ["policy:", "orchestrator:", "pool:"];
|
|
189
192
|
const RESERVED_TARGETS = new Set(["user", "system"]);
|
|
190
193
|
|
|
@@ -199,7 +202,7 @@ function describeAgent(agent: AgentCard): string {
|
|
|
199
202
|
function ambiguousMessage(ref: string, candidates: AgentCard[]): string {
|
|
200
203
|
const list = candidates.slice(0, 8).map(describeAgent).join(", ");
|
|
201
204
|
const more = candidates.length > 8 ? `, +${candidates.length - 8} more` : "";
|
|
202
|
-
return `agent reference "${ref}" is ambiguous — matches ${candidates.length}: ${list}${more}. Use a full id, label, or a fan-out selector (tag:/cap:/label:).`;
|
|
205
|
+
return `agent reference "${ref}" is ambiguous — matches ${candidates.length}: ${list}${more}. Use a full id, label, or a fan-out selector (tag:/cap:/label:/team:).`;
|
|
203
206
|
}
|
|
204
207
|
|
|
205
208
|
function notFoundMessage(ref: string, agents: AgentCard[]): string {
|
|
@@ -227,7 +230,7 @@ export function planSend(to: string, agents: AgentCard[], opts: ResolveOptions =
|
|
|
227
230
|
return { kind: "fanout", to: target, receipt: fanoutReceipt(recipients) };
|
|
228
231
|
}
|
|
229
232
|
if (FANOUT_PREFIXES.some((p) => target.startsWith(p))) {
|
|
230
|
-
const recipients = matchAgents(target.replace(/^cap:/, "capability:"), agents)
|
|
233
|
+
const recipients = matchAgents(target.replace(/^cap:/, "capability:"), agents, opts)
|
|
231
234
|
.filter((a) => isAgentOnline(a, now)).map((a) => a.id);
|
|
232
235
|
return { kind: "fanout", to: target, receipt: fanoutReceipt(recipients) };
|
|
233
236
|
}
|
package/src/channel-target.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type { ChannelRouteTarget } from "./types";
|
|
|
15
15
|
export function parseChannelRouteTarget(target: string): ChannelRouteTarget {
|
|
16
16
|
if (target.startsWith("pool:")) return { type: "pool", id: target.slice("pool:".length) };
|
|
17
17
|
if (target.startsWith("policy:")) return { type: "policy", id: target.slice("policy:".length) };
|
|
18
|
+
if (target.startsWith("team:")) return { type: "team", id: target.slice("team:".length) };
|
|
18
19
|
if (target.startsWith("label:")) return { type: "label", id: target.slice("label:".length) };
|
|
19
20
|
if (target.startsWith("tag:")) return { type: "tag", id: target.slice("tag:".length) };
|
|
20
21
|
if (target.startsWith("cap:")) return { type: "capability", id: target.slice("cap:".length) };
|
package/src/db/agent-search.ts
CHANGED
|
@@ -95,6 +95,8 @@ export interface AgentSearchFilter {
|
|
|
95
95
|
status?: string | string[];
|
|
96
96
|
/** Exact parent agent id — backs the `spawnedBy:` / `spawnedBy:me` fleet filter (#221). */
|
|
97
97
|
spawnedBy?: string;
|
|
98
|
+
/** Exact durable team id — backs roster lookups for first-class teams (#356). */
|
|
99
|
+
teamId?: string;
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
export interface AgentSearchSort {
|
|
@@ -149,6 +151,10 @@ export function searchAgents(filter: AgentSearchFilter = {}, sort: AgentSearchSo
|
|
|
149
151
|
clauses.push("spawned_by = ?");
|
|
150
152
|
params.push(filter.spawnedBy);
|
|
151
153
|
}
|
|
154
|
+
if (filter.teamId) {
|
|
155
|
+
clauses.push("team_id = ?");
|
|
156
|
+
params.push(filter.teamId);
|
|
157
|
+
}
|
|
152
158
|
|
|
153
159
|
const statuses = searchValues(filter.status);
|
|
154
160
|
if (!statuses.includes("all")) {
|
package/src/db/agents.ts
CHANGED
|
@@ -157,8 +157,8 @@ export function upsertAgent(input: RegisterAgentInput): AgentCard {
|
|
|
157
157
|
const instanceProvided = Boolean(input.instanceId);
|
|
158
158
|
const contextState = mergeRelayContextMarkers(input.id, input.context);
|
|
159
159
|
const stmt = getDb().query(`
|
|
160
|
-
INSERT INTO agents (id, name, kind, label, tags, machine, rig, capabilities, ready, status, instance_id, epoch, provider_capabilities, context_state, meta, spawned_by, last_seen, created_at)
|
|
161
|
-
VALUES ($id, $name, $kind, $label, $tags, $machine, $rig, $capabilities, $ready, $status, $instanceId, $initialEpoch, $providerCapabilities, $contextState, $meta, $spawnedBy, $now, $now)
|
|
160
|
+
INSERT INTO agents (id, name, kind, label, tags, machine, rig, capabilities, ready, status, instance_id, epoch, provider_capabilities, context_state, meta, spawned_by, team_id, last_seen, created_at)
|
|
161
|
+
VALUES ($id, $name, $kind, $label, $tags, $machine, $rig, $capabilities, $ready, $status, $instanceId, $initialEpoch, $providerCapabilities, $contextState, $meta, $spawnedBy, $teamId, $now, $now)
|
|
162
162
|
ON CONFLICT(id) DO UPDATE SET
|
|
163
163
|
name = $name,
|
|
164
164
|
kind = $kind,
|
|
@@ -178,6 +178,7 @@ export function upsertAgent(input: RegisterAgentInput): AgentCard {
|
|
|
178
178
|
context_state = coalesce($contextState, agents.context_state),
|
|
179
179
|
meta = $meta,
|
|
180
180
|
spawned_by = coalesce($spawnedBy, agents.spawned_by),
|
|
181
|
+
team_id = coalesce($teamId, agents.team_id),
|
|
181
182
|
last_seen = $now
|
|
182
183
|
`);
|
|
183
184
|
|
|
@@ -201,6 +202,7 @@ export function upsertAgent(input: RegisterAgentInput): AgentCard {
|
|
|
201
202
|
$contextState: contextState ? JSON.stringify(contextState) : null,
|
|
202
203
|
$meta: JSON.stringify(input.meta ?? {}),
|
|
203
204
|
$spawnedBy: input.spawnedBy ?? null,
|
|
205
|
+
$teamId: input.teamId ?? null,
|
|
204
206
|
$now: now,
|
|
205
207
|
});
|
|
206
208
|
if (contextState) recordContextSnapshot(input.id, contextState, now);
|
|
@@ -235,6 +237,13 @@ export function setLabel(id: string, label: string | null): boolean {
|
|
|
235
237
|
);
|
|
236
238
|
}
|
|
237
239
|
|
|
240
|
+
export function setAgentTeam(agentId: string, teamId: string): AgentCard | null {
|
|
241
|
+
const result = getDb()
|
|
242
|
+
.query("UPDATE agents SET team_id = ?, last_seen = ? WHERE id = ?")
|
|
243
|
+
.run(teamId, Date.now(), agentId);
|
|
244
|
+
return result.changes > 0 ? getAgent(agentId) : null;
|
|
245
|
+
}
|
|
246
|
+
|
|
238
247
|
export function setTags(id: string, tags: string[]): AgentCard | null {
|
|
239
248
|
const normalized = [...new Set(tags.map((tag) => tag.trim()).filter(Boolean))];
|
|
240
249
|
const result = getDb().query("UPDATE agents SET tags = ?, last_seen = ? WHERE id = ?").run(JSON.stringify(normalized), Date.now(), id);
|
package/src/db/channels.ts
CHANGED
|
@@ -108,6 +108,7 @@ export function rowToChannelBinding(row: any): ChannelBinding {
|
|
|
108
108
|
export function bindingTargetToLegacyTarget(target: ChannelRouteTarget): string {
|
|
109
109
|
if (target.type === "agent") return target.id;
|
|
110
110
|
if (target.type === "label") return `label:${target.id}`;
|
|
111
|
+
if (target.type === "team") return `team:${target.id}`;
|
|
111
112
|
if (target.type === "tag") return `tag:${target.id}`;
|
|
112
113
|
if (target.type === "capability") return `cap:${target.id}`;
|
|
113
114
|
if (target.type === "broadcast") return "broadcast";
|
|
@@ -160,6 +161,7 @@ export function channelTargetMatches(target: ChannelRouteTarget, channelId?: str
|
|
|
160
161
|
const channelCandidates = candidates.filter((agent) => agentCanServeChannel(agent, channelId));
|
|
161
162
|
if (target.type === "pool") return poolSelectorMatches(target.id, channelCandidates);
|
|
162
163
|
if (target.type === "label") return channelCandidates.filter((agent) => agent.label === target.id);
|
|
164
|
+
if (target.type === "team") return channelCandidates.filter((agent) => agent.teamId === target.id);
|
|
163
165
|
if (target.type === "tag") return channelCandidates.filter((agent) => agent.tags.includes(target.id));
|
|
164
166
|
if (target.type === "capability") return channelCandidates.filter((agent) => agent.capabilities.includes(target.id));
|
|
165
167
|
if (target.type === "broadcast") return channelCandidates;
|
|
@@ -573,4 +575,3 @@ export function resolveChannelRoutes(channelId: string, conversationId?: string)
|
|
|
573
575
|
const exact = rows.filter((row) => row.conversation_key === conversationId);
|
|
574
576
|
return (exact.length ? exact : rows.filter((row) => row.conversation_key === "")).map(rowToChannelBinding);
|
|
575
577
|
}
|
|
576
|
-
|