agent-relay-server 0.127.0 → 0.127.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.
Files changed (70) hide show
  1. package/docs/openapi.json +1 -1
  2. package/package.json +2 -2
  3. package/public/assets/{MessageBubble-D2cnxyTy.js → MessageBubble-DnXnkt4a.js} +2 -2
  4. package/public/assets/{MessageBubble-D2cnxyTy.js.map → MessageBubble-DnXnkt4a.js.map} +1 -1
  5. package/public/assets/{PlanGraphPanel-Cnxt-x7R.js → PlanGraphPanel-D6CPEpQD.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-Cnxt-x7R.js.map → PlanGraphPanel-D6CPEpQD.js.map} +1 -1
  7. package/public/assets/{activity-BEm1BQB1.js → activity-ZPwHcPLK.js} +2 -2
  8. package/public/assets/{activity-BEm1BQB1.js.map → activity-ZPwHcPLK.js.map} +1 -1
  9. package/public/assets/{agent-profiles-C8V_oCiM.js → agent-profiles-CT0E9LV1.js} +2 -2
  10. package/public/assets/{agent-profiles-C8V_oCiM.js.map → agent-profiles-CT0E9LV1.js.map} +1 -1
  11. package/public/assets/{agents-D8mc9X1j.js → agents-Dt5TncjC.js} +2 -2
  12. package/public/assets/{agents-D8mc9X1j.js.map → agents-Dt5TncjC.js.map} +1 -1
  13. package/public/assets/{automation-BEhJZkNQ.js → automation-yzUJbvT2.js} +2 -2
  14. package/public/assets/{automation-BEhJZkNQ.js.map → automation-yzUJbvT2.js.map} +1 -1
  15. package/public/assets/chat-DhYX9BDG.js +2 -0
  16. package/public/assets/chat-DhYX9BDG.js.map +1 -0
  17. package/public/assets/{formatted-body-Rv9RDPsQ.js → formatted-body-D5-1aiyB.js} +3 -3
  18. package/public/assets/{formatted-body-Rv9RDPsQ.js.map → formatted-body-D5-1aiyB.js.map} +1 -1
  19. package/public/assets/{formatted-body-impl-XFU4tPeT.js → formatted-body-impl-Do5NbJUA.js} +2 -2
  20. package/public/assets/{formatted-body-impl-XFU4tPeT.js.map → formatted-body-impl-Do5NbJUA.js.map} +1 -1
  21. package/public/assets/{index-Bhy_jnX4.js → index-BdNafiPz.js} +6 -6
  22. package/public/assets/{index-Bhy_jnX4.js.map → index-BdNafiPz.js.map} +1 -1
  23. package/public/assets/{insights-DpXPAEwH.js → insights-i6AEZT_a.js} +2 -2
  24. package/public/assets/{insights-DpXPAEwH.js.map → insights-i6AEZT_a.js.map} +1 -1
  25. package/public/assets/{maintenance-BrkqfGwx.js → maintenance-CJ6wikpg.js} +2 -2
  26. package/public/assets/{maintenance-BrkqfGwx.js.map → maintenance-CJ6wikpg.js.map} +1 -1
  27. package/public/assets/{managed-agents-CLANxOAN.js → managed-agents-JZn0xNJR.js} +3 -3
  28. package/public/assets/managed-agents-JZn0xNJR.js.map +1 -0
  29. package/public/assets/{markdown-preview-impl-CcZFkjn-.js → markdown-preview-impl-BsvAkpR4.js} +2 -2
  30. package/public/assets/{markdown-preview-impl-CcZFkjn-.js.map → markdown-preview-impl-BsvAkpR4.js.map} +1 -1
  31. package/public/assets/{memory-BoQw8q-i.js → memory-BE9IwCdC.js} +2 -2
  32. package/public/assets/{memory-BoQw8q-i.js.map → memory-BE9IwCdC.js.map} +1 -1
  33. package/public/assets/{messages-DfKNuph2.js → messages-vXgdc3H4.js} +2 -2
  34. package/public/assets/{messages-DfKNuph2.js.map → messages-vXgdc3H4.js.map} +1 -1
  35. package/public/assets/{orchestrators-sQh8e7-V.js → orchestrators-CribhLg-.js} +2 -2
  36. package/public/assets/{orchestrators-sQh8e7-V.js.map → orchestrators-CribhLg-.js.map} +1 -1
  37. package/public/assets/{overview-B0z8ZNtp.js → overview-q3O9K9VK.js} +2 -2
  38. package/public/assets/{overview-B0z8ZNtp.js.map → overview-q3O9K9VK.js.map} +1 -1
  39. package/public/assets/{pairs-B3FhGmn-.js → pairs-BNOnd0IE.js} +2 -2
  40. package/public/assets/{pairs-B3FhGmn-.js.map → pairs-BNOnd0IE.js.map} +1 -1
  41. package/public/assets/{prompt-settings-CCj_s008.js → prompt-settings-Bdz_xCgD.js} +2 -2
  42. package/public/assets/{prompt-settings-CCj_s008.js.map → prompt-settings-Bdz_xCgD.js.map} +1 -1
  43. package/public/assets/{registry-mD_y06Ru.js → registry-DPcyGypi.js} +2 -2
  44. package/public/assets/{registry-mD_y06Ru.js.map → registry-DPcyGypi.js.map} +1 -1
  45. package/public/assets/{security-CTSA9niL.js → security-Z3l3u-Pl.js} +2 -2
  46. package/public/assets/{security-CTSA9niL.js.map → security-Z3l3u-Pl.js.map} +1 -1
  47. package/public/assets/{select-gEQEHCIf.js → select-BX_lW49D.js} +2 -2
  48. package/public/assets/{select-gEQEHCIf.js.map → select-BX_lW49D.js.map} +1 -1
  49. package/public/assets/{settings-ByAR-TYj.js → settings-Y381jZt7.js} +2 -2
  50. package/public/assets/{settings-ByAR-TYj.js.map → settings-Y381jZt7.js.map} +1 -1
  51. package/public/assets/{tasks-CZyJX881.js → tasks-Di6d0_Ch.js} +2 -2
  52. package/public/assets/{tasks-CZyJX881.js.map → tasks-Di6d0_Ch.js.map} +1 -1
  53. package/public/assets/{teams-BrL4sEL7.js → teams-DJv4l-_l.js} +2 -2
  54. package/public/assets/{teams-BrL4sEL7.js.map → teams-DJv4l-_l.js.map} +1 -1
  55. package/public/assets/{terminal-viewer-impl-LjIph8ql.js → terminal-viewer-impl-3zBLpRel.js} +2 -2
  56. package/public/assets/{terminal-viewer-impl-LjIph8ql.js.map → terminal-viewer-impl-3zBLpRel.js.map} +1 -1
  57. package/public/assets/{work-queue-CSu5kRsw.js → work-queue-D73-hP0c.js} +2 -2
  58. package/public/assets/{work-queue-CSu5kRsw.js.map → work-queue-D73-hP0c.js.map} +1 -1
  59. package/public/index.html +2 -2
  60. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  61. package/runner/src/mcp-outbox.ts +13 -1
  62. package/runner/src/provisioning.ts +121 -0
  63. package/runner/src/runner-core.ts +1 -1
  64. package/src/routes/agent-sessions.ts +4 -1
  65. package/src/routes/agents.ts +3 -0
  66. package/src/services/task-entity.ts +32 -1
  67. package/src/workspace-actions.ts +4 -2
  68. package/public/assets/chat-BAH9bEPx.js +0 -2
  69. package/public/assets/chat-BAH9bEPx.js.map +0 -1
  70. package/public/assets/managed-agents-CLANxOAN.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import{a as e,gt as t,jn as n,rr as r,yn as i}from"./lucide-react-NXHPIMsX.js";import{p as a,t as o}from"./store-D-Frk_EC.js";import{K as s,ct as c,d as l}from"./display-aTnwsqQh.js";import{t as u}from"./badge-BCvp7LK3.js";import{t as d}from"./button-DyHjsHY3.js";import{At as f,G as p,wt as m}from"./index-Bhy_jnX4.js";import{r as h,t as g}from"./card-BoqYxpVW.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-CSu5kRsw.js.map
1
+ import{a as e,gt as t,jn as n,rr as r,yn as i}from"./lucide-react-NXHPIMsX.js";import{p as a,t as o}from"./store-D-Frk_EC.js";import{K as s,ct as c,d as l}from"./display-aTnwsqQh.js";import{t as u}from"./badge-BCvp7LK3.js";import{t as d}from"./button-DyHjsHY3.js";import{At as f,G as p,wt as m}from"./index-BdNafiPz.js";import{r as h,t as g}from"./card-BoqYxpVW.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-D73-hP0c.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"work-queue-CSu5kRsw.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-D73-hP0c.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":"wXAWM,EAAwC,CAC5C,KAAM,kDACN,QAAS,wDACT,QAAS,2DACT,YAAa,wDACd,CAED,SAAgB,GAAgB,CAC9B,IAAM,EAAM,GAAQ,CACd,EAAa,EAAe,GAAM,EAAE,WAAW,CAC/C,EAAU,EAAe,GAAM,EAAE,QAAQ,CACzC,EAAM,EAAe,GAAM,EAAE,IAAI,CACjC,EAAc,EAAe,GAAM,EAAE,YAAY,CACjD,EAAU,EAAe,GAAM,EAAE,QAAQ,CACzC,EAAiB,EAAe,GAAM,EAAE,eAAe,CACvD,EAAY,EAAe,GAAM,EAAE,UAAU,CAC7C,EAAc,EAAe,GAAM,EAAE,YAAY,CACjD,EAAkB,EAAe,GAAM,EAAE,gBAAgB,CACzD,EAAqB,EAAe,GAAM,EAAE,mBAAmB,CAE/D,EAAgB,GAAkB,CAClC,EAAQ,GAAmB,CAE3B,EAAiB,EAAM,OAAQ,GAAM,EAAE,UAAU,CAAC,OAExD,SAAS,EAAY,EAAqB,CACxC,GAAI,CAAC,EAAQ,KAAM,CAAE,EAAU,aAAc,mCAAmC,CAAE,OAC9E,EAAK,aAAe,QAAU,EAAK,KAAM,EAAY,EAAK,KAAK,GAAG,CAC7D,EAAK,aAAe,WAAa,EAAK,SAAS,EAAQ,EAAK,QAAQ,GAAG,CAGlF,SAAS,EAAa,EAAqB,CAEzC,EAAY,cAAe,WADb,EAAK,QAAU,EAAK,aAAe,OAAS,IAAI,EAAK,KAAO,YAAY,EAAK,MAC/C,+BAAkC,CACxE,EAAK,aAAe,QAAU,EAAK,KAAM,EAAmB,EAAK,KAAM,WAAW,CAC7E,EAAK,aAAe,WAAa,EAAK,SAAS,EAAgB,EAAK,QAAQ,GAAG,EACxF,CAGJ,SAAS,EAAa,EAAqB,CACrC,EAAK,MAAM,EAAe,EAAK,KAAK,CAG1C,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,qBAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6DAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mCAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAY,UAAU,gCAAkC,CAAA,EACxD,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,iCAAwB,aAAe,CAAA,CACpD,EAAiB,IAChB,EAAA,EAAA,MAAC,EAAD,CAAO,UAAU,wEAAjB,CACG,EAAe,aACV,GAEN,IACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mCAAf,EACE,EAAA,EAAA,KAAC,QAAD,CAAO,UAAU,kDAAyC,WAAgB,CAAA,EAC1E,EAAA,EAAA,MAAC,SAAD,CACE,UAAU,mEACV,MAAO,EAAQ,KACf,SAAW,GAAM,EAAI,CAAE,QAAS,CAAE,GAAG,EAAS,KAAM,EAAE,OAAO,MAAO,CAAE,CAAC,UAHzE,EAKE,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,YAAG,kBAAwB,CAAA,CACxC,EAAc,IAAK,IAAM,EAAA,EAAA,KAAC,SAAD,CAAmB,MAAO,EAAE,YAAK,EAAY,EAAE,CAAU,CAA5C,EAAE,GAA0C,CAAC,CAC7E,GACL,GACF,IAEN,EAAA,EAAA,KAAC,EAAD,CAAY,UAAU,mCACpB,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,0BAAf,CACG,EAAM,SAAW,IAChB,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,2DAAkD,sBAAyB,CAAA,CAE3F,EAAM,IAAK,IACV,EAAA,EAAA,KAAC,EAAD,CAEQ,OACD,MACL,UAAY,GAAO,EAAW,GAAM,EAAY,EAAW,GAAI,CAAG,EAAG,MAAM,IAAI,CAC/E,YAAe,EAAY,EAAK,CAChC,aAAgB,EAAa,EAAK,CAClC,SAAU,EAAK,SAAa,EAAa,EAAK,CAAG,IAAA,GACjD,CAPK,EAAK,GAOV,CACF,CACE,GACK,CAAA,CACT,GAIV,SAAS,EAAc,CACrB,OAAM,MAAK,YAAW,UAAS,WAAU,YAQxC,CACD,IAAM,EAAgB,EAAgB,EAAK,WAAa,EAAgB,KAClE,EAAc,EAAc,EAAK,SAAW,kDAElD,OACE,EAAA,EAAA,KAAC,EAAD,CAAM,UAAW,EAAK,UAAY,4BAA8B,aAC9D,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,gBACrB,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,kCAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,0BAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,kDAAf,EACE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,wCAAgC,EAAK,MAAa,CAAA,EAClE,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAW,8BAA8B,aAAkB,EAAK,SAAiB,CAAA,EAC1G,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAW,8BAA8B,aAAgB,EAAK,OAAe,CAAA,CACrG,EAAK,aAAe,YACnB,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAU,uEAA8D,MAAW,CAAA,CAE1G,GACL,EAAK,OACJ,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,2DAAmD,EAAK,KAAS,CAAA,EAEhF,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,2EAAf,CACG,EAAK,QACJ,EAAA,EAAA,MAAC,OAAD,CAAM,MAAM,iBAAZ,EACE,EAAA,EAAA,KAAC,EAAD,CAAc,UAAU,sBAAwB,CAAA,CAAC,EAAU,EAAK,MAAM,CACjE,GAER,EAAK,SACJ,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,qBAAa,EAAK,OAAc,CAAA,EAElD,EAAA,EAAA,MAAC,OAAD,CAAM,MAAO,OAAO,EAAK,UAAU,UAAnC,EACE,EAAA,EAAA,KAAC,EAAD,CAAU,UAAU,sBAAwB,CAAA,CAAC,EAAQ,EAAK,EAAK,UAAU,CACpE,GACH,GACF,IACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,wCAAf,CACG,EAAK,YACJ,EAAA,EAAA,MAAC,EAAD,CAAQ,KAAK,KAAK,UAAU,2DAA2D,QAAS,WAAhG,EACE,EAAA,EAAA,KAAC,EAAD,CAAc,UAAU,mBAAqB,CAAA,CAAA,SACtC,GAEV,IACC,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,KAAK,QAAQ,UAAU,UAAU,cAAc,QAAS,WAAU,SAEtE,CAAA,EAEX,EAAA,EAAA,MAAC,EAAD,CAAQ,KAAK,KAAK,QAAQ,UAAU,UAAU,oFAAoF,QAAS,WAA3I,EACE,EAAA,EAAA,KAAC,EAAD,CAAG,UAAU,mBAAqB,CAAA,CAAA,UAC3B,GACL,GACF,GACM,CAAA,CACT,CAAA"}
package/public/index.html CHANGED
@@ -12,7 +12,7 @@
12
12
  <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='6' fill='%2309090b'/%3E%3Ccircle cx='16' cy='16' r='4.5' fill='%2358a6ff'/%3E%3Ccircle cx='6' cy='8' r='2.5' fill='%233fb950'/%3E%3Ccircle cx='26' cy='8' r='2.5' fill='%233fb950'/%3E%3Ccircle cx='6' cy='24' r='2.5' fill='%233fb950'/%3E%3Ccircle cx='26' cy='24' r='2.5' fill='%233fb950'/%3E%3Cline x1='8' y1='9.5' x2='13' y2='14' stroke='%2330363d' stroke-width='1.5'/%3E%3Cline x1='24' y1='9.5' x2='19' y2='14' stroke='%2330363d' stroke-width='1.5'/%3E%3Cline x1='8' y1='22.5' x2='13' y2='18' stroke='%2330363d' stroke-width='1.5'/%3E%3Cline x1='24' y1='22.5' x2='19' y2='18' stroke='%2330363d' stroke-width='1.5'/%3E%3C/svg%3E">
13
13
  <link rel="manifest" href="manifest.webmanifest">
14
14
  <link rel="apple-touch-icon" href="icons/agent-relay-192.png">
15
- <script type="module" crossorigin src="./assets/index-Bhy_jnX4.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-BdNafiPz.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
18
  <link rel="modulepreload" crossorigin href="./assets/lucide-react-NXHPIMsX.js">
@@ -36,7 +36,7 @@
36
36
  <link rel="modulepreload" crossorigin href="./assets/agent-type-icon-BmBR-9CH.js">
37
37
  <link rel="modulepreload" crossorigin href="./assets/button-DyHjsHY3.js">
38
38
  <link rel="modulepreload" crossorigin href="./assets/copy-button-C67FUG3E.js">
39
- <link rel="modulepreload" crossorigin href="./assets/formatted-body-Rv9RDPsQ.js">
39
+ <link rel="modulepreload" crossorigin href="./assets/formatted-body-D5-1aiyB.js">
40
40
  <link rel="modulepreload" crossorigin href="./assets/task-chip-CeujtXYT.js">
41
41
  <link rel="modulepreload" crossorigin href="./assets/badge-BCvp7LK3.js">
42
42
  <link rel="modulepreload" crossorigin href="./assets/input-Rc4oWv4q.js">
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-relay-runner",
3
3
  "description": "Thin Agent Relay runner bridge for Claude Code",
4
- "version": "0.127.0",
4
+ "version": "0.127.2",
5
5
  "agentRelayContracts": {
6
6
  "providerPluginProtocol": 1
7
7
  }
@@ -67,7 +67,12 @@ export async function deliverRunnerOutboxEvent(input: RunnerOutboxDelivery): Pro
67
67
  }
68
68
  logger.warn("outbox", `dropping event with unknown kind: ${record.kind}`);
69
69
  } catch (error) {
70
- if (isHttpStatusError(error, 409)) return;
70
+ if (isHttpStatusError(error, 409)) {
71
+ if (record.kind === "session-message-batch") {
72
+ logger.error("outbox", `relay reported duplicate session batch; acking row seq=${record.seq} batchKey=${record.idempotencyKey} itemKeys=${sessionBatchItemKeys(record).join(",")} error=${errMessage(error)}`);
73
+ }
74
+ return;
75
+ }
71
76
  if (isHttpAuthError(error)) input.recoverRuntimeTokenAfterAuthFailure("outbox");
72
77
  throw error;
73
78
  }
@@ -89,6 +94,13 @@ function isDeterministicValidationError(error: unknown): boolean {
89
94
  return status === 400 || status === 413 || status === 422;
90
95
  }
91
96
 
97
+ function sessionBatchItemKeys(record: OutboxRecord): string[] {
98
+ const messages = Array.isArray((record.payload as { messages?: unknown }).messages)
99
+ ? (record.payload as { messages: SendMessageInput[] }).messages
100
+ : [];
101
+ return messages.map((message, index) => message.idempotencyKey ?? `${record.idempotencyKey}:${index}`);
102
+ }
103
+
92
104
  export async function deliverBufferedMcpCall(input: {
93
105
  record: OutboxRecord;
94
106
  relayUrl: string;
@@ -1,3 +1,4 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
2
3
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
3
4
  import { sanitizeFsName } from "agent-relay-sdk/fs-name";
@@ -355,6 +356,126 @@ export function writeCodexHooksJson(codexHome: string, hooks: ProvisioningHookSe
355
356
  return;
356
357
  }
357
358
  writeFileSync(join(codexHome, "hooks.json"), JSON.stringify({ hooks }, null, 2), { mode: 0o600 });
359
+ writeCodexHookTrustState(codexHome, hooks);
360
+ }
361
+
362
+ const CODEX_HOOK_EVENT_STATE_KEYS: Record<string, string> = {
363
+ PreToolUse: "pre_tool_use",
364
+ PermissionRequest: "permission_request",
365
+ PostToolUse: "post_tool_use",
366
+ PreCompact: "pre_compact",
367
+ PostCompact: "post_compact",
368
+ SessionStart: "session_start",
369
+ UserPromptSubmit: "user_prompt_submit",
370
+ SubagentStart: "subagent_start",
371
+ SubagentStop: "subagent_stop",
372
+ Stop: "stop",
373
+ };
374
+
375
+ interface CodexHookTrustEntry {
376
+ key: string;
377
+ trustedHash: string;
378
+ }
379
+
380
+ export function codexHookTrustEntries(codexHome: string, hooks: ProvisioningHookSet): CodexHookTrustEntry[] {
381
+ const sourcePath = join(codexHome, "hooks.json");
382
+ const entries: CodexHookTrustEntry[] = [];
383
+ for (const [eventName, groups] of Object.entries(hooks)) {
384
+ const eventKey = CODEX_HOOK_EVENT_STATE_KEYS[eventName];
385
+ if (!eventKey) continue;
386
+ groups.forEach((group, groupIndex) => {
387
+ group.hooks.forEach((handler, handlerIndex) => {
388
+ if (handler.type !== "command") return;
389
+ entries.push({
390
+ key: `${sourcePath}:${eventKey}:${groupIndex}:${handlerIndex}`,
391
+ trustedHash: codexCommandHookHash(eventKey, group.matcher, group, handler),
392
+ });
393
+ });
394
+ });
395
+ }
396
+ return entries;
397
+ }
398
+
399
+ function writeCodexHookTrustState(codexHome: string, hooks: ProvisioningHookSet): void {
400
+ const entries = codexHookTrustEntries(codexHome, hooks);
401
+ if (!entries.length) return;
402
+ const configPath = join(codexHome, "config.toml");
403
+ let config = existsSync(configPath) ? readFileSync(configPath, "utf8") : "";
404
+ for (const entry of entries) {
405
+ config = upsertCodexHookTrustEntry(config, entry);
406
+ }
407
+ writeFileSync(configPath, config, { mode: 0o600 });
408
+ }
409
+
410
+ function upsertCodexHookTrustEntry(config: string, entry: CodexHookTrustEntry): string {
411
+ const header = `[hooks.state.${tomlQuotedString(entry.key)}]`;
412
+ const lines = config.length ? config.split(/\n/) : [];
413
+ const headerIndex = lines.findIndex((line) => line.trim() === header);
414
+ if (headerIndex < 0) {
415
+ const prefix = config.endsWith("\n") || config.length === 0 ? config : `${config}\n`;
416
+ return `${prefix}\n${header}\ntrusted_hash = ${tomlQuotedString(entry.trustedHash)}\n`;
417
+ }
418
+
419
+ let nextHeaderIndex = lines.length;
420
+ for (let i = headerIndex + 1; i < lines.length; i += 1) {
421
+ if (/^\s*\[.*\]\s*$/.test(lines[i] ?? "")) {
422
+ nextHeaderIndex = i;
423
+ break;
424
+ }
425
+ }
426
+
427
+ const trustedHashLine = `trusted_hash = ${tomlQuotedString(entry.trustedHash)}`;
428
+ const existingIndex = lines
429
+ .slice(headerIndex + 1, nextHeaderIndex)
430
+ .findIndex((line) => /^\s*trusted_hash\s*=/.test(line));
431
+ if (existingIndex >= 0) {
432
+ lines[headerIndex + 1 + existingIndex] = trustedHashLine;
433
+ } else {
434
+ lines.splice(nextHeaderIndex, 0, trustedHashLine);
435
+ }
436
+ return lines.join("\n");
437
+ }
438
+
439
+ function codexCommandHookHash(
440
+ eventKey: string,
441
+ matcher: string | undefined,
442
+ group: ProvisioningHookSet[string][number],
443
+ handler: ProvisioningHookSet[string][number]["hooks"][number],
444
+ ): string {
445
+ // Mirrors Codex's command_hook_hash: hash the normalized hook identity, not the source file.
446
+ const normalizedHandler: Record<string, unknown> = {
447
+ type: "command",
448
+ command: handler.command,
449
+ async: false,
450
+ timeout: Math.max(1, handler.timeout ?? 600),
451
+ };
452
+ if (handler.statusMessage !== undefined) normalizedHandler.statusMessage = handler.statusMessage;
453
+
454
+ const identity: Record<string, unknown> = {
455
+ event_name: eventKey,
456
+ hooks: [normalizedHandler],
457
+ };
458
+ if (matcher !== undefined) identity.matcher = matcher;
459
+
460
+ const canonical = canonicalJson(identity);
461
+ const hash = createHash("sha256").update(JSON.stringify(canonical)).digest("hex");
462
+ return `sha256:${hash}`;
463
+ }
464
+
465
+ function canonicalJson(value: unknown): unknown {
466
+ if (Array.isArray(value)) return value.map(canonicalJson);
467
+ if (value && typeof value === "object") {
468
+ return Object.fromEntries(
469
+ Object.entries(value as Record<string, unknown>)
470
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
471
+ .map(([key, val]) => [key, canonicalJson(val)]),
472
+ );
473
+ }
474
+ return value;
475
+ }
476
+
477
+ function tomlQuotedString(value: string): string {
478
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
358
479
  }
359
480
 
360
481
  // #1096 — write relay-owned settings into an ISOLATED Claude managed home's `settings.json`
@@ -1598,7 +1598,7 @@ export class AgentRunner {
1598
1598
  });
1599
1599
  this.sessionOutbox.enqueue({
1600
1600
  kind: "session-message-batch",
1601
- idempotencyKey: `session-batch:${this.agentId}:${++this.sessionBatchSeq}:${messages.length}`,
1601
+ idempotencyKey: `session-batch:${this.agentId}:${this.options.instanceId}:${this.options.startedAt}:${++this.sessionBatchSeq}:${messages.length}`,
1602
1602
  payload: { messages },
1603
1603
  });
1604
1604
  }
@@ -614,7 +614,10 @@ export const postAgentTerminalSession: Handler = async (req, params) => {
614
614
  resource: { agentId: agent.id, orchestratorId: orchestrator.id, policyName: metaPolicyName, spawnRequestId: metaSpawnRequestId, terminalAttach: true },
615
615
  });
616
616
  if (denied) return denied;
617
- if (agent.providerCapabilities?.terminal?.live?.read && metaTmuxSession) {
617
+ // Provider-owned attach surfaces are more specific than the runner's tmux pane.
618
+ // Codex runs its real interactive TUI as a guest attached to the app-server thread;
619
+ // the runner pane can be a stale launcher frame and is not the right input target.
620
+ if (agent.providerCapabilities?.terminal?.attach?.create !== true && agent.providerCapabilities?.terminal?.live?.read && metaTmuxSession) {
618
621
  return json({
619
622
  mode: "live",
620
623
  orchestratorId: orchestrator.id,
@@ -216,6 +216,9 @@ export const postHeartbeat: Handler = async (req, params) => {
216
216
  if (!session.ok) return error(session.error!, agentSessionStatus(session.error));
217
217
  // #416 — capture unavailability before heartbeat to detect the transition.
218
218
  const beforeHeartbeat = getAgent(params.id!);
219
+ if (beforeHeartbeat?.kind === "channel" && !beforeHeartbeat.ready) {
220
+ return error("channel agent not ready; re-register required", 409);
221
+ }
219
222
  const wasUnavailable = beforeHeartbeat ? isAgentUnavailable(beforeHeartbeat) : false;
220
223
  if (!heartbeat(params.id!, guard)) return error("agent not found", 404);
221
224
  if (wasUnavailable) {
@@ -38,7 +38,7 @@ import { errMessage } from "agent-relay-sdk";
38
38
  import { findSpawnCommandByRequestId } from "../commands-db";
39
39
  import { defaultIssueRepo } from "../config";
40
40
  import { emitTaskChanged } from "../sse";
41
- import type { CommandStatus, Task, TaskDetail, TaskEvent, TaskGateState, TaskListItem, TaskStatus, WorkspaceRecord } from "../types";
41
+ import type { CommandStatus, Task, TaskDeliverableEventMetadata, TaskDetail, TaskEvent, TaskGateState, TaskListItem, TaskStatus, WorkspaceRecord } from "../types";
42
42
  import { removeSubscriptionsForTask } from "../db/notification-subscriptions";
43
43
  import { emitRelayEvent } from "../events";
44
44
  import { routeNotification } from "../notification-router";
@@ -389,6 +389,30 @@ export function reconcileAutoScaffoldedTaskReaps(opts: ReconcileAutoScaffoldedTa
389
389
  return { dryRun, scanned: completed.length + canceled.length + skipped.length, completed, canceled, skipped };
390
390
  }
391
391
 
392
+ function latestDeliverableEvent(taskId: number): TaskEvent | undefined {
393
+ return [...listTaskEvents(taskId)].reverse().find((event) => event.type === "deliverable");
394
+ }
395
+
396
+ function restoreDeliveredDisposition(task: TaskDetail, event: TaskEvent): void {
397
+ const metadata = event.metadata as unknown as Partial<TaskDeliverableEventMetadata>;
398
+ if (metadata.disposition === "failed" && task.status !== "failed") {
399
+ setTaskEntityStatus(task.id, {
400
+ status: "failed",
401
+ ...(typeof metadata.reason === "string" && metadata.reason.trim() ? { note: metadata.reason } : {}),
402
+ actor: "task-lifecycle",
403
+ });
404
+ return;
405
+ }
406
+ if (metadata.disposition === "blocked" && task.status !== "blocked" && Array.isArray(metadata.blockedBy) && metadata.blockedBy.length > 0) {
407
+ setTaskEntityStatus(task.id, {
408
+ status: "blocked",
409
+ blockedBy: metadata.blockedBy,
410
+ ...(typeof metadata.reason === "string" && metadata.reason.trim() ? { note: metadata.reason } : {}),
411
+ actor: "task-lifecycle",
412
+ });
413
+ }
414
+ }
415
+
392
416
  // #889 — finalize bound tasks for a reaped agent whose doneWhen is "agent-exit" / "reaped".
393
417
  // Called by the transient reaper after requesting shutdown so ephemeral run-and-exit workers
394
418
  // don't leave their auto-scaffolded Task stuck in_progress forever.
@@ -407,6 +431,13 @@ export function finalizeEphemeralTasksOnReap(agentId: string, opts: { spawnReque
407
431
  if (!isAgentExitDoneWhen(task.doneWhen)) continue;
408
432
  if (opts.producedWork === false) {
409
433
  try {
434
+ const deliverable = latestDeliverableEvent(taskId);
435
+ if (deliverable) {
436
+ if (!task.reapedAt) markTaskAgentReaped(taskId, { agentId, reason: opts.reason ?? "agent-exit" });
437
+ restoreDeliveredDisposition(task, deliverable);
438
+ finalized.push(taskId);
439
+ continue;
440
+ }
410
441
  if (!task.reapedAt) markTaskAgentReaped(taskId, { agentId, reason: opts.reason ?? "agent-exit" });
411
442
  setTaskEntityStatus(taskId, { status: "canceled", note: "worker reaped without landing or producing work; task canceled", actor: "task-lifecycle" });
412
443
  notifyEmptyTransientReap(agentId, task, opts);
@@ -28,7 +28,7 @@ export { LAND_STRATEGIES, DEFAULT_MERGE_STRATEGY } from "./workspace-merge";
28
28
  import { claimMetadataPatch, workspaceActiveClaim } from "./workspace-claim";
29
29
  import { DIRTY_WORKTREE_LAND_SKIP_REASON, READY_TO_LAND_STATUSES, TERMINAL_WORKSPACE_STATUSES, worktreeReapable } from "./workspace-phase";
30
30
  import { CLEAR_MERGE_NO_PROGRESS, scheduleRepoMerge } from "./workspace-auto-merge";
31
- import { fetchHostMergePreview } from "./workspace-probe";
31
+ import { fetchHostMergePreview, refreshWorkspaceGitState } from "./workspace-probe";
32
32
  import type { Command, WorkspaceAutoMergePolicy, WorkspaceMergeStrategy, WorkspaceRecord, WorkspaceStatus } from "./types";
33
33
 
34
34
  // Single source of truth for the action verb set. The route's `optionalEnum` and
@@ -251,7 +251,9 @@ export async function applyWorkspaceAction(workspace: WorkspaceRecord, input: Ap
251
251
  if (!nextStatus) return { ok: false, httpStatus: 400, error: `unsupported action: ${action}` };
252
252
 
253
253
  if (READY_TO_LAND_STATUSES.has(nextStatus)) {
254
- const dirtyCount = metadataNumber(workspace.metadata, "gitDirtyCount") ?? 0;
254
+ await refreshWorkspaceGitState(workspace);
255
+ const freshWorkspace = getWorkspace(workspace.id) ?? workspace;
256
+ const dirtyCount = metadataNumber(freshWorkspace.metadata, "gitDirtyCount") ?? 0;
255
257
  if (dirtyCount > 0) {
256
258
  return {
257
259
  ok: false,