agent-relay-server 0.123.0 → 0.123.2
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/README.md +3 -1
- package/docs/openapi.json +1 -1
- package/package.json +3 -2
- package/public/assets/MessageBubble-NCggGKTA.js +2 -0
- package/public/assets/{MessageBubble-BTeFExjj.js.map → MessageBubble-NCggGKTA.js.map} +1 -1
- package/public/assets/{PlanGraphPanel-jU1DXg7w.js → PlanGraphPanel-CSdszmGk.js} +2 -2
- package/public/assets/{PlanGraphPanel-jU1DXg7w.js.map → PlanGraphPanel-CSdszmGk.js.map} +1 -1
- package/public/assets/{activity-DUig2iYW.js → activity-Cyp2nolX.js} +2 -2
- package/public/assets/{activity-DUig2iYW.js.map → activity-Cyp2nolX.js.map} +1 -1
- package/public/assets/{agent-profiles-D-oFmb6S.js → agent-profiles-D5Izg0Vn.js} +2 -2
- package/public/assets/{agent-profiles-D-oFmb6S.js.map → agent-profiles-D5Izg0Vn.js.map} +1 -1
- package/public/assets/{agent-type-icon-Dan2l3Tm.js → agent-type-icon-BxUopALO.js} +2 -2
- package/public/assets/{agent-type-icon-Dan2l3Tm.js.map → agent-type-icon-BxUopALO.js.map} +1 -1
- package/public/assets/agents-DIOxuJ6M.js +2 -0
- package/public/assets/{agents-BkC6mcTh.js.map → agents-DIOxuJ6M.js.map} +1 -1
- package/public/assets/{analytics-C3Dlmqe9.js → analytics-DqAuUAwX.js} +3 -3
- package/public/assets/{analytics-C3Dlmqe9.js.map → analytics-DqAuUAwX.js.map} +1 -1
- package/public/assets/{automation-AeMZ70WQ.js → automation-Bn6mOLAR.js} +2 -2
- package/public/assets/{automation-AeMZ70WQ.js.map → automation-Bn6mOLAR.js.map} +1 -1
- package/public/assets/{badge-DCIT3Irj.js → badge-XEru6wto.js} +2 -2
- package/public/assets/{badge-DCIT3Irj.js.map → badge-XEru6wto.js.map} +1 -1
- package/public/assets/branch-state-badge-Mf0Ow77K.js +2 -0
- package/public/assets/{branch-state-badge-CNW10MTC.js.map → branch-state-badge-Mf0Ow77K.js.map} +1 -1
- package/public/assets/{button-CzdTkRIf.js → button-C3CQSr4p.js} +2 -2
- package/public/assets/{button-CzdTkRIf.js.map → button-C3CQSr4p.js.map} +1 -1
- package/public/assets/card-C5XTIPvq.js +2 -0
- package/public/assets/{card-C3BGuVC6.js.map → card-C5XTIPvq.js.map} +1 -1
- package/public/assets/{channels-Bc_mEd56.js → channels-DVRkVeky.js} +2 -2
- package/public/assets/{channels-Bc_mEd56.js.map → channels-DVRkVeky.js.map} +1 -1
- package/public/assets/chat-eUaPz399.js +2 -0
- package/public/assets/{chat-BCNCuTgw.js.map → chat-eUaPz399.js.map} +1 -1
- package/public/assets/{connectors-h0d5Mc6e.js → connectors-Cqubkw9C.js} +2 -2
- package/public/assets/{connectors-h0d5Mc6e.js.map → connectors-Cqubkw9C.js.map} +1 -1
- package/public/assets/copy-button-Bil2CjzT.js +2 -0
- package/public/assets/{copy-button-B3o1jpLu.js.map → copy-button-Bil2CjzT.js.map} +1 -1
- package/public/assets/{dist-OUPTEP6q.js → dist-4VwhnLtj.js} +2 -2
- package/public/assets/{dist-OUPTEP6q.js.map → dist-4VwhnLtj.js.map} +1 -1
- package/public/assets/{dist-BIjlN-xB.js → dist-B8tD9ZcR.js} +2 -2
- package/public/assets/{dist-BIjlN-xB.js.map → dist-B8tD9ZcR.js.map} +1 -1
- package/public/assets/{dist-BInckSJC.js → dist-BJaV2BD4.js} +2 -2
- package/public/assets/{dist-BInckSJC.js.map → dist-BJaV2BD4.js.map} +1 -1
- package/public/assets/{dist-B-T7lg0a.js → dist-C5z05Vkz.js} +2 -2
- package/public/assets/{dist-B-T7lg0a.js.map → dist-C5z05Vkz.js.map} +1 -1
- package/public/assets/dist-CFtFIhu3.js +2 -0
- package/public/assets/{dist-DT-OV6sQ.js.map → dist-CFtFIhu3.js.map} +1 -1
- package/public/assets/{dist-C7aTlosb.js → dist-CGaHpxnN.js} +2 -2
- package/public/assets/{dist-C7aTlosb.js.map → dist-CGaHpxnN.js.map} +1 -1
- package/public/assets/dist-CjyfQThq.js +2 -0
- package/public/assets/{dist-DGfp5dLz.js.map → dist-CjyfQThq.js.map} +1 -1
- package/public/assets/dist-CrUl2aQp.js +2 -0
- package/public/assets/{dist-B6woQ9Tb.js.map → dist-CrUl2aQp.js.map} +1 -1
- package/public/assets/{dist-B3e4B9pg.js → dist-DKmTXfpP.js} +2 -2
- package/public/assets/{dist-B3e4B9pg.js.map → dist-DKmTXfpP.js.map} +1 -1
- package/public/assets/{dist-B9G83DXv.js → dist-V3wu3BHw.js} +2 -2
- package/public/assets/{dist-B9G83DXv.js.map → dist-V3wu3BHw.js.map} +1 -1
- package/public/assets/{dist-BlzVGRhh.js → dist-r8wyUTkK.js} +2 -2
- package/public/assets/{dist-BlzVGRhh.js.map → dist-r8wyUTkK.js.map} +1 -1
- package/public/assets/{es2015-CnKXYKpf.js → es2015-5DTJe35n.js} +2 -2
- package/public/assets/{es2015-CnKXYKpf.js.map → es2015-5DTJe35n.js.map} +1 -1
- package/public/assets/formatted-body-CvrQ2D1J.js +3 -0
- package/public/assets/{formatted-body-CVr3Iev9.js.map → formatted-body-CvrQ2D1J.js.map} +1 -1
- package/public/assets/{formatted-body-impl-Cq5SRGpm.js → formatted-body-impl-BJ7jdbpR.js} +2 -2
- package/public/assets/{formatted-body-impl-Cq5SRGpm.js.map → formatted-body-impl-BJ7jdbpR.js.map} +1 -1
- package/public/assets/index-DnUo0Zqg.js +25 -0
- package/public/assets/index-DnUo0Zqg.js.map +1 -0
- package/public/assets/{input-B6Q0NWCS.js → input-CXMbJ848.js} +2 -2
- package/public/assets/{input-B6Q0NWCS.js.map → input-CXMbJ848.js.map} +1 -1
- package/public/assets/{insights-C8lIBXg3.js → insights-CQGH5LhD.js} +2 -2
- package/public/assets/{insights-C8lIBXg3.js.map → insights-CQGH5LhD.js.map} +1 -1
- package/public/assets/{integrations-7OFsJtY7.js → integrations-9G5TJnnU.js} +2 -2
- package/public/assets/{integrations-7OFsJtY7.js.map → integrations-9G5TJnnU.js.map} +1 -1
- package/public/assets/{lib-BHsWPMKB.js → lib-BcUW1l-W.js} +2 -2
- package/public/assets/{lib-BHsWPMKB.js.map → lib-BcUW1l-W.js.map} +1 -1
- package/public/assets/lucide-react-CoU_Xts2.js +10 -0
- package/public/assets/{lucide-react-DuhlF2Sj.js.map → lucide-react-CoU_Xts2.js.map} +1 -1
- package/public/assets/maintenance-7c7UvpAt.js +2 -0
- package/public/assets/{maintenance-HDpg20OY.js.map → maintenance-7c7UvpAt.js.map} +1 -1
- package/public/assets/{managed-agents-muCIMxng.js → managed-agents-yh4HPO5a.js} +4 -4
- package/public/assets/{managed-agents-muCIMxng.js.map → managed-agents-yh4HPO5a.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-Cx7pCfA6.js → markdown-preview-impl-BuOn5-Uc.js} +2 -2
- package/public/assets/{markdown-preview-impl-Cx7pCfA6.js.map → markdown-preview-impl-BuOn5-Uc.js.map} +1 -1
- package/public/assets/memory-Qv0JN8CE.js +2 -0
- package/public/assets/{memory-CTO1FfJ-.js.map → memory-Qv0JN8CE.js.map} +1 -1
- package/public/assets/messages-CgjHJmc2.js +2 -0
- package/public/assets/{messages-RWSXG2UM.js.map → messages-CgjHJmc2.js.map} +1 -1
- package/public/assets/{orchestrators-5aiYw3yx.js → orchestrators-CgL4CFzb.js} +2 -2
- package/public/assets/{orchestrators-5aiYw3yx.js.map → orchestrators-CgL4CFzb.js.map} +1 -1
- package/public/assets/{overview-Du6XLt5l.js → overview-Bri04eYE.js} +2 -2
- package/public/assets/{overview-Du6XLt5l.js.map → overview-Bri04eYE.js.map} +1 -1
- package/public/assets/{pairs-BDRnlEta.js → pairs-C78LwUH2.js} +2 -2
- package/public/assets/{pairs-BDRnlEta.js.map → pairs-C78LwUH2.js.map} +1 -1
- package/public/assets/{projects-DwsWE9es.js → projects-CquHTMo1.js} +2 -2
- package/public/assets/{projects-DwsWE9es.js.map → projects-CquHTMo1.js.map} +1 -1
- package/public/assets/prompt-settings-DDEkMqPZ.js +2 -0
- package/public/assets/{prompt-settings-CaJX_LbA.js.map → prompt-settings-DDEkMqPZ.js.map} +1 -1
- package/public/assets/{react-dom-DWBmcawP.js → react-dom-BQICbUuF.js} +2 -2
- package/public/assets/{react-dom-DWBmcawP.js.map → react-dom-BQICbUuF.js.map} +1 -1
- package/public/assets/registry-5KhP5FE5.js +2 -0
- package/public/assets/registry-5KhP5FE5.js.map +1 -0
- package/public/assets/{security-BpVrdYhJ.js → security-wvnRKFvm.js} +2 -2
- package/public/assets/{security-BpVrdYhJ.js.map → security-wvnRKFvm.js.map} +1 -1
- package/public/assets/{select-DallbU93.js → select-BIfbVmLf.js} +2 -2
- package/public/assets/{select-DallbU93.js.map → select-BIfbVmLf.js.map} +1 -1
- package/public/assets/settings-B6Ntahx7.js +6 -0
- package/public/assets/{settings-BUQirdyI.js.map → settings-B6Ntahx7.js.map} +1 -1
- package/public/assets/{store-DMZSTozC.js → store-zFLzd0Jd.js} +5 -5
- package/public/assets/{store-DMZSTozC.js.map → store-zFLzd0Jd.js.map} +1 -1
- package/public/assets/{task-chip-DXfvXBDx.js → task-chip-BHM-e3I8.js} +2 -2
- package/public/assets/{task-chip-DXfvXBDx.js.map → task-chip-BHM-e3I8.js.map} +1 -1
- package/public/assets/{tasks-k8mQDfKy.js → tasks-CUglafo5.js} +2 -2
- package/public/assets/{tasks-k8mQDfKy.js.map → tasks-CUglafo5.js.map} +1 -1
- package/public/assets/{teams-DI-Lhhbl.js → teams-EE8RHhHM.js} +2 -2
- package/public/assets/{teams-DI-Lhhbl.js.map → teams-EE8RHhHM.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-BL853Fpi.js → terminal-viewer-impl-DBRkdyag.js} +3 -3
- package/public/assets/{terminal-viewer-impl-BL853Fpi.js.map → terminal-viewer-impl-DBRkdyag.js.map} +1 -1
- package/public/assets/{use-keyboard-viewport-CP109w9y.js → use-keyboard-viewport-CAa7IQTm.js} +2 -2
- package/public/assets/{use-keyboard-viewport-CP109w9y.js.map → use-keyboard-viewport-CAa7IQTm.js.map} +1 -1
- package/public/assets/user-avatar-kYPfLgXp.js +2 -0
- package/public/assets/{user-avatar-D9xyGi3u.js.map → user-avatar-kYPfLgXp.js.map} +1 -1
- package/public/assets/{work-queue-DrLIdvxB.js → work-queue-BFQVVL4T.js} +2 -2
- package/public/assets/{work-queue-DrLIdvxB.js.map → work-queue-BFQVVL4T.js.map} +1 -1
- package/public/assets/workspaces-CR6f7G0R.js +3 -0
- package/public/assets/{workspaces-CnNL6T8Q.js.map → workspaces-CR6f7G0R.js.map} +1 -1
- package/public/index.html +23 -23
- package/runner/hooks/rg-r-guard/hooks.json +18 -0
- package/runner/hooks/rg-r-guard/rg-r-guard.cjs +144 -0
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/src/launch-assembly.ts +34 -1
- package/runner/src/provisioning.ts +15 -0
- package/src/services/spawn-chain-inspector.ts +134 -0
- package/public/assets/MessageBubble-BTeFExjj.js +0 -2
- package/public/assets/agents-BkC6mcTh.js +0 -2
- package/public/assets/branch-state-badge-CNW10MTC.js +0 -2
- package/public/assets/card-C3BGuVC6.js +0 -2
- package/public/assets/chat-BCNCuTgw.js +0 -2
- package/public/assets/copy-button-B3o1jpLu.js +0 -2
- package/public/assets/dist-B6woQ9Tb.js +0 -2
- package/public/assets/dist-DGfp5dLz.js +0 -2
- package/public/assets/dist-DT-OV6sQ.js +0 -2
- package/public/assets/formatted-body-CVr3Iev9.js +0 -3
- package/public/assets/index--9UMzCFf.js +0 -25
- package/public/assets/index--9UMzCFf.js.map +0 -1
- package/public/assets/lucide-react-DuhlF2Sj.js +0 -10
- package/public/assets/maintenance-HDpg20OY.js +0 -2
- package/public/assets/memory-CTO1FfJ-.js +0 -2
- package/public/assets/messages-RWSXG2UM.js +0 -2
- package/public/assets/prompt-settings-CaJX_LbA.js +0 -2
- package/public/assets/registry-C-uIL0Nl.js +0 -2
- package/public/assets/registry-C-uIL0Nl.js.map +0 -1
- package/public/assets/settings-BUQirdyI.js +0 -6
- package/public/assets/user-avatar-D9xyGi3u.js +0 -2
- package/public/assets/workspaces-CnNL6T8Q.js +0 -3
package/public/assets/{use-keyboard-viewport-CP109w9y.js → use-keyboard-viewport-CAa7IQTm.js}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{
|
|
2
|
-
//# sourceMappingURL=use-keyboard-viewport-
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{gr as t}from"./lucide-react-CoU_Xts2.js";var n=e(t(),1),r=100;function i(e){if(!e)return!1;let t=e.tagName;return t===`INPUT`||t===`TEXTAREA`||e.isContentEditable===!0}function a(e=!0){let[t,a]=(0,n.useState)(null);return(0,n.useEffect)(()=>{if(!e){a(null);return}let t=window.visualViewport;if(!t)return;function n(){a(i(document.activeElement)&&window.innerHeight-t.height>r?t.height:null)}return n(),t.addEventListener(`resize`,n),window.addEventListener(`focusout`,n),()=>{t.removeEventListener(`resize`,n),window.removeEventListener(`focusout`,n)}},[e]),t}export{a as t};
|
|
2
|
+
//# sourceMappingURL=use-keyboard-viewport-CAa7IQTm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-keyboard-viewport-
|
|
1
|
+
{"version":3,"file":"use-keyboard-viewport-CAa7IQTm.js","names":[],"sources":["../../dashboard/src/hooks/use-keyboard-viewport.ts"],"sourcesContent":["import { useEffect, useState } from 'react'\n\n// On-screen keyboards are tall; browser chrome / toolbar deltas are small.\n// 100px cleanly separates \"keyboard is open\" from incidental viewport jitter.\nconst KEYBOARD_MIN_DELTA = 100\n\nfunction isEditableTarget(el: Element | null): boolean {\n if (!el) return false\n const tag = el.tagName\n return tag === 'INPUT' || tag === 'TEXTAREA' || (el as HTMLElement).isContentEditable === true\n}\n\n/**\n * Returns a pixel height to apply while the on-screen keyboard is open, or null\n * when it's closed (so the element falls back to its CSS height, e.g. 100dvh).\n *\n * iPad Safari ignores `interactive-widget=resizes-content`, so the keyboard\n * shrinks only the *visual* viewport, never the layout viewport — `dvh` units\n * don't react to it. Pinning `visualViewport.height` is what actually makes a\n * fullscreen container shrink for the keyboard there.\n *\n * The trap (#270 follow-up): if you pin `vv.height` unconditionally and never\n * reset it, the container stays shrunk after the keyboard closes, and iOS's\n * documented post-dismiss bug (a late resize event reporting wrong/offset\n * dimensions) gets applied blindly — leaving the layout too narrow / dialogs\n * too tall until an orientation toggle resets the OS viewport.\n *\n * So we pin a height ONLY while the keyboard is genuinely open (an editable\n * element is focused AND the visual viewport is meaningfully shorter than the\n * layout viewport) and return null otherwise. Returning null hands sizing back\n * to CSS, which recovers automatically — the same reset the orientation toggle\n * triggers, minus the manual flip. This also no-ops for iPad hardware keyboards\n * (no visual-viewport shrink → no spurious pin).\n *\n * @param active when false the hook stays inert and returns null (e.g. a closed dialog)\n */\nexport function useKeyboardViewportHeight(active = true): number | null {\n const [height, setHeight] = useState<number | null>(null)\n\n useEffect(() => {\n if (!active) {\n setHeight(null)\n return\n }\n const vv = window.visualViewport\n if (!vv) return\n\n function onResize() {\n const keyboardOpen =\n isEditableTarget(document.activeElement) && window.innerHeight - vv!.height > KEYBOARD_MIN_DELTA\n setHeight(keyboardOpen ? vv!.height : null)\n }\n\n onResize()\n vv.addEventListener('resize', onResize)\n // Dismissing the keyboard often blurs without a reliable resize — re-check on focus loss.\n window.addEventListener('focusout', onResize)\n return () => {\n vv.removeEventListener('resize', onResize)\n window.removeEventListener('focusout', onResize)\n }\n }, [active])\n\n return height\n}\n"],"mappings":"uGAIM,EAAqB,IAE3B,SAAS,EAAiB,EAA6B,CACrD,GAAI,CAAC,EAAI,MAAO,GAChB,IAAM,EAAM,EAAG,QACf,OAAO,IAAQ,SAAW,IAAQ,YAAe,EAAmB,oBAAsB,GA2B5F,SAAgB,EAA0B,EAAS,GAAqB,CACtE,GAAM,CAAC,EAAQ,IAAA,EAAA,EAAA,UAAqC,KAAK,CA0BzD,OAxBA,EAAA,EAAA,eAAgB,CACd,GAAI,CAAC,EAAQ,CACX,EAAU,KAAK,CACf,OAEF,IAAM,EAAK,OAAO,eAClB,GAAI,CAAC,EAAI,OAET,SAAS,GAAW,CAGlB,EADE,EAAiB,SAAS,cAAc,EAAI,OAAO,YAAc,EAAI,OAAS,EACvD,EAAI,OAAS,KAAK,CAO7C,OAJA,GAAU,CACV,EAAG,iBAAiB,SAAU,EAAS,CAEvC,OAAO,iBAAiB,WAAY,EAAS,KAChC,CACX,EAAG,oBAAoB,SAAU,EAAS,CAC1C,OAAO,oBAAoB,WAAY,EAAS,GAEjD,CAAC,EAAO,CAAC,CAEL"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{ar as t,gr as n,rr as r,tr as i}from"./lucide-react-CoU_Xts2.js";import{_ as a,rt as o}from"./display-GFbGUG0O.js";import{t as s}from"./agent-type-icon-BxUopALO.js";var c=e(n(),1),l=r(),u={id:o};function d({user:e,address:n,distinct:r=!1,className:o}){let d=e?.avatarUrl??(e?.avatarArtifactId?`/artifacts/${encodeURIComponent(e.avatarArtifactId)}/content`:null),[f,p]=(0,c.useState)(null);(0,c.useEffect)(()=>{if(!d){p(null);return}let e=!1,n=null;return(async()=>{try{let r=await t(d);if(e)return;n=URL.createObjectURL(r),p(n)}catch{e||p(null)}})(),()=>{e=!0,n&&URL.revokeObjectURL(n)}},[d]);let m=e?.displayName||e?.handle||`User`;if(f)return(0,l.jsx)(`img`,{src:f,alt:m,title:m,className:i(`w-7 h-7 rounded-md object-cover`,o)});if(r){let e=(m.trim()[0]||`U`).toUpperCase();return(0,l.jsx)(`span`,{title:m,className:i(`inline-flex w-7 h-7 items-center justify-center rounded-md bg-muted text-xs font-semibold ring-1 ring-foreground/10`,a(n),o),children:e})}return(0,l.jsx)(s,{agent:u,className:o})}export{d as t};
|
|
2
|
+
//# sourceMappingURL=user-avatar-kYPfLgXp.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-avatar-
|
|
1
|
+
{"version":3,"file":"user-avatar-kYPfLgXp.js","names":[],"sources":["../../dashboard/src/components/shared/user-avatar.tsx"],"sourcesContent":["import { useEffect, useState } from 'react'\nimport { AgentTypeIcon } from '@/components/shared/agent-type-icon'\nimport { apiBlob } from '@/lib/api'\nimport { humanAccentClass } from '@/lib/display'\nimport { HUMAN_AGENT_ID } from '@/lib/constants'\nimport { cn } from '@/lib/utils'\nimport type { Agent, User } from '@/types'\n\n// Minimal stub so AgentTypeIcon resolves the generic 'user' face when no avatar exists.\nconst HUMAN_AGENT_STUB = { id: HUMAN_AGENT_ID } as unknown as Agent\n\n// A human's face (#390). Renders, in order of preference: the user's content-addressed avatar\n// image (the FK lands fully in #630 — until then `avatarArtifactId` is null and this path is\n// dormant), then a colored initial when we need to distinguish multiple operators, then the\n// generic operator icon. Degrades gracefully at every step so a single seeded user looks\n// exactly as before.\nexport function UserAvatar({\n user,\n address,\n distinct = false,\n className,\n}: {\n user: User | undefined\n /** The human address (`user` / `user:<id>`) — drives the deterministic accent color. */\n address?: string\n /** When true (multiple humans in view), fall back to a colored initial instead of the icon. */\n distinct?: boolean\n className?: string\n}) {\n const avatarUrl = user?.avatarUrl ?? (user?.avatarArtifactId ? `/artifacts/${encodeURIComponent(user.avatarArtifactId)}/content` : null)\n const [objectUrl, setObjectUrl] = useState<string | null>(null)\n\n useEffect(() => {\n if (!avatarUrl) { setObjectUrl(null); return }\n let cancelled = false\n let url: string | null = null\n void (async () => {\n try {\n const blob = await apiBlob(avatarUrl)\n if (cancelled) return\n url = URL.createObjectURL(blob)\n setObjectUrl(url)\n } catch {\n if (!cancelled) setObjectUrl(null)\n }\n })()\n return () => { cancelled = true; if (url) URL.revokeObjectURL(url) }\n }, [avatarUrl])\n\n const title = user?.displayName || user?.handle || 'User'\n\n if (objectUrl) {\n return <img src={objectUrl} alt={title} title={title} className={cn('w-7 h-7 rounded-md object-cover', className)} />\n }\n if (distinct) {\n const initial = (title.trim()[0] || 'U').toUpperCase()\n return (\n <span\n title={title}\n className={cn('inline-flex w-7 h-7 items-center justify-center rounded-md bg-muted text-xs font-semibold ring-1 ring-foreground/10', humanAccentClass(address), className)}\n >\n {initial}\n </span>\n )\n }\n return <AgentTypeIcon agent={HUMAN_AGENT_STUB} className={className} />\n}\n"],"mappings":"yOASM,EAAmB,CAAE,GAAI,EAAgB,CAO/C,SAAgB,EAAW,CACzB,OACA,UACA,WAAW,GACX,aAQC,CACD,IAAM,EAAY,GAAM,YAAc,GAAM,iBAAmB,cAAc,mBAAmB,EAAK,iBAAiB,CAAC,UAAY,MAC7H,CAAC,EAAW,IAAA,EAAA,EAAA,UAAwC,KAAK,EAE/D,EAAA,EAAA,eAAgB,CACd,GAAI,CAAC,EAAW,CAAE,EAAa,KAAK,CAAE,OACtC,IAAI,EAAY,GACZ,EAAqB,KAWzB,OAVM,SAAY,CAChB,GAAI,CACF,IAAM,EAAO,MAAM,EAAQ,EAAU,CACrC,GAAI,EAAW,OACf,EAAM,IAAI,gBAAgB,EAAK,CAC/B,EAAa,EAAI,MACX,CACD,GAAW,EAAa,KAAK,KAElC,KACS,CAAE,EAAY,GAAU,GAAK,IAAI,gBAAgB,EAAI,GACjE,CAAC,EAAU,CAAC,CAEf,IAAM,EAAQ,GAAM,aAAe,GAAM,QAAU,OAEnD,GAAI,EACF,OAAO,EAAA,EAAA,KAAC,MAAD,CAAK,IAAK,EAAW,IAAK,EAAc,QAAO,UAAW,EAAG,kCAAmC,EAAU,CAAI,CAAA,CAEvH,GAAI,EAAU,CACZ,IAAM,GAAW,EAAM,MAAM,CAAC,IAAM,KAAK,aAAa,CACtD,OACE,EAAA,EAAA,KAAC,OAAD,CACS,QACP,UAAW,EAAG,sHAAuH,EAAiB,EAAQ,CAAE,EAAU,UAEzK,EACI,CAAA,CAGX,OAAO,EAAA,EAAA,KAAC,EAAD,CAAe,MAAO,EAA6B,YAAa,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
//# sourceMappingURL=work-queue-
|
|
1
|
+
import{a as e,gt as t,jn as n,rr as r,yn as i}from"./lucide-react-CoU_Xts2.js";import{p as a,t as o}from"./store-zFLzd0Jd.js";import{K as s,ct as c,d as l}from"./display-GFbGUG0O.js";import{t as u}from"./badge-XEru6wto.js";import{t as d}from"./button-C3CQSr4p.js";import{At as f,G as p,wt as m}from"./index-DnUo0Zqg.js";import{r as h,t as g}from"./card-C5XTIPvq.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=m(),S=f(),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`?`T${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)(p,{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)(i,{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)(n,{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)(i,{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)(e,{className:`w-3.5 h-3.5 mr-1`}),` Cancel`]})]})]})})})}export{y as WorkQueueView};
|
|
2
|
+
//# sourceMappingURL=work-queue-BFQVVL4T.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-queue-DrLIdvxB.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' ? `T${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
|
+
{"version":3,"file":"work-queue-BFQVVL4T.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' ? `T${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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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{At as t,Cn as n,En as r,Gt as i,Ht as a,Mt as o,Nt as s,Q as c,R as l,Tn as u,Wn as d,Yn as f,Yt as p,a as m,g as h,gr as g,h as _,ir as v,ln as y,qt as b,rr as x,zt as S}from"./lucide-react-CoU_Xts2.js";import{p as C,t as w}from"./store-zFLzd0Jd.js";import{u as T}from"./types-CRptbSYh.js";import{K as E,d as D}from"./display-GFbGUG0O.js";import{t as O}from"./badge-XEru6wto.js";import{t as k}from"./button-C3CQSr4p.js";import{t as ee}from"./copy-button-Bil2CjzT.js";import{t as A}from"./task-chip-BHM-e3I8.js";import{o as j,r as M,s as N,t as P}from"./card-C5XTIPvq.js";var F=e(g(),1),I=x(),L=new Set([`active`,`ready`,`conflict`,`review_requested`,`merge_planned`,`cleanup_requested`]),R=new Set(T),z=`__all__`,B=`__unassigned__`;function V(e,t){let n=new Map;for(let t of e){let e=t.repoRoot||t.sourceCwd||`unknown repo`;n.set(e,[...n.get(e)||[],t])}return[...n.entries()].map(([e,n])=>({repoRoot:e,workspaces:n.sort(t)})).sort((e,t)=>e.repoRoot.localeCompare(t.repoRoot))}function H(e,t){let n=new Map;for(let t of e){let e=t.projectSlug||t.repoRoot||t.sourceCwd||`unassigned`;n.set(e,[...n.get(e)||[],t])}return[...n.entries()].map(([e,n])=>{let r=n[0],i=!r.projectId;return{key:e,title:i?r.repoRoot||r.sourceCwd||`unknown repo`:r.projectTitle||r.projectSlug||r.repoRoot,...r.projectId?{projectId:r.projectId}:{},...r.projectSlug?{projectSlug:r.projectSlug}:{},unassigned:i,repoGroups:V(n,t)}}).sort((e,t)=>{let n=e.repoGroups.some(e=>e.workspaces.some(e=>L.has(e.status)));return n===t.repoGroups.some(e=>e.workspaces.some(e=>L.has(e.status)))?e.title.localeCompare(t.title):n?-1:1})}function U(e,t,n){let r=t===`all`?e:t===`cleaned`?e.filter(e=>R.has(e.status)):e.filter(e=>!R.has(e.status));return n===`__all__`?r:n===`__unassigned__`?r.filter(e=>!e.projectId):r.filter(e=>e.projectId===n)}function W({group:e,renderWorkspaceRow:t}){let n=e.repoGroups.flatMap(e=>e.workspaces),r=n.filter(e=>L.has(e.status)).length,i=n.filter(e=>e.status===`ready`||e.status===`review_requested`||e.status===`merge_planned`).length,a=n.filter(e=>e.status===`conflict`).length,o=e.repoGroups.length>1;return(0,I.jsxs)(P,{children:[(0,I.jsx)(j,{className:`px-4 py-3`,children:(0,I.jsx)(`div`,{className:`flex min-w-0 items-start gap-3`,children:(0,I.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,I.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,I.jsx)(N,{className:`truncate text-sm font-medium`,title:e.title,children:e.title}),e.unassigned&&(0,I.jsx)(O,{variant:`outline`,className:`text-[10px]`,children:`Unassigned`})]}),(0,I.jsxs)(`div`,{className:`mt-1 flex flex-wrap gap-1.5`,children:[(0,I.jsxs)(O,{variant:`outline`,className:`text-[10px]`,children:[n.length,` total`]}),(0,I.jsxs)(O,{variant:`outline`,className:`text-[10px]`,children:[r,` live`]}),i>0&&(0,I.jsxs)(O,{variant:`outline`,className:`border-emerald-500/20 text-emerald-400 text-[10px]`,children:[i,` merge signal`]}),a>0&&(0,I.jsxs)(O,{variant:`outline`,className:`border-red-500/20 text-red-400 text-[10px]`,children:[a,` conflict`]})]})]})})}),(0,I.jsx)(M,{className:`p-0`,children:e.repoGroups.map(e=>(0,I.jsxs)(`div`,{className:`border-t border-border first:border-t-0`,children:[o&&(0,I.jsxs)(`div`,{className:`flex items-center gap-2 border-b border-dashed border-border/80 px-3 py-2`,children:[(0,I.jsx)(S,{className:`h-3.5 w-3.5 shrink-0 text-muted-foreground`}),(0,I.jsx)(`span`,{className:`truncate font-mono text-[11px] text-muted-foreground`,title:e.repoRoot,children:e.repoRoot})]}),e.workspaces.map(e=>t(e))]},e.repoRoot))})]})}function G(e){let t=e.split(`/`).filter(Boolean);return t.length<=3?e||`-`:`/${t.slice(-3).join(`/`)}`}function K(){let e=w(e=>e.workspaceOrphans),t=w(e=>e.fetchWorkspaceOrphans),n=w(e=>e.reclaimWorkspaceOrphan);return(0,F.useEffect)(()=>{t()},[t]),e.length?(0,I.jsxs)(P,{className:`border-amber-500/30`,children:[(0,I.jsxs)(j,{className:`px-4 py-3`,children:[(0,I.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,I.jsx)(c,{className:`h-4 w-4 text-amber-400`}),(0,I.jsx)(N,{className:`text-sm font-medium`,children:`Orphaned worktrees`}),(0,I.jsx)(O,{variant:`outline`,className:`border-amber-500/30 text-amber-400 text-[10px]`,children:e.length}),(0,I.jsx)(k,{size:`icon-sm`,variant:`ghost`,className:`ml-auto h-6 w-6`,title:`Rescan`,onClick:()=>void t(),children:(0,I.jsx)(l,{className:`h-3.5 w-3.5`})})]}),(0,I.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,I.jsx)(M,{className:`p-0`,children:e.map(e=>(0,I.jsxs)(`div`,{className:`flex items-center gap-3 border-t border-border px-4 py-2 text-xs`,children:[(0,I.jsx)(S,{className:`h-3.5 w-3.5 shrink-0 text-muted-foreground`}),(0,I.jsx)(`span`,{className:`truncate font-medium`,title:e.branch,children:e.branch||`(detached)`}),(0,I.jsx)(`span`,{className:`truncate font-mono text-muted-foreground`,title:e.worktreePath,children:G(e.worktreePath)}),e.hadTerminalRow&&(0,I.jsx)(O,{variant:`outline`,className:`text-[10px]`,children:`cleanup failed`}),(0,I.jsxs)(k,{size:`sm`,variant:`outline`,className:`ml-auto h-7 text-xs`,onClick:()=>void n(e),children:[(0,I.jsx)(c,{className:`mr-1 h-3.5 w-3.5`}),`Reclaim`]})]},e.worktreePath))})]}):null}function q(e,t,n){if(n){let t=e.find(e=>e.branch&&e.branch===n);if(t)return t}if(!t)return null;let r=e.filter(e=>e.ownerAgentId===t);return r.length===0?null:r.length===1?r[0]:null}var J=new Set([`active`,`ready`,`conflict`,`review_requested`,`merge_planned`]),Y=new Set([`active`,`ready`,`review_requested`,`merge_planned`,`conflict`]),X={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`},Z={active:`active`,ready:`ready`,conflict:`conflict`,review_requested:`review`,merge_planned:`merge planned`,merged:`merged`,abandoned:`abandoned`,cleanup_requested:`cleanup`,cleaned:`cleaned`},Q={conflict:0,ready:1,review_requested:2,merge_planned:3,active:4,cleanup_requested:5,merged:6,abandoned:7,cleaned:8};function $(e,t){return e?D(e):t||`-`}function te(e){let t=e.split(`/`).filter(Boolean);return t.length<=3?e||`-`:`/${t.slice(-3).join(`/`)}`}function ne(e){return X[e]||`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`}function re(e,t){let n=(Q[e.status]??99)-(Q[t.status]??99);return n===0?Number(t.updatedAt||0)-Number(e.updatedAt||0):n}function ie({workspace:e,expanded:i,onToggleDetails:o}){let s=w(e=>e.workspaceAction),c=w(e=>e.purgeWorkspace),l=w(e=>e.fetchWorkspaceMergePreview),d=w(e=>e.openFilesAt),f=e.status===`cleaned`||e.status===`merged`||e.status===`abandoned`,g=f||e.status===`cleanup_requested`,_=e.worktreePath||e.sourceCwd||e.repoRoot,v=w(t=>t.workspaceGitState[e.id]),y=!!v&&v.available!==!1&&v.landed===!0,b=e.mode===`isolated`&&!!e.worktreePath&&Y.has(e.status)&&!y;async function x(){await l(e.id),await s(e.id,`merge`)}return(0,I.jsxs)(`div`,{className:`flex flex-wrap justify-end gap-1.5`,children:[(0,I.jsx)(k,{size:`icon-sm`,variant:`ghost`,title:i?`Hide details`:`Show diff & timeline`,onClick:o,children:i?(0,I.jsx)(u,{className:`h-3.5 w-3.5`}):(0,I.jsx)(n,{className:`h-3.5 w-3.5`})}),(0,I.jsx)(k,{size:`icon-sm`,variant:`ghost`,title:`Open workspace`,disabled:!_||e.status===`cleaned`,onClick:()=>void d({path:_}),children:(0,I.jsx)(S,{className:`h-3.5 w-3.5`})}),(0,I.jsx)(ee,{value:_,label:`Copy path`,copiedLabel:`Copied path`,size:`icon-sm`,variant:`ghost`,disabled:!_}),(0,I.jsx)(k,{size:`icon-sm`,variant:`outline`,title:`Mark ready`,disabled:g||e.status===`ready`,onClick:()=>void s(e.id,`ready`),children:(0,I.jsx)(r,{className:`h-3.5 w-3.5`})}),(0,I.jsx)(k,{size:`icon-sm`,variant:`outline`,title:`Request review`,disabled:g||e.status===`review_requested`,onClick:()=>void s(e.id,`request-review`),children:(0,I.jsx)(p,{className:`h-3.5 w-3.5`})}),(0,I.jsx)(k,{size:`icon-sm`,variant:`outline`,title:`Mark merge planned`,disabled:g||e.status===`merge_planned`,onClick:()=>void s(e.id,`merge-plan`),children:(0,I.jsx)(a,{className:`h-3.5 w-3.5`})}),(0,I.jsx)(k,{size:`icon-sm`,variant:`default`,title:y?`Already merged into base — nothing to land`:`Merge & land work`,disabled:!b,onClick:()=>void x(),children:(0,I.jsx)(t,{className:`h-3.5 w-3.5`})}),(0,I.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 s(e.id,`cleanup`),children:(0,I.jsx)(h,{className:`h-3.5 w-3.5`})}),f&&(0,I.jsx)(k,{size:`icon-sm`,variant:`ghost`,title:`Purge record (clears the row, does not touch disk)`,onClick:()=>void c(e.id),children:(0,I.jsx)(m,{className:`h-3.5 w-3.5`})})]})}function ae({workspace:e,unmerged:t}){let n=w(t=>t.workspaceMergePreview[e.id]),i=w(e=>e.fetchWorkspaceMergePreview),a=t>0&&Y.has(e.status);return(0,F.useEffect)(()=>{a&&n===void 0&&i(e.id)},[a,n,e.id,i]),!a||n===void 0||n.available===!1?null:n.conflict?(0,I.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,I.jsx)(_,{className:`h-3 w-3`}),`conflict`]}):n.conflict===!1?(0,I.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,I.jsx)(r,{className:`h-3 w-3`}),n.strategy===`pr`?`PR ready`:`clean`]}):null}function oe({workspace:e}){let t=C(),n=w(t=>t.workspaceGitState[e.id]),a=w(e=>e.fetchWorkspaceGitState),s=e.mode===`isolated`&&!!e.worktreePath&&J.has(e.status);if((0,F.useEffect)(()=>{s&&n===void 0&&a(e.id)},[s,n,e.id,a]),!s)return null;let c=(0,I.jsx)(k,{size:`icon-sm`,variant:`ghost`,className:`h-5 w-5`,title:`Refresh git state`,onClick:()=>void a(e.id),children:(0,I.jsx)(l,{className:`h-3 w-3`})});if(n===void 0)return(0,I.jsx)(`div`,{className:`flex items-center gap-1 text-[11px] text-muted-foreground`,children:`Loading git state…`});if(n.available===!1)return(0,I.jsxs)(`div`,{className:`flex items-center gap-1 text-[11px] text-muted-foreground`,title:n.reason,children:[(0,I.jsx)(`span`,{children:`git state unavailable`}),c]});if(n.missing)return(0,I.jsxs)(`div`,{className:`flex items-center gap-1 text-[11px] text-muted-foreground`,children:[`worktree gone `,c]});if(n.error)return(0,I.jsxs)(`div`,{className:`flex items-center gap-1 text-[11px] text-red-400`,title:n.error,children:[(0,I.jsx)(`span`,{children:`git error`}),c]});let u=n.ahead??0,p=n.landed===!0,m=p?0:n.unmergedAhead??u,h=n.behind??0,g=n.dirtyCount??0,_=m===0&&g===0&&!p,v=n.lastCommit;return(0,I.jsxs)(`div`,{className:`space-y-0.5`,children:[(0,I.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[11px]`,children:[_?(0,I.jsx)(`span`,{className:`text-muted-foreground`,children:`no unmerged work`}):(0,I.jsxs)(I.Fragment,{children:[p?(0,I.jsxs)(`span`,{className:`flex items-center gap-0.5 text-emerald-400`,title:`Work already landed in ${n.baseRef||`base`} via squash/cherry-pick — safe to clean up`,children:[(0,I.jsx)(r,{className:`h-3 w-3`}),`landed`]}):(0,I.jsxs)(`span`,{className:m>0?`flex items-center text-emerald-400`:`flex items-center text-muted-foreground`,title:u===m?`${m} commit(s) ahead of base`:`${m} unmerged commit(s) (${u} ahead of base)`,children:[(0,I.jsx)(d,{className:`h-3 w-3`}),m]}),(0,I.jsxs)(`span`,{className:h>0?`flex items-center text-amber-400`:`flex items-center text-muted-foreground`,title:`${h} commit(s) behind base`,children:[(0,I.jsx)(f,{className:`h-3 w-3`}),h]}),g>0&&(0,I.jsxs)(`span`,{className:`flex items-center text-orange-400`,title:`${g} uncommitted change(s)`,children:[(0,I.jsx)(i,{className:`h-3 w-3`}),g]}),!p&&(0,I.jsx)(ae,{workspace:e,unmerged:m})]}),c]}),v&&(0,I.jsxs)(`div`,{className:`flex min-w-0 items-center gap-1 text-[11px] text-muted-foreground`,title:`${v.sha.slice(0,8)} — ${v.message}`,children:[(0,I.jsx)(o,{className:`h-3 w-3 shrink-0`}),(0,I.jsx)(`span`,{className:`truncate`,children:v.message||v.sha.slice(0,8)}),v.at?(0,I.jsxs)(`span`,{className:`shrink-0 opacity-70`,children:[`· `,E(t,v.at)]}):null]})]})}function se({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,I.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-muted-foreground`,children:[(0,I.jsxs)(`span`,{className:`flex items-center gap-1 font-medium text-foreground`,children:[(0,I.jsx)(y,{className:`h-3 w-3`}),`Timeline`]}),r.map((e,n)=>(0,I.jsxs)(`span`,{className:`flex items-center gap-1`,children:[n>0&&(0,I.jsx)(`span`,{className:`opacity-40`,children:`→`}),(0,I.jsx)(`span`,{className:`text-foreground`,children:e.label}),(0,I.jsx)(`span`,{title:new Date(e.at).toLocaleString(),children:E(t,e.at)})]},e.label))]})}function ce(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 le({workspace:e}){let t=w(t=>t.workspaceDiff[e.id]),n=w(e=>e.fetchWorkspaceDiff),r=e.mode===`isolated`&&!!e.worktreePath;if((0,F.useEffect)(()=>{r&&t===void 0&&n(e.id)},[r,t,e.id,n]),!r)return null;if(t===void 0)return(0,I.jsx)(`div`,{className:`text-[11px] text-muted-foreground`,children:`Loading diff…`});if(t.available===!1)return(0,I.jsxs)(`div`,{className:`text-[11px] text-muted-foreground`,children:[`Diff unavailable`,t.reason?`: ${t.reason}`:``]});if(t.missing)return(0,I.jsx)(`div`,{className:`text-[11px] text-muted-foreground`,children:`Worktree no longer on disk`});if(t.error)return(0,I.jsxs)(`div`,{className:`text-[11px] text-red-400`,children:[`Diff error: `,t.error]});if(!t.files.length)return(0,I.jsxs)(`div`,{className:`text-[11px] text-muted-foreground`,children:[`No committed changes against `,t.baseRef||`base`]});let i=(0,I.jsx)(k,{size:`icon-sm`,variant:`ghost`,className:`h-5 w-5`,title:`Refresh diff`,onClick:()=>void n(e.id),children:(0,I.jsx)(l,{className:`h-3 w-3`})});return(0,I.jsxs)(`div`,{className:`space-y-1`,children:[(0,I.jsxs)(`div`,{className:`flex items-center gap-2 text-[11px] font-medium`,children:[(0,I.jsx)(b,{className:`h-3 w-3`}),(0,I.jsxs)(`span`,{children:[t.files.length,` file`,t.files.length===1?``:`s`,` vs `,t.baseRef||`base`]}),i]}),(0,I.jsx)(`ul`,{className:`space-y-0.5 text-[11px]`,children:t.files.map(e=>(0,I.jsxs)(`li`,{className:`flex items-center gap-2 font-mono`,children:[(0,I.jsx)(`span`,{className:`truncate`,title:e.path,children:e.path}),e.binary?(0,I.jsx)(`span`,{className:`shrink-0 text-muted-foreground`,children:`binary`}):(0,I.jsxs)(`span`,{className:`shrink-0`,children:[(0,I.jsxs)(`span`,{className:`text-emerald-400`,children:[`+`,e.additions??0]}),` `,(0,I.jsxs)(`span`,{className:`text-red-400`,children:[`−`,e.deletions??0]})]})]},e.path))}),t.patch&&(0,I.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
|
+
`).map((e,t)=>(0,I.jsx)(`div`,{className:ce(e),children:e||` `},t)),t.truncated&&(0,I.jsx)(`div`,{className:`mt-1 text-muted-foreground`,children:`… patch truncated`})]})]})}function ue({workspace:e}){return(0,I.jsxs)(`div`,{className:`space-y-3 border-t border-dashed border-border bg-muted/20 px-3 py-3`,children:[(0,I.jsx)(se,{workspace:e}),(0,I.jsx)(le,{workspace:e})]})}function de(e){let t=e.match(/\/pull\/(\d+)/);return t?`PR #${t[1]}`:`View PR`}function fe({workspace:e}){let n=e.metadata?.mergeResult,r=n?.prUrl,i=n?.error||e.metadata?.mergeError;return!r&&!i?null:(0,I.jsxs)(I.Fragment,{children:[r&&(0,I.jsxs)(`a`,{href:r,target:`_blank`,rel:`noreferrer`,title:r,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,I.jsx)(t,{className:`h-2.5 w-2.5`}),de(r)]}),i&&(0,I.jsxs)(O,{variant:`outline`,className:`max-w-[16rem] border-amber-500/30 text-amber-400 text-[10px]`,title:i,children:[(0,I.jsx)(_,{className:`mr-0.5 h-2.5 w-2.5 shrink-0`}),(0,I.jsxs)(`span`,{className:`truncate`,children:[`merge: `,i]})]})]})}function pe({workspace:e}){let t=C(),[n,r]=(0,F.useState)(!1),i=w(e=>e.agentsById),a=w(t=>t.workspaceFocusId===e.id),o=w(e=>e.deliverableTasks),c=w(e=>e.openTaskDetail),l=e.ownerAgentId?i[e.ownerAgentId]:void 0,u=q(o,e.ownerAgentId,e.branch),d=w(t=>t.workspaceGitState[e.id]),f=!!d&&d.available!==!1&&d.landed===!0,p=u!=null&&(e.status===`merged`||e.status===`cleaned`||e.status===`abandoned`||f),m=e.stewardAgentId?i[e.stewardAgentId]:void 0,h=e.worktreePath||e.sourceCwd||e.repoRoot,g=(0,F.useRef)(null);return(0,F.useEffect)(()=>{a&&g.current?.scrollIntoView({behavior:`smooth`,block:`center`})},[a]),(0,I.jsxs)(`div`,{ref:g,className:`border-t border-border ${a?`bg-primary/5 ring-1 ring-inset ring-primary/40`:``}`,children:[(0,I.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,I.jsxs)(`div`,{className:`min-w-0 space-y-1`,children:[(0,I.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,I.jsx)(s,{className:`h-3.5 w-3.5 shrink-0 text-muted-foreground`}),(0,I.jsx)(`span`,{className:`truncate font-medium`,title:e.branch||e.id,children:e.branch||e.id})]}),(0,I.jsxs)(`div`,{className:`flex flex-wrap gap-1`,children:[(0,I.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,I.jsxs)(O,{variant:`outline`,className:`text-[10px]`,children:[`requested `,e.requestedMode]}),(0,I.jsx)(O,{variant:`outline`,className:`text-[10px] ${ne(e.status)}`,children:Z[e.status]||e.status}),(0,I.jsx)(fe,{workspace:e}),p&&u&&(0,I.jsx)(A,{id:u.id,title:u.title,status:u.status,onClick:()=>c(u)})]})]}),(0,I.jsxs)(`div`,{className:`min-w-0 space-y-1`,children:[(0,I.jsx)(`div`,{className:`truncate font-mono text-xs`,title:h,children:te(h)}),(0,I.jsx)(`div`,{className:`truncate text-xs text-muted-foreground`,title:e.sourceCwd,children:e.sourceCwd}),(0,I.jsx)(oe,{workspace:e})]}),(0,I.jsxs)(`div`,{className:`grid grid-cols-[4.5rem_minmax(0,1fr)] gap-x-2 gap-y-1 text-xs`,children:[(0,I.jsx)(`span`,{className:`text-muted-foreground`,children:`Owner`}),(0,I.jsx)(`span`,{className:`truncate`,title:e.ownerAgentId,children:$(l,e.ownerPolicyName||e.ownerAgentId)}),(0,I.jsx)(`span`,{className:`text-muted-foreground`,children:`Steward`}),(0,I.jsx)(`span`,{className:`truncate`,title:e.stewardAgentId,children:$(m,e.stewardAgentId)}),(0,I.jsx)(`span`,{className:`text-muted-foreground`,children:`Updated`}),(0,I.jsx)(`span`,{children:E(t,e.updatedAt)}),e.baseRef&&(0,I.jsxs)(I.Fragment,{children:[(0,I.jsx)(`span`,{className:`text-muted-foreground`,children:`Base`}),(0,I.jsx)(`span`,{className:`truncate`,title:e.baseSha,children:e.baseRef})]})]}),(0,I.jsx)(ie,{workspace:e,expanded:n,onToggleDetails:()=>r(e=>!e)})]}),n&&(0,I.jsx)(ue,{workspace:e})]})}function me(){let[e,t]=(0,F.useState)(`active`),[n,r]=(0,F.useState)(z),[i,a]=(0,F.useState)([]),o=w(e=>e.workspaces),s=w(e=>e.set),c=w(e=>e.workspaceFocusId);(0,F.useEffect)(()=>{c&&(t(`all`),r(z))},[c]),(0,F.useEffect)(()=>()=>{s({workspaceFocusId:null})},[s]),(0,F.useEffect)(()=>{let e=!1;return v(`GET`,`/projects`).then(t=>{e||a(t.projects??[])}).catch(()=>{e||a([])}),()=>{e=!0}},[]);let l=(0,F.useMemo)(()=>U(o,e,n),[o,e,n]),u=(0,F.useMemo)(()=>H(l,re),[l]),d=o.filter(e=>L.has(e.status)).length,f=o.filter(e=>e.mode===`isolated`).length,p=o.filter(e=>e.status===`ready`||e.status===`review_requested`||e.status===`merge_planned`).length,m=o.filter(e=>e.status===`conflict`).length,h=o.filter(e=>e.status===`merged`||e.status===`abandoned`||e.status===`cleaned`).length,g=[{key:`active`,label:`Active`,count:o.length-h},{key:`cleaned`,label:`Done`,count:h},{key:`all`,label:`All`,count:o.length}],_=(0,F.useMemo)(()=>{let e=[{value:z,label:`All Projects`}];for(let t of i)e.push({value:t.id,label:t.title||t.slug});return e.push({value:B,label:`Unassigned`}),e},[i]);return(0,I.jsxs)(`div`,{className:`space-y-4`,children:[(0,I.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,I.jsxs)(`div`,{children:[(0,I.jsx)(`h1`,{className:`text-2xl font-semibold`,children:`Workspaces`}),(0,I.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Agent worktrees, ownership, and merge readiness.`})]}),(0,I.jsxs)(`div`,{className:`ml-auto flex flex-wrap gap-1.5`,children:[(0,I.jsxs)(O,{variant:`outline`,children:[f,` isolated`]}),(0,I.jsxs)(O,{variant:`outline`,children:[d,` live`]}),(0,I.jsxs)(O,{variant:`outline`,children:[p,` ready`]}),m>0&&(0,I.jsxs)(O,{variant:`outline`,className:`border-red-500/20 text-red-400`,children:[m,` conflict`]})]})]}),(0,I.jsx)(K,{}),(0,I.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,I.jsx)(`div`,{className:`flex rounded-md border border-border bg-card p-1`,children:g.map(n=>(0,I.jsxs)(k,{size:`sm`,variant:e===n.key?`secondary`:`ghost`,className:`h-7 text-xs`,onClick:()=>t(n.key),children:[n.label,(0,I.jsx)(`span`,{className:`ml-1 text-muted-foreground`,children:n.count})]},n.key))}),(0,I.jsx)(`select`,{className:`h-8 min-w-[12rem] rounded-md border border-border bg-card px-2.5 text-sm text-foreground`,value:n,onChange:e=>r(e.target.value),children:_.map(e=>(0,I.jsx)(`option`,{value:e.value,children:e.label},e.value))})]}),u.length===0?(0,I.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`,n===`__all__`?``:` in this project filter`]}):(0,I.jsx)(`div`,{className:`space-y-3`,children:u.map(e=>(0,I.jsx)(W,{group:e,renderWorkspaceRow:e=>(0,I.jsx)(pe,{workspace:e},e.id)},e.key))})]})}export{me as WorkspacesView};
|
|
3
|
+
//# sourceMappingURL=workspaces-CR6f7G0R.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaces-CnNL6T8Q.js","names":[],"sources":["../../dashboard/src/components/views/workspaces-list.tsx","../../dashboard/src/lib/task-resolution.ts","../../dashboard/src/components/views/workspaces.tsx"],"sourcesContent":["import { useEffect, type ReactNode } from 'react'\nimport { FolderOpen, PackageOpen, RefreshCw } from 'lucide-react'\nimport { TERMINAL_WORKSPACE_STATUS_VALUES } from 'agent-relay-sdk/types'\nimport { useRelayStore } from '@/store'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport type { WorkspaceOrphan, WorkspaceRecord, WorkspaceStatus } from '@/types'\n\nexport const LIVE_WORKSPACE_STATUSES = new Set<WorkspaceStatus>(['active', 'ready', 'conflict', 'review_requested', 'merge_planned', 'cleanup_requested'])\nconst TERMINAL_STATUSES = new Set<WorkspaceStatus>(TERMINAL_WORKSPACE_STATUS_VALUES)\n\nexport type WorkspaceFilter = 'active' | 'cleaned' | 'all'\nexport type WorkspaceProjectFilter = string\n\nexport const ALL_PROJECTS = '__all__'\nexport const UNASSIGNED_PROJECTS = '__unassigned__'\n\ntype RepoWorkspaceGroup = {\n repoRoot: string\n workspaces: WorkspaceRecord[]\n}\n\nexport type WorkspaceProjectGroup = {\n key: string\n title: string\n projectId?: string\n projectSlug?: string\n unassigned: boolean\n repoGroups: RepoWorkspaceGroup[]\n}\n\nfunction repoWorkspaceGroups(workspaces: WorkspaceRecord[], workspaceSort: (a: WorkspaceRecord, b: WorkspaceRecord) => number): RepoWorkspaceGroup[] {\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) => a.repoRoot.localeCompare(b.repoRoot))\n}\n\nexport function groupWorkspaces(workspaces: WorkspaceRecord[], workspaceSort: (a: WorkspaceRecord, b: WorkspaceRecord) => number): WorkspaceProjectGroup[] {\n const grouped = new Map<string, WorkspaceRecord[]>()\n for (const workspace of workspaces) {\n const key = workspace.projectSlug || workspace.repoRoot || workspace.sourceCwd || 'unassigned'\n grouped.set(key, [...(grouped.get(key) || []), workspace])\n }\n return [...grouped.entries()]\n .map(([key, items]) => {\n const sample = items[0]!\n const unassigned = !sample.projectId\n const title = unassigned ? (sample.repoRoot || sample.sourceCwd || 'unknown repo') : (sample.projectTitle || sample.projectSlug || sample.repoRoot)\n return {\n key,\n title,\n ...(sample.projectId ? { projectId: sample.projectId } : {}),\n ...(sample.projectSlug ? { projectSlug: sample.projectSlug } : {}),\n unassigned,\n repoGroups: repoWorkspaceGroups(items, workspaceSort),\n }\n })\n .sort((a, b) => {\n const liveA = a.repoGroups.some((group) => group.workspaces.some((item) => LIVE_WORKSPACE_STATUSES.has(item.status)))\n const liveB = b.repoGroups.some((group) => group.workspaces.some((item) => LIVE_WORKSPACE_STATUSES.has(item.status)))\n if (liveA !== liveB) return liveA ? -1 : 1\n return a.title.localeCompare(b.title)\n })\n}\n\nexport function filterWorkspaces(workspaces: WorkspaceRecord[], filter: WorkspaceFilter, projectFilter: WorkspaceProjectFilter): WorkspaceRecord[] {\n const byStatus = filter === 'all'\n ? workspaces\n : filter === 'cleaned'\n ? workspaces.filter((workspace) => TERMINAL_STATUSES.has(workspace.status))\n : workspaces.filter((workspace) => !TERMINAL_STATUSES.has(workspace.status))\n if (projectFilter === ALL_PROJECTS) return byStatus\n if (projectFilter === UNASSIGNED_PROJECTS) return byStatus.filter((workspace) => !workspace.projectId)\n return byStatus.filter((workspace) => workspace.projectId === projectFilter)\n}\n\nexport function ProjectGroup({\n group,\n renderWorkspaceRow,\n}: {\n group: WorkspaceProjectGroup\n renderWorkspaceRow: (workspace: WorkspaceRecord) => ReactNode\n}) {\n const workspaces = group.repoGroups.flatMap((repoGroup) => repoGroup.workspaces)\n const liveCount = workspaces.filter((item) => LIVE_WORKSPACE_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 const showRepoSubgroups = group.repoGroups.length > 1\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 <div className=\"flex flex-wrap items-center gap-2\">\n <CardTitle className=\"truncate text-sm font-medium\" title={group.title}>{group.title}</CardTitle>\n {group.unassigned && <Badge variant=\"outline\" className=\"text-[10px]\">Unassigned</Badge>}\n </div>\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 {group.repoGroups.map((repoGroup) => (\n <div key={repoGroup.repoRoot} className=\"border-t border-border first:border-t-0\">\n {showRepoSubgroups && (\n <div className=\"flex items-center gap-2 border-b border-dashed border-border/80 px-3 py-2\">\n <FolderOpen className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate font-mono text-[11px] text-muted-foreground\" title={repoGroup.repoRoot}>{repoGroup.repoRoot}</span>\n </div>\n )}\n {repoGroup.workspaces.map((workspace) => renderWorkspaceRow(workspace))}\n </div>\n ))}\n </CardContent>\n </Card>\n )\n}\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\nexport function 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 const renderOrphan = (orphan: WorkspaceOrphan) => (\n <div key={orphan.worktreePath} className=\"flex items-center gap-3 border-t border-border px-4 py-2 text-xs\">\n <FolderOpen 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 <PackageOpen className=\"mr-1 h-3.5 w-3.5\" />Reclaim\n </Button>\n </div>\n )\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\">{orphans.map(renderOrphan)}</CardContent>\n </Card>\n )\n}\n","import type { Task } from '@/types'\n\nconst DONE_STATUSES = new Set(['done', 'failed', 'canceled'])\n\n/**\n * Resolve the authoritative Task for a workspace row.\n *\n * Branch match is canonical: if workspace.branch matches a task's branch, that IS the task\n * that produced this workspace. ownerAgentId alone is ambiguous when an agent runs multiple\n * tasks sequentially — a merged/cleaned row for task #1's branch must not show task #2.\n *\n * Fall-back when no branch match:\n * Exactly one ownerAgentId match → use it (unambiguous).\n * Multiple matches → return null (no chip beats a wrong chip).\n */\nexport function resolveWorkspaceTask(\n deliverableTasks: Task[],\n ownerAgentId: string | undefined,\n branch: string | undefined,\n): Task | null {\n if (branch) {\n const byBranch = deliverableTasks.find((t) => t.branch && t.branch === branch)\n if (byBranch) return byBranch\n }\n if (!ownerAgentId) return null\n const candidates = deliverableTasks.filter((t) => t.ownerAgentId === ownerAgentId)\n if (candidates.length === 0) return null\n if (candidates.length === 1) return candidates[0]!\n return null // >1 owner match, no branch: ambiguous — no chip beats a wrong chip\n}\n\n/**\n * Resolve the agent's current working Task for the agent detail drawer.\n *\n * Returns the most-recent non-done deliverable owned by this agent.\n * Returns null when the agent has no active task, preventing a stale\n * \"Working on\" chip from showing a previously-completed task.\n */\nexport function resolveAgentTask(\n deliverableTasks: Task[],\n agentId: string,\n): Task | null {\n return (\n deliverableTasks\n .filter((t) => t.ownerAgentId === agentId && !DONE_STATUSES.has(t.status))\n .sort((a, b) => Number(b.updatedAt) - Number(a.updatedAt))[0] ?? null\n )\n}\n","import { useEffect, useMemo, useRef, useState } from 'react'\nimport { ArrowDown, ArrowUp, Check, ChevronDown, ChevronRight, Clock, Eye, FileDiff, FilePen, Flag, FolderOpen, GitBranch, GitCommitHorizontal, GitMerge, RefreshCw, TriangleAlert, Trash2, X } from 'lucide-react'\nimport { api } from '@/lib/api'\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 { displayName, timeAgo } from '@/lib/display'\nimport { ALL_PROJECTS, filterWorkspaces, groupWorkspaces, LIVE_WORKSPACE_STATUSES, OrphanPanel, ProjectGroup, UNASSIGNED_PROJECTS, type WorkspaceFilter, type WorkspaceProjectFilter } from './workspaces-list'\nimport { TaskChip } from '@/components/shared/task-chip'\nimport { resolveWorkspaceTask } from '@/lib/task-resolution'\nimport type { Agent, Project, WorkspaceRecord, WorkspaceStatus } from '@/types'\n\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\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\nfunction agentLabel(agent: Agent | undefined, fallback: string | undefined): string {\n if (agent) return displayName(agent)\n return fallback || '-'\n}\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 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 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// 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 deliverableTasks = useRelayStore((s) => s.deliverableTasks)\n const openTaskDetail = useRelayStore((s) => s.openTaskDetail)\n const owner = workspace.ownerAgentId ? agentsById[workspace.ownerAgentId] : undefined\n const ownerTask = resolveWorkspaceTask(deliverableTasks, workspace.ownerAgentId, workspace.branch)\n const gitState = useRelayStore((s) => s.workspaceGitState[workspace.id])\n const landed = !!gitState && gitState.available !== false && gitState.landed === true\n const showTaskChip = ownerTask != null && (workspace.status === 'merged' || workspace.status === 'cleaned' || workspace.status === 'abandoned' || landed)\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 {showTaskChip && ownerTask && (\n <TaskChip id={ownerTask.id} title={ownerTask.title} status={ownerTask.status} onClick={() => openTaskDetail(ownerTask)} />\n )}\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\nexport function WorkspacesView() {\n const [filter, setFilter] = useState<WorkspaceFilter>('active')\n const [projectFilter, setProjectFilter] = useState<WorkspaceProjectFilter>(ALL_PROJECTS)\n const [projects, setProjects] = useState<Project[]>([])\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) {\n setFilter('all')\n setProjectFilter(ALL_PROJECTS)\n }\n }, [workspaceFocusId])\n useEffect(() => () => { set({ workspaceFocusId: null }) }, [set])\n useEffect(() => {\n let cancelled = false\n api<{ projects: Project[] }>('GET', '/projects')\n .then((res) => { if (!cancelled) setProjects(res.projects ?? []) })\n .catch(() => { if (!cancelled) setProjects([]) })\n return () => { cancelled = true }\n }, [])\n\n const visibleWorkspaces = useMemo(() => filterWorkspaces(workspaces, filter, projectFilter), [workspaces, filter, projectFilter])\n const grouped = useMemo(() => groupWorkspaces(visibleWorkspaces, workspaceSort), [visibleWorkspaces])\n const liveCount = workspaces.filter((item) => LIVE_WORKSPACE_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) => item.status === 'merged' || item.status === 'abandoned' || item.status === 'cleaned').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 const projectOptions = useMemo(() => {\n const options = [{ value: ALL_PROJECTS, label: 'All Projects' }]\n for (const project of projects) {\n options.push({ value: project.id, label: project.title || project.slug })\n }\n options.push({ value: UNASSIGNED_PROJECTS, label: 'Unassigned' })\n return options\n }, [projects])\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 <select\n className=\"h-8 min-w-[12rem] rounded-md border border-border bg-card px-2.5 text-sm text-foreground\"\n value={projectFilter}\n onChange={(e) => setProjectFilter(e.target.value)}\n >\n {projectOptions.map((option) => (\n <option key={option.value} value={option.value}>{option.label}</option>\n ))}\n </select>\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{projectFilter !== ALL_PROJECTS ? ' in this project filter' : ''}\n </div>\n ) : (\n <div className=\"space-y-3\">\n {grouped.map((group) => (\n <ProjectGroup key={group.key} group={group} renderWorkspaceRow={(workspace) => <WorkspaceRow key={workspace.id} workspace={workspace} />} />\n ))}\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"ioBASa,EAA0B,IAAI,IAAqB,CAAC,SAAU,QAAS,WAAY,mBAAoB,gBAAiB,oBAAoB,CAAC,CACpJ,EAAoB,IAAI,IAAqB,EAAiC,CAKvE,EAAe,UACf,EAAsB,iBAgBnC,SAAS,EAAoB,EAA+B,EAAyF,CACnJ,IAAM,EAAU,IAAI,IACpB,IAAK,IAAM,KAAa,EAAY,CAClC,IAAM,EAAM,EAAU,UAAY,EAAU,WAAa,eACzD,EAAQ,IAAI,EAAK,CAAC,GAAI,EAAQ,IAAI,EAAI,EAAI,EAAE,CAAG,EAAU,CAAC,CAE5D,MAAO,CAAC,GAAG,EAAQ,SAAS,CAAC,CAC1B,KAAK,CAAC,EAAU,MAAY,CAAE,WAAU,WAAY,EAAM,KAAK,EAAc,CAAE,EAAE,CACjF,MAAM,EAAG,IAAM,EAAE,SAAS,cAAc,EAAE,SAAS,CAAC,CAGzD,SAAgB,EAAgB,EAA+B,EAA4F,CACzJ,IAAM,EAAU,IAAI,IACpB,IAAK,IAAM,KAAa,EAAY,CAClC,IAAM,EAAM,EAAU,aAAe,EAAU,UAAY,EAAU,WAAa,aAClF,EAAQ,IAAI,EAAK,CAAC,GAAI,EAAQ,IAAI,EAAI,EAAI,EAAE,CAAG,EAAU,CAAC,CAE5D,MAAO,CAAC,GAAG,EAAQ,SAAS,CAAC,CAC1B,KAAK,CAAC,EAAK,KAAW,CACrB,IAAM,EAAS,EAAM,GACf,EAAa,CAAC,EAAO,UAE3B,MAAO,CACL,MACA,MAHY,EAAc,EAAO,UAAY,EAAO,WAAa,eAAmB,EAAO,cAAgB,EAAO,aAAe,EAAO,SAIxI,GAAI,EAAO,UAAY,CAAE,UAAW,EAAO,UAAW,CAAG,EAAE,CAC3D,GAAI,EAAO,YAAc,CAAE,YAAa,EAAO,YAAa,CAAG,EAAE,CACjE,aACA,WAAY,EAAoB,EAAO,EAAc,CACtD,EACD,CACD,MAAM,EAAG,IAAM,CACd,IAAM,EAAQ,EAAE,WAAW,KAAM,GAAU,EAAM,WAAW,KAAM,GAAS,EAAwB,IAAI,EAAK,OAAO,CAAC,CAAC,CAGrH,OADI,IADU,EAAE,WAAW,KAAM,GAAU,EAAM,WAAW,KAAM,GAAS,EAAwB,IAAI,EAAK,OAAO,CAAC,CACtG,CACP,EAAE,MAAM,cAAc,EAAE,MAAM,CADT,EAAQ,GAAK,GAEzC,CAGN,SAAgB,EAAiB,EAA+B,EAAyB,EAA0D,CACjJ,IAAM,EAAW,IAAW,MACxB,EACA,IAAW,UACT,EAAW,OAAQ,GAAc,EAAkB,IAAI,EAAU,OAAO,CAAC,CACzE,EAAW,OAAQ,GAAc,CAAC,EAAkB,IAAI,EAAU,OAAO,CAAC,CAGhF,OAFI,IAAA,UAAuC,EACvC,IAAA,iBAA8C,EAAS,OAAQ,GAAc,CAAC,EAAU,UAAU,CAC/F,EAAS,OAAQ,GAAc,EAAU,YAAc,EAAc,CAG9E,SAAgB,EAAa,CAC3B,QACA,sBAIC,CACD,IAAM,EAAa,EAAM,WAAW,QAAS,GAAc,EAAU,WAAW,CAC1E,EAAY,EAAW,OAAQ,GAAS,EAAwB,IAAI,EAAK,OAAO,CAAC,CAAC,OAClF,EAAa,EAAW,OAAQ,GAAS,EAAK,SAAW,SAAW,EAAK,SAAW,oBAAsB,EAAK,SAAW,gBAAgB,CAAC,OAC3I,EAAgB,EAAW,OAAQ,GAAS,EAAK,SAAW,WAAW,CAAC,OACxE,EAAoB,EAAM,WAAW,OAAS,EAEpD,OACE,EAAA,EAAA,MAAC,EAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAY,UAAU,sBACpB,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,2CACb,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,0BAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6CAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAW,UAAU,+BAA+B,MAAO,EAAM,eAAQ,EAAM,MAAkB,CAAA,CAChG,EAAM,aAAc,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAU,uBAAc,aAAkB,CAAA,CACpF,IACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uCAAf,EACE,EAAA,EAAA,MAAC,EAAD,CAAO,QAAQ,UAAU,UAAU,uBAAnC,CAAkD,EAAW,OAAO,SAAc,IAClF,EAAA,EAAA,MAAC,EAAD,CAAO,QAAQ,UAAU,UAAU,uBAAnC,CAAkD,EAAU,QAAa,GACxE,EAAa,IAAK,EAAA,EAAA,MAAC,EAAD,CAAO,QAAQ,UAAU,UAAU,8DAAnC,CAAyF,EAAW,gBAAqB,GAC3I,EAAgB,IAAK,EAAA,EAAA,MAAC,EAAD,CAAO,QAAQ,UAAU,UAAU,sDAAnC,CAAiF,EAAc,YAAiB,GAClI,GACF,GACF,CAAA,CACK,CAAA,EACb,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,eACpB,EAAM,WAAW,IAAK,IACrB,EAAA,EAAA,MAAC,MAAD,CAA8B,UAAU,mDAAxC,CACG,IACC,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,qFAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAY,UAAU,6CAA+C,CAAA,EACrE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,uDAAuD,MAAO,EAAU,kBAAW,EAAU,SAAgB,CAAA,CACzH,GAEP,EAAU,WAAW,IAAK,GAAc,EAAmB,EAAU,CAAC,CACnE,EARI,EAAU,SAQd,CACN,CACU,CAAA,CACT,CAAA,CAAA,CAIX,SAAS,EAAU,EAAsB,CACvC,IAAM,EAAQ,EAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,CAE7C,OADI,EAAM,QAAU,EAAU,GAAQ,IAC/B,IAAI,EAAM,MAAM,GAAG,CAAC,KAAK,IAAI,GAGtC,SAAgB,GAAc,CAC5B,IAAM,EAAU,EAAe,GAAM,EAAE,iBAAiB,CAClD,EAAwB,EAAe,GAAM,EAAE,sBAAsB,CACrE,EAAyB,EAAe,GAAM,EAAE,uBAAuB,CAoB7E,OAlBA,EAAA,EAAA,eAAgB,CACd,GAA4B,EAC3B,CAAC,EAAsB,CAAC,CAEtB,EAAQ,QAeX,EAAA,EAAA,MAAC,EAAD,CAAM,UAAU,+BAAhB,EACE,EAAA,EAAA,MAAC,EAAD,CAAY,UAAU,qBAAtB,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mCAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,yBAA2B,CAAA,EAClD,EAAA,EAAA,KAAC,EAAD,CAAW,UAAU,+BAAsB,qBAA8B,CAAA,EACzE,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAU,0DAAkD,EAAQ,OAAe,CAAA,EAC5G,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,UAAU,QAAQ,QAAQ,UAAU,kBAAkB,MAAM,SAAS,YAAe,KAAK,GAAuB,WAC3H,EAAA,EAAA,KAAC,EAAD,CAAW,UAAU,cAAgB,CAAA,CAC9B,CAAA,CACL,IACN,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,8CAAqC,sFAAuF,CAAA,CAC9H,IACb,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,eAAO,EAAQ,IAzBpB,IACpB,EAAA,EAAA,MAAC,MAAD,CAA+B,UAAU,4EAAzC,EACE,EAAA,EAAA,KAAC,EAAD,CAAY,UAAU,6CAA+C,CAAA,EACrE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,uBAAuB,MAAO,EAAO,gBAAS,EAAO,QAAU,aAAoB,CAAA,EACnG,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,2CAA2C,MAAO,EAAO,sBAAe,EAAU,EAAO,aAAa,CAAQ,CAAA,CAC7H,EAAO,iBAAkB,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAU,uBAAc,iBAAsB,CAAA,EACjG,EAAA,EAAA,MAAC,EAAD,CAAQ,KAAK,KAAK,QAAQ,UAAU,UAAU,sBAAsB,YAAe,KAAK,EAAuB,EAAO,UAAtH,EACE,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,mBAAqB,CAAA,CAAA,UACrC,GACL,EARI,EAAO,aAQX,CAgBmD,CAAe,CAAA,CACjE,GA5BmB,KClI9B,SAAgB,EACd,EACA,EACA,EACa,CACb,GAAI,EAAQ,CACV,IAAM,EAAW,EAAiB,KAAM,GAAM,EAAE,QAAU,EAAE,SAAW,EAAO,CAC9E,GAAI,EAAU,OAAO,EAEvB,GAAI,CAAC,EAAc,OAAO,KAC1B,IAAM,EAAa,EAAiB,OAAQ,GAAM,EAAE,eAAiB,EAAa,CAGlF,OAFI,EAAW,SAAW,EAAU,KAChC,EAAW,SAAW,EAAU,EAAW,GACxC,KCfT,IAAM,EAAqB,IAAI,IAAqB,CAAC,SAAU,QAAS,WAAY,mBAAoB,gBAAgB,CAAC,CACnH,EAAqB,IAAI,IAAqB,CAAC,SAAU,QAAS,mBAAoB,gBAAiB,WAAW,CAAC,CAEnH,EAAgD,CACpD,OAAQ,kDACR,MAAO,2DACP,SAAU,+CACV,iBAAkB,wDAClB,cAAe,wDACf,OAAQ,wDACR,UAAW,kDACX,kBAAmB,wDACnB,QAAS,kDACV,CAEK,EAAgD,CACpD,OAAQ,SACR,MAAO,QACP,SAAU,WACV,iBAAkB,SAClB,cAAe,gBACf,OAAQ,SACR,UAAW,YACX,kBAAmB,UACnB,QAAS,UACV,CAEK,EAAgD,CACpD,SAAU,EACV,MAAO,EACP,iBAAkB,EAClB,cAAe,EACf,OAAQ,EACR,kBAAmB,EACnB,OAAQ,EACR,UAAW,EACX,QAAS,EACV,CAED,SAAS,EAAW,EAA0B,EAAsC,CAElF,OADI,EAAc,GAAY,EAAM,CAC7B,GAAY,IAGrB,SAAS,GAAU,EAAsB,CACvC,IAAM,EAAQ,EAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,CAE7C,OADI,EAAM,QAAU,EAAU,GAAQ,IAC/B,IAAI,EAAM,MAAM,GAAG,CAAC,KAAK,IAAI,GAGtC,SAAS,GAAW,EAAiC,CACnD,OAAO,EAAa,IAAW,kDAGjC,SAAS,GAAc,EAAoB,EAA4B,CACrE,IAAM,GAAU,EAAa,EAAE,SAAW,KAAO,EAAa,EAAE,SAAW,IAE3E,OADI,IAAW,EACR,OAAO,EAAE,WAAa,EAAE,CAAG,OAAO,EAAE,WAAa,EAAE,CADjC,EAI3B,SAAS,GAAiB,CAAE,YAAW,WAAU,mBAAmG,CAClJ,IAAM,EAAkB,EAAe,GAAM,EAAE,gBAAgB,CACzD,EAAiB,EAAe,GAAM,EAAE,eAAe,CACvD,EAA6B,EAAe,GAAM,EAAE,2BAA2B,CAC/E,EAAc,EAAe,GAAM,EAAE,YAAY,CACjD,EAAW,EAAU,SAAW,WAAa,EAAU,SAAW,UAAY,EAAU,SAAW,YACnG,EAAW,GAAY,EAAU,SAAW,oBAC5C,EAAW,EAAU,cAAgB,EAAU,WAAa,EAAU,SAGtE,EAAW,EAAe,GAAM,EAAE,kBAAkB,EAAU,IAAI,CAClE,EAAS,CAAC,CAAC,GAAY,EAAS,YAAc,IAAS,EAAS,SAAW,GAC3E,EAAY,EAAU,OAAS,YAAc,EAAQ,EAAU,cAAiB,EAAmB,IAAI,EAAU,OAAO,EAAI,CAAC,EAInI,eAAe,GAAQ,CACrB,MAAM,EAA2B,EAAU,GAAG,CAC9C,MAAM,EAAgB,EAAU,GAAI,QAAQ,CAG9C,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,8CAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,UAAU,QAAQ,QAAQ,MAAO,EAAW,eAAiB,uBAAwB,QAAS,WACxG,GAAW,EAAA,EAAA,KAAC,GAAD,CAAa,UAAU,cAAgB,CAAA,EAAG,EAAA,EAAA,KAAC,EAAD,CAAc,UAAU,cAAgB,CAAA,CACvF,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CACE,KAAK,UACL,QAAQ,QACR,MAAM,iBACN,SAAU,CAAC,GAAY,EAAU,SAAW,UAC5C,YAAe,KAAK,EAAY,CAAE,KAAM,EAAU,CAAC,WAEnD,EAAA,EAAA,KAAC,EAAD,CAAY,UAAU,cAAgB,CAAA,CAC/B,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CAAY,MAAO,EAAU,MAAM,YAAY,YAAY,cAAc,KAAK,UAAU,QAAQ,QAAQ,SAAU,CAAC,EAAY,CAAA,EAC/H,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,UAAU,QAAQ,UAAU,MAAM,aAAa,SAAU,GAAY,EAAU,SAAW,QAAS,YAAe,KAAK,EAAgB,EAAU,GAAI,QAAQ,WACxK,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,cAAgB,CAAA,CAC1B,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,UAAU,QAAQ,UAAU,MAAM,iBAAiB,SAAU,GAAY,EAAU,SAAW,mBAAoB,YAAe,KAAK,EAAgB,EAAU,GAAI,iBAAiB,WAChM,EAAA,EAAA,KAAC,EAAD,CAAK,UAAU,cAAgB,CAAA,CACxB,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,UAAU,QAAQ,UAAU,MAAM,qBAAqB,SAAU,GAAY,EAAU,SAAW,gBAAiB,YAAe,KAAK,EAAgB,EAAU,GAAI,aAAa,WAC7L,EAAA,EAAA,KAAC,EAAD,CAAM,UAAU,cAAgB,CAAA,CACzB,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,UAAU,QAAQ,UAAU,MAAO,EAAS,6CAA+C,oBAAqB,SAAU,CAAC,EAAW,YAAe,KAAK,GAAO,WAC5K,EAAA,EAAA,KAAC,EAAD,CAAU,UAAU,cAAgB,CAAA,CAC7B,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,UAAU,QAAQ,UAAU,MAAM,qDAAqD,SAAU,EAAU,SAAW,WAAa,EAAU,SAAW,oBAAqB,YAAe,KAAK,EAAgB,EAAU,GAAI,UAAU,WACpP,EAAA,EAAA,KAAC,EAAD,CAAQ,UAAU,cAAgB,CAAA,CAC3B,CAAA,CACR,IACC,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,UAAU,QAAQ,QAAQ,MAAM,qDAAqD,YAAe,KAAK,EAAe,EAAU,GAAG,WAChJ,EAAA,EAAA,KAAC,EAAD,CAAG,UAAU,cAAgB,CAAA,CACtB,CAAA,CAEP,GAOV,SAAS,GAAiB,CAAE,YAAW,YAA8D,CACnG,IAAM,EAAU,EAAe,GAAM,EAAE,sBAAsB,EAAU,IAAI,CACrE,EAA6B,EAAe,GAAM,EAAE,2BAA2B,CAC/E,EAAW,EAAW,GAAK,EAAmB,IAAI,EAAU,OAAO,CAqBzE,OAnBA,EAAA,EAAA,eAAgB,CACV,GAAY,IAAY,IAAA,IAAW,EAAgC,EAAU,GAAG,EACnF,CAAC,EAAU,EAAS,EAAU,GAAI,EAA2B,CAAC,CAE7D,CAAC,GAAY,IAAY,IAAA,IAAa,EAAQ,YAAc,GAAc,KAC1E,EAAQ,UAER,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,2CAA2C,MAAO,gBAAgB,EAAQ,SAAW,OAAO,mDAA5G,EACE,EAAA,EAAA,KAAC,EAAD,CAAe,UAAU,UAAY,CAAA,CAAA,WAChC,GAGP,EAAQ,WAAa,IAErB,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,6CAA6C,MAAO,sBAAsB,EAAQ,WAAa,KAAO,KAAO,wBAAwB,QAAQ,EAAQ,SAAW,kBAAhL,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,UAAY,CAAA,CAAC,EAAQ,WAAa,KAAO,WAAa,QAClE,GAGJ,KAMT,SAAS,GAAS,CAAE,aAA6C,CAC/D,IAAM,EAAM,GAAQ,CACd,EAAW,EAAe,GAAM,EAAE,kBAAkB,EAAU,IAAI,CAClE,EAAyB,EAAe,GAAM,EAAE,uBAAuB,CACvE,EAAW,EAAU,OAAS,YAAc,EAAQ,EAAU,cAAiB,EAAmB,IAAI,EAAU,OAAO,CAM7H,IAJA,EAAA,EAAA,eAAgB,CACV,GAAY,IAAa,IAAA,IAAW,EAA4B,EAAU,GAAG,EAChF,CAAC,EAAU,EAAU,EAAU,GAAI,EAAuB,CAAC,CAE1D,CAAC,EAAU,OAAO,KAEtB,IAAM,GACJ,EAAA,EAAA,KAAC,EAAD,CACE,KAAK,UACL,QAAQ,QACR,UAAU,UACV,MAAM,oBACN,YAAe,KAAK,EAAuB,EAAU,GAAG,WAExD,EAAA,EAAA,KAAC,EAAD,CAAW,UAAU,UAAY,CAAA,CAC1B,CAAA,CAGX,GAAI,IAAa,IAAA,GACf,OAAO,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,qEAA4D,qBAAwB,CAAA,CAE5G,GAAI,EAAS,YAAc,GACzB,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,4DAA4D,MAAO,EAAS,gBAA3F,EACE,EAAA,EAAA,KAAC,OAAD,CAAA,SAAM,wBAA4B,CAAA,CACjC,EACG,GAGV,GAAI,EAAS,QACX,OAAO,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,qEAAf,CAA2E,iBAAe,EAAc,GAEjH,GAAI,EAAS,MACX,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mDAAmD,MAAO,EAAS,eAAlF,EACE,EAAA,EAAA,KAAC,OAAD,CAAA,SAAM,YAAgB,CAAA,CACrB,EACG,GAIV,IAAM,EAAQ,EAAS,OAAS,EAG1B,EAAS,EAAS,SAAW,GAC7B,EAAW,EAAS,EAAK,EAAS,eAAiB,EACnD,EAAS,EAAS,QAAU,EAC5B,EAAQ,EAAS,YAAc,EAC/B,EAAQ,IAAa,GAAK,IAAU,GAAK,CAAC,EAC1C,EAAS,EAAS,WAExB,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uBAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,qEAAf,CACG,GACC,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,iCAAwB,mBAAuB,CAAA,EAE/D,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,CACG,GACC,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,6CAA6C,MAAO,0BAA0B,EAAS,SAAW,OAAO,qDAAzH,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,UAAY,CAAA,CAAA,SACxB,IAEP,EAAA,EAAA,MAAC,OAAD,CAAM,UAAW,EAAW,EAAI,qCAAuC,0CAA2C,MAAO,IAAU,EAAuE,GAAG,EAAS,0BAAxE,GAAG,EAAS,uBAAuB,EAAM,0BAAvL,EACE,EAAA,EAAA,KAAC,EAAD,CAAS,UAAU,UAAY,CAAA,CAAC,EAC3B,IAET,EAAA,EAAA,MAAC,OAAD,CAAM,UAAW,EAAS,EAAI,mCAAqC,0CAA2C,MAAO,GAAG,EAAO,iCAA/H,EACE,EAAA,EAAA,KAAC,EAAD,CAAW,UAAU,UAAY,CAAA,CAAC,EAC7B,GACN,EAAQ,IACP,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,oCAAoC,MAAO,GAAG,EAAM,iCAApE,EACE,EAAA,EAAA,KAAC,EAAD,CAAS,UAAU,UAAY,CAAA,CAAC,EAC3B,GAER,CAAC,IAAU,EAAA,EAAA,KAAC,GAAD,CAA6B,YAAqB,WAAY,CAAA,CACzE,CAAA,CAAA,CAEJ,EACG,GACL,IACC,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,oEAAoE,MAAO,GAAG,EAAO,IAAI,MAAM,EAAG,EAAE,CAAC,KAAK,EAAO,mBAAhI,EACE,EAAA,EAAA,KAAC,EAAD,CAAqB,UAAU,mBAAqB,CAAA,EACpD,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,oBAAY,EAAO,SAAW,EAAO,IAAI,MAAM,EAAG,EAAE,CAAQ,CAAA,CAC3E,EAAO,IAAK,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,+BAAhB,CAAsC,KAAG,EAAQ,EAAK,EAAO,GAAG,CAAQ,GAAG,KACpF,GAEJ,GAMV,SAAS,GAAkB,CAAE,aAA6C,CACxE,IAAM,EAAM,GAAQ,CACd,EAAW,OAAO,EAAU,UAAU,UAAa,SAAY,EAAU,SAAS,SAAsB,IAAA,GACxG,EAAQ,CACZ,CAAE,MAAO,UAAW,GAAI,EAAU,UAAW,CAC7C,CAAE,MAAO,QAAS,GAAI,EAAU,QAAS,CACzC,CAAE,MAAO,SAAU,GAAI,EAAU,CACjC,CAAE,MAAO,UAAW,GAAI,EAAU,UAAW,CAC9C,CAAC,OAAQ,GAAgD,OAAO,EAAK,IAAO,UAAY,EAAK,GAAK,EAAE,CAErG,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,yFAAf,EACE,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,+DAAhB,EAAsE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,UAAY,CAAA,CAAA,WAAe,GACjH,EAAM,KAAK,EAAM,KAChB,EAAA,EAAA,MAAC,OAAD,CAAuB,UAAU,mCAAjC,CACG,EAAI,IAAK,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,sBAAa,IAAQ,CAAA,EAC/C,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,2BAAmB,EAAK,MAAa,CAAA,EACrD,EAAA,EAAA,KAAC,OAAD,CAAM,MAAO,IAAI,KAAK,EAAK,GAAG,CAAC,gBAAgB,UAAG,EAAQ,EAAK,EAAK,GAAG,CAAQ,CAAA,CAC1E,EAJI,EAAK,MAIT,CACP,CACE,GAIV,SAAS,GAAc,EAAsB,CAK3C,OAJI,EAAK,WAAW,IAAI,EAAI,CAAC,EAAK,WAAW,MAAM,CAAS,mBACxD,EAAK,WAAW,IAAI,EAAI,CAAC,EAAK,WAAW,MAAM,CAAS,eACxD,EAAK,WAAW,KAAK,CAAS,gBAC9B,EAAK,WAAW,QAAQ,EAAI,EAAK,WAAW,SAAS,EAAI,EAAK,WAAW,MAAM,EAAI,EAAK,WAAW,MAAM,CAAS,wBAC/G,GAIT,SAAS,GAAkB,CAAE,aAA6C,CACxE,IAAM,EAAO,EAAe,GAAM,EAAE,cAAc,EAAU,IAAI,CAC1D,EAAqB,EAAe,GAAM,EAAE,mBAAmB,CAC/D,EAAW,EAAU,OAAS,YAAc,EAAQ,EAAU,aAMpE,IAJA,EAAA,EAAA,eAAgB,CACV,GAAY,IAAS,IAAA,IAAW,EAAwB,EAAU,GAAG,EACxE,CAAC,EAAU,EAAM,EAAU,GAAI,EAAmB,CAAC,CAElD,CAAC,EAAU,OAAO,KACtB,GAAI,IAAS,IAAA,GAAW,OAAO,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,6CAAoC,gBAAmB,CAAA,CACrG,GAAI,EAAK,YAAc,GAAO,OAAO,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6CAAf,CAAmD,mBAAiB,EAAK,OAAS,KAAK,EAAK,SAAW,GAAS,GACrJ,GAAI,EAAK,QAAS,OAAO,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,6CAAoC,6BAAgC,CAAA,CAC5G,GAAI,EAAK,MAAO,OAAO,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,oCAAf,CAA0C,eAAa,EAAK,MAAY,GAC/F,GAAI,CAAC,EAAK,MAAM,OAAQ,OAAO,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6CAAf,CAAmD,gCAA8B,EAAK,SAAW,OAAa,GAE7I,IAAM,GACJ,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,UAAU,QAAQ,QAAQ,UAAU,UAAU,MAAM,eAAe,YAAe,KAAK,EAAmB,EAAU,GAAG,WAClI,EAAA,EAAA,KAAC,EAAD,CAAW,UAAU,UAAY,CAAA,CAC1B,CAAA,CAGX,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,qBAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,2DAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAU,UAAU,UAAY,CAAA,EAChC,EAAA,EAAA,MAAC,OAAD,CAAA,SAAA,CAAO,EAAK,MAAM,OAAO,QAAM,EAAK,MAAM,SAAW,EAAI,GAAK,IAAI,OAAK,EAAK,SAAW,OAAc,CAAA,CAAA,CACpG,EACG,IACN,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,mCACX,EAAK,MAAM,IAAK,IACf,EAAA,EAAA,MAAC,KAAD,CAAoB,UAAU,6CAA9B,EACE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,WAAW,MAAO,EAAK,cAAO,EAAK,KAAY,CAAA,CAC9D,EAAK,QACJ,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,0CAAiC,SAAa,CAAA,EAE9D,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,oBAAhB,EACE,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,4BAAhB,CAAmC,IAAE,EAAK,WAAa,EAAS,GAAC,KACjE,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,wBAAhB,CAA+B,IAAE,EAAK,WAAa,EAAS,GACvD,GAEN,EAVI,EAAK,KAUT,CACL,CACC,CAAA,CACJ,EAAK,QACJ,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6GAAf,CACG,EAAK,MAAM,MAAM;EAAK,CAAC,KAAK,EAAM,KACjC,EAAA,EAAA,KAAC,MAAD,CAAa,UAAW,GAAc,EAAK,UAAG,GAAQ,IAAU,CAAtD,EAAsD,CAChE,CACD,EAAK,YAAa,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,sCAA6B,oBAAuB,CAAA,CAClF,GAEJ,GAIV,SAAS,EAAiB,CAAE,aAA6C,CACvE,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,gFAAf,EACE,EAAA,EAAA,KAAC,GAAD,CAA8B,YAAa,CAAA,EAC3C,EAAA,EAAA,KAAC,GAAD,CAA8B,YAAa,CAAA,CACvC,GAOV,SAAS,GAAQ,EAAqB,CACpC,IAAM,EAAQ,EAAI,MAAM,gBAAgB,CACxC,OAAO,EAAQ,OAAO,EAAM,KAAO,UAGrC,SAAS,GAAa,CAAE,aAA6C,CACnE,IAAM,EAAS,EAAU,UAAU,YAC7B,EAAQ,GAAQ,MAChB,EAAQ,GAAQ,OAAU,EAAU,UAAU,WAEpD,MADI,CAAC,GAAS,CAAC,EAAc,MAE3B,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,CACG,IACC,EAAA,EAAA,MAAC,IAAD,CACE,KAAM,EACN,OAAO,SACP,IAAI,aACJ,MAAO,EACP,UAAU,8IALZ,EAOE,EAAA,EAAA,KAAC,EAAD,CAAU,UAAU,cAAgB,CAAA,CAAC,GAAQ,EAAM,CACjD,GAEL,IACC,EAAA,EAAA,MAAC,EAAD,CAAO,QAAQ,UAAU,UAAU,+DAA+D,MAAO,WAAzG,EACE,EAAA,EAAA,KAAC,EAAD,CAAe,UAAU,8BAAgC,CAAA,EAAA,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,oBAAhB,CAA2B,UAAQ,EAAa,GACnG,GAET,CAAA,CAAA,CAIP,SAAS,GAAa,CAAE,aAA6C,CACnE,IAAM,EAAM,GAAQ,CACd,CAAC,EAAU,IAAA,EAAA,EAAA,UAAwB,GAAM,CACzC,EAAa,EAAe,GAAM,EAAE,WAAW,CAC/C,EAAU,EAAe,GAAM,EAAE,mBAAqB,EAAU,GAAG,CACnE,EAAmB,EAAe,GAAM,EAAE,iBAAiB,CAC3D,EAAiB,EAAe,GAAM,EAAE,eAAe,CACvD,EAAQ,EAAU,aAAe,EAAW,EAAU,cAAgB,IAAA,GACtE,EAAY,EAAqB,EAAkB,EAAU,aAAc,EAAU,OAAO,CAC5F,EAAW,EAAe,GAAM,EAAE,kBAAkB,EAAU,IAAI,CAClE,EAAS,CAAC,CAAC,GAAY,EAAS,YAAc,IAAS,EAAS,SAAW,GAC3E,EAAe,GAAa,OAAS,EAAU,SAAW,UAAY,EAAU,SAAW,WAAa,EAAU,SAAW,aAAe,GAC5I,EAAU,EAAU,eAAiB,EAAW,EAAU,gBAAkB,IAAA,GAC5E,EAAO,EAAU,cAAgB,EAAU,WAAa,EAAU,SAClE,GAAA,EAAA,EAAA,QAAgC,KAAK,CAO3C,OAJA,EAAA,EAAA,eAAgB,CACV,GAAS,EAAO,SAAS,eAAe,CAAE,SAAU,SAAU,MAAO,SAAU,CAAC,EACnF,CAAC,EAAQ,CAAC,EAGX,EAAA,EAAA,MAAC,MAAD,CAAK,IAAK,EAAQ,UAAW,0BAA0B,EAAU,iDAAmD,cAApH,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,oIAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6BAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,2CAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAW,UAAU,6CAA+C,CAAA,EACpE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,uBAAuB,MAAO,EAAU,QAAU,EAAU,YAAK,EAAU,QAAU,EAAU,GAAU,CAAA,CACrH,IACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,gCAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAW,eAAe,EAAU,OAAS,WAAa,iCAAmC,8CACnH,EAAU,KACL,CAAA,CACP,EAAU,eAAiB,EAAU,gBAAkB,EAAU,OAChE,EAAA,EAAA,MAAC,EAAD,CAAO,QAAQ,UAAU,UAAU,uBAAnC,CAAiD,aAAW,EAAU,cAAsB,IAE9F,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAW,eAAe,GAAW,EAAU,OAAO,YAAK,EAAa,EAAU,SAAW,EAAU,OAAe,CAAA,EAC/I,EAAA,EAAA,KAAC,GAAD,CAAyB,YAAa,CAAA,CACrC,GAAgB,IACf,EAAA,EAAA,KAAC,EAAD,CAAU,GAAI,EAAU,GAAI,MAAO,EAAU,MAAO,OAAQ,EAAU,OAAQ,YAAe,EAAe,EAAU,CAAI,CAAA,CAExH,GACF,IAEN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6BAAf,EACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,6BAA6B,MAAO,WAAO,GAAU,EAAK,CAAO,CAAA,EAChF,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,yCAAyC,MAAO,EAAU,mBAAY,EAAU,UAAgB,CAAA,EAC/G,EAAA,EAAA,KAAC,GAAD,CAAqB,YAAa,CAAA,CAC9B,IAEN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,yEAAf,EACE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,iCAAwB,QAAY,CAAA,EACpD,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,WAAW,MAAO,EAAU,sBAAe,EAAW,EAAO,EAAU,iBAAmB,EAAU,aAAa,CAAQ,CAAA,EACzI,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,iCAAwB,UAAc,CAAA,EACtD,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,WAAW,MAAO,EAAU,wBAAiB,EAAW,EAAS,EAAU,eAAe,CAAQ,CAAA,EAClH,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,iCAAwB,UAAc,CAAA,EACtD,EAAA,EAAA,KAAC,OAAD,CAAA,SAAO,EAAQ,EAAK,EAAU,UAAU,CAAQ,CAAA,CAC/C,EAAU,UACT,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,iCAAwB,OAAW,CAAA,EACnD,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,WAAW,MAAO,EAAU,iBAAU,EAAU,QAAe,CAAA,CAC9E,CAAA,CAAA,CAED,IAEN,EAAA,EAAA,KAAC,GAAD,CAA6B,YAAqB,WAAU,oBAAuB,EAAa,GAAU,CAAC,EAAM,CAAI,CAAA,CACjH,GACL,IAAY,EAAA,EAAA,KAAC,EAAD,CAA6B,YAAa,CAAA,CACnD,GAIV,SAAgB,IAAiB,CAC/B,GAAM,CAAC,EAAQ,IAAA,EAAA,EAAA,UAAuC,SAAS,CACzD,CAAC,EAAe,IAAA,EAAA,EAAA,UAAqD,EAAa,CAClF,CAAC,EAAU,IAAA,EAAA,EAAA,UAAmC,EAAE,CAAC,CACjD,EAAa,EAAe,GAAM,EAAE,WAAW,CAC/C,EAAM,EAAe,GAAM,EAAE,IAAI,CACjC,EAAmB,EAAe,GAAM,EAAE,iBAAiB,EAKjE,EAAA,EAAA,eAAgB,CACV,IACF,EAAU,MAAM,CAChB,EAAiB,EAAa,GAE/B,CAAC,EAAiB,CAAC,EACtB,EAAA,EAAA,mBAAsB,CAAE,EAAI,CAAE,iBAAkB,KAAM,CAAC,EAAI,CAAC,EAAI,CAAC,EACjE,EAAA,EAAA,eAAgB,CACd,IAAI,EAAY,GAIhB,OAHA,EAA6B,MAAO,YAAY,CAC7C,KAAM,GAAQ,CAAO,GAAW,EAAY,EAAI,UAAY,EAAE,CAAC,EAAG,CAClE,UAAY,CAAO,GAAW,EAAY,EAAE,CAAC,EAAG,KACtC,CAAE,EAAY,KAC1B,EAAE,CAAC,CAEN,IAAM,GAAA,EAAA,EAAA,aAAkC,EAAiB,EAAY,EAAQ,EAAc,CAAE,CAAC,EAAY,EAAQ,EAAc,CAAC,CAC3H,GAAA,EAAA,EAAA,aAAwB,EAAgB,EAAmB,GAAc,CAAE,CAAC,EAAkB,CAAC,CAC/F,EAAY,EAAW,OAAQ,GAAS,EAAwB,IAAI,EAAK,OAAO,CAAC,CAAC,OAClF,EAAgB,EAAW,OAAQ,GAAS,EAAK,OAAS,WAAW,CAAC,OACtE,EAAa,EAAW,OAAQ,GAAS,EAAK,SAAW,SAAW,EAAK,SAAW,oBAAsB,EAAK,SAAW,gBAAgB,CAAC,OAC3I,EAAgB,EAAW,OAAQ,GAAS,EAAK,SAAW,WAAW,CAAC,OACxE,EAAY,EAAW,OAAQ,GAAS,EAAK,SAAW,UAAY,EAAK,SAAW,aAAe,EAAK,SAAW,UAAU,CAAC,OAE9H,EAAyE,CAC7E,CAAE,IAAK,SAAU,MAAO,SAAU,MAFhB,EAAW,OAAS,EAEgB,CACtD,CAAE,IAAK,UAAW,MAAO,OAAQ,MAAO,EAAW,CACnD,CAAE,IAAK,MAAO,MAAO,MAAO,MAAO,EAAW,OAAQ,CACvD,CACK,GAAA,EAAA,EAAA,aAA+B,CACnC,IAAM,EAAU,CAAC,CAAE,MAAO,EAAc,MAAO,eAAgB,CAAC,CAChE,IAAK,IAAM,KAAW,EACpB,EAAQ,KAAK,CAAE,MAAO,EAAQ,GAAI,MAAO,EAAQ,OAAS,EAAQ,KAAM,CAAC,CAG3E,OADA,EAAQ,KAAK,CAAE,MAAO,EAAqB,MAAO,aAAc,CAAC,CAC1D,GACN,CAAC,EAAS,CAAC,CAEd,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,qBAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6CAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,kCAAyB,aAAe,CAAA,EACtD,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,yCAAgC,mDAAoD,CAAA,CAC7F,CAAA,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,0CAAf,EACE,EAAA,EAAA,MAAC,EAAD,CAAO,QAAQ,mBAAf,CAA0B,EAAc,YAAiB,IACzD,EAAA,EAAA,MAAC,EAAD,CAAO,QAAQ,mBAAf,CAA0B,EAAU,QAAa,IACjD,EAAA,EAAA,MAAC,EAAD,CAAO,QAAQ,mBAAf,CAA0B,EAAW,SAAc,GAClD,EAAgB,IAAK,EAAA,EAAA,MAAC,EAAD,CAAO,QAAQ,UAAU,UAAU,0CAAnC,CAAqE,EAAc,YAAiB,GACtH,GACF,IAEN,EAAA,EAAA,KAAC,EAAD,EAAe,CAAA,EAEf,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6CAAf,EACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,4DACZ,EAAQ,IAAK,IACZ,EAAA,EAAA,MAAC,EAAD,CAEE,KAAK,KACL,QAAS,IAAW,EAAK,IAAM,YAAc,QAC7C,UAAU,cACV,YAAe,EAAU,EAAK,IAAI,UALpC,CAOG,EAAK,OACN,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,sCAA8B,EAAK,MAAa,CAAA,CACzD,EARF,EAAK,IAQH,CACT,CACE,CAAA,EACN,EAAA,EAAA,KAAC,SAAD,CACE,UAAU,2FACV,MAAO,EACP,SAAW,GAAM,EAAiB,EAAE,OAAO,MAAM,UAEhD,EAAe,IAAK,IACnB,EAAA,EAAA,KAAC,SAAD,CAA2B,MAAO,EAAO,eAAQ,EAAO,MAAe,CAA1D,EAAO,MAAmD,CACvE,CACK,CAAA,CACL,GAEL,EAAQ,SAAW,GAClB,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,gGAAf,CAAsG,MAChG,IAAW,MAAQ,GAAK,EAAO,cAAY,IAAA,UAA6D,GAA5B,0BAC5E,IAEN,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,qBACZ,EAAQ,IAAK,IACZ,EAAA,EAAA,KAAC,EAAD,CAAqC,QAAO,mBAAqB,IAAc,EAAA,EAAA,KAAC,GAAD,CAA4C,YAAa,CAAtC,EAAU,GAA4B,CAAI,CAAzH,EAAM,IAAmH,CAC5I,CACE,CAAA,CAEJ"}
|
|
1
|
+
{"version":3,"file":"workspaces-CR6f7G0R.js","names":[],"sources":["../../dashboard/src/components/views/workspaces-list.tsx","../../dashboard/src/lib/task-resolution.ts","../../dashboard/src/components/views/workspaces.tsx"],"sourcesContent":["import { useEffect, type ReactNode } from 'react'\nimport { FolderOpen, PackageOpen, RefreshCw } from 'lucide-react'\nimport { TERMINAL_WORKSPACE_STATUS_VALUES } from 'agent-relay-sdk/types'\nimport { useRelayStore } from '@/store'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport type { WorkspaceOrphan, WorkspaceRecord, WorkspaceStatus } from '@/types'\n\nexport const LIVE_WORKSPACE_STATUSES = new Set<WorkspaceStatus>(['active', 'ready', 'conflict', 'review_requested', 'merge_planned', 'cleanup_requested'])\nconst TERMINAL_STATUSES = new Set<WorkspaceStatus>(TERMINAL_WORKSPACE_STATUS_VALUES)\n\nexport type WorkspaceFilter = 'active' | 'cleaned' | 'all'\nexport type WorkspaceProjectFilter = string\n\nexport const ALL_PROJECTS = '__all__'\nexport const UNASSIGNED_PROJECTS = '__unassigned__'\n\ntype RepoWorkspaceGroup = {\n repoRoot: string\n workspaces: WorkspaceRecord[]\n}\n\nexport type WorkspaceProjectGroup = {\n key: string\n title: string\n projectId?: string\n projectSlug?: string\n unassigned: boolean\n repoGroups: RepoWorkspaceGroup[]\n}\n\nfunction repoWorkspaceGroups(workspaces: WorkspaceRecord[], workspaceSort: (a: WorkspaceRecord, b: WorkspaceRecord) => number): RepoWorkspaceGroup[] {\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) => a.repoRoot.localeCompare(b.repoRoot))\n}\n\nexport function groupWorkspaces(workspaces: WorkspaceRecord[], workspaceSort: (a: WorkspaceRecord, b: WorkspaceRecord) => number): WorkspaceProjectGroup[] {\n const grouped = new Map<string, WorkspaceRecord[]>()\n for (const workspace of workspaces) {\n const key = workspace.projectSlug || workspace.repoRoot || workspace.sourceCwd || 'unassigned'\n grouped.set(key, [...(grouped.get(key) || []), workspace])\n }\n return [...grouped.entries()]\n .map(([key, items]) => {\n const sample = items[0]!\n const unassigned = !sample.projectId\n const title = unassigned ? (sample.repoRoot || sample.sourceCwd || 'unknown repo') : (sample.projectTitle || sample.projectSlug || sample.repoRoot)\n return {\n key,\n title,\n ...(sample.projectId ? { projectId: sample.projectId } : {}),\n ...(sample.projectSlug ? { projectSlug: sample.projectSlug } : {}),\n unassigned,\n repoGroups: repoWorkspaceGroups(items, workspaceSort),\n }\n })\n .sort((a, b) => {\n const liveA = a.repoGroups.some((group) => group.workspaces.some((item) => LIVE_WORKSPACE_STATUSES.has(item.status)))\n const liveB = b.repoGroups.some((group) => group.workspaces.some((item) => LIVE_WORKSPACE_STATUSES.has(item.status)))\n if (liveA !== liveB) return liveA ? -1 : 1\n return a.title.localeCompare(b.title)\n })\n}\n\nexport function filterWorkspaces(workspaces: WorkspaceRecord[], filter: WorkspaceFilter, projectFilter: WorkspaceProjectFilter): WorkspaceRecord[] {\n const byStatus = filter === 'all'\n ? workspaces\n : filter === 'cleaned'\n ? workspaces.filter((workspace) => TERMINAL_STATUSES.has(workspace.status))\n : workspaces.filter((workspace) => !TERMINAL_STATUSES.has(workspace.status))\n if (projectFilter === ALL_PROJECTS) return byStatus\n if (projectFilter === UNASSIGNED_PROJECTS) return byStatus.filter((workspace) => !workspace.projectId)\n return byStatus.filter((workspace) => workspace.projectId === projectFilter)\n}\n\nexport function ProjectGroup({\n group,\n renderWorkspaceRow,\n}: {\n group: WorkspaceProjectGroup\n renderWorkspaceRow: (workspace: WorkspaceRecord) => ReactNode\n}) {\n const workspaces = group.repoGroups.flatMap((repoGroup) => repoGroup.workspaces)\n const liveCount = workspaces.filter((item) => LIVE_WORKSPACE_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 const showRepoSubgroups = group.repoGroups.length > 1\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 <div className=\"flex flex-wrap items-center gap-2\">\n <CardTitle className=\"truncate text-sm font-medium\" title={group.title}>{group.title}</CardTitle>\n {group.unassigned && <Badge variant=\"outline\" className=\"text-[10px]\">Unassigned</Badge>}\n </div>\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 {group.repoGroups.map((repoGroup) => (\n <div key={repoGroup.repoRoot} className=\"border-t border-border first:border-t-0\">\n {showRepoSubgroups && (\n <div className=\"flex items-center gap-2 border-b border-dashed border-border/80 px-3 py-2\">\n <FolderOpen className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate font-mono text-[11px] text-muted-foreground\" title={repoGroup.repoRoot}>{repoGroup.repoRoot}</span>\n </div>\n )}\n {repoGroup.workspaces.map((workspace) => renderWorkspaceRow(workspace))}\n </div>\n ))}\n </CardContent>\n </Card>\n )\n}\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\nexport function 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 const renderOrphan = (orphan: WorkspaceOrphan) => (\n <div key={orphan.worktreePath} className=\"flex items-center gap-3 border-t border-border px-4 py-2 text-xs\">\n <FolderOpen 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 <PackageOpen className=\"mr-1 h-3.5 w-3.5\" />Reclaim\n </Button>\n </div>\n )\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\">{orphans.map(renderOrphan)}</CardContent>\n </Card>\n )\n}\n","import type { Task } from '@/types'\n\nconst DONE_STATUSES = new Set(['done', 'failed', 'canceled'])\n\n/**\n * Resolve the authoritative Task for a workspace row.\n *\n * Branch match is canonical: if workspace.branch matches a task's branch, that IS the task\n * that produced this workspace. ownerAgentId alone is ambiguous when an agent runs multiple\n * tasks sequentially — a merged/cleaned row for task #1's branch must not show task #2.\n *\n * Fall-back when no branch match:\n * Exactly one ownerAgentId match → use it (unambiguous).\n * Multiple matches → return null (no chip beats a wrong chip).\n */\nexport function resolveWorkspaceTask(\n deliverableTasks: Task[],\n ownerAgentId: string | undefined,\n branch: string | undefined,\n): Task | null {\n if (branch) {\n const byBranch = deliverableTasks.find((t) => t.branch && t.branch === branch)\n if (byBranch) return byBranch\n }\n if (!ownerAgentId) return null\n const candidates = deliverableTasks.filter((t) => t.ownerAgentId === ownerAgentId)\n if (candidates.length === 0) return null\n if (candidates.length === 1) return candidates[0]!\n return null // >1 owner match, no branch: ambiguous — no chip beats a wrong chip\n}\n\n/**\n * Resolve the agent's current working Task for the agent detail drawer.\n *\n * Returns the most-recent non-done deliverable owned by this agent.\n * Returns null when the agent has no active task, preventing a stale\n * \"Working on\" chip from showing a previously-completed task.\n */\nexport function resolveAgentTask(\n deliverableTasks: Task[],\n agentId: string,\n): Task | null {\n return (\n deliverableTasks\n .filter((t) => t.ownerAgentId === agentId && !DONE_STATUSES.has(t.status))\n .sort((a, b) => Number(b.updatedAt) - Number(a.updatedAt))[0] ?? null\n )\n}\n","import { useEffect, useMemo, useRef, useState } from 'react'\nimport { ArrowDown, ArrowUp, Check, ChevronDown, ChevronRight, Clock, Eye, FileDiff, FilePen, Flag, FolderOpen, GitBranch, GitCommitHorizontal, GitMerge, RefreshCw, TriangleAlert, Trash2, X } from 'lucide-react'\nimport { api } from '@/lib/api'\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 { displayName, timeAgo } from '@/lib/display'\nimport { ALL_PROJECTS, filterWorkspaces, groupWorkspaces, LIVE_WORKSPACE_STATUSES, OrphanPanel, ProjectGroup, UNASSIGNED_PROJECTS, type WorkspaceFilter, type WorkspaceProjectFilter } from './workspaces-list'\nimport { TaskChip } from '@/components/shared/task-chip'\nimport { resolveWorkspaceTask } from '@/lib/task-resolution'\nimport type { Agent, Project, WorkspaceRecord, WorkspaceStatus } from '@/types'\n\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\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\nfunction agentLabel(agent: Agent | undefined, fallback: string | undefined): string {\n if (agent) return displayName(agent)\n return fallback || '-'\n}\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 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 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// 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 deliverableTasks = useRelayStore((s) => s.deliverableTasks)\n const openTaskDetail = useRelayStore((s) => s.openTaskDetail)\n const owner = workspace.ownerAgentId ? agentsById[workspace.ownerAgentId] : undefined\n const ownerTask = resolveWorkspaceTask(deliverableTasks, workspace.ownerAgentId, workspace.branch)\n const gitState = useRelayStore((s) => s.workspaceGitState[workspace.id])\n const landed = !!gitState && gitState.available !== false && gitState.landed === true\n const showTaskChip = ownerTask != null && (workspace.status === 'merged' || workspace.status === 'cleaned' || workspace.status === 'abandoned' || landed)\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 {showTaskChip && ownerTask && (\n <TaskChip id={ownerTask.id} title={ownerTask.title} status={ownerTask.status} onClick={() => openTaskDetail(ownerTask)} />\n )}\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\nexport function WorkspacesView() {\n const [filter, setFilter] = useState<WorkspaceFilter>('active')\n const [projectFilter, setProjectFilter] = useState<WorkspaceProjectFilter>(ALL_PROJECTS)\n const [projects, setProjects] = useState<Project[]>([])\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) {\n setFilter('all')\n setProjectFilter(ALL_PROJECTS)\n }\n }, [workspaceFocusId])\n useEffect(() => () => { set({ workspaceFocusId: null }) }, [set])\n useEffect(() => {\n let cancelled = false\n api<{ projects: Project[] }>('GET', '/projects')\n .then((res) => { if (!cancelled) setProjects(res.projects ?? []) })\n .catch(() => { if (!cancelled) setProjects([]) })\n return () => { cancelled = true }\n }, [])\n\n const visibleWorkspaces = useMemo(() => filterWorkspaces(workspaces, filter, projectFilter), [workspaces, filter, projectFilter])\n const grouped = useMemo(() => groupWorkspaces(visibleWorkspaces, workspaceSort), [visibleWorkspaces])\n const liveCount = workspaces.filter((item) => LIVE_WORKSPACE_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) => item.status === 'merged' || item.status === 'abandoned' || item.status === 'cleaned').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 const projectOptions = useMemo(() => {\n const options = [{ value: ALL_PROJECTS, label: 'All Projects' }]\n for (const project of projects) {\n options.push({ value: project.id, label: project.title || project.slug })\n }\n options.push({ value: UNASSIGNED_PROJECTS, label: 'Unassigned' })\n return options\n }, [projects])\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 <select\n className=\"h-8 min-w-[12rem] rounded-md border border-border bg-card px-2.5 text-sm text-foreground\"\n value={projectFilter}\n onChange={(e) => setProjectFilter(e.target.value)}\n >\n {projectOptions.map((option) => (\n <option key={option.value} value={option.value}>{option.label}</option>\n ))}\n </select>\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{projectFilter !== ALL_PROJECTS ? ' in this project filter' : ''}\n </div>\n ) : (\n <div className=\"space-y-3\">\n {grouped.map((group) => (\n <ProjectGroup key={group.key} group={group} renderWorkspaceRow={(workspace) => <WorkspaceRow key={workspace.id} workspace={workspace} />} />\n ))}\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
package/public/index.html
CHANGED
|
@@ -12,34 +12,34 @@
|
|
|
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-DnUo0Zqg.js"></script>
|
|
16
16
|
<link rel="modulepreload" crossorigin href="./assets/chunk-CilyBKbf.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-C_JMBXql.js">
|
|
18
|
-
<link rel="modulepreload" crossorigin href="./assets/lucide-react-
|
|
19
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
20
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
21
|
-
<link rel="modulepreload" crossorigin href="./assets/react-dom-
|
|
22
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
23
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
24
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
25
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
18
|
+
<link rel="modulepreload" crossorigin href="./assets/lucide-react-CoU_Xts2.js">
|
|
19
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-C5z05Vkz.js">
|
|
20
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-B8tD9ZcR.js">
|
|
21
|
+
<link rel="modulepreload" crossorigin href="./assets/react-dom-BQICbUuF.js">
|
|
22
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-V3wu3BHw.js">
|
|
23
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-CFtFIhu3.js">
|
|
24
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-CGaHpxnN.js">
|
|
25
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-DKmTXfpP.js">
|
|
26
26
|
<link rel="modulepreload" crossorigin href="./assets/dist-BiLuYU6G.js">
|
|
27
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
28
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
29
|
-
<link rel="modulepreload" crossorigin href="./assets/es2015-
|
|
30
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
31
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
32
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
27
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-BJaV2BD4.js">
|
|
28
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-CjyfQThq.js">
|
|
29
|
+
<link rel="modulepreload" crossorigin href="./assets/es2015-5DTJe35n.js">
|
|
30
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-r8wyUTkK.js">
|
|
31
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-4VwhnLtj.js">
|
|
32
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-CrUl2aQp.js">
|
|
33
33
|
<link rel="modulepreload" crossorigin href="./assets/types-CRptbSYh.js">
|
|
34
34
|
<link rel="modulepreload" crossorigin href="./assets/display-GFbGUG0O.js">
|
|
35
|
-
<link rel="modulepreload" crossorigin href="./assets/store-
|
|
36
|
-
<link rel="modulepreload" crossorigin href="./assets/agent-type-icon-
|
|
37
|
-
<link rel="modulepreload" crossorigin href="./assets/button-
|
|
38
|
-
<link rel="modulepreload" crossorigin href="./assets/copy-button-
|
|
39
|
-
<link rel="modulepreload" crossorigin href="./assets/formatted-body-
|
|
40
|
-
<link rel="modulepreload" crossorigin href="./assets/task-chip-
|
|
41
|
-
<link rel="modulepreload" crossorigin href="./assets/badge-
|
|
42
|
-
<link rel="modulepreload" crossorigin href="./assets/input-
|
|
35
|
+
<link rel="modulepreload" crossorigin href="./assets/store-zFLzd0Jd.js">
|
|
36
|
+
<link rel="modulepreload" crossorigin href="./assets/agent-type-icon-BxUopALO.js">
|
|
37
|
+
<link rel="modulepreload" crossorigin href="./assets/button-C3CQSr4p.js">
|
|
38
|
+
<link rel="modulepreload" crossorigin href="./assets/copy-button-Bil2CjzT.js">
|
|
39
|
+
<link rel="modulepreload" crossorigin href="./assets/formatted-body-CvrQ2D1J.js">
|
|
40
|
+
<link rel="modulepreload" crossorigin href="./assets/task-chip-BHM-e3I8.js">
|
|
41
|
+
<link rel="modulepreload" crossorigin href="./assets/badge-XEru6wto.js">
|
|
42
|
+
<link rel="modulepreload" crossorigin href="./assets/input-CXMbJ848.js">
|
|
43
43
|
<link rel="modulepreload" crossorigin href="./assets/themes-DjSwmAZN.js">
|
|
44
44
|
<link rel="stylesheet" crossorigin href="./assets/index-DcmG5gLR.css">
|
|
45
45
|
</head>
|