agent-relay-server 0.123.1 → 0.124.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +3 -1
  2. package/docs/openapi.json +1 -1
  3. package/package.json +4 -3
  4. package/public/assets/{MessageBubble-NCggGKTA.js → MessageBubble-SQK8-Mzp.js} +2 -2
  5. package/public/assets/{MessageBubble-NCggGKTA.js.map → MessageBubble-SQK8-Mzp.js.map} +1 -1
  6. package/public/assets/{PlanGraphPanel-CSdszmGk.js → PlanGraphPanel-CS-aow4S.js} +2 -2
  7. package/public/assets/{PlanGraphPanel-CSdszmGk.js.map → PlanGraphPanel-CS-aow4S.js.map} +1 -1
  8. package/public/assets/{activity-Cyp2nolX.js → activity-W31XvhC2.js} +2 -2
  9. package/public/assets/{activity-Cyp2nolX.js.map → activity-W31XvhC2.js.map} +1 -1
  10. package/public/assets/{agent-profiles-D5Izg0Vn.js → agent-profiles-DXysA9To.js} +2 -2
  11. package/public/assets/{agent-profiles-D5Izg0Vn.js.map → agent-profiles-DXysA9To.js.map} +1 -1
  12. package/public/assets/{agents-DIOxuJ6M.js → agents-B5GRNRnN.js} +2 -2
  13. package/public/assets/{agents-DIOxuJ6M.js.map → agents-B5GRNRnN.js.map} +1 -1
  14. package/public/assets/{automation-Bn6mOLAR.js → automation-DYlgHYcb.js} +2 -2
  15. package/public/assets/{automation-Bn6mOLAR.js.map → automation-DYlgHYcb.js.map} +1 -1
  16. package/public/assets/{chat-eUaPz399.js → chat-Clz6qOtn.js} +2 -2
  17. package/public/assets/{chat-eUaPz399.js.map → chat-Clz6qOtn.js.map} +1 -1
  18. package/public/assets/{formatted-body-CvrQ2D1J.js → formatted-body-BYQ-SPtj.js} +3 -3
  19. package/public/assets/{formatted-body-CvrQ2D1J.js.map → formatted-body-BYQ-SPtj.js.map} +1 -1
  20. package/public/assets/{formatted-body-impl-BJ7jdbpR.js → formatted-body-impl-L1EMnNW6.js} +2 -2
  21. package/public/assets/{formatted-body-impl-BJ7jdbpR.js.map → formatted-body-impl-L1EMnNW6.js.map} +1 -1
  22. package/public/assets/{index-DnUo0Zqg.js → index-0uIGXT14.js} +6 -6
  23. package/public/assets/{index-DnUo0Zqg.js.map → index-0uIGXT14.js.map} +1 -1
  24. package/public/assets/index-DOJ7KidA.css +2 -0
  25. package/public/assets/{insights-CQGH5LhD.js → insights-DwZxq1Dn.js} +2 -2
  26. package/public/assets/{insights-CQGH5LhD.js.map → insights-DwZxq1Dn.js.map} +1 -1
  27. package/public/assets/{maintenance-7c7UvpAt.js → maintenance-COcmUDnf.js} +2 -2
  28. package/public/assets/{maintenance-7c7UvpAt.js.map → maintenance-COcmUDnf.js.map} +1 -1
  29. package/public/assets/{managed-agents-yh4HPO5a.js → managed-agents-D9-dgMeX.js} +2 -2
  30. package/public/assets/{managed-agents-yh4HPO5a.js.map → managed-agents-D9-dgMeX.js.map} +1 -1
  31. package/public/assets/{markdown-preview-impl-BuOn5-Uc.js → markdown-preview-impl-DH372bL-.js} +2 -2
  32. package/public/assets/{markdown-preview-impl-BuOn5-Uc.js.map → markdown-preview-impl-DH372bL-.js.map} +1 -1
  33. package/public/assets/{memory-Qv0JN8CE.js → memory-B9hRK8sk.js} +2 -2
  34. package/public/assets/{memory-Qv0JN8CE.js.map → memory-B9hRK8sk.js.map} +1 -1
  35. package/public/assets/{messages-CgjHJmc2.js → messages-BHkHlXzF.js} +2 -2
  36. package/public/assets/{messages-CgjHJmc2.js.map → messages-BHkHlXzF.js.map} +1 -1
  37. package/public/assets/{orchestrators-CgL4CFzb.js → orchestrators-CXy3SDSn.js} +2 -2
  38. package/public/assets/{orchestrators-CgL4CFzb.js.map → orchestrators-CXy3SDSn.js.map} +1 -1
  39. package/public/assets/{overview-Bri04eYE.js → overview-D2yRyCQq.js} +2 -2
  40. package/public/assets/{overview-Bri04eYE.js.map → overview-D2yRyCQq.js.map} +1 -1
  41. package/public/assets/{pairs-C78LwUH2.js → pairs-5tXdHylI.js} +2 -2
  42. package/public/assets/{pairs-C78LwUH2.js.map → pairs-5tXdHylI.js.map} +1 -1
  43. package/public/assets/{prompt-settings-DDEkMqPZ.js → prompt-settings-Bn5mmi9w.js} +2 -2
  44. package/public/assets/{prompt-settings-DDEkMqPZ.js.map → prompt-settings-Bn5mmi9w.js.map} +1 -1
  45. package/public/assets/registry-C3sB7qv5.js +4 -0
  46. package/public/assets/registry-C3sB7qv5.js.map +1 -0
  47. package/public/assets/{security-wvnRKFvm.js → security-CmD-YE-_.js} +2 -2
  48. package/public/assets/{security-wvnRKFvm.js.map → security-CmD-YE-_.js.map} +1 -1
  49. package/public/assets/{select-BIfbVmLf.js → select-C0UlhK2k.js} +2 -2
  50. package/public/assets/{select-BIfbVmLf.js.map → select-C0UlhK2k.js.map} +1 -1
  51. package/public/assets/{settings-B6Ntahx7.js → settings-DrqMG5nG.js} +2 -2
  52. package/public/assets/{settings-B6Ntahx7.js.map → settings-DrqMG5nG.js.map} +1 -1
  53. package/public/assets/{tasks-CUglafo5.js → tasks-ChQjtXHu.js} +2 -2
  54. package/public/assets/{tasks-CUglafo5.js.map → tasks-ChQjtXHu.js.map} +1 -1
  55. package/public/assets/{teams-EE8RHhHM.js → teams-itjNlDLp.js} +2 -2
  56. package/public/assets/{teams-EE8RHhHM.js.map → teams-itjNlDLp.js.map} +1 -1
  57. package/public/assets/{terminal-viewer-impl-DBRkdyag.js → terminal-viewer-impl-B9M6s2vB.js} +2 -2
  58. package/public/assets/{terminal-viewer-impl-DBRkdyag.js.map → terminal-viewer-impl-B9M6s2vB.js.map} +1 -1
  59. package/public/assets/{work-queue-BFQVVL4T.js → work-queue-Kw0uMkEF.js} +2 -2
  60. package/public/assets/{work-queue-BFQVVL4T.js.map → work-queue-Kw0uMkEF.js.map} +1 -1
  61. package/public/index.html +3 -3
  62. package/runner/hooks/rg-r-guard/hooks.json +18 -0
  63. package/runner/hooks/rg-r-guard/rg-r-guard.cjs +144 -0
  64. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  65. package/src/db/provisioning-registry.ts +174 -1
  66. package/src/routes/provisioning-registry.ts +3 -0
  67. package/src/routing-policy-store.ts +55 -0
  68. package/src/services/spawn-chain-inspector.ts +134 -0
  69. package/src/spawn-targets.ts +130 -28
  70. package/public/assets/index-DcmG5gLR.css +0 -2
  71. package/public/assets/registry-5KhP5FE5.js +0 -2
  72. package/public/assets/registry-5KhP5FE5.js.map +0 -1
@@ -1,3 +1,3 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./formatted-body-impl-BJ7jdbpR.js","./chunk-CilyBKbf.js","./index-DnUo0Zqg.js","./preload-helper-C_JMBXql.js","./dist-DKmTXfpP.js","./dist-C5z05Vkz.js","./lucide-react-CoU_Xts2.js","./dist-B8tD9ZcR.js","./dist-V3wu3BHw.js","./react-dom-BQICbUuF.js","./dist-CFtFIhu3.js","./dist-CGaHpxnN.js","./dist-BiLuYU6G.js","./dist-BJaV2BD4.js","./dist-CjyfQThq.js","./es2015-5DTJe35n.js","./dist-r8wyUTkK.js","./dist-4VwhnLtj.js","./dist-CrUl2aQp.js","./store-zFLzd0Jd.js","./display-GFbGUG0O.js","./types-CRptbSYh.js","./agent-type-icon-BxUopALO.js","./copy-button-Bil2CjzT.js","./button-C3CQSr4p.js","./task-chip-BHM-e3I8.js","./badge-XEru6wto.js","./input-CXMbJ848.js","./themes-DjSwmAZN.js","./index-DcmG5gLR.css","./lib-BcUW1l-W.js","./lib-Cc7Il-6i.js"])))=>i.map(i=>d[i]);
2
- import{r as e}from"./chunk-CilyBKbf.js";import{gr as t,rr as n}from"./lucide-react-CoU_Xts2.js";import{t as r}from"./preload-helper-C_JMBXql.js";var i=e(t(),1),a=n(),o=(0,i.lazy)(()=>r(()=>import(`./formatted-body-impl-BJ7jdbpR.js`).then(e=>({default:e.FormattedBody})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]),import.meta.url));function s(e){return(0,a.jsx)(i.Suspense,{fallback:(0,a.jsx)(`span`,{className:e.className,children:e.text}),children:(0,a.jsx)(o,{...e})})}export{s as t};
3
- //# sourceMappingURL=formatted-body-CvrQ2D1J.js.map
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./formatted-body-impl-L1EMnNW6.js","./chunk-CilyBKbf.js","./index-0uIGXT14.js","./preload-helper-C_JMBXql.js","./dist-DKmTXfpP.js","./dist-C5z05Vkz.js","./lucide-react-CoU_Xts2.js","./dist-B8tD9ZcR.js","./dist-V3wu3BHw.js","./react-dom-BQICbUuF.js","./dist-CFtFIhu3.js","./dist-CGaHpxnN.js","./dist-BiLuYU6G.js","./dist-BJaV2BD4.js","./dist-CjyfQThq.js","./es2015-5DTJe35n.js","./dist-r8wyUTkK.js","./dist-4VwhnLtj.js","./dist-CrUl2aQp.js","./store-zFLzd0Jd.js","./display-GFbGUG0O.js","./types-CRptbSYh.js","./agent-type-icon-BxUopALO.js","./copy-button-Bil2CjzT.js","./button-C3CQSr4p.js","./task-chip-BHM-e3I8.js","./badge-XEru6wto.js","./input-CXMbJ848.js","./themes-DjSwmAZN.js","./index-DOJ7KidA.css","./lib-BcUW1l-W.js","./lib-Cc7Il-6i.js"])))=>i.map(i=>d[i]);
2
+ import{r as e}from"./chunk-CilyBKbf.js";import{gr as t,rr as n}from"./lucide-react-CoU_Xts2.js";import{t as r}from"./preload-helper-C_JMBXql.js";var i=e(t(),1),a=n(),o=(0,i.lazy)(()=>r(()=>import(`./formatted-body-impl-L1EMnNW6.js`).then(e=>({default:e.FormattedBody})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]),import.meta.url));function s(e){return(0,a.jsx)(i.Suspense,{fallback:(0,a.jsx)(`span`,{className:e.className,children:e.text}),children:(0,a.jsx)(o,{...e})})}export{s as t};
3
+ //# sourceMappingURL=formatted-body-BYQ-SPtj.js.map
@@ -1 +1 @@
1
- {"version":3,"mappings":";sKASM,uBACJ,OAAO,qCAAyB,KAAM,IAAO,CAAE,QAAS,EAAE,cAAe,EAAE,2HAC5E,CAID,SAAgB,EAAc,EAA2B,CACvD,OACE,SAAC,WAAD,CAAU,UAAU,SAAC,OAAD,CAAM,UAAW,EAAM,mBAAY,EAAM,KAAY,YACvE,SAAC,EAAD,CAAmB,GAAI,EAAS,EACvB","names":[],"ignoreList":[],"sources":["../../dashboard/src/components/shared/formatted-body.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react'\nimport type { FormattedBodyProps } from './formatted-body-impl'\n\n// Lazy boundary for react-markdown (#201). FormattedBody is statically imported\n// by the always-mounted ThreadModal, which would otherwise drag react-markdown +\n// remark/rehype plugins into the initial bundle. Behind this boundary the\n// markdown chunk loads on first actual render (thread opened, or chat/messages\n// view navigated to). The fallback renders the plain text so there's no blank\n// flash while the chunk resolves on first use.\nconst LazyFormattedBody = lazy(() =>\n import('./formatted-body-impl').then((m) => ({ default: m.FormattedBody })),\n)\n\nexport type { FormattedBodyProps }\n\nexport function FormattedBody(props: FormattedBodyProps) {\n return (\n <Suspense fallback={<span className={props.className}>{props.text}</span>}>\n <LazyFormattedBody {...props} />\n </Suspense>\n )\n}\n"],"file":"formatted-body-CvrQ2D1J.js"}
1
+ {"version":3,"mappings":";sKASM,uBACJ,OAAO,qCAAyB,KAAM,IAAO,CAAE,QAAS,EAAE,cAAe,EAAE,2HAC5E,CAID,SAAgB,EAAc,EAA2B,CACvD,OACE,SAAC,WAAD,CAAU,UAAU,SAAC,OAAD,CAAM,UAAW,EAAM,mBAAY,EAAM,KAAY,YACvE,SAAC,EAAD,CAAmB,GAAI,EAAS,EACvB","names":[],"ignoreList":[],"sources":["../../dashboard/src/components/shared/formatted-body.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react'\nimport type { FormattedBodyProps } from './formatted-body-impl'\n\n// Lazy boundary for react-markdown (#201). FormattedBody is statically imported\n// by the always-mounted ThreadModal, which would otherwise drag react-markdown +\n// remark/rehype plugins into the initial bundle. Behind this boundary the\n// markdown chunk loads on first actual render (thread opened, or chat/messages\n// view navigated to). The fallback renders the plain text so there's no blank\n// flash while the chunk resolves on first use.\nconst LazyFormattedBody = lazy(() =>\n import('./formatted-body-impl').then((m) => ({ default: m.FormattedBody })),\n)\n\nexport type { FormattedBodyProps }\n\nexport function FormattedBody(props: FormattedBodyProps) {\n return (\n <Suspense fallback={<span className={props.className}>{props.text}</span>}>\n <LazyFormattedBody {...props} />\n </Suspense>\n )\n}\n"],"file":"formatted-body-BYQ-SPtj.js"}
@@ -1,3 +1,3 @@
1
- import{r as e}from"./chunk-CilyBKbf.js";import{gr as t,rr as n,tr as r}from"./lucide-react-CoU_Xts2.js";import{t as i}from"./store-zFLzd0Jd.js";import{L as a}from"./display-GFbGUG0O.js";import{t as o}from"./task-chip-BHM-e3I8.js";import{X as s,ft as c}from"./index-DnUo0Zqg.js";import{i as l,n as u,r as d,t as f}from"./lib-BcUW1l-W.js";var p=e(t(),1),m=n(),h=/((?:[A-Za-z0-9][\w.-]*\/[A-Za-z0-9][\w.-]*)?#\d+)\b|(https?:\/\/[^\s`'")\]]+)|(\bT\d+\b(?!\/))|((?:\/[\w.@+-][^\s`'")\]]*|(?:\.{1,2}\/|[\w.@+-]+\/)[^\s`'")\]]*))/g,g=/\bT(\d+)\b(?!\/)/g,_=/((?:[A-Za-z0-9][\w.-]*\/[A-Za-z0-9][\w.-]*)?#(\d+))\b/g,v=/[.,;:!?]+$/,y=new Set([`src`,`app`,`apps`,`lib`,`server`,`client`,`dashboard`,`orchestrator`,`runner`,`sdk`,`codex`,`claude`,`public`,`docs`,`test`,`tests`,`scripts`,`config`,`configs`]),b={...d,attributes:{...d.attributes,code:[...d.attributes?.code||[],[`className`,/^language-[\w-]+$/]]}};function x(e){if(e===`/api`||e.startsWith(`/api/`))return!1;if(e.startsWith(`/`)||e.startsWith(`./`)||e.startsWith(`../`))return!0;let t=e.split(`/`).filter(Boolean),n=t[0]??``,r=t[t.length-1]??``;return n.startsWith(`.`)||y.has(n)?!0:r.includes(`.`)}function S(e){if(!e||typeof e!=`object`)return null;let t=e,n=typeof t.id==`number`?t.id:typeof t.id==`string`?Number(t.id):NaN;if(!Number.isSafeInteger(n)||n<=0)return null;let r=typeof t.title==`string`&&t.title.trim()?t.title:typeof t.label==`string`&&t.label.trim()?t.label:void 0,i=typeof t.status==`string`&&t.status.trim()?t.status:void 0;return{id:n,meta:{...r?{title:r}:{},...i?{status:i}:{}}}}function C(e){let t=new Set;for(let n of e.matchAll(g)){let e=Number(n[1]);Number.isSafeInteger(e)&&e>0&&t.add(e)}return[...t].sort((e,t)=>e-t)}function w(e,t,n){let r=new Set(e),i=new Map;if(r.size===0)return i;for(let e of t){let t=S(e);t&&r.has(t.id)&&i.set(t.id,t.meta)}for(let e of n){let t=S(e);t&&r.has(t.id)&&i.set(t.id,t.meta)}return i}function T(e,t,n){if(e.length===0)return``;let r=w(e,t,n);return e.map(e=>{let t=r.get(e);return`${e}:${t?.title??``}:${t?.status??``}`}).join(`|`)}function E(e,t){let n=new Set;for(let r of e.matchAll(_)){let e=r[1],i=r[2];if(!e||!i)continue;let a=e.lastIndexOf(`#`),o=e.slice(0,a)||t;o&&n.add(`${o}#${i}`)}return[...n].sort()}function D(e,t){let n=new Set(e),r=new Map;if(n.size===0)return r;for(let e of Object.values(t)){let t=`${e.repo}#${e.externalKey}`;n.has(t)&&r.set(t,e)}return r}function O(e,t){if(e.length===0)return``;let n=D(e,t);return e.map(e=>{let t=n.get(e);return`${e}:${t?.id??``}:${t?.status??``}:${t?.updatedAt??``}:${t?.url??``}`}).join(`|`)}function k(e,t,n,r){let i=[],a=0;for(let n of e.matchAll(h)){let o=n[1],s=n[2],c=n[3],l=n[4],u=o||s||c||l,d=n.index??0;if(!u||d<a)continue;if(c){let t=Number(c.slice(1));Number.isSafeInteger(t)&&t>0&&(d>a&&i.push({text:e.slice(a,d)}),i.push({text:c,taskId:t}),a=d+c.length);continue}if(o){let n=o.lastIndexOf(`#`),s=o.slice(0,n)||t,c=o.slice(n+1),l=e.slice(d+o.length).match(/^(?:\/\d+)+/)?.[0]??``;if(s){d>a&&i.push({text:e.slice(a,d)});let t=r?.get(`${s}#${c}`);i.push({text:o,issueUrl:`https://github.com/${s}/issues/${c}`,...t?{issueRef:t}:{}}),l&&i.push({text:l}),a=d+o.length+l.length}else l&&(d>a&&i.push({text:e.slice(a,d)}),i.push({text:o+l}),a=d+o.length+l.length);continue}if(s){let t=s.replace(v,``),n=s.slice(t.length);d>a&&i.push({text:e.slice(a,d)}),i.push({text:t,url:t}),n&&i.push({text:n}),a=d+s.length;continue}if(!u.includes(`/`))continue;let f=u.replace(v,``),p=u.slice(f.length),m=f.match(/^(.*):(\d+)$/),h=m?Number(m[2]):void 0;m&&(f=m[1]),x(f)&&(d>a&&i.push({text:e.slice(a,d)}),i.push({text:f,path:f,line:Number.isFinite(h)?h:void 0}),p&&i.push({text:p}),a=d+u.length)}return a<e.length&&i.push({text:e.slice(a)}),i}function A({text:e,onOpenPath:t,onPreviewPath:n,onPreviewPathEnd:r,isPathLinkable:i,resolvePathTitle:a,issueRepo:c,onOpenTask:l,loadedTasksById:u,issueRefsByKey:d}){let f=(0,p.useMemo)(()=>k(e,c,u,d),[e,c,u,d]);return f.length===1&&!f[0]?.path&&!f[0]?.url&&!f[0]?.issueUrl&&!f[0]?.taskId?(0,m.jsx)(m.Fragment,{children:e}):(0,m.jsx)(m.Fragment,{children:f.map((e,c)=>{if(e.taskId!=null){if(l){let t=u?.get(e.taskId);return(0,m.jsx)(o,{id:e.taskId,title:t?.title,status:t?.status,compact:!0,onClick:()=>l(e.taskId)},`task-${e.taskId}-${c}`)}return(0,m.jsx)(`span`,{children:e.text},c)}if(e.issueUrl)return e.issueRef?(0,m.jsx)(s,{issueRef:e.issueRef,compact:!0},`${e.issueUrl}-${c}`):(0,m.jsx)(`a`,{href:e.issueUrl,target:`_blank`,rel:`noreferrer`,className:`font-medium underline decoration-dotted underline-offset-2`,onClick:e=>e.stopPropagation(),children:e.text},`${e.issueUrl}-${c}`);if(e.url)return(0,m.jsx)(`a`,{href:e.url,target:`_blank`,rel:`noreferrer`,className:`underline decoration-dotted underline-offset-2`,onClick:e=>e.stopPropagation(),children:e.text},`${e.url}-${c}`);if(e.path&&t){if(i&&!i(e.path,e.line))return(0,m.jsxs)(`span`,{children:[e.text,e.line?`:${e.line}`:``]},`${e.path}-${c}`);let o=a?.(e.path,e.line);return(0,m.jsxs)(`button`,{type:`button`,className:`chat-link rounded-sm font-mono underline decoration-dotted underline-offset-2`,onClick:n=>{n.stopPropagation(),t(e.path,e.line)},onMouseEnter:t=>n?.(e.path,e.line,t.currentTarget.getBoundingClientRect()),onFocus:t=>n?.(e.path,e.line,t.currentTarget.getBoundingClientRect()),onMouseLeave:()=>r?.(),onBlur:()=>r?.(),title:o?`Open ${o}`:`Open file`,children:[e.text,e.line?`:${e.line}`:``]},`${e.path}-${c}`)}return(0,m.jsx)(`span`,{children:e.text},c)})})}var j=({className:e,children:t})=>{let n=/language-([\w-]+)/.exec(e||``),r=String(t).replace(/\n$/,``);return n?(0,m.jsx)(c,{content:r,path:`.${n[1]}`,languageHint:n[1],compact:!0}):(0,m.jsx)(`code`,{className:e,children:t})},M=({href:e,children:t})=>(0,m.jsx)(`a`,{href:e,target:`_blank`,rel:`noreferrer`,onClick:e=>e.stopPropagation(),children:t});function N(e,t,n=!1){return p.Children.map(e,e=>{if(typeof e==`string`)return n?e:(0,m.jsx)(A,{text:e,onOpenPath:t.onOpenPath,onPreviewPath:t.onPreviewPath,onPreviewPathEnd:t.onPreviewPathEnd,isPathLinkable:t.isPathLinkable,resolvePathTitle:t.resolvePathTitle,issueRepo:t.issueRepo,onOpenTask:t.onOpenTask,loadedTasksById:t.loadedTasksById,issueRefsByKey:t.issueRefsByKey});if(!p.isValidElement(e))return e;let r=e.type;if(r===A||r===M||r===`a`)return e;let i=e.props;if(i?.children==null)return e;let a=n||r===j||r===`code`||r===`pre`;return p.cloneElement(e,void 0,N(i.children,t,a))})}function P({text:e,rawBody:t,className:n,onOpenPath:o,onPreviewPath:s,onPreviewPathEnd:c,isPathLinkable:d,resolvePathTitle:h,issueRepo:g,onPrimary:_,onOpenTask:v}){let y=(0,p.useMemo)(()=>a(t||``),[t]),x=F(y?y.text:e),S=i(e=>e.stats?.defaultIssueRepo),k=g??S,A=(0,p.useMemo)(()=>C(x),[x]),P=(0,p.useMemo)(()=>E(x,k),[x,k]),I=i(e=>T(A,e.tasks,e.deliverableTasks)),L=i(e=>e.openTaskDetailById),R=i(e=>O(P,e.issueRefsById)),z=(0,p.useMemo)(()=>{let e=i.getState();return w(A,e.tasks,e.deliverableTasks)},[A,I]),B=(0,p.useMemo)(()=>D(P,i.getState().issueRefsById),[P,R]),V=v??L,H=(0,p.useMemo)(()=>{let e={onOpenPath:o,onPreviewPath:s,onPreviewPathEnd:c,isPathLinkable:d,resolvePathTitle:h,issueRepo:k,onOpenTask:V,loadedTasksById:z,issueRefsByKey:B},t=t=>({children:n})=>(0,m.jsx)(t,{children:N(n,e)});return{p:t(`p`),li:t(`li`),h1:t(`h1`),h2:t(`h2`),h3:t(`h3`),h4:t(`h4`),h5:t(`h5`),h6:t(`h6`),td:t(`td`),th:t(`th`),blockquote:t(`blockquote`),a:M,code:j}},[o,s,c,d,h,k,V,z,B]),U=(0,p.useMemo)(()=>(0,m.jsx)(l,{remarkPlugins:[f],rehypePlugins:[[u,b]],components:H,children:x}),[H,x]);return(0,m.jsxs)(`div`,{className:n,children:[(0,m.jsx)(`div`,{className:r(`chat-markdown`,_&&`chat-markdown-on-primary`),children:U}),y&&y.meta.length>0&&(0,m.jsx)(`div`,{className:`mt-1.5 flex flex-wrap gap-1`,children:y.meta.map(([e,t])=>(0,m.jsxs)(`span`,{className:`inline-flex text-[10px] leading-tight bg-muted px-1.5 py-0.5 rounded text-muted-foreground font-mono`,children:[e,`: `,t]},e))})]})}function F(e){return e.includes(`\\n`)?e.replace(/\\n/g,`
1
+ import{r as e}from"./chunk-CilyBKbf.js";import{gr as t,rr as n,tr as r}from"./lucide-react-CoU_Xts2.js";import{t as i}from"./store-zFLzd0Jd.js";import{L as a}from"./display-GFbGUG0O.js";import{t as o}from"./task-chip-BHM-e3I8.js";import{X as s,ft as c}from"./index-0uIGXT14.js";import{i as l,n as u,r as d,t as f}from"./lib-BcUW1l-W.js";var p=e(t(),1),m=n(),h=/((?:[A-Za-z0-9][\w.-]*\/[A-Za-z0-9][\w.-]*)?#\d+)\b|(https?:\/\/[^\s`'")\]]+)|(\bT\d+\b(?!\/))|((?:\/[\w.@+-][^\s`'")\]]*|(?:\.{1,2}\/|[\w.@+-]+\/)[^\s`'")\]]*))/g,g=/\bT(\d+)\b(?!\/)/g,_=/((?:[A-Za-z0-9][\w.-]*\/[A-Za-z0-9][\w.-]*)?#(\d+))\b/g,v=/[.,;:!?]+$/,y=new Set([`src`,`app`,`apps`,`lib`,`server`,`client`,`dashboard`,`orchestrator`,`runner`,`sdk`,`codex`,`claude`,`public`,`docs`,`test`,`tests`,`scripts`,`config`,`configs`]),b={...d,attributes:{...d.attributes,code:[...d.attributes?.code||[],[`className`,/^language-[\w-]+$/]]}};function x(e){if(e===`/api`||e.startsWith(`/api/`))return!1;if(e.startsWith(`/`)||e.startsWith(`./`)||e.startsWith(`../`))return!0;let t=e.split(`/`).filter(Boolean),n=t[0]??``,r=t[t.length-1]??``;return n.startsWith(`.`)||y.has(n)?!0:r.includes(`.`)}function S(e){if(!e||typeof e!=`object`)return null;let t=e,n=typeof t.id==`number`?t.id:typeof t.id==`string`?Number(t.id):NaN;if(!Number.isSafeInteger(n)||n<=0)return null;let r=typeof t.title==`string`&&t.title.trim()?t.title:typeof t.label==`string`&&t.label.trim()?t.label:void 0,i=typeof t.status==`string`&&t.status.trim()?t.status:void 0;return{id:n,meta:{...r?{title:r}:{},...i?{status:i}:{}}}}function C(e){let t=new Set;for(let n of e.matchAll(g)){let e=Number(n[1]);Number.isSafeInteger(e)&&e>0&&t.add(e)}return[...t].sort((e,t)=>e-t)}function w(e,t,n){let r=new Set(e),i=new Map;if(r.size===0)return i;for(let e of t){let t=S(e);t&&r.has(t.id)&&i.set(t.id,t.meta)}for(let e of n){let t=S(e);t&&r.has(t.id)&&i.set(t.id,t.meta)}return i}function T(e,t,n){if(e.length===0)return``;let r=w(e,t,n);return e.map(e=>{let t=r.get(e);return`${e}:${t?.title??``}:${t?.status??``}`}).join(`|`)}function E(e,t){let n=new Set;for(let r of e.matchAll(_)){let e=r[1],i=r[2];if(!e||!i)continue;let a=e.lastIndexOf(`#`),o=e.slice(0,a)||t;o&&n.add(`${o}#${i}`)}return[...n].sort()}function D(e,t){let n=new Set(e),r=new Map;if(n.size===0)return r;for(let e of Object.values(t)){let t=`${e.repo}#${e.externalKey}`;n.has(t)&&r.set(t,e)}return r}function O(e,t){if(e.length===0)return``;let n=D(e,t);return e.map(e=>{let t=n.get(e);return`${e}:${t?.id??``}:${t?.status??``}:${t?.updatedAt??``}:${t?.url??``}`}).join(`|`)}function k(e,t,n,r){let i=[],a=0;for(let n of e.matchAll(h)){let o=n[1],s=n[2],c=n[3],l=n[4],u=o||s||c||l,d=n.index??0;if(!u||d<a)continue;if(c){let t=Number(c.slice(1));Number.isSafeInteger(t)&&t>0&&(d>a&&i.push({text:e.slice(a,d)}),i.push({text:c,taskId:t}),a=d+c.length);continue}if(o){let n=o.lastIndexOf(`#`),s=o.slice(0,n)||t,c=o.slice(n+1),l=e.slice(d+o.length).match(/^(?:\/\d+)+/)?.[0]??``;if(s){d>a&&i.push({text:e.slice(a,d)});let t=r?.get(`${s}#${c}`);i.push({text:o,issueUrl:`https://github.com/${s}/issues/${c}`,...t?{issueRef:t}:{}}),l&&i.push({text:l}),a=d+o.length+l.length}else l&&(d>a&&i.push({text:e.slice(a,d)}),i.push({text:o+l}),a=d+o.length+l.length);continue}if(s){let t=s.replace(v,``),n=s.slice(t.length);d>a&&i.push({text:e.slice(a,d)}),i.push({text:t,url:t}),n&&i.push({text:n}),a=d+s.length;continue}if(!u.includes(`/`))continue;let f=u.replace(v,``),p=u.slice(f.length),m=f.match(/^(.*):(\d+)$/),h=m?Number(m[2]):void 0;m&&(f=m[1]),x(f)&&(d>a&&i.push({text:e.slice(a,d)}),i.push({text:f,path:f,line:Number.isFinite(h)?h:void 0}),p&&i.push({text:p}),a=d+u.length)}return a<e.length&&i.push({text:e.slice(a)}),i}function A({text:e,onOpenPath:t,onPreviewPath:n,onPreviewPathEnd:r,isPathLinkable:i,resolvePathTitle:a,issueRepo:c,onOpenTask:l,loadedTasksById:u,issueRefsByKey:d}){let f=(0,p.useMemo)(()=>k(e,c,u,d),[e,c,u,d]);return f.length===1&&!f[0]?.path&&!f[0]?.url&&!f[0]?.issueUrl&&!f[0]?.taskId?(0,m.jsx)(m.Fragment,{children:e}):(0,m.jsx)(m.Fragment,{children:f.map((e,c)=>{if(e.taskId!=null){if(l){let t=u?.get(e.taskId);return(0,m.jsx)(o,{id:e.taskId,title:t?.title,status:t?.status,compact:!0,onClick:()=>l(e.taskId)},`task-${e.taskId}-${c}`)}return(0,m.jsx)(`span`,{children:e.text},c)}if(e.issueUrl)return e.issueRef?(0,m.jsx)(s,{issueRef:e.issueRef,compact:!0},`${e.issueUrl}-${c}`):(0,m.jsx)(`a`,{href:e.issueUrl,target:`_blank`,rel:`noreferrer`,className:`font-medium underline decoration-dotted underline-offset-2`,onClick:e=>e.stopPropagation(),children:e.text},`${e.issueUrl}-${c}`);if(e.url)return(0,m.jsx)(`a`,{href:e.url,target:`_blank`,rel:`noreferrer`,className:`underline decoration-dotted underline-offset-2`,onClick:e=>e.stopPropagation(),children:e.text},`${e.url}-${c}`);if(e.path&&t){if(i&&!i(e.path,e.line))return(0,m.jsxs)(`span`,{children:[e.text,e.line?`:${e.line}`:``]},`${e.path}-${c}`);let o=a?.(e.path,e.line);return(0,m.jsxs)(`button`,{type:`button`,className:`chat-link rounded-sm font-mono underline decoration-dotted underline-offset-2`,onClick:n=>{n.stopPropagation(),t(e.path,e.line)},onMouseEnter:t=>n?.(e.path,e.line,t.currentTarget.getBoundingClientRect()),onFocus:t=>n?.(e.path,e.line,t.currentTarget.getBoundingClientRect()),onMouseLeave:()=>r?.(),onBlur:()=>r?.(),title:o?`Open ${o}`:`Open file`,children:[e.text,e.line?`:${e.line}`:``]},`${e.path}-${c}`)}return(0,m.jsx)(`span`,{children:e.text},c)})})}var j=({className:e,children:t})=>{let n=/language-([\w-]+)/.exec(e||``),r=String(t).replace(/\n$/,``);return n?(0,m.jsx)(c,{content:r,path:`.${n[1]}`,languageHint:n[1],compact:!0}):(0,m.jsx)(`code`,{className:e,children:t})},M=({href:e,children:t})=>(0,m.jsx)(`a`,{href:e,target:`_blank`,rel:`noreferrer`,onClick:e=>e.stopPropagation(),children:t});function N(e,t,n=!1){return p.Children.map(e,e=>{if(typeof e==`string`)return n?e:(0,m.jsx)(A,{text:e,onOpenPath:t.onOpenPath,onPreviewPath:t.onPreviewPath,onPreviewPathEnd:t.onPreviewPathEnd,isPathLinkable:t.isPathLinkable,resolvePathTitle:t.resolvePathTitle,issueRepo:t.issueRepo,onOpenTask:t.onOpenTask,loadedTasksById:t.loadedTasksById,issueRefsByKey:t.issueRefsByKey});if(!p.isValidElement(e))return e;let r=e.type;if(r===A||r===M||r===`a`)return e;let i=e.props;if(i?.children==null)return e;let a=n||r===j||r===`code`||r===`pre`;return p.cloneElement(e,void 0,N(i.children,t,a))})}function P({text:e,rawBody:t,className:n,onOpenPath:o,onPreviewPath:s,onPreviewPathEnd:c,isPathLinkable:d,resolvePathTitle:h,issueRepo:g,onPrimary:_,onOpenTask:v}){let y=(0,p.useMemo)(()=>a(t||``),[t]),x=F(y?y.text:e),S=i(e=>e.stats?.defaultIssueRepo),k=g??S,A=(0,p.useMemo)(()=>C(x),[x]),P=(0,p.useMemo)(()=>E(x,k),[x,k]),I=i(e=>T(A,e.tasks,e.deliverableTasks)),L=i(e=>e.openTaskDetailById),R=i(e=>O(P,e.issueRefsById)),z=(0,p.useMemo)(()=>{let e=i.getState();return w(A,e.tasks,e.deliverableTasks)},[A,I]),B=(0,p.useMemo)(()=>D(P,i.getState().issueRefsById),[P,R]),V=v??L,H=(0,p.useMemo)(()=>{let e={onOpenPath:o,onPreviewPath:s,onPreviewPathEnd:c,isPathLinkable:d,resolvePathTitle:h,issueRepo:k,onOpenTask:V,loadedTasksById:z,issueRefsByKey:B},t=t=>({children:n})=>(0,m.jsx)(t,{children:N(n,e)});return{p:t(`p`),li:t(`li`),h1:t(`h1`),h2:t(`h2`),h3:t(`h3`),h4:t(`h4`),h5:t(`h5`),h6:t(`h6`),td:t(`td`),th:t(`th`),blockquote:t(`blockquote`),a:M,code:j}},[o,s,c,d,h,k,V,z,B]),U=(0,p.useMemo)(()=>(0,m.jsx)(l,{remarkPlugins:[f],rehypePlugins:[[u,b]],components:H,children:x}),[H,x]);return(0,m.jsxs)(`div`,{className:n,children:[(0,m.jsx)(`div`,{className:r(`chat-markdown`,_&&`chat-markdown-on-primary`),children:U}),y&&y.meta.length>0&&(0,m.jsx)(`div`,{className:`mt-1.5 flex flex-wrap gap-1`,children:y.meta.map(([e,t])=>(0,m.jsxs)(`span`,{className:`inline-flex text-[10px] leading-tight bg-muted px-1.5 py-0.5 rounded text-muted-foreground font-mono`,children:[e,`: `,t]},e))})]})}function F(e){return e.includes(`\\n`)?e.replace(/\\n/g,`
2
2
  `):e}export{P as FormattedBody};
3
- //# sourceMappingURL=formatted-body-impl-BJ7jdbpR.js.map
3
+ //# sourceMappingURL=formatted-body-impl-L1EMnNW6.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatted-body-impl-BJ7jdbpR.js","names":[],"sources":["../../dashboard/src/components/shared/formatted-body-impl.tsx"],"sourcesContent":["import React, { useMemo } from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport rehypeSanitize, { defaultSchema } from 'rehype-sanitize'\nimport remarkGfm from 'remark-gfm'\nimport { CodePreview } from './code-preview'\nimport { IssueRefChip } from './issue-ref-chip'\nimport { TaskChip } from './task-chip'\nimport { cn } from '@/lib/utils'\nimport { parseJsonBody } from '@/lib/display'\nimport { useRelayStore } from '@/store'\nimport type { IssueRef } from '@/lib/issue-types'\n\ntype LoadedTaskChip = { title?: string; status?: string }\n\nexport interface FormattedBodyProps {\n text: string\n rawBody?: string\n className?: string\n onOpenPath?: (path: string, line?: number) => void\n onPreviewPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewPathEnd?: () => void\n isPathLinkable?: (path: string, line?: number) => boolean\n resolvePathTitle?: (path: string, line?: number) => string | undefined\n /** Per-message tracker repo (`owner/repo`) for bare `#NNN` linkify (#533) — the originating\n * agent's project repo, resolved server-side. Falls back to the server-global default (#511). */\n issueRepo?: string\n /** Rendered inside a primary-background bubble (operator's outbound message). Flips link\n * color to primary-foreground so links stay legible against the accent bg (#612). */\n onPrimary?: boolean\n /** Override handler for clicking a resolved T<NNN> chip. Defaults to the store's\n * openTaskDetailById so callers rarely need to pass this explicitly. */\n onOpenTask?: (id: number) => void\n}\n\n// Issue-ref is FIRST so the path matcher can't swallow `owner/repo#NNN` as a file path.\n// URLs still win when they start earlier (leftmost-match), so a URL fragment like\n// `…/a#12` is never mis-linkified.\n// T<NNN> (group 3) is matched before the path group but only when NOT followed by `/`\n// (negative lookahead) so `T128/foo.ts` still routes to the file-path group (#873).\nconst LINK_RE = /((?:[A-Za-z0-9][\\w.-]*\\/[A-Za-z0-9][\\w.-]*)?#\\d+)\\b|(https?:\\/\\/[^\\s`'\")\\]]+)|(\\bT\\d+\\b(?!\\/))|((?:\\/[\\w.@+-][^\\s`'\")\\]]*|(?:\\.{1,2}\\/|[\\w.@+-]+\\/)[^\\s`'\")\\]]*))/g\nconst TASK_REF_RE = /\\bT(\\d+)\\b(?!\\/)/g\nconst ISSUE_REF_RE = /((?:[A-Za-z0-9][\\w.-]*\\/[A-Za-z0-9][\\w.-]*)?#(\\d+))\\b/g\nconst TRAILING_PUNCTUATION = /[.,;:!?]+$/\nconst KNOWN_PROJECT_DIRS = new Set(['src', 'app', 'apps', 'lib', 'server', 'client', 'dashboard', 'orchestrator', 'runner', 'sdk', 'codex', 'claude', 'public', 'docs', 'test', 'tests', 'scripts', 'config', 'configs'])\n\nconst markdownSchema = {\n ...defaultSchema,\n attributes: {\n ...defaultSchema.attributes,\n code: [\n ...(defaultSchema.attributes?.code || []),\n ['className', /^language-[\\w-]+$/],\n ],\n },\n}\n\nfunction looksLikeFilePath(path: string): boolean {\n if (path === '/api' || path.startsWith('/api/')) return false\n if (path.startsWith('/')) return true\n if (path.startsWith('./') || path.startsWith('../')) return true\n const segments = path.split('/').filter(Boolean)\n const first = segments[0] ?? ''\n const last = segments[segments.length - 1] ?? ''\n if (first.startsWith('.')) return true\n if (KNOWN_PROJECT_DIRS.has(first)) return true\n return last.includes('.')\n}\n\nfunction taskChipMeta(task: unknown): { id: number; meta: LoadedTaskChip } | null {\n if (!task || typeof task !== 'object') return null\n const record = task as Record<string, unknown>\n const id = typeof record.id === 'number' ? record.id : typeof record.id === 'string' ? Number(record.id) : NaN\n if (!Number.isSafeInteger(id) || id <= 0) return null\n const title = typeof record.title === 'string' && record.title.trim()\n ? record.title\n : typeof record.label === 'string' && record.label.trim()\n ? record.label\n : undefined\n const status = typeof record.status === 'string' && record.status.trim() ? record.status : undefined\n return { id, meta: { ...(title ? { title } : {}), ...(status ? { status } : {}) } }\n}\n\nfunction extractTaskRefIds(text: string): number[] {\n const ids = new Set<number>()\n for (const match of text.matchAll(TASK_REF_RE)) {\n const id = Number(match[1])\n if (Number.isSafeInteger(id) && id > 0) ids.add(id)\n }\n return [...ids].sort((a, b) => a - b)\n}\n\nfunction collectLoadedTasksById(ids: number[], tasks: unknown[], deliverableTasks: unknown[]): Map<number, LoadedTaskChip> {\n const wanted = new Set(ids)\n const map = new Map<number, LoadedTaskChip>()\n if (wanted.size === 0) return map\n for (const task of tasks) {\n const resolved = taskChipMeta(task)\n if (resolved && wanted.has(resolved.id)) map.set(resolved.id, resolved.meta)\n }\n for (const task of deliverableTasks) {\n const resolved = taskChipMeta(task)\n if (resolved && wanted.has(resolved.id)) map.set(resolved.id, resolved.meta)\n }\n return map\n}\n\nfunction taskMetaVersion(ids: number[], tasks: unknown[], deliverableTasks: unknown[]): string {\n if (ids.length === 0) return ''\n const loaded = collectLoadedTasksById(ids, tasks, deliverableTasks)\n return ids.map((id) => {\n const meta = loaded.get(id)\n return `${id}:${meta?.title ?? ''}:${meta?.status ?? ''}`\n }).join('|')\n}\n\nfunction extractIssueRefKeys(text: string, issueRepo?: string): string[] {\n const keys = new Set<string>()\n for (const match of text.matchAll(ISSUE_REF_RE)) {\n const raw = match[1]\n const num = match[2]\n if (!raw || !num) continue\n const hashIdx = raw.lastIndexOf('#')\n const repo = raw.slice(0, hashIdx) || issueRepo\n if (repo) keys.add(`${repo}#${num}`)\n }\n return [...keys].sort()\n}\n\nfunction collectIssueRefsByKey(keys: string[], issueRefsById: Record<string, IssueRef>): Map<string, IssueRef> {\n const wanted = new Set(keys)\n const map = new Map<string, IssueRef>()\n if (wanted.size === 0) return map\n for (const ref of Object.values(issueRefsById)) {\n const key = `${ref.repo}#${ref.externalKey}`\n if (wanted.has(key)) map.set(key, ref)\n }\n return map\n}\n\nfunction issueRefsVersion(keys: string[], issueRefsById: Record<string, IssueRef>): string {\n if (keys.length === 0) return ''\n const refs = collectIssueRefsByKey(keys, issueRefsById)\n return keys.map((key) => {\n const ref = refs.get(key)\n return `${key}:${ref?.id ?? ''}:${ref?.status ?? ''}:${ref?.updatedAt ?? ''}:${ref?.url ?? ''}`\n }).join('|')\n}\n\nexport function linkParts(\n text: string,\n issueRepo?: string,\n /** Optional loaded task metadata map/set. `T<id>` refs link by pattern even when absent. */\n _knownTaskIds?: { has: (id: number) => boolean },\n /** Map from `repo#externalKey` (e.g. `edimuj/agent-relay#888`) to the full IssueRef.\n * When provided and the ref resolves, the part gets `issueRef` for chip rendering. */\n issueRefsByKey?: Map<string, IssueRef>,\n): Array<{ text: string; path?: string; url?: string; line?: number; issueUrl?: string; taskId?: number; issueRef?: IssueRef }> {\n const parts: Array<{ text: string; path?: string; url?: string; line?: number; issueUrl?: string; taskId?: number; issueRef?: IssueRef }> = []\n let cursor = 0\n for (const match of text.matchAll(LINK_RE)) {\n const rawIssue = match[1]\n const rawUrl = match[2]\n const rawTask = match[3]\n const rawPath = match[4]\n const raw = rawIssue || rawUrl || rawTask || rawPath\n const index = match.index ?? 0\n if (!raw || index < cursor) continue\n\n if (rawTask) {\n const id = Number(rawTask.slice(1))\n if (Number.isSafeInteger(id) && id > 0) {\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: rawTask, taskId: id })\n cursor = index + rawTask.length\n }\n continue\n }\n\n if (rawIssue) {\n // `#NNN` (uses the configured default repo) or `owner/repo#NNN` (explicit repo).\n // With no resolvable repo we leave it as plain text — never emit a broken link.\n const hashIdx = rawIssue.lastIndexOf('#')\n const repo = rawIssue.slice(0, hashIdx) || issueRepo\n const num = rawIssue.slice(hashIdx + 1)\n // Slash-shorthand like `#508/509/513` (#531): the trailing `/509/513` is a run of\n // bare ref numbers, NOT a file path. Consume it as plain text so the path matcher\n // (which would otherwise claim `/509/...` as a phantom path link) can't reach it.\n const trailingRefs = text.slice(index + rawIssue.length).match(/^(?:\\/\\d+)+/)?.[0] ?? ''\n if (repo) {\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n const issueRef = issueRefsByKey?.get(`${repo}#${num}`)\n parts.push({ text: rawIssue, issueUrl: `https://github.com/${repo}/issues/${num}`, ...(issueRef ? { issueRef } : {}) })\n if (trailingRefs) parts.push({ text: trailingRefs })\n cursor = index + rawIssue.length + trailingRefs.length\n } else if (trailingRefs) {\n // No repo to link against, but still neutralize the slash run so it stays plain text.\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: rawIssue + trailingRefs })\n cursor = index + rawIssue.length + trailingRefs.length\n }\n continue\n }\n\n if (rawUrl) {\n const url = rawUrl.replace(TRAILING_PUNCTUATION, '')\n const trimmed = rawUrl.slice(url.length)\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: url, url })\n if (trimmed) parts.push({ text: trimmed })\n cursor = index + rawUrl.length\n continue\n }\n\n if (!raw.includes('/')) continue\n let path = raw.replace(TRAILING_PUNCTUATION, '')\n const trimmed = raw.slice(path.length)\n const lineMatch = path.match(/^(.*):(\\d+)$/)\n const line = lineMatch ? Number(lineMatch[2]) : undefined\n if (lineMatch) path = lineMatch[1]!\n if (!looksLikeFilePath(path)) continue\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: path, path, line: Number.isFinite(line) ? line : undefined })\n if (trimmed) parts.push({ text: trimmed })\n cursor = index + raw.length\n }\n if (cursor < text.length) parts.push({ text: text.slice(cursor) })\n return parts\n}\n\nfunction TextWithLinks({\n text,\n onOpenPath,\n onPreviewPath,\n onPreviewPathEnd,\n isPathLinkable,\n resolvePathTitle,\n issueRepo,\n onOpenTask,\n loadedTasksById,\n issueRefsByKey,\n}: {\n text: string\n onOpenPath?: (path: string, line?: number) => void\n onPreviewPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewPathEnd?: () => void\n isPathLinkable?: (path: string, line?: number) => boolean\n resolvePathTitle?: (path: string, line?: number) => string | undefined\n issueRepo?: string\n onOpenTask?: (id: number) => void\n loadedTasksById?: Map<number, LoadedTaskChip>\n issueRefsByKey?: Map<string, IssueRef>\n}) {\n const parts = useMemo(() => linkParts(text, issueRepo, loadedTasksById, issueRefsByKey), [text, issueRepo, loadedTasksById, issueRefsByKey])\n if (parts.length === 1 && !parts[0]?.path && !parts[0]?.url && !parts[0]?.issueUrl && !parts[0]?.taskId) return <>{text}</>\n return (\n <>\n {parts.map((part, index) => {\n if (part.taskId != null) {\n if (onOpenTask) {\n const t = loadedTasksById?.get(part.taskId)\n return (\n <TaskChip\n key={`task-${part.taskId}-${index}`}\n id={part.taskId}\n title={t?.title}\n status={t?.status}\n compact\n onClick={() => onOpenTask(part.taskId!)}\n />\n )\n }\n return <span key={index}>{part.text}</span>\n }\n if (part.issueUrl) {\n if (part.issueRef) {\n return <IssueRefChip key={`${part.issueUrl}-${index}`} issueRef={part.issueRef} compact />\n }\n return (\n <a\n key={`${part.issueUrl}-${index}`}\n href={part.issueUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"font-medium underline decoration-dotted underline-offset-2\"\n onClick={(e) => e.stopPropagation()}\n >\n {part.text}\n </a>\n )\n }\n if (part.url) {\n return (\n <a\n key={`${part.url}-${index}`}\n href={part.url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"underline decoration-dotted underline-offset-2\"\n onClick={(e) => e.stopPropagation()}\n >\n {part.text}\n </a>\n )\n }\n if (part.path && onOpenPath) {\n if (isPathLinkable && !isPathLinkable(part.path, part.line)) return <span key={`${part.path}-${index}`}>{part.text}{part.line ? `:${part.line}` : ''}</span>\n const resolvedTitle = resolvePathTitle?.(part.path, part.line)\n return (\n <button\n key={`${part.path}-${index}`}\n type=\"button\"\n className=\"chat-link rounded-sm font-mono underline decoration-dotted underline-offset-2\"\n onClick={(e) => { e.stopPropagation(); onOpenPath(part.path!, part.line) }}\n onMouseEnter={(e) => onPreviewPath?.(part.path!, part.line, e.currentTarget.getBoundingClientRect())}\n onFocus={(e) => onPreviewPath?.(part.path!, part.line, e.currentTarget.getBoundingClientRect())}\n onMouseLeave={() => onPreviewPathEnd?.()}\n onBlur={() => onPreviewPathEnd?.()}\n title={resolvedTitle ? `Open ${resolvedTitle}` : 'Open file'}\n >\n {part.text}{part.line ? `:${part.line}` : ''}\n </button>\n )\n }\n return <span key={index}>{part.text}</span>\n })}\n </>\n )\n}\n\ninterface LinkifyContext {\n onOpenPath?: (path: string, line?: number) => void\n onPreviewPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewPathEnd?: () => void\n isPathLinkable?: (path: string, line?: number) => boolean\n resolvePathTitle?: (path: string, line?: number) => string | undefined\n issueRepo?: string\n onOpenTask?: (id: number) => void\n loadedTasksById?: Map<number, LoadedTaskChip>\n issueRefsByKey?: Map<string, IssueRef>\n}\n\n// Inline code & fenced blocks (the `code`/`pre` overrides) — descend with `inCode`\n// set so `#NNN` inside backticks renders verbatim instead of linkifying.\nconst CodeComponent = ({ className: codeClass, children }: { className?: string; children?: React.ReactNode }) => {\n const match = /language-([\\w-]+)/.exec(codeClass || '')\n const code = String(children).replace(/\\n$/, '')\n if (match) {\n return <CodePreview content={code} path={`.${match[1]}`} languageHint={match[1]} compact />\n }\n return <code className={codeClass}>{children}</code>\n}\n\n// Markdown links — render the anchor and pass children through untouched. The\n// recursive walk skips this subtree so we never linkify a `#NNN` *inside* link\n// text (which would nest one <a> inside another). Text adjacent to a link still\n// linkifies via the enclosing block's walk.\nconst AnchorComponent = ({ href, children }: { href?: string; children?: React.ReactNode }) => (\n <a href={href} target=\"_blank\" rel=\"noreferrer\" onClick={(e) => e.stopPropagation()}>\n {children}\n </a>\n)\n\n// Recursively linkify every text leaf in a markdown subtree (#550). react-markdown\n// hands each block override its already-rendered React children, so nested inline\n// elements (strong/em/del/headings/table cells/…) arrive as element children with\n// their text strings buried one or more levels deep. We descend through them and\n// apply `linkParts` (via TextWithLinks) at every string leaf, keeping the linkify\n// rule in this ONE place. `inCode` suppresses linkify inside code/pre; anchors are\n// left whole to avoid nested links.\nfunction processTextChildren(children: React.ReactNode, ctx: LinkifyContext, inCode = false): React.ReactNode {\n return React.Children.map(children, (child) => {\n if (typeof child === 'string') {\n if (inCode) return child\n return (\n <TextWithLinks\n text={child}\n onOpenPath={ctx.onOpenPath}\n onPreviewPath={ctx.onPreviewPath}\n onPreviewPathEnd={ctx.onPreviewPathEnd}\n isPathLinkable={ctx.isPathLinkable}\n resolvePathTitle={ctx.resolvePathTitle}\n issueRepo={ctx.issueRepo}\n onOpenTask={ctx.onOpenTask}\n loadedTasksById={ctx.loadedTasksById}\n issueRefsByKey={ctx.issueRefsByKey}\n />\n )\n }\n if (!React.isValidElement(child)) return child\n const type = child.type\n // Already-linkified leaves carry their text in a prop, not children — leave whole.\n if (type === TextWithLinks) return child\n // Never linkify inside a link (no nested <a>); render the anchor subtree verbatim.\n if (type === AnchorComponent || type === 'a') return child\n const props = child.props as { children?: React.ReactNode }\n if (props?.children == null) return child\n const nextInCode = inCode || type === CodeComponent || type === 'code' || type === 'pre'\n return React.cloneElement(child, undefined, processTextChildren(props.children, ctx, nextInCode))\n })\n}\n\nexport function FormattedBody({ text, rawBody, className, onOpenPath, onPreviewPath, onPreviewPathEnd, isPathLinkable, resolvePathTitle, issueRepo: issueRepoProp, onPrimary, onOpenTask: onOpenTaskProp }: FormattedBodyProps) {\n const parsed = useMemo(() => parseJsonBody(rawBody || ''), [rawBody])\n const content = unescapeNewlines(parsed ? parsed.text : text)\n // Per-message tracker repo for bare #NNN linkify (#533): the originating agent's project\n // repo (`issueRepoProp`) wins; the server-global default (#511) is only a last-resort\n // fallback for messages with no resolvable project. undefined → #NNN stays plain text.\n const globalIssueRepo = useRelayStore((s) => s.stats?.defaultIssueRepo)\n const issueRepo = issueRepoProp ?? globalIssueRepo\n const taskRefIds = useMemo(() => extractTaskRefIds(content), [content])\n const issueRefKeys = useMemo(() => extractIssueRefKeys(content, issueRepo), [content, issueRepo])\n\n // T<NNN> linkify (#989): pattern matches render immediately; loaded task rows only hydrate\n // chip title/status. The selector returns a primitive version for refs in THIS message, so\n // unrelated task polling does not re-render every bubble.\n const storeTaskMetaVersion = useRelayStore((s) => taskMetaVersion(taskRefIds, s.tasks, s.deliverableTasks))\n const storeOpenTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n // #888: issue-ref chips use the same per-message primitive subscription.\n const storeIssueRefsVersion = useRelayStore((s) => issueRefsVersion(issueRefKeys, s.issueRefsById))\n\n const loadedTasksById = useMemo(() => {\n const state = useRelayStore.getState()\n return collectLoadedTasksById(taskRefIds, state.tasks, state.deliverableTasks)\n }, [taskRefIds, storeTaskMetaVersion])\n\n const issueRefsByKey = useMemo(() => {\n return collectIssueRefsByKey(issueRefKeys, useRelayStore.getState().issueRefsById)\n }, [issueRefKeys, storeIssueRefsVersion])\n\n const onOpenTask = onOpenTaskProp ?? storeOpenTaskDetailById\n\n const components = useMemo(() => {\n const ctx: LinkifyContext = { onOpenPath, onPreviewPath, onPreviewPathEnd, isPathLinkable, resolvePathTitle, issueRepo, onOpenTask, loadedTasksById, issueRefsByKey }\n // Every text-bearing block routes its children through the recursive walk, which\n // descends into nested inline elements (strong/em/del/code/links) on its own — so\n // these stay one-liners and the linkify rule lives only in processTextChildren.\n const block = (Tag: 'p' | 'li' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'td' | 'th' | 'blockquote') =>\n ({ children }: { children?: React.ReactNode }) => <Tag>{processTextChildren(children, ctx)}</Tag>\n return {\n p: block('p'),\n li: block('li'),\n h1: block('h1'),\n h2: block('h2'),\n h3: block('h3'),\n h4: block('h4'),\n h5: block('h5'),\n h6: block('h6'),\n td: block('td'),\n th: block('th'),\n blockquote: block('blockquote'),\n a: AnchorComponent,\n code: CodeComponent,\n }\n }, [onOpenPath, onPreviewPath, onPreviewPathEnd, isPathLinkable, resolvePathTitle, issueRepo, onOpenTask, loadedTasksById, issueRefsByKey])\n\n const markdown = useMemo(() => (\n <ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[[rehypeSanitize, markdownSchema]]} components={components}>\n {content}\n </ReactMarkdown>\n ), [components, content])\n\n return (\n <div className={className}>\n <div className={cn('chat-markdown', onPrimary && 'chat-markdown-on-primary')}>\n {markdown}\n </div>\n {parsed && parsed.meta.length > 0 && (\n <div className=\"mt-1.5 flex flex-wrap gap-1\">\n {parsed.meta.map(([k, v]) => (\n <span key={k} className=\"inline-flex text-[10px] leading-tight bg-muted px-1.5 py-0.5 rounded text-muted-foreground font-mono\">\n {k}: {v}\n </span>\n ))}\n </div>\n )}\n </div>\n )\n}\n\nfunction unescapeNewlines(text: string): string {\n if (!text.includes('\\\\n')) return text\n return text.replace(/\\\\n/g, '\\n')\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"formatted-body-impl-L1EMnNW6.js","names":[],"sources":["../../dashboard/src/components/shared/formatted-body-impl.tsx"],"sourcesContent":["import React, { useMemo } from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport rehypeSanitize, { defaultSchema } from 'rehype-sanitize'\nimport remarkGfm from 'remark-gfm'\nimport { CodePreview } from './code-preview'\nimport { IssueRefChip } from './issue-ref-chip'\nimport { TaskChip } from './task-chip'\nimport { cn } from '@/lib/utils'\nimport { parseJsonBody } from '@/lib/display'\nimport { useRelayStore } from '@/store'\nimport type { IssueRef } from '@/lib/issue-types'\n\ntype LoadedTaskChip = { title?: string; status?: string }\n\nexport interface FormattedBodyProps {\n text: string\n rawBody?: string\n className?: string\n onOpenPath?: (path: string, line?: number) => void\n onPreviewPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewPathEnd?: () => void\n isPathLinkable?: (path: string, line?: number) => boolean\n resolvePathTitle?: (path: string, line?: number) => string | undefined\n /** Per-message tracker repo (`owner/repo`) for bare `#NNN` linkify (#533) — the originating\n * agent's project repo, resolved server-side. Falls back to the server-global default (#511). */\n issueRepo?: string\n /** Rendered inside a primary-background bubble (operator's outbound message). Flips link\n * color to primary-foreground so links stay legible against the accent bg (#612). */\n onPrimary?: boolean\n /** Override handler for clicking a resolved T<NNN> chip. Defaults to the store's\n * openTaskDetailById so callers rarely need to pass this explicitly. */\n onOpenTask?: (id: number) => void\n}\n\n// Issue-ref is FIRST so the path matcher can't swallow `owner/repo#NNN` as a file path.\n// URLs still win when they start earlier (leftmost-match), so a URL fragment like\n// `…/a#12` is never mis-linkified.\n// T<NNN> (group 3) is matched before the path group but only when NOT followed by `/`\n// (negative lookahead) so `T128/foo.ts` still routes to the file-path group (#873).\nconst LINK_RE = /((?:[A-Za-z0-9][\\w.-]*\\/[A-Za-z0-9][\\w.-]*)?#\\d+)\\b|(https?:\\/\\/[^\\s`'\")\\]]+)|(\\bT\\d+\\b(?!\\/))|((?:\\/[\\w.@+-][^\\s`'\")\\]]*|(?:\\.{1,2}\\/|[\\w.@+-]+\\/)[^\\s`'\")\\]]*))/g\nconst TASK_REF_RE = /\\bT(\\d+)\\b(?!\\/)/g\nconst ISSUE_REF_RE = /((?:[A-Za-z0-9][\\w.-]*\\/[A-Za-z0-9][\\w.-]*)?#(\\d+))\\b/g\nconst TRAILING_PUNCTUATION = /[.,;:!?]+$/\nconst KNOWN_PROJECT_DIRS = new Set(['src', 'app', 'apps', 'lib', 'server', 'client', 'dashboard', 'orchestrator', 'runner', 'sdk', 'codex', 'claude', 'public', 'docs', 'test', 'tests', 'scripts', 'config', 'configs'])\n\nconst markdownSchema = {\n ...defaultSchema,\n attributes: {\n ...defaultSchema.attributes,\n code: [\n ...(defaultSchema.attributes?.code || []),\n ['className', /^language-[\\w-]+$/],\n ],\n },\n}\n\nfunction looksLikeFilePath(path: string): boolean {\n if (path === '/api' || path.startsWith('/api/')) return false\n if (path.startsWith('/')) return true\n if (path.startsWith('./') || path.startsWith('../')) return true\n const segments = path.split('/').filter(Boolean)\n const first = segments[0] ?? ''\n const last = segments[segments.length - 1] ?? ''\n if (first.startsWith('.')) return true\n if (KNOWN_PROJECT_DIRS.has(first)) return true\n return last.includes('.')\n}\n\nfunction taskChipMeta(task: unknown): { id: number; meta: LoadedTaskChip } | null {\n if (!task || typeof task !== 'object') return null\n const record = task as Record<string, unknown>\n const id = typeof record.id === 'number' ? record.id : typeof record.id === 'string' ? Number(record.id) : NaN\n if (!Number.isSafeInteger(id) || id <= 0) return null\n const title = typeof record.title === 'string' && record.title.trim()\n ? record.title\n : typeof record.label === 'string' && record.label.trim()\n ? record.label\n : undefined\n const status = typeof record.status === 'string' && record.status.trim() ? record.status : undefined\n return { id, meta: { ...(title ? { title } : {}), ...(status ? { status } : {}) } }\n}\n\nfunction extractTaskRefIds(text: string): number[] {\n const ids = new Set<number>()\n for (const match of text.matchAll(TASK_REF_RE)) {\n const id = Number(match[1])\n if (Number.isSafeInteger(id) && id > 0) ids.add(id)\n }\n return [...ids].sort((a, b) => a - b)\n}\n\nfunction collectLoadedTasksById(ids: number[], tasks: unknown[], deliverableTasks: unknown[]): Map<number, LoadedTaskChip> {\n const wanted = new Set(ids)\n const map = new Map<number, LoadedTaskChip>()\n if (wanted.size === 0) return map\n for (const task of tasks) {\n const resolved = taskChipMeta(task)\n if (resolved && wanted.has(resolved.id)) map.set(resolved.id, resolved.meta)\n }\n for (const task of deliverableTasks) {\n const resolved = taskChipMeta(task)\n if (resolved && wanted.has(resolved.id)) map.set(resolved.id, resolved.meta)\n }\n return map\n}\n\nfunction taskMetaVersion(ids: number[], tasks: unknown[], deliverableTasks: unknown[]): string {\n if (ids.length === 0) return ''\n const loaded = collectLoadedTasksById(ids, tasks, deliverableTasks)\n return ids.map((id) => {\n const meta = loaded.get(id)\n return `${id}:${meta?.title ?? ''}:${meta?.status ?? ''}`\n }).join('|')\n}\n\nfunction extractIssueRefKeys(text: string, issueRepo?: string): string[] {\n const keys = new Set<string>()\n for (const match of text.matchAll(ISSUE_REF_RE)) {\n const raw = match[1]\n const num = match[2]\n if (!raw || !num) continue\n const hashIdx = raw.lastIndexOf('#')\n const repo = raw.slice(0, hashIdx) || issueRepo\n if (repo) keys.add(`${repo}#${num}`)\n }\n return [...keys].sort()\n}\n\nfunction collectIssueRefsByKey(keys: string[], issueRefsById: Record<string, IssueRef>): Map<string, IssueRef> {\n const wanted = new Set(keys)\n const map = new Map<string, IssueRef>()\n if (wanted.size === 0) return map\n for (const ref of Object.values(issueRefsById)) {\n const key = `${ref.repo}#${ref.externalKey}`\n if (wanted.has(key)) map.set(key, ref)\n }\n return map\n}\n\nfunction issueRefsVersion(keys: string[], issueRefsById: Record<string, IssueRef>): string {\n if (keys.length === 0) return ''\n const refs = collectIssueRefsByKey(keys, issueRefsById)\n return keys.map((key) => {\n const ref = refs.get(key)\n return `${key}:${ref?.id ?? ''}:${ref?.status ?? ''}:${ref?.updatedAt ?? ''}:${ref?.url ?? ''}`\n }).join('|')\n}\n\nexport function linkParts(\n text: string,\n issueRepo?: string,\n /** Optional loaded task metadata map/set. `T<id>` refs link by pattern even when absent. */\n _knownTaskIds?: { has: (id: number) => boolean },\n /** Map from `repo#externalKey` (e.g. `edimuj/agent-relay#888`) to the full IssueRef.\n * When provided and the ref resolves, the part gets `issueRef` for chip rendering. */\n issueRefsByKey?: Map<string, IssueRef>,\n): Array<{ text: string; path?: string; url?: string; line?: number; issueUrl?: string; taskId?: number; issueRef?: IssueRef }> {\n const parts: Array<{ text: string; path?: string; url?: string; line?: number; issueUrl?: string; taskId?: number; issueRef?: IssueRef }> = []\n let cursor = 0\n for (const match of text.matchAll(LINK_RE)) {\n const rawIssue = match[1]\n const rawUrl = match[2]\n const rawTask = match[3]\n const rawPath = match[4]\n const raw = rawIssue || rawUrl || rawTask || rawPath\n const index = match.index ?? 0\n if (!raw || index < cursor) continue\n\n if (rawTask) {\n const id = Number(rawTask.slice(1))\n if (Number.isSafeInteger(id) && id > 0) {\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: rawTask, taskId: id })\n cursor = index + rawTask.length\n }\n continue\n }\n\n if (rawIssue) {\n // `#NNN` (uses the configured default repo) or `owner/repo#NNN` (explicit repo).\n // With no resolvable repo we leave it as plain text — never emit a broken link.\n const hashIdx = rawIssue.lastIndexOf('#')\n const repo = rawIssue.slice(0, hashIdx) || issueRepo\n const num = rawIssue.slice(hashIdx + 1)\n // Slash-shorthand like `#508/509/513` (#531): the trailing `/509/513` is a run of\n // bare ref numbers, NOT a file path. Consume it as plain text so the path matcher\n // (which would otherwise claim `/509/...` as a phantom path link) can't reach it.\n const trailingRefs = text.slice(index + rawIssue.length).match(/^(?:\\/\\d+)+/)?.[0] ?? ''\n if (repo) {\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n const issueRef = issueRefsByKey?.get(`${repo}#${num}`)\n parts.push({ text: rawIssue, issueUrl: `https://github.com/${repo}/issues/${num}`, ...(issueRef ? { issueRef } : {}) })\n if (trailingRefs) parts.push({ text: trailingRefs })\n cursor = index + rawIssue.length + trailingRefs.length\n } else if (trailingRefs) {\n // No repo to link against, but still neutralize the slash run so it stays plain text.\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: rawIssue + trailingRefs })\n cursor = index + rawIssue.length + trailingRefs.length\n }\n continue\n }\n\n if (rawUrl) {\n const url = rawUrl.replace(TRAILING_PUNCTUATION, '')\n const trimmed = rawUrl.slice(url.length)\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: url, url })\n if (trimmed) parts.push({ text: trimmed })\n cursor = index + rawUrl.length\n continue\n }\n\n if (!raw.includes('/')) continue\n let path = raw.replace(TRAILING_PUNCTUATION, '')\n const trimmed = raw.slice(path.length)\n const lineMatch = path.match(/^(.*):(\\d+)$/)\n const line = lineMatch ? Number(lineMatch[2]) : undefined\n if (lineMatch) path = lineMatch[1]!\n if (!looksLikeFilePath(path)) continue\n if (index > cursor) parts.push({ text: text.slice(cursor, index) })\n parts.push({ text: path, path, line: Number.isFinite(line) ? line : undefined })\n if (trimmed) parts.push({ text: trimmed })\n cursor = index + raw.length\n }\n if (cursor < text.length) parts.push({ text: text.slice(cursor) })\n return parts\n}\n\nfunction TextWithLinks({\n text,\n onOpenPath,\n onPreviewPath,\n onPreviewPathEnd,\n isPathLinkable,\n resolvePathTitle,\n issueRepo,\n onOpenTask,\n loadedTasksById,\n issueRefsByKey,\n}: {\n text: string\n onOpenPath?: (path: string, line?: number) => void\n onPreviewPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewPathEnd?: () => void\n isPathLinkable?: (path: string, line?: number) => boolean\n resolvePathTitle?: (path: string, line?: number) => string | undefined\n issueRepo?: string\n onOpenTask?: (id: number) => void\n loadedTasksById?: Map<number, LoadedTaskChip>\n issueRefsByKey?: Map<string, IssueRef>\n}) {\n const parts = useMemo(() => linkParts(text, issueRepo, loadedTasksById, issueRefsByKey), [text, issueRepo, loadedTasksById, issueRefsByKey])\n if (parts.length === 1 && !parts[0]?.path && !parts[0]?.url && !parts[0]?.issueUrl && !parts[0]?.taskId) return <>{text}</>\n return (\n <>\n {parts.map((part, index) => {\n if (part.taskId != null) {\n if (onOpenTask) {\n const t = loadedTasksById?.get(part.taskId)\n return (\n <TaskChip\n key={`task-${part.taskId}-${index}`}\n id={part.taskId}\n title={t?.title}\n status={t?.status}\n compact\n onClick={() => onOpenTask(part.taskId!)}\n />\n )\n }\n return <span key={index}>{part.text}</span>\n }\n if (part.issueUrl) {\n if (part.issueRef) {\n return <IssueRefChip key={`${part.issueUrl}-${index}`} issueRef={part.issueRef} compact />\n }\n return (\n <a\n key={`${part.issueUrl}-${index}`}\n href={part.issueUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"font-medium underline decoration-dotted underline-offset-2\"\n onClick={(e) => e.stopPropagation()}\n >\n {part.text}\n </a>\n )\n }\n if (part.url) {\n return (\n <a\n key={`${part.url}-${index}`}\n href={part.url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"underline decoration-dotted underline-offset-2\"\n onClick={(e) => e.stopPropagation()}\n >\n {part.text}\n </a>\n )\n }\n if (part.path && onOpenPath) {\n if (isPathLinkable && !isPathLinkable(part.path, part.line)) return <span key={`${part.path}-${index}`}>{part.text}{part.line ? `:${part.line}` : ''}</span>\n const resolvedTitle = resolvePathTitle?.(part.path, part.line)\n return (\n <button\n key={`${part.path}-${index}`}\n type=\"button\"\n className=\"chat-link rounded-sm font-mono underline decoration-dotted underline-offset-2\"\n onClick={(e) => { e.stopPropagation(); onOpenPath(part.path!, part.line) }}\n onMouseEnter={(e) => onPreviewPath?.(part.path!, part.line, e.currentTarget.getBoundingClientRect())}\n onFocus={(e) => onPreviewPath?.(part.path!, part.line, e.currentTarget.getBoundingClientRect())}\n onMouseLeave={() => onPreviewPathEnd?.()}\n onBlur={() => onPreviewPathEnd?.()}\n title={resolvedTitle ? `Open ${resolvedTitle}` : 'Open file'}\n >\n {part.text}{part.line ? `:${part.line}` : ''}\n </button>\n )\n }\n return <span key={index}>{part.text}</span>\n })}\n </>\n )\n}\n\ninterface LinkifyContext {\n onOpenPath?: (path: string, line?: number) => void\n onPreviewPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewPathEnd?: () => void\n isPathLinkable?: (path: string, line?: number) => boolean\n resolvePathTitle?: (path: string, line?: number) => string | undefined\n issueRepo?: string\n onOpenTask?: (id: number) => void\n loadedTasksById?: Map<number, LoadedTaskChip>\n issueRefsByKey?: Map<string, IssueRef>\n}\n\n// Inline code & fenced blocks (the `code`/`pre` overrides) — descend with `inCode`\n// set so `#NNN` inside backticks renders verbatim instead of linkifying.\nconst CodeComponent = ({ className: codeClass, children }: { className?: string; children?: React.ReactNode }) => {\n const match = /language-([\\w-]+)/.exec(codeClass || '')\n const code = String(children).replace(/\\n$/, '')\n if (match) {\n return <CodePreview content={code} path={`.${match[1]}`} languageHint={match[1]} compact />\n }\n return <code className={codeClass}>{children}</code>\n}\n\n// Markdown links — render the anchor and pass children through untouched. The\n// recursive walk skips this subtree so we never linkify a `#NNN` *inside* link\n// text (which would nest one <a> inside another). Text adjacent to a link still\n// linkifies via the enclosing block's walk.\nconst AnchorComponent = ({ href, children }: { href?: string; children?: React.ReactNode }) => (\n <a href={href} target=\"_blank\" rel=\"noreferrer\" onClick={(e) => e.stopPropagation()}>\n {children}\n </a>\n)\n\n// Recursively linkify every text leaf in a markdown subtree (#550). react-markdown\n// hands each block override its already-rendered React children, so nested inline\n// elements (strong/em/del/headings/table cells/…) arrive as element children with\n// their text strings buried one or more levels deep. We descend through them and\n// apply `linkParts` (via TextWithLinks) at every string leaf, keeping the linkify\n// rule in this ONE place. `inCode` suppresses linkify inside code/pre; anchors are\n// left whole to avoid nested links.\nfunction processTextChildren(children: React.ReactNode, ctx: LinkifyContext, inCode = false): React.ReactNode {\n return React.Children.map(children, (child) => {\n if (typeof child === 'string') {\n if (inCode) return child\n return (\n <TextWithLinks\n text={child}\n onOpenPath={ctx.onOpenPath}\n onPreviewPath={ctx.onPreviewPath}\n onPreviewPathEnd={ctx.onPreviewPathEnd}\n isPathLinkable={ctx.isPathLinkable}\n resolvePathTitle={ctx.resolvePathTitle}\n issueRepo={ctx.issueRepo}\n onOpenTask={ctx.onOpenTask}\n loadedTasksById={ctx.loadedTasksById}\n issueRefsByKey={ctx.issueRefsByKey}\n />\n )\n }\n if (!React.isValidElement(child)) return child\n const type = child.type\n // Already-linkified leaves carry their text in a prop, not children — leave whole.\n if (type === TextWithLinks) return child\n // Never linkify inside a link (no nested <a>); render the anchor subtree verbatim.\n if (type === AnchorComponent || type === 'a') return child\n const props = child.props as { children?: React.ReactNode }\n if (props?.children == null) return child\n const nextInCode = inCode || type === CodeComponent || type === 'code' || type === 'pre'\n return React.cloneElement(child, undefined, processTextChildren(props.children, ctx, nextInCode))\n })\n}\n\nexport function FormattedBody({ text, rawBody, className, onOpenPath, onPreviewPath, onPreviewPathEnd, isPathLinkable, resolvePathTitle, issueRepo: issueRepoProp, onPrimary, onOpenTask: onOpenTaskProp }: FormattedBodyProps) {\n const parsed = useMemo(() => parseJsonBody(rawBody || ''), [rawBody])\n const content = unescapeNewlines(parsed ? parsed.text : text)\n // Per-message tracker repo for bare #NNN linkify (#533): the originating agent's project\n // repo (`issueRepoProp`) wins; the server-global default (#511) is only a last-resort\n // fallback for messages with no resolvable project. undefined → #NNN stays plain text.\n const globalIssueRepo = useRelayStore((s) => s.stats?.defaultIssueRepo)\n const issueRepo = issueRepoProp ?? globalIssueRepo\n const taskRefIds = useMemo(() => extractTaskRefIds(content), [content])\n const issueRefKeys = useMemo(() => extractIssueRefKeys(content, issueRepo), [content, issueRepo])\n\n // T<NNN> linkify (#989): pattern matches render immediately; loaded task rows only hydrate\n // chip title/status. The selector returns a primitive version for refs in THIS message, so\n // unrelated task polling does not re-render every bubble.\n const storeTaskMetaVersion = useRelayStore((s) => taskMetaVersion(taskRefIds, s.tasks, s.deliverableTasks))\n const storeOpenTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n // #888: issue-ref chips use the same per-message primitive subscription.\n const storeIssueRefsVersion = useRelayStore((s) => issueRefsVersion(issueRefKeys, s.issueRefsById))\n\n const loadedTasksById = useMemo(() => {\n const state = useRelayStore.getState()\n return collectLoadedTasksById(taskRefIds, state.tasks, state.deliverableTasks)\n }, [taskRefIds, storeTaskMetaVersion])\n\n const issueRefsByKey = useMemo(() => {\n return collectIssueRefsByKey(issueRefKeys, useRelayStore.getState().issueRefsById)\n }, [issueRefKeys, storeIssueRefsVersion])\n\n const onOpenTask = onOpenTaskProp ?? storeOpenTaskDetailById\n\n const components = useMemo(() => {\n const ctx: LinkifyContext = { onOpenPath, onPreviewPath, onPreviewPathEnd, isPathLinkable, resolvePathTitle, issueRepo, onOpenTask, loadedTasksById, issueRefsByKey }\n // Every text-bearing block routes its children through the recursive walk, which\n // descends into nested inline elements (strong/em/del/code/links) on its own — so\n // these stay one-liners and the linkify rule lives only in processTextChildren.\n const block = (Tag: 'p' | 'li' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'td' | 'th' | 'blockquote') =>\n ({ children }: { children?: React.ReactNode }) => <Tag>{processTextChildren(children, ctx)}</Tag>\n return {\n p: block('p'),\n li: block('li'),\n h1: block('h1'),\n h2: block('h2'),\n h3: block('h3'),\n h4: block('h4'),\n h5: block('h5'),\n h6: block('h6'),\n td: block('td'),\n th: block('th'),\n blockquote: block('blockquote'),\n a: AnchorComponent,\n code: CodeComponent,\n }\n }, [onOpenPath, onPreviewPath, onPreviewPathEnd, isPathLinkable, resolvePathTitle, issueRepo, onOpenTask, loadedTasksById, issueRefsByKey])\n\n const markdown = useMemo(() => (\n <ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[[rehypeSanitize, markdownSchema]]} components={components}>\n {content}\n </ReactMarkdown>\n ), [components, content])\n\n return (\n <div className={className}>\n <div className={cn('chat-markdown', onPrimary && 'chat-markdown-on-primary')}>\n {markdown}\n </div>\n {parsed && parsed.meta.length > 0 && (\n <div className=\"mt-1.5 flex flex-wrap gap-1\">\n {parsed.meta.map(([k, v]) => (\n <span key={k} className=\"inline-flex text-[10px] leading-tight bg-muted px-1.5 py-0.5 rounded text-muted-foreground font-mono\">\n {k}: {v}\n </span>\n ))}\n </div>\n )}\n </div>\n )\n}\n\nfunction unescapeNewlines(text: string): string {\n if (!text.includes('\\\\n')) return text\n return text.replace(/\\\\n/g, '\\n')\n}\n"],"mappings":"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"}