agent-relay-server 0.63.0 → 0.63.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 (80) hide show
  1. package/docs/openapi.json +1 -1
  2. package/package.json +4 -2
  3. package/public/assets/{MessageBubble-Cl6ufPHw.js → MessageBubble-D130169C.js} +2 -2
  4. package/public/assets/{MessageBubble-Cl6ufPHw.js.map → MessageBubble-D130169C.js.map} +1 -1
  5. package/public/assets/{PlanGraphPanel-BnfjmkQ8.js → PlanGraphPanel-CbwbZmKY.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-BnfjmkQ8.js.map → PlanGraphPanel-CbwbZmKY.js.map} +1 -1
  7. package/public/assets/{activity-aFxvGLtd.js → activity-SJjW9fgO.js} +2 -2
  8. package/public/assets/{activity-aFxvGLtd.js.map → activity-SJjW9fgO.js.map} +1 -1
  9. package/public/assets/{agents-DW5djToQ.js → agents-GPly8tGL.js} +2 -2
  10. package/public/assets/{agents-DW5djToQ.js.map → agents-GPly8tGL.js.map} +1 -1
  11. package/public/assets/{automation-Do4OpauI.js → automation-BFO5jLy8.js} +2 -2
  12. package/public/assets/{automation-Do4OpauI.js.map → automation-BFO5jLy8.js.map} +1 -1
  13. package/public/assets/chat-hP_8I1aE.js +2 -0
  14. package/public/assets/{chat-CvaQ2czL.js.map → chat-hP_8I1aE.js.map} +1 -1
  15. package/public/assets/{formatted-body-impl-0Lf8e1Ic.js → formatted-body-impl-DaBd6Vrd.js} +2 -2
  16. package/public/assets/{formatted-body-impl-0Lf8e1Ic.js.map → formatted-body-impl-DaBd6Vrd.js.map} +1 -1
  17. package/public/assets/{index-C3su8ezq.js → index-CmM_Ozy7.js} +5 -5
  18. package/public/assets/{index-C3su8ezq.js.map → index-CmM_Ozy7.js.map} +1 -1
  19. package/public/assets/{insights-64uExH1E.js → insights-4Z7kZ7NB.js} +2 -2
  20. package/public/assets/{insights-64uExH1E.js.map → insights-4Z7kZ7NB.js.map} +1 -1
  21. package/public/assets/{maintenance-EVBpKH07.js → maintenance-17uq77zC.js} +2 -2
  22. package/public/assets/{maintenance-EVBpKH07.js.map → maintenance-17uq77zC.js.map} +1 -1
  23. package/public/assets/{managed-agents-BLZPxGuw.js → managed-agents-CT_1x4C3.js} +2 -2
  24. package/public/assets/{managed-agents-BLZPxGuw.js.map → managed-agents-CT_1x4C3.js.map} +1 -1
  25. package/public/assets/{markdown-preview-impl-Bg6rCWYN.js → markdown-preview-impl-DzcW3gVL.js} +2 -2
  26. package/public/assets/{markdown-preview-impl-Bg6rCWYN.js.map → markdown-preview-impl-DzcW3gVL.js.map} +1 -1
  27. package/public/assets/{memory-Bjr8bgjj.js → memory-B-zfQSp3.js} +2 -2
  28. package/public/assets/{memory-Bjr8bgjj.js.map → memory-B-zfQSp3.js.map} +1 -1
  29. package/public/assets/{messages-CRYw1F4z.js → messages-Ckt9aNgZ.js} +2 -2
  30. package/public/assets/{messages-CRYw1F4z.js.map → messages-Ckt9aNgZ.js.map} +1 -1
  31. package/public/assets/{orchestrators-D-co1vdp.js → orchestrators-DVpGfTtB.js} +2 -2
  32. package/public/assets/{orchestrators-D-co1vdp.js.map → orchestrators-DVpGfTtB.js.map} +1 -1
  33. package/public/assets/{overview-Ddy5czri.js → overview-C8FoAUmj.js} +2 -2
  34. package/public/assets/{overview-Ddy5czri.js.map → overview-C8FoAUmj.js.map} +1 -1
  35. package/public/assets/{pairs-CFVM8uHL.js → pairs-WEM0MEmH.js} +2 -2
  36. package/public/assets/{pairs-CFVM8uHL.js.map → pairs-WEM0MEmH.js.map} +1 -1
  37. package/public/assets/{security-Df4_76bh.js → security-FzjxX7I2.js} +2 -2
  38. package/public/assets/{security-Df4_76bh.js.map → security-FzjxX7I2.js.map} +1 -1
  39. package/public/assets/{select-Cq3BBnL9.js → select-BPpSWAx4.js} +2 -2
  40. package/public/assets/{select-Cq3BBnL9.js.map → select-BPpSWAx4.js.map} +1 -1
  41. package/public/assets/{settings-DOLEYXZL.js → settings-Bl8wcAYn.js} +2 -2
  42. package/public/assets/{settings-DOLEYXZL.js.map → settings-Bl8wcAYn.js.map} +1 -1
  43. package/public/assets/{tasks-BcsmPi9_.js → tasks-Dk6TtgpH.js} +2 -2
  44. package/public/assets/{tasks-BcsmPi9_.js.map → tasks-Dk6TtgpH.js.map} +1 -1
  45. package/public/assets/{teams-BQrV3eKI.js → teams-DRJjIYRl.js} +2 -2
  46. package/public/assets/{teams-BQrV3eKI.js.map → teams-DRJjIYRl.js.map} +1 -1
  47. package/public/assets/{terminal-viewer-impl-Dle04qpH.js → terminal-viewer-impl-YWCrgF9G.js} +2 -2
  48. package/public/assets/{terminal-viewer-impl-Dle04qpH.js.map → terminal-viewer-impl-YWCrgF9G.js.map} +1 -1
  49. package/public/assets/{work-queue-BLhTLg4o.js → work-queue-DYtnYiKs.js} +2 -2
  50. package/public/assets/{work-queue-BLhTLg4o.js.map → work-queue-DYtnYiKs.js.map} +1 -1
  51. package/public/index.html +1 -1
  52. package/runner/src/adapter.ts +2 -1
  53. package/runner/src/config.ts +143 -5
  54. package/src/artifact-storage.ts +3 -5
  55. package/src/bus.ts +3 -4
  56. package/src/cli/_shared.ts +5 -4
  57. package/src/cli/agent-detect.ts +6 -4
  58. package/src/cli/introspect.ts +3 -2
  59. package/src/cli/token.ts +2 -1
  60. package/src/cli/workspace.ts +2 -1
  61. package/src/config.ts +176 -1
  62. package/src/connectors.ts +3 -3
  63. package/src/db/connection.ts +2 -1
  64. package/src/db/continuation-archives.ts +3 -3
  65. package/src/index.ts +6 -5
  66. package/src/maintenance/constants.ts +39 -22
  67. package/src/provider-config-store.ts +2 -1
  68. package/src/provider-state.ts +1 -3
  69. package/src/routes/agent-sessions.ts +5 -4
  70. package/src/routes/orchestrator-proxy.ts +5 -4
  71. package/src/routes/workspaces.ts +5 -4
  72. package/src/security.ts +13 -7
  73. package/src/services/issue-lifecycle.ts +1 -4
  74. package/src/services/plan-graph.ts +1 -4
  75. package/src/upgrade.ts +7 -9
  76. package/src/workspace-auto-merge.ts +4 -5
  77. package/src/workspace-claim.ts +1 -2
  78. package/src/workspace-orphans.ts +2 -7
  79. package/src/workspace-probe.ts +2 -1
  80. package/public/assets/chat-CvaQ2czL.js +0 -2
@@ -1,2 +1,2 @@
1
- import{Ln as e,ct as t,hn as n,i as r,in as i}from"./lucide-react-DWYjU-AF.js";import{c as a,t as o}from"./store-C1E8jmNE.js";import{U as s,d as c,ut as l}from"./display-B06I5sqa.js";import{t as u}from"./badge-CZhIdlTJ.js";import{t as d}from"./button-2taQeNHW.js";import{G as f,s as p,z as m}from"./index-C3su8ezq.js";import{r as h,t as g}from"./card-DzIsAI41.js";var _=e(),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),l=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&&l(e.message.id)}function T(e){g(`Cancel Item`,`Cancel "${e.title||(e.sourceType===`task`?`Task #${e.id}`:`Message #${e.id}`)}"? This cannot be undone.`,()=>{e.sourceType===`task`&&e.task?y(e.task,`canceled`):e.sourceType===`message`&&e.message&&v(e.message.id)})}function E(e){e.task&&d(e.task)}return(0,_.jsxs)(`div`,{className:`space-y-4`,children:[(0,_.jsxs)(`div`,{className:`flex items-center justify-between flex-wrap gap-2`,children:[(0,_.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,_.jsx)(t,{className:`w-5 h-5 text-muted-foreground`}),(0,_.jsx)(`h2`,{className:`text-lg font-semibold`,children:`Work Queue`}),C>0&&(0,_.jsxs)(u,{className:`bg-orange-500/20 text-orange-400 border-orange-500/30 border`,children:[C,` claimable`]})]}),(0,_.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,_.jsx)(`label`,{className:`text-xs text-muted-foreground shrink-0`,children:`Claim as`}),(0,_.jsxs)(`select`,{className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm`,value:r.from,onChange:e=>i({compose:{...r,from:e.target.value}}),children:[(0,_.jsx)(`option`,{value:``,children:`Select agent...`}),x.map(e=>(0,_.jsx)(`option`,{value:e.id,children:c(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]?c(n[e]):e.slice(-10),onClaim:()=>w(t),onCancel:()=>T(t),onEvents:t.task?()=>E(t):void 0},t.id))]})})]})}function b({item:e,now:t,agentName:a,onClaim:o,onCancel:c,onEvents:f}){let p=l[e.severity]||l.info,m=v[e.status]||`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`;return(0,_.jsx)(g,{className:e.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:e.title}),(0,_.jsx)(u,{variant:`outline`,className:`text-xs px-1.5 py-0 border ${p}`,children:e.severity}),(0,_.jsx)(u,{variant:`outline`,className:`text-xs px-1.5 py-0 border ${m}`,children:e.status}),e.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`})]}),e.body&&(0,_.jsx)(`p`,{className:`text-xs text-muted-foreground line-clamp-2 mb-2`,children:e.body}),(0,_.jsxs)(`div`,{className:`flex items-center gap-3 text-xs text-muted-foreground flex-wrap`,children:[e.owner&&(0,_.jsxs)(`span`,{title:`Owner`,children:[(0,_.jsx)(i,{className:`w-3 h-3 inline mr-1`}),a(e.owner)]}),e.source&&(0,_.jsx)(`span`,{className:`font-mono`,children:e.source}),(0,_.jsxs)(`span`,{title:String(e.updatedAt),children:[(0,_.jsx)(n,{className:`w-3 h-3 inline mr-1`}),s(t,e.updatedAt)]})]})]}),(0,_.jsxs)(`div`,{className:`flex flex-col gap-1 shrink-0`,children:[e.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:c,children:[(0,_.jsx)(r,{className:`w-3.5 h-3.5 mr-1`}),` Cancel`]})]})]})})})}export{y as WorkQueueView};
2
- //# sourceMappingURL=work-queue-BLhTLg4o.js.map
1
+ import{Ln as e,ct as t,hn as n,i as r,in as i}from"./lucide-react-DWYjU-AF.js";import{c as a,t as o}from"./store-C1E8jmNE.js";import{U as s,d as c,ut as l}from"./display-B06I5sqa.js";import{t as u}from"./badge-CZhIdlTJ.js";import{t as d}from"./button-2taQeNHW.js";import{G as f,s as p,z as m}from"./index-CmM_Ozy7.js";import{r as h,t as g}from"./card-DzIsAI41.js";var _=e(),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),l=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&&l(e.message.id)}function T(e){g(`Cancel Item`,`Cancel "${e.title||(e.sourceType===`task`?`Task #${e.id}`:`Message #${e.id}`)}"? This cannot be undone.`,()=>{e.sourceType===`task`&&e.task?y(e.task,`canceled`):e.sourceType===`message`&&e.message&&v(e.message.id)})}function E(e){e.task&&d(e.task)}return(0,_.jsxs)(`div`,{className:`space-y-4`,children:[(0,_.jsxs)(`div`,{className:`flex items-center justify-between flex-wrap gap-2`,children:[(0,_.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,_.jsx)(t,{className:`w-5 h-5 text-muted-foreground`}),(0,_.jsx)(`h2`,{className:`text-lg font-semibold`,children:`Work Queue`}),C>0&&(0,_.jsxs)(u,{className:`bg-orange-500/20 text-orange-400 border-orange-500/30 border`,children:[C,` claimable`]})]}),(0,_.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,_.jsx)(`label`,{className:`text-xs text-muted-foreground shrink-0`,children:`Claim as`}),(0,_.jsxs)(`select`,{className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm`,value:r.from,onChange:e=>i({compose:{...r,from:e.target.value}}),children:[(0,_.jsx)(`option`,{value:``,children:`Select agent...`}),x.map(e=>(0,_.jsx)(`option`,{value:e.id,children:c(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]?c(n[e]):e.slice(-10),onClaim:()=>w(t),onCancel:()=>T(t),onEvents:t.task?()=>E(t):void 0},t.id))]})})]})}function b({item:e,now:t,agentName:a,onClaim:o,onCancel:c,onEvents:f}){let p=l[e.severity]||l.info,m=v[e.status]||`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`;return(0,_.jsx)(g,{className:e.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:e.title}),(0,_.jsx)(u,{variant:`outline`,className:`text-xs px-1.5 py-0 border ${p}`,children:e.severity}),(0,_.jsx)(u,{variant:`outline`,className:`text-xs px-1.5 py-0 border ${m}`,children:e.status}),e.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`})]}),e.body&&(0,_.jsx)(`p`,{className:`text-xs text-muted-foreground line-clamp-2 mb-2`,children:e.body}),(0,_.jsxs)(`div`,{className:`flex items-center gap-3 text-xs text-muted-foreground flex-wrap`,children:[e.owner&&(0,_.jsxs)(`span`,{title:`Owner`,children:[(0,_.jsx)(i,{className:`w-3 h-3 inline mr-1`}),a(e.owner)]}),e.source&&(0,_.jsx)(`span`,{className:`font-mono`,children:e.source}),(0,_.jsxs)(`span`,{title:String(e.updatedAt),children:[(0,_.jsx)(n,{className:`w-3 h-3 inline mr-1`}),s(t,e.updatedAt)]})]})]}),(0,_.jsxs)(`div`,{className:`flex flex-col gap-1 shrink-0`,children:[e.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:c,children:[(0,_.jsx)(r,{className:`w-3.5 h-3.5 mr-1`}),` Cancel`]})]})]})})})}export{y as WorkQueueView};
2
+ //# sourceMappingURL=work-queue-DYtnYiKs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"work-queue-BLhTLg4o.js","names":[],"sources":["../../dashboard/src/components/views/work-queue.tsx"],"sourcesContent":["import { useRelayStore, useNow } from '@/store'\nimport { useWorkQueueItems, useComposeAgents } from '@/hooks/use-selectors'\nimport { Card, CardContent } from '@/components/ui/card'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { ScrollArea } from '@/components/ui/scroll-area'\nimport { ListChecks, CheckCircle2, Calendar, X } from 'lucide-react'\nimport { displayName, timeAgo } from '@/lib/display'\nimport { SEVERITY_COLORS } from '@/lib/constants'\nimport type { WorkQueueItem } from '@/types'\n\nconst STATUS_COLORS: Record<string, string> = {\n open: 'bg-blue-500/10 text-blue-400 border-blue-500/20',\n blocked: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n claimed: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n in_progress: 'bg-purple-500/10 text-purple-400 border-purple-500/20',\n}\n\nexport function WorkQueueView() {\n const now = useNow()\n const agentsById = useRelayStore((s) => s.agentsById)\n const compose = useRelayStore((s) => s.compose)\n const set = useRelayStore((s) => s.set)\n const doClaimTask = useRelayStore((s) => s.doClaimTask)\n const doClaim = useRelayStore((s) => s.doClaim)\n const openTaskEvents = useRelayStore((s) => s.openTaskEvents)\n const showError = useRelayStore((s) => s.showError)\n const openConfirm = useRelayStore((s) => s.openConfirm)\n const doDeleteMessage = useRelayStore((s) => s.doDeleteMessage)\n const doUpdateTaskStatus = useRelayStore((s) => s.doUpdateTaskStatus)\n\n const composeAgents = useComposeAgents()\n const items = useWorkQueueItems()\n\n const claimableCount = items.filter((i) => i.claimable).length\n\n function handleClaim(item: WorkQueueItem) {\n if (!compose.from) { showError('Validation', 'Select a \"Claim as\" agent first.'); return }\n if (item.sourceType === 'task' && item.task) doClaimTask(item.task.id)\n else if (item.sourceType === 'message' && item.message) doClaim(item.message.id)\n }\n\n function handleCancel(item: WorkQueueItem) {\n const label = item.title || (item.sourceType === 'task' ? `Task #${item.id}` : `Message #${item.id}`)\n openConfirm('Cancel Item', `Cancel \"${label}\"? This cannot be undone.`, () => {\n if (item.sourceType === 'task' && item.task) doUpdateTaskStatus(item.task, 'canceled')\n else if (item.sourceType === 'message' && item.message) doDeleteMessage(item.message.id)\n })\n }\n\n function handleEvents(item: WorkQueueItem) {\n if (item.task) openTaskEvents(item.task)\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n <ListChecks className=\"w-5 h-5 text-muted-foreground\" />\n <h2 className=\"text-lg font-semibold\">Work Queue</h2>\n {claimableCount > 0 && (\n <Badge className=\"bg-orange-500/20 text-orange-400 border-orange-500/30 border\">\n {claimableCount} claimable\n </Badge>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n <label className=\"text-xs text-muted-foreground shrink-0\">Claim as</label>\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={compose.from}\n onChange={(e) => set({ compose: { ...compose, from: e.target.value } })}\n >\n <option value=\"\">Select agent...</option>\n {composeAgents.map((a) => <option key={a.id} value={a.id}>{displayName(a)}</option>)}\n </select>\n </div>\n </div>\n\n <ScrollArea className=\"h-[calc(100dvh-11rem)]\">\n <div className=\"space-y-2 pr-2\">\n {items.length === 0 && (\n <div className=\"text-center text-muted-foreground py-16 text-sm\">Work queue is empty</div>\n )}\n {items.map((item) => (\n <WorkQueueCard\n key={item.id}\n item={item}\n now={now}\n agentName={(id) => agentsById[id] ? displayName(agentsById[id]) : id.slice(-10)}\n onClaim={() => handleClaim(item)}\n onCancel={() => handleCancel(item)}\n onEvents={item.task ? () => handleEvents(item) : undefined}\n />\n ))}\n </div>\n </ScrollArea>\n </div>\n )\n}\n\nfunction WorkQueueCard({\n item, now, agentName, onClaim, onCancel, onEvents,\n}: {\n item: WorkQueueItem\n now: number\n agentName: (id: string) => string\n onClaim: () => void\n onCancel: () => void\n onEvents?: () => void\n}) {\n const severityClass = SEVERITY_COLORS[item.severity] || SEVERITY_COLORS.info\n const statusClass = STATUS_COLORS[item.status] || 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'\n\n return (\n <Card className={item.claimable ? 'ring-1 ring-orange-500/30' : ''}>\n <CardContent className=\"p-3\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap mb-1\">\n <span className=\"font-medium text-sm truncate\">{item.title}</span>\n <Badge variant=\"outline\" className={`text-xs px-1.5 py-0 border ${severityClass}`}>{item.severity}</Badge>\n <Badge variant=\"outline\" className={`text-xs px-1.5 py-0 border ${statusClass}`}>{item.status}</Badge>\n {item.sourceType === 'message' && (\n <Badge variant=\"outline\" className=\"text-xs px-1.5 py-0 border border-zinc-500/20 text-zinc-400\">msg</Badge>\n )}\n </div>\n {item.body && (\n <p className=\"text-xs text-muted-foreground line-clamp-2 mb-2\">{item.body}</p>\n )}\n <div className=\"flex items-center gap-3 text-xs text-muted-foreground flex-wrap\">\n {item.owner && (\n <span title=\"Owner\">\n <CheckCircle2 className=\"w-3 h-3 inline mr-1\" />{agentName(item.owner)}\n </span>\n )}\n {item.source && (\n <span className=\"font-mono\">{item.source}</span>\n )}\n <span title={String(item.updatedAt)}>\n <Calendar className=\"w-3 h-3 inline mr-1\" />{timeAgo(now, item.updatedAt)}\n </span>\n </div>\n </div>\n <div className=\"flex flex-col gap-1 shrink-0\">\n {item.claimable && (\n <Button size=\"sm\" className=\"h-7 text-xs bg-orange-600 hover:bg-orange-700 text-white\" onClick={onClaim}>\n <CheckCircle2 className=\"w-3.5 h-3.5 mr-1\" /> Claim\n </Button>\n )}\n {onEvents && (\n <Button size=\"sm\" variant=\"outline\" className=\"h-7 text-xs\" onClick={onEvents}>\n Events\n </Button>\n )}\n <Button size=\"sm\" variant=\"outline\" className=\"h-7 text-xs text-red-400 hover:text-red-300 hover:bg-red-500/10 border-red-500/20\" onClick={onCancel}>\n <X className=\"w-3.5 h-3.5 mr-1\" /> Cancel\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"work-queue-DYtnYiKs.js","names":[],"sources":["../../dashboard/src/components/views/work-queue.tsx"],"sourcesContent":["import { useRelayStore, useNow } from '@/store'\nimport { useWorkQueueItems, useComposeAgents } from '@/hooks/use-selectors'\nimport { Card, CardContent } from '@/components/ui/card'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { ScrollArea } from '@/components/ui/scroll-area'\nimport { ListChecks, CheckCircle2, Calendar, X } from 'lucide-react'\nimport { displayName, timeAgo } from '@/lib/display'\nimport { SEVERITY_COLORS } from '@/lib/constants'\nimport type { WorkQueueItem } from '@/types'\n\nconst STATUS_COLORS: Record<string, string> = {\n open: 'bg-blue-500/10 text-blue-400 border-blue-500/20',\n blocked: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n claimed: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n in_progress: 'bg-purple-500/10 text-purple-400 border-purple-500/20',\n}\n\nexport function WorkQueueView() {\n const now = useNow()\n const agentsById = useRelayStore((s) => s.agentsById)\n const compose = useRelayStore((s) => s.compose)\n const set = useRelayStore((s) => s.set)\n const doClaimTask = useRelayStore((s) => s.doClaimTask)\n const doClaim = useRelayStore((s) => s.doClaim)\n const openTaskEvents = useRelayStore((s) => s.openTaskEvents)\n const showError = useRelayStore((s) => s.showError)\n const openConfirm = useRelayStore((s) => s.openConfirm)\n const doDeleteMessage = useRelayStore((s) => s.doDeleteMessage)\n const doUpdateTaskStatus = useRelayStore((s) => s.doUpdateTaskStatus)\n\n const composeAgents = useComposeAgents()\n const items = useWorkQueueItems()\n\n const claimableCount = items.filter((i) => i.claimable).length\n\n function handleClaim(item: WorkQueueItem) {\n if (!compose.from) { showError('Validation', 'Select a \"Claim as\" agent first.'); return }\n if (item.sourceType === 'task' && item.task) doClaimTask(item.task.id)\n else if (item.sourceType === 'message' && item.message) doClaim(item.message.id)\n }\n\n function handleCancel(item: WorkQueueItem) {\n const label = item.title || (item.sourceType === 'task' ? `Task #${item.id}` : `Message #${item.id}`)\n openConfirm('Cancel Item', `Cancel \"${label}\"? This cannot be undone.`, () => {\n if (item.sourceType === 'task' && item.task) doUpdateTaskStatus(item.task, 'canceled')\n else if (item.sourceType === 'message' && item.message) doDeleteMessage(item.message.id)\n })\n }\n\n function handleEvents(item: WorkQueueItem) {\n if (item.task) openTaskEvents(item.task)\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n <ListChecks className=\"w-5 h-5 text-muted-foreground\" />\n <h2 className=\"text-lg font-semibold\">Work Queue</h2>\n {claimableCount > 0 && (\n <Badge className=\"bg-orange-500/20 text-orange-400 border-orange-500/30 border\">\n {claimableCount} claimable\n </Badge>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n <label className=\"text-xs text-muted-foreground shrink-0\">Claim as</label>\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={compose.from}\n onChange={(e) => set({ compose: { ...compose, from: e.target.value } })}\n >\n <option value=\"\">Select agent...</option>\n {composeAgents.map((a) => <option key={a.id} value={a.id}>{displayName(a)}</option>)}\n </select>\n </div>\n </div>\n\n <ScrollArea className=\"h-[calc(100dvh-11rem)]\">\n <div className=\"space-y-2 pr-2\">\n {items.length === 0 && (\n <div className=\"text-center text-muted-foreground py-16 text-sm\">Work queue is empty</div>\n )}\n {items.map((item) => (\n <WorkQueueCard\n key={item.id}\n item={item}\n now={now}\n agentName={(id) => agentsById[id] ? displayName(agentsById[id]) : id.slice(-10)}\n onClaim={() => handleClaim(item)}\n onCancel={() => handleCancel(item)}\n onEvents={item.task ? () => handleEvents(item) : undefined}\n />\n ))}\n </div>\n </ScrollArea>\n </div>\n )\n}\n\nfunction WorkQueueCard({\n item, now, agentName, onClaim, onCancel, onEvents,\n}: {\n item: WorkQueueItem\n now: number\n agentName: (id: string) => string\n onClaim: () => void\n onCancel: () => void\n onEvents?: () => void\n}) {\n const severityClass = SEVERITY_COLORS[item.severity] || SEVERITY_COLORS.info\n const statusClass = STATUS_COLORS[item.status] || 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'\n\n return (\n <Card className={item.claimable ? 'ring-1 ring-orange-500/30' : ''}>\n <CardContent className=\"p-3\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap mb-1\">\n <span className=\"font-medium text-sm truncate\">{item.title}</span>\n <Badge variant=\"outline\" className={`text-xs px-1.5 py-0 border ${severityClass}`}>{item.severity}</Badge>\n <Badge variant=\"outline\" className={`text-xs px-1.5 py-0 border ${statusClass}`}>{item.status}</Badge>\n {item.sourceType === 'message' && (\n <Badge variant=\"outline\" className=\"text-xs px-1.5 py-0 border border-zinc-500/20 text-zinc-400\">msg</Badge>\n )}\n </div>\n {item.body && (\n <p className=\"text-xs text-muted-foreground line-clamp-2 mb-2\">{item.body}</p>\n )}\n <div className=\"flex items-center gap-3 text-xs text-muted-foreground flex-wrap\">\n {item.owner && (\n <span title=\"Owner\">\n <CheckCircle2 className=\"w-3 h-3 inline mr-1\" />{agentName(item.owner)}\n </span>\n )}\n {item.source && (\n <span className=\"font-mono\">{item.source}</span>\n )}\n <span title={String(item.updatedAt)}>\n <Calendar className=\"w-3 h-3 inline mr-1\" />{timeAgo(now, item.updatedAt)}\n </span>\n </div>\n </div>\n <div className=\"flex flex-col gap-1 shrink-0\">\n {item.claimable && (\n <Button size=\"sm\" className=\"h-7 text-xs bg-orange-600 hover:bg-orange-700 text-white\" onClick={onClaim}>\n <CheckCircle2 className=\"w-3.5 h-3.5 mr-1\" /> Claim\n </Button>\n )}\n {onEvents && (\n <Button size=\"sm\" variant=\"outline\" className=\"h-7 text-xs\" onClick={onEvents}>\n Events\n </Button>\n )}\n <Button size=\"sm\" variant=\"outline\" className=\"h-7 text-xs text-red-400 hover:text-red-300 hover:bg-red-500/10 border-red-500/20\" onClick={onCancel}>\n <X className=\"w-3.5 h-3.5 mr-1\" /> Cancel\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n )\n}\n"],"mappings":"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"}
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-C3su8ezq.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-CmM_Ozy7.js"></script>
16
16
  <link rel="modulepreload" crossorigin href="./assets/chunk-CilyBKbf.js">
17
17
  <link rel="modulepreload" crossorigin href="./assets/preload-helper-CP_edrrL.js">
18
18
  <link rel="modulepreload" crossorigin href="./assets/lucide-react-DWYjU-AF.js">
@@ -1,6 +1,7 @@
1
1
  import type { AgentProfile, Message } from "agent-relay-sdk";
2
2
  import { isRecord } from "agent-relay-sdk";
3
3
  import type { SessionEvent } from "./session-insights";
4
+ import { messageBodyMaxCharsFromEnv } from "./config";
4
5
 
5
6
  export type SemanticStatus = "idle" | "busy" | "offline" | "error";
6
7
  type ProviderWorkKind = "provider-turn" | "subagent";
@@ -202,7 +203,7 @@ export const DEFAULT_PROVIDER_MESSAGE_BODY_MAX_CHARS = 24_000;
202
203
  // Resolve the delivered-body cap. Deployment-dependent (the right ceiling tracks the host's
203
204
  // context budget), so it's overridable via env — set on the orchestrator/host that spawns runners.
204
205
  export function providerMessageBodyMaxChars(): number {
205
- const raw = process.env.AGENT_RELAY_MESSAGE_BODY_MAX_CHARS;
206
+ const raw = messageBodyMaxCharsFromEnv();
206
207
  if (raw !== undefined) {
207
208
  const parsed = Number.parseInt(raw.trim(), 10);
208
209
  if (Number.isFinite(parsed) && parsed > 0) return parsed;
@@ -1,11 +1,26 @@
1
1
  import { execFileSync } from "node:child_process";
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { homedir, hostname } from "node:os";
4
- import { join, resolve } from "node:path";
4
+ import { dirname, join, resolve } from "node:path";
5
5
  import { DEFAULT_RELAY_URL, RELAY_TOKEN_HEADER, errMessage, stringValue } from "agent-relay-sdk";
6
6
  import type { SettingEntry, WorkspaceMetadata } from "agent-relay-sdk";
7
7
  import { sanitizeFsName } from "agent-relay-sdk/fs-name";
8
- import type { ProviderConfig } from "./adapter";
8
+
9
+ interface ProviderConfig {
10
+ command: string;
11
+ defaultArgs: string[];
12
+ env: Record<string, string>;
13
+ pluginDirs: string[];
14
+ defaultCapabilities: string[];
15
+ defaultApprovalMode: string;
16
+ defaultTags: string[];
17
+ chatCaptureMode: "final" | "full";
18
+ reasoningCapture?: boolean;
19
+ headless: {
20
+ tmuxPrefix: string;
21
+ shutdownTimeoutMs: number;
22
+ };
23
+ }
9
24
 
10
25
  interface GlobalRunnerConfig {
11
26
  relayUrl: string;
@@ -17,7 +32,7 @@ interface LoadedProviderConfig extends ProviderConfig {
17
32
  path: string;
18
33
  }
19
34
 
20
- function agentRelayHome(): string {
35
+ export function agentRelayHome(): string {
21
36
  return process.env.AGENT_RELAY_HOME || join(homedir(), ".agent-relay");
22
37
  }
23
38
 
@@ -48,12 +63,135 @@ export function loadGlobalConfig(home = agentRelayHome()): GlobalRunnerConfig {
48
63
  const path = join(home, "config.json");
49
64
  const parsed = readJson(path);
50
65
  return {
51
- relayUrl: stringValue(parsed.relayUrl) ?? process.env.AGENT_RELAY_URL ?? DEFAULT_RELAY_URL,
52
- token: stringValue(parsed.token) ?? process.env.AGENT_RELAY_TOKEN,
66
+ relayUrl: stringValue(parsed.relayUrl) ?? relayUrlFromEnv() ?? DEFAULT_RELAY_URL,
67
+ token: stringValue(parsed.token) ?? relayTokenFromEnv(),
53
68
  defaultCwd: stringValue(parsed.defaultCwd) ?? process.cwd(),
54
69
  };
55
70
  }
56
71
 
72
+ function relayUrlFromEnv(): string | undefined {
73
+ return process.env.AGENT_RELAY_URL;
74
+ }
75
+
76
+ export function relayTokenFromEnv(): string | undefined {
77
+ return process.env.AGENT_RELAY_TOKEN;
78
+ }
79
+
80
+ export function runtimeTokenProfileFromEnv(): string | undefined {
81
+ return process.env.AGENT_RELAY_TOKEN_PROFILE;
82
+ }
83
+
84
+ export function runtimeTokenJtiFromEnv(): string | undefined {
85
+ return process.env.AGENT_RELAY_TOKEN_JTI;
86
+ }
87
+
88
+ export function runtimeTokenExpiresAtFromEnv(): string | undefined {
89
+ return process.env.AGENT_RELAY_TOKEN_EXPIRES_AT;
90
+ }
91
+
92
+ export function agentProfileNameFromEnv(): string | undefined {
93
+ return process.env.AGENT_RELAY_AGENT_PROFILE;
94
+ }
95
+
96
+ export function agentProfileJsonFromEnv(): string | undefined {
97
+ return process.env.AGENT_RELAY_AGENT_PROFILE_JSON;
98
+ }
99
+
100
+ export function workspaceJsonFromEnv(): string | undefined {
101
+ return process.env.AGENT_RELAY_WORKSPACE_JSON;
102
+ }
103
+
104
+ export function tmuxSessionFromEnv(): string | undefined {
105
+ return process.env.AGENT_RELAY_TMUX_SESSION;
106
+ }
107
+
108
+ export function policyNameFromEnv(): string | undefined {
109
+ return process.env.AGENT_RELAY_POLICY;
110
+ }
111
+
112
+ export function spawnRequestIdFromEnv(): string | undefined {
113
+ return process.env.AGENT_RELAY_SPAWN_REQUEST_ID;
114
+ }
115
+
116
+ export function automationIdFromEnv(): string | undefined {
117
+ return process.env.AGENT_RELAY_AUTOMATION_ID;
118
+ }
119
+
120
+ export function automationRunIdFromEnv(): string | undefined {
121
+ return process.env.AGENT_RELAY_AUTOMATION_RUN_ID;
122
+ }
123
+
124
+ export function lifecycleFromEnv(): string | undefined {
125
+ return process.env.AGENT_RELAY_LIFECYCLE;
126
+ }
127
+
128
+ export function sessionDebugEnabled(): boolean {
129
+ return process.env.AGENT_RELAY_SESSION_DEBUG === "1";
130
+ }
131
+
132
+ export function logLevelFromEnv(): string | undefined {
133
+ return process.env.AGENT_RELAY_LOG_LEVEL;
134
+ }
135
+
136
+ export function mcpProxyEnabledFromEnv(): boolean {
137
+ return !["0", "false", "off"].includes((process.env.AGENT_RELAY_MCP_PROXY ?? "").trim().toLowerCase());
138
+ }
139
+
140
+ export function runnerInfoFileFromEnv(): string | undefined {
141
+ return process.env.AGENT_RELAY_RUNNER_INFO_FILE;
142
+ }
143
+
144
+ export function runnerOutboxDirFromEnv(): string | undefined {
145
+ return process.env.AGENT_RELAY_RUNNER_OUTBOX_DIR;
146
+ }
147
+
148
+ export function runnerOutboxDirWithInfoFallback(): string | undefined {
149
+ const dir = runnerOutboxDirFromEnv();
150
+ if (dir) return dir;
151
+ const infoFile = runnerInfoFileFromEnv();
152
+ return infoFile ? join(dirname(infoFile), "outbox") : undefined;
153
+ }
154
+
155
+ export function capsFromEnv(): string | undefined {
156
+ return process.env.AGENT_RELAY_CAPS;
157
+ }
158
+
159
+ export function tagsFromEnv(): string | undefined {
160
+ return process.env.AGENT_RELAY_TAGS;
161
+ }
162
+
163
+ export function workspaceModeFromEnv(): string | undefined {
164
+ return process.env.AGENT_RELAY_WORKSPACE_MODE;
165
+ }
166
+
167
+ export function orchestratorBaseDirFromEnv(): string | undefined {
168
+ return process.env.AGENT_RELAY_ORCHESTRATOR_BASE_DIR;
169
+ }
170
+
171
+ export function runnerLogFileFromEnv(): string | undefined {
172
+ return typeof process.env.AGENT_RELAY_LOG_FILE === "string" ? process.env.AGENT_RELAY_LOG_FILE : undefined;
173
+ }
174
+
175
+ export function orchestratorUrlFromEnv(): string | undefined {
176
+ return process.env.AGENT_RELAY_ORCHESTRATOR_URL;
177
+ }
178
+
179
+ export function contextStateDirFromEnv(): string | undefined {
180
+ return process.env.AGENT_RELAY_CONTEXT_STATE_DIR;
181
+ }
182
+
183
+ export function attachmentCacheDirFromEnv(): string | undefined {
184
+ return process.env.AGENT_RELAY_ATTACHMENT_CACHE_DIR;
185
+ }
186
+
187
+ export function providerHomeRootFromEnv(): string {
188
+ return process.env.AGENT_RELAY_PROVIDER_HOME_ROOT || join(homedir(), ".agent-relay", "provider-homes");
189
+ }
190
+
191
+ export function messageBodyMaxCharsFromEnv(): string | undefined {
192
+ return process.env.AGENT_RELAY_MESSAGE_BODY_MAX_CHARS;
193
+ }
194
+
57
195
  export function loadProviderConfig(provider: string, home = agentRelayHome()): LoadedProviderConfig {
58
196
  const path = join(providersDir(home), `${provider}.json`);
59
197
  const raw = readJson(path);
@@ -4,6 +4,7 @@ import { tmpdir } from "node:os";
4
4
  import { dirname, join, relative, resolve } from "node:path";
5
5
  import { Readable } from "node:stream";
6
6
  import { expandTilde } from "./utils";
7
+ import { artifactsDir, maxArtifactBytesFromEnv } from "./config";
7
8
 
8
9
  interface ArtifactStorageBackend {
9
10
  readonly scheme: string;
@@ -20,14 +21,11 @@ interface ArtifactStorageBackend {
20
21
  const DEFAULT_MAX_ARTIFACT_BYTES = 50 * 1024 * 1024;
21
22
 
22
23
  function artifactRoot(): string {
23
- return expandTilde(process.env.AGENT_RELAY_ARTIFACTS_DIR || "~/.agent-relay/artifacts");
24
+ return expandTilde(artifactsDir());
24
25
  }
25
26
 
26
27
  export function maxArtifactBytes(): number {
27
- const raw = process.env.AGENT_RELAY_MAX_ARTIFACT_BYTES;
28
- if (!raw) return DEFAULT_MAX_ARTIFACT_BYTES;
29
- const parsed = Number(raw);
30
- return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : DEFAULT_MAX_ARTIFACT_BYTES;
28
+ return maxArtifactBytesFromEnv(DEFAULT_MAX_ARTIFACT_BYTES);
31
29
  }
32
30
 
33
31
  export function normalizeDigest(digest: string): string {
package/src/bus.ts CHANGED
@@ -15,6 +15,7 @@ import { applyCommandToRecipe } from "./recipe-runner";
15
15
  import { enqueueContinuationInjectionAfterClear } from "./self-resume";
16
16
  import { messageMatchesAgent, targetMatchesAgent } from "./agent-ref";
17
17
  import { probeWorkspaceOnTurnEnd } from "./workspace-probe";
18
+ import { busStaleGraceMs } from "./config";
18
19
  import {
19
20
  BusProtocolError,
20
21
  parseBusFrame,
@@ -107,11 +108,9 @@ export function getBusConnectionCount(): number {
107
108
  return busConnections.size;
108
109
  }
109
110
 
110
- export function expireStaleBusAgents(graceMs = Number(process.env.AGENT_RELAY_STALE_GRACE_MS) || 120_000): { agentIds: string[]; orphanedTasks: Task[] } {
111
+ export function expireStaleBusAgents(graceMs = busStaleGraceMs()): { agentIds: string[]; orphanedTasks: Task[] } {
111
112
  const cutoff = Date.now() - graceMs;
112
- const rows = getDb()
113
- .query("SELECT id FROM agents WHERE status = 'stale' AND last_seen < ? AND id NOT IN ('user', 'system')")
114
- .all(cutoff) as Array<{ id: string }>;
113
+ const rows = getDb().query("SELECT id FROM agents WHERE status = 'stale' AND last_seen < ? AND id NOT IN ('user', 'system')").all(cutoff) as Array<{ id: string }>;
115
114
  const orphanedTasks: Task[] = [];
116
115
  for (const row of rows) {
117
116
  // Bus-grace staleness across a relay restart/reconnect — the process may be alive.
@@ -5,11 +5,12 @@
5
5
  import { createInterface } from "node:readline/promises";
6
6
  import { stdin as input, stdout as output } from "node:process";
7
7
  import { RELAY_TOKEN_HEADER } from "agent-relay-sdk";
8
+ import { cliRelayToken, cliRelayUrl } from "../config";
8
9
 
9
10
  export async function apiRequest(method: string, path: string, body?: unknown): Promise<unknown> {
10
- const baseUrl = process.env.AGENT_RELAY_URL || "http://127.0.0.1:4850";
11
+ const baseUrl = cliRelayUrl();
11
12
  const headers: Record<string, string> = {};
12
- const token = process.env.AGENT_RELAY_TOKEN;
13
+ const token = cliRelayToken();
13
14
  if (token) headers[RELAY_TOKEN_HEADER] = token;
14
15
  if (body !== undefined) headers["Content-Type"] = "application/json";
15
16
  const response = await fetch(new URL(path, baseUrl), {
@@ -27,9 +28,9 @@ export async function apiRequest(method: string, path: string, body?: unknown):
27
28
  }
28
29
 
29
30
  export async function apiRawRequest(method: string, path: string, body: BodyInit, extraHeaders: Record<string, string> = {}): Promise<unknown> {
30
- const baseUrl = process.env.AGENT_RELAY_URL || "http://127.0.0.1:4850";
31
+ const baseUrl = cliRelayUrl();
31
32
  const headers: Record<string, string> = { ...extraHeaders };
32
- const token = process.env.AGENT_RELAY_TOKEN;
33
+ const token = cliRelayToken();
33
34
  if (token) headers[RELAY_TOKEN_HEADER] = token;
34
35
  const response = await fetch(new URL(path, baseUrl), { method, headers, body });
35
36
  const text = await response.text();
@@ -5,6 +5,7 @@
5
5
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
6
6
  import { join, resolve } from "node:path";
7
7
  import { apiRequest, uniqueStrings } from "./_shared";
8
+ import { agentIdFromEnv, codexStatePathFromEnv, contextPathFromEnv } from "../config";
8
9
 
9
10
  export async function detectActivePairId(agentId: string): Promise<string | undefined> {
10
11
  const pairs = await apiRequest("GET", `/api/pairs?agent=${encodeURIComponent(agentId)}&status=active`) as Array<{ id?: string }>;
@@ -12,15 +13,16 @@ export async function detectActivePairId(agentId: string): Promise<string | unde
12
13
  }
13
14
 
14
15
  export async function detectAgentId(): Promise<string | undefined> {
15
- const explicit = process.env.AGENT_RELAY_ID;
16
+ const explicit = agentIdFromEnv();
16
17
  if (explicit) return explicit;
17
18
 
18
19
  const contextMatch = currentAgentContextId();
19
20
  if (contextMatch) return contextMatch;
20
21
 
21
22
  const cwd = process.cwd();
22
- const explicitCodexState = process.env.AGENT_RELAY_CODEX_STATE_PATH
23
- ? readCodexState(process.env.AGENT_RELAY_CODEX_STATE_PATH)
23
+ const explicitCodexStatePath = codexStatePathFromEnv();
24
+ const explicitCodexState = explicitCodexStatePath
25
+ ? readCodexState(explicitCodexStatePath)
24
26
  : null;
25
27
  if (explicitCodexState?.agentId) return explicitCodexState.agentId;
26
28
 
@@ -48,7 +50,7 @@ export async function detectAgentId(): Promise<string | undefined> {
48
50
  }
49
51
 
50
52
  function currentAgentContextId(): string | undefined {
51
- const explicitPath = process.env.AGENT_RELAY_CONTEXT_PATH;
53
+ const explicitPath = contextPathFromEnv();
52
54
  if (explicitPath) {
53
55
  const explicit = readAgentContext(explicitPath);
54
56
  if (explicit?.agentId && contextMatchesCurrentProcess(explicit)) return explicit.agentId;
@@ -2,6 +2,7 @@
2
2
  // end-of-session self-view as a bounded, structured Insights observation (#185).
3
3
  import { apiRequest, readStdin } from "./_shared";
4
4
  import { detectAgentId } from "./agent-detect";
5
+ import { projectFromEnv, providerSessionIdFromEnv } from "../config";
5
6
 
6
7
  // Insights #185: capture the agent's end-of-session self-view as a bounded, structured
7
8
  // artifact (epic #183, docs/self-improvement.md). Manual rail in v0 — the agent or
@@ -61,8 +62,8 @@ export async function handleIntrospectCommand(args: string[]): Promise<void> {
61
62
  if (!from) throw new Error("Could not detect current Agent Relay ID. Pass --from AGENT_ID or set AGENT_RELAY_ID.");
62
63
 
63
64
  const observation = await apiRequest("POST", "/api/insights/observations", {
64
- sessionId: sessionId || process.env.AGENT_RELAY_PROVIDER_SESSION_ID || `manual-${from}`,
65
- project: project || process.env.AGENT_RELAY_PROJECT || process.cwd().split("/").filter(Boolean).at(-1) || process.cwd(),
65
+ sessionId: sessionId || providerSessionIdFromEnv() || `manual-${from}`,
66
+ project: project || projectFromEnv() || process.cwd().split("/").filter(Boolean).at(-1) || process.cwd(),
66
67
  agentId: from,
67
68
  signal: "introspection",
68
69
  source: "agent",
package/src/cli/token.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  // Token commands — auto-split from cli.ts (#294). create/list/revoke/verify for
2
2
  // component JWTs.
3
3
  import { apiRequest, splitTagArgs } from "./_shared";
4
+ import { cliRelayToken } from "../config";
4
5
 
5
6
  export async function handleTokenCommand(args: string[]): Promise<void> {
6
7
  const action = args[0] ?? "list";
@@ -45,7 +46,7 @@ export async function handleTokenCommand(args: string[]): Promise<void> {
45
46
  return;
46
47
  }
47
48
  if (action === "verify") {
48
- const token = rest.find((arg) => !arg.startsWith("--")) ?? process.env.AGENT_RELAY_TOKEN;
49
+ const token = rest.find((arg) => !arg.startsWith("--")) ?? cliRelayToken();
49
50
  if (!token) throw new Error("Usage: agent-relay token verify TOKEN");
50
51
  const payload = decodeJwtPayload(token);
51
52
  if (!payload) throw new Error("not a component JWT");
@@ -7,13 +7,14 @@ import { describeWorkspacePhase, readyContract, type WorkspacePhaseView } from "
7
7
  import type { WorkspaceDepsRefreshResult } from "agent-relay-sdk";
8
8
  import { AUTO_MERGE_POLICIES } from "../workspace-merge";
9
9
  import type { WorkspaceAutoMergePolicy } from "../types";
10
+ import { workspaceJsonFromEnv } from "../config";
10
11
 
11
12
  export const WORKSPACE_USAGE = "Usage: agent-relay workspace <status|diagnostics|ready|land|claim|release|list|cleanup-stale|deps> [--id ID] [--strategy ...] [--auto-merge on-green|on-approval|manual] [--reviewer HANDLE] [--purpose TEXT] [--repo PATH] [--wait] [--timeout SECONDS] [--check] [--execute] [--json]";
12
13
 
13
14
  // The agent's own isolated-workspace id, published in AGENT_RELAY_WORKSPACE_JSON
14
15
  // by the orchestrator at spawn. Undefined for shared-workspace / non-managed agents.
15
16
  function currentWorkspaceId(): string | undefined {
16
- const json = process.env.AGENT_RELAY_WORKSPACE_JSON;
17
+ const json = workspaceJsonFromEnv();
17
18
  if (!json) return undefined;
18
19
  try {
19
20
  const parsed = JSON.parse(json) as { id?: string };