quadwork 1.10.1 → 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/quadwork.js +84 -56
- package/out/404.html +1 -1
- package/out/__next.__PAGE__.txt +1 -1
- package/out/__next._full.txt +1 -1
- package/out/__next._head.txt +1 -1
- package/out/__next._index.txt +1 -1
- package/out/__next._tree.txt +1 -1
- package/out/_next/static/chunks/{0gaekhrfy94vz.js → 0a5314ra5t9bs.js} +1 -1
- package/out/_next/static/chunks/{0hirada7763yr.js → 0ge87xt6a9j~..js} +1 -1
- package/out/_next/static/chunks/{16g.ca89g7fib.js → 0n~dq4kpx9xxx.js} +1 -1
- package/out/_next/static/chunks/turbopack-0qm-e3ifrz~2u.js +1 -0
- package/out/_not-found/__next._full.txt +1 -1
- package/out/_not-found/__next._head.txt +1 -1
- package/out/_not-found/__next._index.txt +1 -1
- package/out/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/out/_not-found/__next._not-found.txt +1 -1
- package/out/_not-found/__next._tree.txt +1 -1
- package/out/_not-found.html +1 -1
- package/out/_not-found.txt +1 -1
- package/out/app-shell/__next._full.txt +1 -1
- package/out/app-shell/__next._head.txt +1 -1
- package/out/app-shell/__next._index.txt +1 -1
- package/out/app-shell/__next._tree.txt +1 -1
- package/out/app-shell/__next.app-shell.__PAGE__.txt +1 -1
- package/out/app-shell/__next.app-shell.txt +1 -1
- package/out/app-shell.html +1 -1
- package/out/app-shell.txt +1 -1
- package/out/index.html +1 -1
- package/out/index.txt +1 -1
- package/out/project/_/__next._full.txt +2 -2
- package/out/project/_/__next._head.txt +1 -1
- package/out/project/_/__next._index.txt +1 -1
- package/out/project/_/__next._tree.txt +1 -1
- package/out/project/_/__next.project.$d$id.__PAGE__.txt +2 -2
- package/out/project/_/__next.project.$d$id.txt +1 -1
- package/out/project/_/__next.project.txt +1 -1
- package/out/project/_/queue/__next._full.txt +1 -1
- package/out/project/_/queue/__next._head.txt +1 -1
- package/out/project/_/queue/__next._index.txt +1 -1
- package/out/project/_/queue/__next._tree.txt +1 -1
- package/out/project/_/queue/__next.project.$d$id.queue.__PAGE__.txt +1 -1
- package/out/project/_/queue/__next.project.$d$id.queue.txt +1 -1
- package/out/project/_/queue/__next.project.$d$id.txt +1 -1
- package/out/project/_/queue/__next.project.txt +1 -1
- package/out/project/_/queue.html +1 -1
- package/out/project/_/queue.txt +1 -1
- package/out/project/_.html +1 -1
- package/out/project/_.txt +2 -2
- package/out/settings/__next._full.txt +1 -1
- package/out/settings/__next._head.txt +1 -1
- package/out/settings/__next._index.txt +1 -1
- package/out/settings/__next._tree.txt +1 -1
- package/out/settings/__next.settings.__PAGE__.txt +1 -1
- package/out/settings/__next.settings.txt +1 -1
- package/out/settings.html +1 -1
- package/out/settings.txt +1 -1
- package/out/setup/__next._full.txt +1 -1
- package/out/setup/__next._head.txt +1 -1
- package/out/setup/__next._index.txt +1 -1
- package/out/setup/__next._tree.txt +1 -1
- package/out/setup/__next.setup.__PAGE__.txt +1 -1
- package/out/setup/__next.setup.txt +1 -1
- package/out/setup.html +1 -1
- package/out/setup.txt +1 -1
- package/package.json +2 -2
- package/server/__tests__/bridge-auto-stop-guard.test.js +134 -0
- package/server/__tests__/scrub-secrets.test.js +234 -0
- package/server/__tests__/v1110-security-qa.test.js +312 -0
- package/server/config.js +29 -5
- package/server/index.js +45 -27
- package/server/install-agentchattr.js +12 -11
- package/server/routes.js +27 -30
- package/server/scrub-secrets.js +51 -0
- package/out/_next/static/chunks/turbopack-0lcwh84lrj9gi.js +0 -1
- /package/out/_next/static/{MA2-1YByee5M0-bbLgqQD → QmshV04af9o06krSyFHwf}/_buildManifest.js +0 -0
- /package/out/_next/static/{MA2-1YByee5M0-bbLgqQD → QmshV04af9o06krSyFHwf}/_clientMiddlewareManifest.js +0 -0
- /package/out/_next/static/{MA2-1YByee5M0-bbLgqQD → QmshV04af9o06krSyFHwf}/_ssgManifest.js +0 -0
|
@@ -24,4 +24,4 @@ WARNING: This link could potentially be dangerous`)){let i=window.open();if(i){t
|
|
|
24
24
|
@re1 & @re2: Review open PRs. If @dev pushed fixes, re-review. Post verdict on PR AND notify @dev here.
|
|
25
25
|
ALL: Communicate via this chat by tagging agents. Your terminal is NOT visible.`},[e]),[P,B]=(0,_.useState)(!1),[L,A]=(0,_.useState)(!1),[I,O]=(0,_.useState)(null),z=(0,_.useRef)(null),H=(0,_.useRef)(!1);(0,_.useEffect)(()=>{H.current=!1,z.current=null,B(!1),A(!1),O(null)},[e]),(0,_.useEffect)(()=>{H.current||fetch("/api/config").then(e=>e.ok?e.json():null).then(i=>{if(!i)return;let r=(i.projects||[]).find(i=>i.id===e);r?.trigger_auto&&B(!0),H.current=!0}).catch(()=>{})},[e]);let F=(0,_.useCallback)(async()=>{try{let i=await fetch("/api/triggers");if(!i.ok)throw Error(`${i.status}`);let s=(await i.json())[e]||null;if(r(s),s){if(s.message&&!y.current&&(n(s.message),y.current=s.message),!S.current)if(s.enabled&&s.interval){let e=Math.max(1,Math.round(s.interval/6e4));a(e),h(String(e))}else"number"==typeof s.intervalMin&&s.intervalMin>0&&(a(s.intervalMin),h(String(s.intervalMin)));!w.current&&"number"==typeof s.durationMin&&s.durationMin>=0&&(d(s.durationMin),f(lO(s.durationMin)))}N(null)}catch(e){N(e.message)}},[e]);(0,_.useEffect)(()=>{s||n(M)},[M,s]),(0,_.useEffect)(()=>{F();let e=window.setInterval(F,5e3);return()=>window.clearInterval(e)},[F]),(0,_.useEffect)(()=>{if(!i?.enabled){D(""),T("");return}let e=()=>{if(i.nextAt&&D(lz(Math.max(0,i.nextAt-Date.now()))),i.expiresAt){let e=Math.max(0,i.expiresAt-Date.now());T(lz(e)),e<=0&&F()}else T("")};e();let r=window.setInterval(e,1e3);return()=>window.clearInterval(r)},[i?.enabled,i?.nextAt,i?.expiresAt,F]);let W=async()=>{k(!0),N(null);let i=parseFloat(u),r=Math.round(60*(Number.isFinite(i)?lI(i):3));r!==c&&(d(r),f(lO(r)));let n=parseInt(l,10),p=Number.isFinite(n)?Math.max(1,Math.min(1440,n)):15;p!==o&&(a(p),h(String(p)));try{let i=await fetch(`/api/triggers/${encodeURIComponent(e)}/start`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({interval:p,duration:r,message:s})});if(!i.ok)throw Error(`${i.status}`);v(!1),b(!1),await F()}catch(e){N(e.message)}finally{k(!1)}},$=async()=>{k(!0),N(null);try{let n=await fetch(`/api/triggers/${encodeURIComponent(e)}/stop`,{method:"POST"});if(!n.ok)throw Error(`${n.status}`);A(!1),r({...i||{interval:0,lastSent:null,nextAt:null,expiresAt:null,message:s,intervalMin:o,durationMin:c},enabled:!1}),await F()}catch(e){N(e.message)}finally{k(!1)}},U=(0,_.useCallback)(async()=>{let i=!P;B(i),i||(O(null),z.current=null);try{let r=await fetch("/api/config");if(!r.ok)return;let s=await r.json(),n=(s.projects||[]).find(i=>i.id===e);n&&(n.trigger_auto=i,await fetch("/api/config",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}))}catch{}},[P,e]),K=(0,_.useRef)(P),q=(0,_.useRef)(i);(0,_.useEffect)(()=>{K.current=P},[P]),(0,_.useEffect)(()=>{q.current=i},[i]);let V=(0,_.useCallback)(async()=>{if(K.current)try{let i=await fetch(`/api/batch-progress?project=${encodeURIComponent(e)}`);if(!i.ok)return;let r=await i.json(),s=r.items.length>0,n=z.current;if(z.current={complete:r.complete,hasItems:s},!n){if(s&&!r.complete&&!q.current?.enabled){A(!0),O(null);let i=parseFloat(u),r=Number.isFinite(i)?lI(i):3,s=Math.round(60*r),n=parseInt(l,10),o=Number.isFinite(n)?Math.max(1,Math.min(1440,n)):15;await fetch(`/api/triggers/${encodeURIComponent(e)}/start`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({interval:o,duration:s,message:y.current||M})}),await F()}s&&r.complete&&q.current?.enabled&&(await fetch(`/api/triggers/${encodeURIComponent(e)}/stop`,{method:"POST"}),A(!1),O("Batch complete — trigger paused. Waiting for next batch."),await F());return}if(s&&r.complete&&!n.complete&&q.current?.enabled){await fetch(`/api/triggers/${encodeURIComponent(e)}/stop`,{method:"POST"}),A(!1),O("Batch complete — trigger paused. Waiting for next batch."),await F();return}if(s&&!r.complete&&(n.complete||!n.hasItems)&&!q.current?.enabled){A(!0),O(null);let i=parseFloat(u),r=Number.isFinite(i)?lI(i):3,s=Math.round(60*r),n=parseInt(l,10),o=Number.isFinite(n)?Math.max(1,Math.min(1440,n)):15;await fetch(`/api/triggers/${encodeURIComponent(e)}/start`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({interval:o,duration:s,message:y.current||M})}),await F()}}catch{}},[e,u,l,M,F]);(0,_.useEffect)(()=>{if(!P)return;V();let e=window.setInterval(V,3e4);return()=>window.clearInterval(e)},[P,V]);let Y=!!i?.enabled;return(0,p.jsxs)("div",{className:"flex flex-col border border-border",children:[(0,p.jsxs)("div",{className:"flex items-center justify-between h-7 px-3 shrink-0 border-b border-border",children:[(0,p.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,p.jsxs)("span",{className:"text-[11px] text-text-muted uppercase tracking-wider",children:["Scheduled Trigger",Y?L?" (auto)":" (running)":""]}),(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"Scheduled Trigger"})," sends a periodic message to all agents on a timer. Use this to keep the autonomous workflow running overnight. First message fires after the configured interval, not immediately."]})]}),(0,p.jsxs)("div",{className:"flex items-center gap-2",children:[E&&(0,p.jsxs)("span",{className:"text-[10px] text-error",children:["err: ",E]}),(0,p.jsxs)("button",{type:"button",onClick:U,title:P?"Auto-trigger ON — trigger follows batch lifecycle":"Auto-trigger OFF — manual start/stop only",className:`px-1.5 py-0.5 text-[10px] border transition-colors ${P?"border-accent/50 text-accent bg-accent/10 hover:bg-accent/20":"border-border text-text-muted hover:text-text hover:border-accent"}`,children:["Auto ",P?"●":"○"]})]})]}),I&&(0,p.jsx)("div",{className:"px-3 py-1 text-[10px] text-accent bg-accent/5 border-b border-border/50",children:I}),Y?(0,p.jsxs)("div",{className:"p-3 flex flex-col gap-2",children:[(0,p.jsx)("div",{className:"text-[11px] text-text-muted",children:"Sending:"}),(0,p.jsx)("pre",{className:"text-[11px] font-mono whitespace-pre-wrap text-text bg-bg-surface border border-border p-2 max-h-28 overflow-auto",children:(i?.message||s).slice(0,400)}),(0,p.jsxs)("div",{className:"flex items-center gap-3 flex-wrap text-[11px]",children:[(0,p.jsxs)("span",{className:"flex items-center gap-1",children:[(0,p.jsxs)("span",{className:"relative inline-flex items-center justify-center w-2 h-2",children:[(0,p.jsx)("span",{className:"absolute inline-flex h-full w-full rounded-full bg-accent opacity-60 animate-ping"}),(0,p.jsx)("span",{className:"relative w-1.5 h-1.5 rounded-full bg-accent"})]}),(0,p.jsx)("span",{className:"text-accent",children:"Running"})]}),(0,p.jsxs)("span",{className:"tabular-nums text-text",children:["Next: ",R]}),j&&(0,p.jsxs)("span",{className:"tabular-nums text-text-muted",children:["Stops in: ",j]}),!i?.expiresAt&&(0,p.jsx)("span",{className:"text-text-muted",children:"(until stopped)"})]}),(0,p.jsx)("button",{onClick:$,disabled:C,className:"self-start px-3 py-1 text-[11px] text-text-muted border border-border hover:text-error hover:border-error/40 disabled:opacity-50 transition-colors",children:C?"Stopping…":"Stop Trigger"})]}):(0,p.jsxs)("div",{className:"p-3 flex flex-col gap-2",children:[(0,p.jsx)("label",{className:"text-[10px] text-text-muted uppercase tracking-wider",children:"Message"}),(0,p.jsx)("textarea",{value:s,onChange:e=>n(e.target.value),rows:6,spellCheck:!1,className:"w-full bg-bg text-text text-[11px] font-mono p-2 border border-border outline-none focus:border-accent resize-y"}),(0,p.jsxs)("div",{className:"flex items-center gap-2 flex-wrap text-[11px]",children:[(0,p.jsx)("span",{className:"text-text-muted",children:"Send every"}),(0,p.jsx)("input",{type:"number",value:l,onChange:e=>{h(e.target.value),v(!0)},onBlur:()=>{let e=parseInt(l,10),i=Number.isFinite(e)?Math.max(1,Math.min(1440,e)):15;a(i),h(String(i))},min:1,max:1440,className:"w-12 bg-transparent border border-border px-1 py-0.5 text-[11px] text-text outline-none focus:border-accent text-center"}),(0,p.jsx)("span",{className:"text-text-muted",children:"min for"}),(0,p.jsx)("input",{type:"number",value:u,onChange:e=>{f(e.target.value),b(!0)},onBlur:()=>{let e=parseFloat(u),i=Math.round(60*(Number.isFinite(e)?lI(e):3));d(i),f(lO(i))},min:.1,max:24,step:.1,className:"w-14 bg-transparent border border-border px-1 py-0.5 text-[11px] text-text outline-none focus:border-accent text-center"}),(0,p.jsx)("span",{className:"text-text-muted",children:"hours"})]}),(0,p.jsx)("button",{onClick:W,disabled:C||!s.trim(),className:"self-start px-3 py-1 text-[11px] font-semibold text-bg bg-accent hover:bg-accent-dim disabled:opacity-50 disabled:cursor-not-allowed transition-colors",children:C?"Starting…":"Start Trigger"})]})]})}function lF({open:e,initialChatId:i="",onClose:r,onSave:s}){let[n,o]=(0,_.useState)(""),[a,l]=(0,_.useState)(i),[h,c]=(0,_.useState)(!1),[d,u]=(0,_.useState)(null),[f,m]=(0,_.useState)(!1);if((0,_.useEffect)(()=>{e&&l(i)},[e,i]),(0,_.useEffect)(()=>{if(!e)return;let i=e=>{"Escape"===e.key&&r()};return window.addEventListener("keydown",i),()=>window.removeEventListener("keydown",i)},[e,r]),!e)return null;let g=async()=>{c(!0),u(null);try{await s(n.trim(),a.trim()),r()}catch(e){u(e.message||"Save failed")}finally{c(!1)}};return(0,p.jsx)("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",onClick:r,role:"dialog","aria-modal":"true","aria-labelledby":"telegram-setup-title",children:(0,p.jsxs)("div",{className:"relative mx-4 max-w-xl w-full max-h-[90vh] overflow-auto rounded-lg border border-white/10 bg-neutral-950 p-6 shadow-2xl",onClick:e=>e.stopPropagation(),children:[(0,p.jsx)("button",{type:"button",onClick:r,"aria-label":"Close",className:"absolute right-3 top-3 rounded p-1 text-neutral-400 hover:bg-white/5 hover:text-white",children:(0,p.jsx)("svg",{width:"18",height:"18",viewBox:"0 0 20 20",fill:"none",stroke:"currentColor",strokeWidth:"1.8",children:(0,p.jsx)("path",{d:"M4 4l12 12M16 4L4 16",strokeLinecap:"round"})})}),(0,p.jsx)("h2",{id:"telegram-setup-title",className:"text-base font-semibold text-white",children:"Set up your Telegram Bridge"}),(0,p.jsx)("p",{className:"mt-2 text-[12px] leading-relaxed text-neutral-300",children:"The bridge forwards AgentChattr messages from your project to a Telegram chat, so you can read and reply on your phone."}),(0,p.jsxs)("div",{className:"mt-4 text-[12px] text-neutral-300 space-y-3",children:[(0,p.jsxs)("section",{children:[(0,p.jsx)("h3",{className:"text-[13px] font-semibold text-white",children:"Step 1 — Create a Telegram bot"}),(0,p.jsxs)("ol",{className:"mt-1 pl-4 list-decimal space-y-0.5 text-neutral-300",children:[(0,p.jsxs)("li",{children:["Open Telegram and search for ",(0,p.jsx)("b",{children:"@BotFather"}),"."]}),(0,p.jsxs)("li",{children:["Send ",(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"/newbot"})," and follow the prompts."]}),(0,p.jsxs)("li",{children:["Choose a name and username (must end in ",(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"bot"}),")."]}),(0,p.jsxs)("li",{children:["BotFather replies with a ",(0,p.jsx)("b",{children:"bot token"})," that looks like ",(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"123456:ABC-DEF1234ghIkl…"}),". Copy it."]})]})]}),(0,p.jsxs)("section",{children:[(0,p.jsx)("h3",{className:"text-[13px] font-semibold text-white",children:"Step 2 — Get your chat ID"}),(0,p.jsxs)("ol",{className:"mt-1 pl-4 list-decimal space-y-0.5 text-neutral-300",children:[(0,p.jsxs)("li",{children:["Open Telegram and search for your new bot by its exact ",(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"@username"}),"."]}),(0,p.jsxs)("li",{children:["Tap ",(0,p.jsx)("b",{children:"Start"})," and send any message (e.g. ",(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"hello"}),")."," ",(0,p.jsxs)("span",{className:"text-neutral-400",children:["You must send at least one message first — ",(0,p.jsx)("code",{className:"bg-white/5 px-0.5 rounded",children:"getUpdates"})," returns ",(0,p.jsx)("code",{className:"bg-white/5 px-0.5 rounded",children:'{"ok":true,"result":[]}'})," until Telegram has an inbound message on record."]})]}),(0,p.jsxs)("li",{children:["Open this URL in your browser (replace ",(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"YOUR_TOKEN"}),"):",(0,p.jsx)("pre",{className:"mt-1 p-2 bg-white/5 rounded text-[11px] text-neutral-200 overflow-auto",children:"https://api.telegram.org/botYOUR_TOKEN/getUpdates"})]}),(0,p.jsxs)("li",{children:["Look for ",(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:'"chat":{"id":<NUMBER>'})," in the JSON. That number is your ",(0,p.jsx)("b",{children:"chat id"}),". Group chat ids are negative (e.g. ",(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"-1001234567890"}),") — paste the full value ",(0,p.jsx)("b",{children:"including the minus sign"}),"."]})]}),(0,p.jsx)("p",{className:"mt-1 text-neutral-400",children:"Or use one of these terminal one-liners:"}),(0,p.jsxs)("p",{className:"mt-1 text-[11px] text-neutral-400",children:["With ",(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"jq"})," (cleanest):"]}),(0,p.jsx)("pre",{className:"mt-1 p-2 bg-white/5 rounded text-[11px] text-neutral-200 overflow-auto",children:"curl -s \"https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates\" | jq '.result[-1].message.chat.id'"}),(0,p.jsxs)("p",{className:"mt-1 text-[11px] text-neutral-400",children:["Without ",(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"jq"})," (pure grep):"]}),(0,p.jsx)("pre",{className:"mt-1 p-2 bg-white/5 rounded text-[11px] text-neutral-200 overflow-auto",children:'curl -s "https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates" | grep -o \'"chat":{"id":-\\?[0-9]*\' | tail -1'}),(0,p.jsxs)("p",{className:"mt-1 text-[11px] text-neutral-500",children:["The first message from a brand-new bot sometimes doesn't propagate to ",(0,p.jsx)("code",{className:"bg-white/5 px-0.5 rounded",children:"getUpdates"})," instantly. If ",(0,p.jsx)("code",{className:"bg-white/5 px-0.5 rounded",children:"result"})," is empty, send 2-3 more messages and retry."]}),(0,p.jsx)("button",{type:"button",onClick:()=>m(e=>!e),className:"mt-2 text-[11px] text-accent hover:underline","aria-expanded":f,children:f?"Hide troubleshooting ▾":"Still empty? Troubleshooting ▸"}),f&&(0,p.jsxs)("div",{className:"mt-2 p-2 border border-white/10 rounded text-[11px] text-neutral-300 space-y-2",children:[(0,p.jsxs)("div",{children:[(0,p.jsx)("b",{className:"text-white",children:"Webhook conflict."})," Something else may be consuming updates. Check and delete any active webhook:",(0,p.jsx)("pre",{className:"mt-1 p-2 bg-white/5 rounded text-[11px] text-neutral-200 overflow-auto",children:'curl -s "https://api.telegram.org/bot<TOKEN>/getWebhookInfo" curl -s "https://api.telegram.org/bot<TOKEN>/deleteWebhook"'})]}),(0,p.jsxs)("div",{children:[(0,p.jsx)("b",{className:"text-white",children:"Stale bridge process."}),"A leftover bridge from a previous install will hold the update queue. Two consumers can't share one bot:",(0,p.jsx)("pre",{className:"mt-1 p-2 bg-white/5 rounded text-[11px] text-neutral-200 overflow-auto",children:"ps aux | grep telegram_bridge | grep -v grep"}),"Kill any PIDs that show up before retrying the curl."]}),(0,p.jsxs)("div",{children:[(0,p.jsx)("b",{className:"text-white",children:"Token / bot mismatch."})," A token that doesn't match the bot you're messaging silently returns empty results. Confirm the token's ",(0,p.jsx)("code",{className:"bg-white/5 px-0.5 rounded",children:"username"})," matches the ",(0,p.jsx)("code",{className:"bg-white/5 px-0.5 rounded",children:"@username"}),"you're chatting with:",(0,p.jsx)("pre",{className:"mt-1 p-2 bg-white/5 rounded text-[11px] text-neutral-200 overflow-auto",children:'curl -s "https://api.telegram.org/bot<TOKEN>/getMe"'})]})]})]}),(0,p.jsxs)("section",{children:[(0,p.jsx)("h3",{className:"text-[13px] font-semibold text-white",children:"Step 3 — Paste credentials below"}),(0,p.jsxs)("div",{className:"mt-2 flex flex-col gap-2",children:[(0,p.jsxs)("label",{className:"text-[11px] text-neutral-400",children:["Bot token",(0,p.jsx)("input",{type:"password",value:n,onChange:e=>o(e.target.value),placeholder:"123456:ABC-DEF…",className:"mt-0.5 w-full bg-transparent border border-white/10 px-2 py-1 text-[12px] text-white outline-none focus:border-accent font-mono"})]}),(0,p.jsxs)("label",{className:"text-[11px] text-neutral-400",children:["Chat id",(0,p.jsx)("input",{type:"text",value:a,onChange:e=>l(e.target.value),placeholder:"123456789",className:"mt-0.5 w-full bg-transparent border border-white/10 px-2 py-1 text-[12px] text-white outline-none focus:border-accent font-mono"})]}),d&&(0,p.jsx)("div",{className:"text-[11px] text-error",children:d}),(0,p.jsx)("button",{type:"button",onClick:g,disabled:h||!n.trim()||!a.trim(),className:"mt-1 self-start px-3 py-1 text-[11px] font-semibold text-bg bg-accent hover:bg-accent-dim disabled:opacity-50 disabled:cursor-not-allowed transition-colors",children:h?"Saving…":"Save and start bridge"})]})]})]})]})})}async function lW(e,i){let r=await fetch(`/api/telegram?action=${encodeURIComponent(e)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}),s=await r.json();if(!r.ok||!1===s.ok)throw Error(s.error||`${r.status}`);return s}function l$({projectId:e}){let[i,r]=(0,_.useState)(null),[s,n]=(0,_.useState)(!1),[o,a]=(0,_.useState)(null),[l,h]=(0,_.useState)(null),[c,d]=(0,_.useState)(!1),[u,f]=(0,_.useState)(null),[m,v]=(0,_.useState)(!1),[x,b]=(0,_.useState)(null),y=(0,_.useRef)(null),S=(0,_.useRef)(m),w=(0,_.useRef)(!1);(0,_.useEffect)(()=>{S.current=m},[m]);let C=(0,_.useRef)(!1);(0,_.useEffect)(()=>{C.current=!1,y.current=null,v(!1),b(null)},[e]),(0,_.useEffect)(()=>{C.current||fetch("/api/config").then(e=>e.ok?e.json():null).then(i=>{if(!i)return;let r=(i.projects||[]).find(i=>i.id===e);r?.telegram_auto&&v(!0),C.current=!0}).catch(()=>{})},[e]);let k=(0,_.useCallback)(async()=>{try{let i=await fetch(`/api/telegram?project=${encodeURIComponent(e)}`);if(!i.ok)throw Error(`${i.status}`);let s=await i.json();r(s),h(null)}catch(e){h(e.message)}},[e]);(0,_.useEffect)(()=>{k();let e=window.setInterval(k,5e3);return()=>window.clearInterval(e)},[k]);let E=e=>{let i=Array.isArray(e?.patched_projects)?e.patched_projects:[];i.length>0?f(`Install Bridge patched ${i.length} AgentChattr config(s) (${i.join(", ")}) to declare [agents.tg]. Click SERVER → Restart so AgentChattr picks up the new agent slug, then click Start again. Without the restart, Start will fail with a 400 registration loop.`):f(null)},N=async()=>{n(!0),a(null);try{if(i&&!i.bridge_installed){let e=await lW("install",{});E(e)}await lW("start",{project_id:e}),await k()}catch(e){a(e.message)}finally{n(!1)}},R=async()=>{n(!0),a(null);try{await lW("stop",{project_id:e}),await k()}catch(e){a(e.message)}finally{n(!1)}},D=async(r,s)=>{await lW("save-config",{project_id:e,bot_token:r,chat_id:s});try{if(i&&!i.bridge_installed){let e=await lW("install",{});E(e)}await lW("start",{project_id:e})}catch(e){a(e.message)}await k()},j=(0,_.useCallback)(async()=>{let i=!m;v(i),i||(b(null),y.current=null);try{let r=await fetch("/api/config");if(!r.ok)return;let s=await r.json(),n=(s.projects||[]).find(i=>i.id===e);n&&(n.telegram_auto=i,await fetch("/api/config",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}))}catch{}},[m,e]),T=(0,_.useCallback)(async()=>{if(S.current)try{let i=await fetch(`/api/batch-progress?project=${encodeURIComponent(e)}`);if(!i.ok)return;let r=await i.json(),s=r.items.length>0,n=y.current;if(y.current={complete:r.complete,hasItems:s},!n){!s||r.complete||w.current||(b("Batch active — auto-starting bridge."),await lW("start",{project_id:e}).catch(()=>{}),await k()),s&&r.complete&&w.current&&(b("Batch complete — bridge paused. Waiting for next batch."),a(null),await lW("stop",{project_id:e}).catch(()=>{}),await k());return}if(s&&r.complete&&!n.complete&&w.current){b("Batch complete — bridge paused. Waiting for next batch."),a(null),await lW("stop",{project_id:e}).catch(()=>{}),await k();return}!s||r.complete||!n.complete&&n.hasItems||w.current||(b("New batch detected — auto-starting bridge."),await lW("start",{project_id:e}).catch(()=>{}),await k())}catch{}},[e,k]);(0,_.useEffect)(()=>{if(!m)return;T();let e=window.setInterval(T,3e4);return()=>window.clearInterval(e)},[m,T]);let M=!!i?.configured,P=!!i?.running;(0,_.useEffect)(()=>{w.current=P},[P]);let B=!P&&!!x,L=o||l||!P&&!B&&i?.last_error||"";return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)("div",{className:"flex flex-col border border-border",children:[(0,p.jsxs)("div",{className:"flex items-center justify-between h-7 px-3 shrink-0 border-b border-border",children:[(0,p.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,p.jsx)("span",{className:"text-[11px] text-text-muted uppercase tracking-wider",children:"Telegram Bridge"}),(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"Telegram Bridge"})," forwards AgentChattr messages to a Telegram bot so you can monitor from your phone. Bidirectional — replies from Telegram appear in chat."]})]}),(0,p.jsxs)("div",{className:"flex items-center gap-1.5",children:[M&&(0,p.jsxs)("button",{type:"button",onClick:j,title:m?"Auto ON — bridge follows batch lifecycle":"Auto OFF — manual start/stop only",className:`px-1.5 py-0.5 text-[10px] border transition-colors ${m?"border-accent/50 text-accent bg-accent/10 hover:bg-accent/20":"border-border text-text-muted hover:text-text hover:border-accent"}`,children:["Auto ",m?"●":"○"]}),L&&(0,p.jsx)("span",{className:"text-[10px] text-error",children:"error"})]})]}),(0,p.jsxs)("div",{className:"p-3 flex flex-col gap-2",children:[M?(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)("div",{className:"flex items-center gap-2 text-[11px] flex-wrap",children:[P?(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)("span",{className:"relative inline-flex items-center justify-center w-2 h-2",children:[(0,p.jsx)("span",{className:"absolute inline-flex h-full w-full rounded-full bg-accent opacity-60 animate-ping"}),(0,p.jsx)("span",{className:"relative w-1.5 h-1.5 rounded-full bg-accent"})]}),(0,p.jsx)("span",{className:"text-accent",children:"Running"})]}):(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)("span",{className:"w-1.5 h-1.5 rounded-full bg-text-muted"}),(0,p.jsx)("span",{className:"text-text-muted",children:"Stopped"})]}),i?.bot_username&&(0,p.jsxs)("span",{className:"text-text-muted",children:["· Bot: @",i.bot_username]}),i?.chat_id&&(0,p.jsxs)("span",{className:"text-text-muted tabular-nums",children:["· Chat: ",i.chat_id]})]}),(0,p.jsxs)("div",{className:"flex items-center gap-2 flex-wrap",children:[P?(0,p.jsx)("button",{onClick:R,disabled:s,className:"px-3 py-1 text-[11px] text-text-muted border border-border hover:text-error hover:border-error/40 disabled:opacity-50 transition-colors",children:s?"Stopping…":"Stop"}):(0,p.jsx)("button",{onClick:N,disabled:s,className:"px-3 py-1 text-[11px] font-semibold text-bg bg-accent hover:bg-accent-dim disabled:opacity-50 transition-colors",children:s?"Starting…":"Start"}),(0,p.jsx)("button",{onClick:()=>d(!0),disabled:s,className:"px-3 py-1 text-[11px] text-text-muted border border-border hover:text-text disabled:opacity-50 transition-colors",children:"How to set up"}),(0,p.jsx)("button",{onClick:()=>d(!0),disabled:s,className:"px-3 py-1 text-[11px] text-text-muted border border-border hover:text-text disabled:opacity-50 transition-colors",children:"Edit credentials"})]})]}):(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)("div",{className:"flex items-center gap-2 text-[11px] text-text-muted",children:[(0,p.jsx)("span",{className:"w-1.5 h-1.5 rounded-full bg-text-muted"}),(0,p.jsx)("span",{children:"Not configured"})]}),(0,p.jsx)("button",{onClick:()=>d(!0),disabled:s,className:"self-start px-3 py-1 text-[11px] font-semibold text-bg bg-accent hover:bg-accent-dim disabled:opacity-50 transition-colors",children:"Set up Telegram Bridge"})]}),u&&(0,p.jsxs)("div",{className:"mt-1 p-2 text-[10px] text-accent border border-accent/40 bg-accent/5 whitespace-pre-wrap break-words",children:[u,(0,p.jsx)("button",{type:"button",onClick:()=>f(null),className:"block mt-1 text-text-muted hover:text-text underline",children:"dismiss"})]}),x&&(0,p.jsx)("div",{className:"mt-1 text-[10px] text-accent",children:x}),L&&(0,p.jsxs)("div",{className:"mt-1 p-2 text-[10px] text-error border border-error/40 bg-error/5 font-mono whitespace-pre-wrap break-words max-h-40 overflow-y-auto",children:[L,o&&(0,p.jsx)("button",{type:"button",onClick:()=>a(null),className:"block mt-1 text-text-muted hover:text-text underline",children:"dismiss"})]})]})]}),(0,p.jsx)(lF,{open:c,initialChatId:i?.chat_id||"",onClose:()=>d(!1),onSave:D})]})}function lU({open:e,initialChannelId:i="",onClose:r,onSave:s}){let[n,o]=(0,_.useState)(""),[a,l]=(0,_.useState)(i),[h,c]=(0,_.useState)(!1),[d,u]=(0,_.useState)(null),[f,m]=(0,_.useState)(!1);if((0,_.useEffect)(()=>{e&&l(i)},[e,i]),(0,_.useEffect)(()=>{if(!e)return;let i=e=>{"Escape"===e.key&&r()};return window.addEventListener("keydown",i),()=>window.removeEventListener("keydown",i)},[e,r]),!e)return null;let g=async()=>{c(!0),u(null);try{await s(n.trim(),a.trim()),r()}catch(e){u(e.message||"Save failed")}finally{c(!1)}};return(0,p.jsx)("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",onClick:r,role:"dialog","aria-modal":"true","aria-labelledby":"discord-setup-title",children:(0,p.jsxs)("div",{className:"relative mx-4 max-w-xl w-full max-h-[90vh] overflow-auto rounded-lg border border-white/10 bg-neutral-950 p-6 shadow-2xl",onClick:e=>e.stopPropagation(),children:[(0,p.jsx)("button",{type:"button",onClick:r,"aria-label":"Close",className:"absolute right-3 top-3 rounded p-1 text-neutral-400 hover:bg-white/5 hover:text-white",children:(0,p.jsx)("svg",{width:"18",height:"18",viewBox:"0 0 20 20",fill:"none",stroke:"currentColor",strokeWidth:"1.8",children:(0,p.jsx)("path",{d:"M4 4l12 12M16 4L4 16",strokeLinecap:"round"})})}),(0,p.jsx)("h2",{id:"discord-setup-title",className:"text-base font-semibold text-white",children:"Set up your Discord Bridge"}),(0,p.jsx)("p",{className:"mt-2 text-[12px] leading-relaxed text-neutral-300",children:"The bridge forwards AgentChattr messages from your project to a Discord channel, so you can read and reply from Discord."}),(0,p.jsxs)("div",{className:"mt-4 text-[12px] text-neutral-300 space-y-3",children:[(0,p.jsxs)("section",{children:[(0,p.jsx)("h3",{className:"text-[13px] font-semibold text-white",children:"Step 1 — Create a Discord Application + Bot"}),(0,p.jsxs)("ol",{className:"mt-1 pl-4 list-decimal space-y-0.5 text-neutral-300",children:[(0,p.jsxs)("li",{children:["Go to the ",(0,p.jsx)("a",{href:"https://discord.com/developers/applications",target:"_blank",rel:"noopener noreferrer",className:"text-accent hover:underline",children:"Discord Developer Portal"}),"."]}),(0,p.jsxs)("li",{children:["Click ",(0,p.jsx)("b",{children:"New Application"}),' → name it (e.g. "QuadWork") → ',(0,p.jsx)("b",{children:"Create"}),"."]}),(0,p.jsxs)("li",{children:["Go to the ",(0,p.jsx)("b",{children:"Bot"})," tab in the left sidebar."]}),(0,p.jsxs)("li",{children:["Click ",(0,p.jsx)("b",{children:"Reset Token"})," → copy the bot token. Save it somewhere safe."]})]})]}),(0,p.jsxs)("section",{children:[(0,p.jsx)("h3",{className:"text-[13px] font-semibold text-white",children:"Step 2 — Enable MESSAGE_CONTENT intent (critical)"}),(0,p.jsxs)("div",{className:"mt-1.5 rounded border border-amber-500/40 bg-amber-500/5 p-3",children:[(0,p.jsxs)("div",{className:"flex items-start gap-2",children:[(0,p.jsx)("span",{className:"text-amber-400 text-[16px] leading-none shrink-0",children:"⚠"}),(0,p.jsxs)("div",{className:"text-[12px] text-amber-200/90 leading-relaxed",children:[(0,p.jsx)("b",{className:"text-amber-300",children:"This step is required."}),"Without it, the bot connects successfully but receives empty message content — no error, no warning, just silent empty messages. This is the #1 cause of Discord bot setup issues."]})]}),(0,p.jsxs)("ol",{className:"mt-2 pl-6 list-decimal space-y-0.5 text-neutral-300 text-[12px]",children:[(0,p.jsxs)("li",{children:["On the same ",(0,p.jsx)("b",{children:"Bot"})," tab, scroll down to ",(0,p.jsx)("b",{children:"Privileged Gateway Intents"}),"."]}),(0,p.jsxs)("li",{children:["Toggle ",(0,p.jsx)("b",{children:"ON"}),": ",(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"MESSAGE CONTENT INTENT"}),"."]}),(0,p.jsxs)("li",{children:["Click ",(0,p.jsx)("b",{children:"Save Changes"}),"."]})]})]})]}),(0,p.jsxs)("section",{children:[(0,p.jsx)("h3",{className:"text-[13px] font-semibold text-white",children:"Step 3 — Invite the bot to your server"}),(0,p.jsxs)("ol",{className:"mt-1 pl-4 list-decimal space-y-0.5 text-neutral-300",children:[(0,p.jsxs)("li",{children:["Go to the ",(0,p.jsx)("b",{children:"OAuth2"})," tab → ",(0,p.jsx)("b",{children:"URL Generator"}),"."]}),(0,p.jsxs)("li",{children:["Under ",(0,p.jsx)("b",{children:"Scopes"}),", check ",(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"bot"}),"."]}),(0,p.jsxs)("li",{children:["Under ",(0,p.jsx)("b",{children:"Bot Permissions"}),", check:",(0,p.jsxs)("ul",{className:"mt-0.5 pl-4 list-disc text-neutral-400",children:[(0,p.jsx)("li",{children:(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"Send Messages"})}),(0,p.jsx)("li",{children:(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"Read Message History"})}),(0,p.jsx)("li",{children:(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"View Channels"})})]})]}),(0,p.jsxs)("li",{children:["Copy the generated URL at the bottom. It will look like:",(0,p.jsx)("pre",{className:"mt-1 p-2 bg-white/5 rounded text-[11px] text-neutral-200 overflow-auto",children:"https://discord.com/oauth2/authorize?client_id=YOUR_APP_ID&permissions=66560&scope=bot"})]}),(0,p.jsxs)("li",{children:["Open it in your browser → select your Discord server → ",(0,p.jsx)("b",{children:"Authorize"}),"."]})]})]}),(0,p.jsxs)("section",{children:[(0,p.jsx)("h3",{className:"text-[13px] font-semibold text-white",children:"Step 4 — Get your channel ID"}),(0,p.jsxs)("ol",{className:"mt-1 pl-4 list-decimal space-y-0.5 text-neutral-300",children:[(0,p.jsxs)("li",{children:["In Discord, go to ",(0,p.jsx)("b",{children:"User Settings"})," → ",(0,p.jsx)("b",{children:"Advanced"})," → enable ",(0,p.jsx)("b",{children:"Developer Mode"}),"."]}),(0,p.jsxs)("li",{children:["Right-click the channel you want to bridge → ",(0,p.jsx)("b",{children:"Copy Channel ID"}),"."]})]}),(0,p.jsxs)("p",{className:"mt-1 text-[11px] text-neutral-500",children:["Channel IDs are large numbers like ",(0,p.jsx)("code",{className:"bg-white/5 px-1 rounded text-[11px]",children:"1234567890123456789"}),"."]})]}),(0,p.jsxs)("section",{children:[(0,p.jsx)("h3",{className:"text-[13px] font-semibold text-white",children:"Step 5 — Paste credentials below"}),(0,p.jsxs)("div",{className:"mt-2 flex flex-col gap-2",children:[(0,p.jsxs)("label",{className:"text-[11px] text-neutral-400",children:["Bot token",(0,p.jsx)("input",{type:"password",value:n,onChange:e=>o(e.target.value),placeholder:"MTIzNDU2Nzg5MDEyMzQ1Njc4OQ...",className:"mt-0.5 w-full bg-transparent border border-white/10 px-2 py-1 text-[12px] text-white outline-none focus:border-accent font-mono"})]}),(0,p.jsxs)("label",{className:"text-[11px] text-neutral-400",children:["Channel ID",(0,p.jsx)("input",{type:"text",value:a,onChange:e=>l(e.target.value),placeholder:"1234567890123456789",className:"mt-0.5 w-full bg-transparent border border-white/10 px-2 py-1 text-[12px] text-white outline-none focus:border-accent font-mono"})]}),d&&(0,p.jsx)("div",{className:"text-[11px] text-error",children:d}),(0,p.jsx)("button",{type:"button",onClick:g,disabled:h||!n.trim()||!a.trim(),className:"mt-1 self-start px-3 py-1 text-[11px] font-semibold text-bg bg-accent hover:bg-accent-dim disabled:opacity-50 disabled:cursor-not-allowed transition-colors",children:h?"Saving…":"Save and start bridge"})]})]}),(0,p.jsxs)("section",{children:[(0,p.jsx)("button",{type:"button",onClick:()=>m(e=>!e),className:"text-[11px] text-accent hover:underline","aria-expanded":f,children:f?"Hide troubleshooting ▾":"Troubleshooting ▸"}),f&&(0,p.jsxs)("div",{className:"mt-2 p-2 border border-white/10 rounded text-[11px] text-neutral-300 space-y-2",children:[(0,p.jsxs)("div",{children:[(0,p.jsx)("b",{className:"text-white",children:"Bot connects but messages are empty."})," ","MESSAGE_CONTENT intent is not enabled. Go back to Step 2 — toggle it on in the Developer Portal under Bot → Privileged Gateway Intents, then restart the bridge."]}),(0,p.jsxs)("div",{children:[(0,p.jsx)("b",{className:"text-white",children:"Bot can't see the channel."})," ","The bot wasn't invited to the server (Step 3) or it lacks the ",(0,p.jsx)("code",{className:"bg-white/5 px-0.5 rounded",children:"View Channels"}),"permission for that specific channel. Check your server's role permissions."]}),(0,p.jsxs)("div",{children:[(0,p.jsx)("b",{className:"text-white",children:'"Disallowed intents" error in logs.'})," ","MESSAGE_CONTENT intent is not toggled in the Developer Portal. This is different from not requesting it in code — the portal toggle is the gatekeeper."]}),(0,p.jsxs)("div",{children:[(0,p.jsx)("b",{className:"text-white",children:"Rate limited."})," ","Normal for high-traffic channels. ",(0,p.jsx)("code",{className:"bg-white/5 px-0.5 rounded",children:"discord.py"})," handles backoff automatically. If persistent, increase the AC poll interval in the bridge config."]})]})]})]})]})})}async function lK(e,i){let r=await fetch(`/api/discord?action=${encodeURIComponent(e)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}),s=await r.json();if(!r.ok||!1===s.ok)throw Error(s.error||`${r.status}`);return s}function lq({projectId:e}){let[i,r]=(0,_.useState)(null),[s,n]=(0,_.useState)(!1),[o,a]=(0,_.useState)(null),[l,h]=(0,_.useState)(null),[c,d]=(0,_.useState)(!1),[u,f]=(0,_.useState)(null),[m,v]=(0,_.useState)(!1),[x,b]=(0,_.useState)(null),y=(0,_.useRef)(null),S=(0,_.useRef)(m),w=(0,_.useRef)(!1);(0,_.useEffect)(()=>{S.current=m},[m]);let C=(0,_.useRef)(!1);(0,_.useEffect)(()=>{C.current=!1,y.current=null,v(!1),b(null)},[e]),(0,_.useEffect)(()=>{C.current||fetch("/api/config").then(e=>e.ok?e.json():null).then(i=>{if(!i)return;let r=(i.projects||[]).find(i=>i.id===e);r?.discord_auto&&v(!0),C.current=!0}).catch(()=>{})},[e]);let k=(0,_.useCallback)(async()=>{try{let i=await fetch(`/api/discord?project=${encodeURIComponent(e)}`);if(!i.ok)throw Error(`${i.status}`);let s=await i.json();r(s),h(null)}catch(e){h(e.message)}},[e]);(0,_.useEffect)(()=>{k();let e=window.setInterval(k,5e3);return()=>window.clearInterval(e)},[k]);let E=e=>{let i=Array.isArray(e?.patched_projects)?e.patched_projects:[];i.length>0?f(`Install Bridge patched ${i.length} AgentChattr config(s) (${i.join(", ")}) to declare [agents.dc]. Click SERVER → Restart so AgentChattr picks up the new agent slug, then click Start again. Without the restart, Start will fail with a 400 registration loop.`):f(null)},N=async()=>{n(!0),a(null);try{if(i&&!i.bridge_installed){let e=await lK("install",{});E(e)}await lK("start",{project_id:e}),await k()}catch(e){a(e.message)}finally{n(!1)}},R=async()=>{n(!0),a(null);try{await lK("stop",{project_id:e}),await k()}catch(e){a(e.message)}finally{n(!1)}},D=async(r,s)=>{await lK("save-config",{project_id:e,bot_token:r,channel_id:s});try{if(i&&!i.bridge_installed){let e=await lK("install",{});E(e)}await lK("start",{project_id:e})}catch(e){a(e.message)}await k()},j=(0,_.useCallback)(async()=>{let i=!m;v(i),i||(b(null),y.current=null);try{let r=await fetch("/api/config");if(!r.ok)return;let s=await r.json(),n=(s.projects||[]).find(i=>i.id===e);n&&(n.discord_auto=i,await fetch("/api/config",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}))}catch{}},[m,e]),T=(0,_.useCallback)(async()=>{if(S.current)try{let i=await fetch(`/api/batch-progress?project=${encodeURIComponent(e)}`);if(!i.ok)return;let r=await i.json(),s=r.items.length>0,n=y.current;if(y.current={complete:r.complete,hasItems:s},!n){!s||r.complete||w.current||(b("Batch active — auto-starting bridge."),await lK("start",{project_id:e}).catch(()=>{}),await k()),s&&r.complete&&w.current&&(b("Batch complete — bridge paused. Waiting for next batch."),a(null),await lK("stop",{project_id:e}).catch(()=>{}),await k());return}if(s&&r.complete&&!n.complete&&w.current){b("Batch complete — bridge paused. Waiting for next batch."),a(null),await lK("stop",{project_id:e}).catch(()=>{}),await k();return}!s||r.complete||!n.complete&&n.hasItems||w.current||(b("New batch detected — auto-starting bridge."),await lK("start",{project_id:e}).catch(()=>{}),await k())}catch{}},[e,k]);(0,_.useEffect)(()=>{if(!m)return;T();let e=window.setInterval(T,3e4);return()=>window.clearInterval(e)},[m,T]);let M=!!i?.configured,P=!!i?.running;(0,_.useEffect)(()=>{w.current=P},[P]);let B=!P&&!!x,L=o||l||!P&&!B&&i?.last_error||"";return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)("div",{className:"flex flex-col border border-border",children:[(0,p.jsxs)("div",{className:"flex items-center justify-between h-7 px-3 shrink-0 border-b border-border",children:[(0,p.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,p.jsx)("span",{className:"text-[11px] text-text-muted uppercase tracking-wider",children:"Discord Bridge"}),(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"Discord Bridge"})," forwards AgentChattr messages to a Discord channel so you can monitor from Discord. Bidirectional — replies from Discord appear in chat."]})]}),(0,p.jsxs)("div",{className:"flex items-center gap-1.5",children:[M&&(0,p.jsxs)("button",{type:"button",onClick:j,title:m?"Auto ON — bridge follows batch lifecycle":"Auto OFF — manual start/stop only",className:`px-1.5 py-0.5 text-[10px] border transition-colors ${m?"border-accent/50 text-accent bg-accent/10 hover:bg-accent/20":"border-border text-text-muted hover:text-text hover:border-accent"}`,children:["Auto ",m?"●":"○"]}),L&&(0,p.jsx)("span",{className:"text-[10px] text-error",children:"error"})]})]}),(0,p.jsxs)("div",{className:"p-3 flex flex-col gap-2",children:[M?(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)("div",{className:"flex items-center gap-2 text-[11px] flex-wrap",children:[P?(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)("span",{className:"relative inline-flex items-center justify-center w-2 h-2",children:[(0,p.jsx)("span",{className:"absolute inline-flex h-full w-full rounded-full bg-accent opacity-60 animate-ping"}),(0,p.jsx)("span",{className:"relative w-1.5 h-1.5 rounded-full bg-accent"})]}),(0,p.jsx)("span",{className:"text-accent",children:"Running"})]}):(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)("span",{className:"w-1.5 h-1.5 rounded-full bg-text-muted"}),(0,p.jsx)("span",{className:"text-text-muted",children:"Stopped"})]}),i?.bot_username&&(0,p.jsxs)("span",{className:"text-text-muted",children:["· Bot: ",i.bot_username]}),i?.channel_id&&(0,p.jsxs)("span",{className:"text-text-muted tabular-nums",children:["· Channel: ",i.channel_id]})]}),(0,p.jsxs)("div",{className:"flex items-center gap-2 flex-wrap",children:[P?(0,p.jsx)("button",{onClick:R,disabled:s,className:"px-3 py-1 text-[11px] text-text-muted border border-border hover:text-error hover:border-error/40 disabled:opacity-50 transition-colors",children:s?"Stopping…":"Stop"}):(0,p.jsx)("button",{onClick:N,disabled:s,className:"px-3 py-1 text-[11px] font-semibold text-bg bg-accent hover:bg-accent-dim disabled:opacity-50 transition-colors",children:s?"Starting…":"Start"}),(0,p.jsx)("button",{onClick:()=>d(!0),disabled:s,className:"px-3 py-1 text-[11px] text-text-muted border border-border hover:text-text disabled:opacity-50 transition-colors",children:"How to set up"}),(0,p.jsx)("button",{onClick:()=>d(!0),disabled:s,className:"px-3 py-1 text-[11px] text-text-muted border border-border hover:text-text disabled:opacity-50 transition-colors",children:"Edit credentials"})]})]}):(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)("div",{className:"flex items-center gap-2 text-[11px] text-text-muted",children:[(0,p.jsx)("span",{className:"w-1.5 h-1.5 rounded-full bg-text-muted"}),(0,p.jsx)("span",{children:"Not configured"})]}),(0,p.jsx)("button",{onClick:()=>d(!0),disabled:s,className:"self-start px-3 py-1 text-[11px] font-semibold text-bg bg-accent hover:bg-accent-dim disabled:opacity-50 transition-colors",children:"Set up Discord Bridge"})]}),u&&(0,p.jsxs)("div",{className:"mt-1 p-2 text-[10px] text-accent border border-accent/40 bg-accent/5 whitespace-pre-wrap break-words",children:[u,(0,p.jsx)("button",{type:"button",onClick:()=>f(null),className:"block mt-1 text-text-muted hover:text-text underline",children:"dismiss"})]}),x&&(0,p.jsx)("div",{className:"mt-1 text-[10px] text-accent",children:x}),L&&(0,p.jsxs)("div",{className:"mt-1 p-2 text-[10px] text-error border border-error/40 bg-error/5 font-mono whitespace-pre-wrap break-words max-h-40 overflow-y-auto",children:[L,o&&(0,p.jsx)("button",{type:"button",onClick:()=>a(null),className:"block mt-1 text-text-muted hover:text-text underline",children:"dismiss"})]})]})]}),(0,p.jsx)(lU,{open:c,initialChannelId:i?.channel_id||"",onClose:()=>d(!1),onSave:D})]})}function lV({projectId:e}){let[i,r]=(0,_.useState)(30),[s,n]=(0,_.useState)("30"),[o,a]=(0,_.useState)(!1),[l,h]=(0,_.useState)(null),[c,d]=(0,_.useState)(null),[u,f]=(0,_.useState)(!1),[m,v]=(0,_.useState)(30),[x,b]=(0,_.useState)("30"),[y,S]=(0,_.useState)(!1);(0,_.useEffect)(()=>{fetch(`/api/loop-guard?project=${encodeURIComponent(e)}`).then(e=>e.ok?e.json():null).then(e=>{e&&"number"==typeof e.value&&(r(e.value),n(String(e.value)))}).catch(()=>{}),fetch("/api/config").then(e=>e.ok?e.json():null).then(i=>{if(!i||!Array.isArray(i.projects))return;let r=i.projects.find(i=>i.id===e);if(!r)return;f(!!r.auto_continue_loop_guard);let s=Number.isFinite(r.auto_continue_delay_sec)?r.auto_continue_delay_sec:30;v(s),b(String(s))}).catch(()=>{})},[e]);let w=async(i,r)=>{S(!0);try{let s=await fetch("/api/config");if(!s.ok)throw Error(`GET /api/config ${s.status}`);let n=await s.json();if(!n||!Array.isArray(n.projects))throw Error("config shape");let o=n.projects.findIndex(i=>i.id===e);if(o<0)throw Error(`project ${e} not found`);n.projects[o]={...n.projects[o],auto_continue_loop_guard:i,auto_continue_delay_sec:r};let a=await fetch("/api/config",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)});if(!a.ok)throw Error(`PUT /api/config ${a.status}`)}finally{S(!1)}};return(0,p.jsxs)("div",{className:"border border-border rounded p-2 text-[11px] font-mono",children:[(0,p.jsxs)("div",{className:"flex items-center gap-1.5 mb-1",children:[(0,p.jsx)("span",{className:"uppercase tracking-wider text-text-muted",children:"Loop Guard"}),(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"Loop Guard"})," pauses agent-to-agent message chains after this many hops with no human reply. Higher values let agents work longer overnight; lower values add safety against runaway loops. AgentChattr accepts ",(0,p.jsx)("b",{children:"4–50"}),"; QuadWork defaults to ",(0,p.jsx)("b",{children:"30"})," (about 5–6 full PR cycles). Posting any chat message yourself resets the counter immediately."]})]}),(0,p.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,p.jsx)("span",{className:"text-text-muted",children:"Pause after"}),(0,p.jsx)("input",{type:"number",min:4,max:50,value:s,onChange:e=>n(e.target.value),disabled:o,className:"w-12 bg-transparent px-1 py-0.5 border border-border rounded text-text outline-none focus:ring-1 focus:ring-accent"}),(0,p.jsx)("span",{className:"text-text-muted",children:"hops"}),(0,p.jsx)("button",{type:"button",onClick:()=>{let i=parseInt(s,10);!Number.isInteger(i)||i<4||i>50?h("Must be an integer between 4 and 50."):(a(!0),h(null),fetch(`/api/loop-guard?project=${encodeURIComponent(e)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:i})}).then(async e=>{if(!e.ok){let i=await e.text().catch(()=>"");throw Error(`${e.status}: ${i.slice(0,120)}`)}return e.json()}).then(e=>{r(e.value),d(e.live)}).catch(e=>h(e.message||String(e))).finally(()=>a(!1)))},disabled:o||s===String(i),className:"ml-auto px-2 py-0.5 text-[10px] text-accent border border-accent/40 rounded hover:bg-accent/10 transition-colors disabled:opacity-30 disabled:cursor-not-allowed",title:"Apply (writes config.toml + live-pushes to AgentChattr)",children:o?"…":"Apply"})]}),l&&(0,p.jsx)("div",{className:"mt-1 text-[10px] text-red-400",children:l}),!1===c&&!l&&(0,p.jsx)("div",{className:"mt-1 text-[10px] text-text-muted",children:"Saved to config.toml — live update failed; takes effect on next AC restart."}),(0,p.jsxs)("label",{className:"mt-2 flex items-center gap-1.5 text-[10px] text-text-muted cursor-pointer select-none",children:[(0,p.jsx)("input",{type:"checkbox",checked:u,disabled:y,onChange:e=>{let i=e.target.checked;f(i),w(i,m).catch(()=>{f(!i)})}}),"Auto-continue after pause",(0,p.jsx)("span",{className:"text-text-muted",children:"— wait"}),(0,p.jsx)("input",{type:"number",min:5,max:300,value:x,disabled:y||!u,onChange:e=>b(e.target.value),onBlur:()=>{let e=parseInt(x,10),i=Number.isFinite(e)?Math.max(5,Math.min(300,e)):30;v(i),b(String(i)),i!==m&&w(u,i).catch(()=>{})},className:"w-10 bg-transparent px-1 py-0.5 border border-border rounded text-text outline-none focus:ring-1 focus:ring-accent disabled:opacity-40 text-center"}),(0,p.jsx)("span",{className:"text-text-muted",children:"s before /continue"})]})]})}function lY({projectId:e}){let i=(0,_.useRef)(null),[r,s]=(0,_.useState)(null),[n,o]=(0,_.useState)(null),[a,l]=(0,_.useState)(null),[h,c]=(0,_.useState)([]),[d,u]=(0,_.useState)(!1),[f,m]=(0,_.useState)(!1);(0,_.useEffect)(()=>{fetch("/api/config").then(e=>e.ok?e.json():null).then(i=>{if(!i||!Array.isArray(i.projects))return;let r=i.projects.find(i=>i.id===e);r&&u(!!r.auto_restore_after_restart)}).catch(()=>{})},[e]);let v=async i=>{m(!0);try{let r=await fetch("/api/config");if(!r.ok)throw Error(`GET /api/config ${r.status}`);let s=await r.json(),n=s.projects?.findIndex(i=>i.id===e)??-1;if(n<0)throw Error("project not found");s.projects[n]={...s.projects[n],auto_restore_after_restart:i};let o=await fetch("/api/config",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(!o.ok)throw Error(`PUT /api/config ${o.status}`)}finally{m(!1)}},x=()=>{fetch(`/api/project-history/snapshots?project=${encodeURIComponent(e)}`).then(e=>e.ok?e.json():null).then(e=>{e&&Array.isArray(e.snapshots)&&c(e.snapshots)}).catch(()=>{})};(0,_.useEffect)(()=>{x();let e=setInterval(x,15e3);return()=>clearInterval(e)},[e]);let b=async i=>{if(window.confirm(`Restore snapshot ${i}? This will replay every message through AgentChattr (tagged by the original sender) and may duplicate history already in the chat. Continue?`)){s("restore"),o(null),l(null);try{let r=await fetch(`/api/project-history/restore?project=${encodeURIComponent(e)}&name=${encodeURIComponent(i)}`,{method:"POST"}),s=await r.json().catch(()=>null);if(!r.ok)throw Error(s&&s.error||`HTTP ${r.status}`);s&&"number"==typeof s.imported&&l(s)}catch(e){o(e.message||String(e))}finally{s(null),x()}}},y=async()=>{s("export"),o(null),l(null);try{let i=await fetch(`/api/project-history?project=${encodeURIComponent(e)}`);if(!i.ok){let e=await i.text().catch(()=>"");throw Error(`HTTP ${i.status}: ${e.slice(0,200)}`)}let r=await i.blob(),s=URL.createObjectURL(r),n=document.createElement("a"),o=new Date().toISOString().slice(0,10);n.href=s,n.download=`${e}-history-${o}.json`,document.body.appendChild(n),n.click(),document.body.removeChild(n),URL.revokeObjectURL(s)}catch(e){o(e.message||String(e))}finally{s(null)}},S=async r=>{let n;if(r.size>0xa00000)return void o(`File too large (${r.size} bytes; limit 10485760)`);s("import"),o(null),l(null);try{let e=await r.text();n=JSON.parse(e)}catch(e){s(null),o(`Invalid JSON: ${e.message||String(e)}`);return}if(!n||"object"!=typeof n||!Array.isArray(n.messages)){s(null),o("File missing 'messages' array");return}let a=!1;if(n.project_id&&n.project_id!==e){if(!window.confirm(`This export is from project '${n.project_id}' but you're importing into '${e}'. Continue anyway?`))return void s(null);a=!0}let h=new Set(["head","dev","reviewer1","reviewer2","re1","re2","t1","t2a","t2b","t3","system"]),c=!1,d=new Set;for(let e of n.messages)if(e&&"object"==typeof e){let i=e.sender;if("string"==typeof i&&h.has(i.toLowerCase())&&(d.add(i),d.size>=5))break}if(d.size>0){if(!window.confirm(`This export contains messages attributed to reserved agent/system identities (${[...d].join(", ")}). Importing will replay them as those agents — only do this for a legitimate disaster-recovery restore. Continue?`))return void s(null);c=!0}let u=!1,f=i=>fetch(`/api/project-history?project=${encodeURIComponent(e)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...n,allow_project_mismatch:a,allow_agent_senders:c,allow_duplicate:u,...i})});try{let e=await f({}),i=await e.json().catch(()=>null);if(409===e.status&&i&&"string"==typeof i.error&&/already imported/i.test(i.error)){if(!window.confirm(`${i.error}
|
|
26
26
|
|
|
27
|
-
This file looks like it was already imported. Re-import will duplicate every message. Continue anyway?`))return void s(null);u=!0,e=await f({allow_duplicate:!0}),i=await e.json().catch(()=>null)}if(!e.ok)throw Error(i&&i.error||`HTTP ${e.status}`);l(i)}catch(e){o(e.message||String(e))}finally{s(null),i.current&&(i.current.value="")}};return(0,p.jsxs)("div",{className:"border border-border rounded p-2 text-[11px] font-mono",children:[(0,p.jsxs)("div",{className:"flex items-center gap-1.5 text-text-muted uppercase tracking-wider mb-1.5",children:["Project History",(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"Project History"})," — export or import the full AgentChattr chat history for this project. Useful for backup, migration, or resuming after a fresh install."]})]}),(0,p.jsxs)("div",{className:"flex items-center gap-1.5 flex-wrap",children:[(0,p.jsx)("button",{type:"button",onClick:y,disabled:null!==r,className:"px-2 py-0.5 text-[10px] text-accent border border-accent/40 rounded hover:bg-accent/10 transition-colors disabled:opacity-30 disabled:cursor-not-allowed",title:"Download a JSON snapshot of this project's chat history",children:"export"===r?"…":`Export ${e} chat`}),(0,p.jsx)("button",{type:"button",onClick:()=>{o(null),l(null),i.current?.click()},disabled:null!==r,className:"px-2 py-0.5 text-[10px] text-text-muted border border-border rounded hover:text-text hover:border-accent transition-colors disabled:opacity-30 disabled:cursor-not-allowed",title:"Restore a previously exported chat history (JSON)",children:"import"===r?"Importing…":"Import history…"}),(0,p.jsx)("input",{ref:i,type:"file",accept:"application/json,.json",className:"hidden",onChange:e=>{let i=e.target.files&&e.target.files[0];i&&S(i)}})]}),n&&(0,p.jsx)("div",{className:"mt-1 text-[10px] text-red-400",children:n}),a&&(0,p.jsxs)("div",{className:"mt-1 text-[10px] text-text-muted",children:["Imported ",a.imported," / ",a.total,a.skipped>0&&` \xb7 skipped ${a.skipped}`,a.errors.length>0&&` \xb7 ${a.errors.length} errors`]}),(0,p.jsxs)("label",{className:"mt-2 flex items-center gap-1.5 text-[10px] text-text-muted cursor-pointer select-none",children:[(0,p.jsx)("input",{type:"checkbox",checked:d,disabled:f,onChange:e=>{let i=e.target.checked;u(i),v(i).catch(()=>u(!i))}}),"Auto-restore newest snapshot after AC restart"]}),h.length>0&&(0,p.jsxs)("div",{className:"mt-2 border-t border-border/50 pt-1.5",children:[(0,p.jsx)("div",{className:"text-[9px] text-text-muted uppercase tracking-wider mb-0.5",children:"Auto-snapshots (before restart)"}),h.map(e=>{let i=new Date(e.mtime).toLocaleString();return(0,p.jsxs)("div",{className:"flex items-center gap-1.5 text-[10px] py-0.5",children:[(0,p.jsx)("span",{className:"text-text-muted tabular-nums flex-1 truncate",title:e.name,children:i}),(0,p.jsxs)("span",{className:"text-text-muted tabular-nums shrink-0",children:[Math.round(e.size/1024),"KB"]}),(0,p.jsx)("button",{type:"button",onClick:()=>b(e.name),disabled:null!==r,className:"px-1.5 py-0.5 text-[10px] text-accent border border-accent/40 rounded hover:bg-accent/10 transition-colors disabled:opacity-30 disabled:cursor-not-allowed",title:`Restore ${e.name}`,children:"restore"===r?"…":"Restore"})]},e.name)})]})]})}let lG=["minimal","low","medium","high"],lX={codex:[{value:"",label:"(CLI default)"},{value:"gpt-5.4",label:"gpt-5.4"},{value:"gpt-5",label:"gpt-5"},{value:"gpt-4o",label:"gpt-4o"}],claude:[{value:"",label:"(CLI default)"},{value:"claude-opus-4-7",label:"claude-opus-4-7"},{value:"claude-opus-4-6",label:"claude-opus-4-6"},{value:"claude-sonnet-4-6",label:"claude-sonnet-4-6"},{value:"claude-haiku-4-5-20251001",label:"claude-haiku-4-5"}]};function lJ(e){return lX[e]||[{value:"",label:"(CLI default)"}]}function lQ({projectId:e,onClose:i}){let[r,s]=(0,_.useState)(null),[n,o]=(0,_.useState)(null),[a,l]=(0,_.useState)(null),[h,c]=(0,_.useState)(new Set),d=(0,_.useCallback)(async()=>{try{let i=await fetch(`/api/project/${encodeURIComponent(e)}/agent-models`);if(!i.ok)throw Error(`${i.status}`);let r=await i.json();s(r.agents||[]),o(null)}catch(e){o(e.message)}},[e]);(0,_.useEffect)(()=>{d()},[d]),(0,_.useEffect)(()=>{let e=e=>{"Escape"===e.key&&i()};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[i]);let u=async(i,r)=>{l(i),o(null);try{let s=await fetch(`/api/project/${encodeURIComponent(e)}/agent-models/${i}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)}),n=await s.json();if(!s.ok||!1===n.ok)throw Error(n.error||`${s.status}`);c(e=>{let r=new Set(e);return r.add(i),r}),await d()}catch(e){o(e.message)}finally{l(null)}},f=async i=>{l(i),o(null);try{let r=await fetch(`/api/agents/${encodeURIComponent(e)}/${encodeURIComponent(i)}/restart`,{method:"POST"}),s=await r.json();if(!r.ok||!1===s.ok)throw Error(s.error||`${r.status}`);c(e=>{let r=new Set(e);return r.delete(i),r})}catch(e){o(e.message)}finally{l(null)}};return(0,p.jsx)("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",onClick:i,role:"dialog","aria-modal":"true","aria-labelledby":"agent-models-title",children:(0,p.jsxs)("div",{className:"relative mx-4 w-full max-w-[520px] max-h-[90vh] overflow-auto rounded-lg border border-white/10 bg-neutral-950 p-6 shadow-2xl",onClick:e=>e.stopPropagation(),children:[(0,p.jsx)("button",{type:"button",onClick:i,"aria-label":"Close",className:"absolute right-3 top-3 rounded p-1 text-neutral-400 hover:bg-white/5 hover:text-white",children:(0,p.jsx)("svg",{width:"18",height:"18",viewBox:"0 0 20 20",fill:"none",stroke:"currentColor",strokeWidth:"1.8",children:(0,p.jsx)("path",{d:"M4 4l12 12M16 4L4 16",strokeLinecap:"round"})})}),(0,p.jsxs)("div",{className:"flex items-center justify-between mb-3",children:[(0,p.jsx)("h2",{id:"agent-models-title",className:"text-base font-semibold text-white",children:"Agent Models"}),n&&(0,p.jsxs)("span",{className:"text-[10px] text-error max-w-[60%] truncate ml-2",title:n,children:["err: ",n]})]}),(0,p.jsxs)("div",{className:"flex flex-col gap-1.5",children:[!r&&(0,p.jsx)("div",{className:"text-[11px] text-text-muted",children:"Loading…"}),r&&0===r.length&&(0,p.jsx)("div",{className:"text-[11px] text-text-muted",children:"No agents configured."}),r&&r.map(e=>(0,p.jsxs)("div",{className:"flex items-center gap-1.5 flex-wrap",children:[(0,p.jsx)("span",{className:"text-[11px] text-text font-semibold w-16 shrink-0",children:e.agent_id}),(0,p.jsx)("span",{className:"text-[10px] text-text-muted w-12 shrink-0",children:e.backend}),h.has(e.agent_id)&&(0,p.jsx)("span",{className:"text-[9px] text-[#ffcc00] border border-[#ffcc00]/40 px-1 py-[1px] shrink-0",title:"Config changed — running session is still on the old model/effort. Click Restart to apply.",children:"restart required"}),(0,p.jsxs)("select",{value:e.model,disabled:a===e.agent_id,onChange:i=>u(e.agent_id,{model:i.target.value}),className:"flex-1 min-w-[140px] bg-transparent border border-border px-1 py-0.5 text-[11px] font-mono text-text outline-none focus:border-accent cursor-pointer disabled:opacity-50",children:[lJ(e.backend).map(e=>(0,p.jsx)("option",{value:e.value,className:"bg-bg-surface",children:e.label},e.value)),e.model&&!lJ(e.backend).some(i=>i.value===e.model)&&(0,p.jsxs)("option",{value:e.model,className:"bg-bg-surface",children:[e.model," (custom)"]})]}),e.reasoning_supported?(0,p.jsxs)("select",{value:e.reasoning_effort||"",disabled:a===e.agent_id,onChange:i=>u(e.agent_id,{reasoning_effort:i.target.value}),className:"bg-transparent border border-border px-1 py-0.5 text-[11px] text-text outline-none focus:border-accent cursor-pointer disabled:opacity-50",children:[(0,p.jsx)("option",{value:"",className:"bg-bg-surface",children:"(default)"}),lG.map(e=>(0,p.jsx)("option",{value:e,className:"bg-bg-surface",children:e},e))]}):(0,p.jsx)("span",{className:"text-[10px] text-text-muted w-16 text-center",children:"—"}),(0,p.jsx)("button",{type:"button",onClick:()=>f(e.agent_id),disabled:a===e.agent_id,title:"Restart this agent to pick up the new model / reasoning setting",className:"shrink-0 px-1.5 py-0.5 text-[10px] text-text-muted border border-border hover:text-accent hover:border-accent/40 disabled:opacity-50 transition-colors",children:a===e.agent_id?"…":"Restart"})]},e.agent_id)),(0,p.jsxs)("p",{className:"mt-2 text-[10px] text-text-muted leading-snug",children:["Codex reasoning effort defaults to ",(0,p.jsx)("code",{className:"text-text",children:"medium"})," for new projects. Blank model falls back to the CLI default. Click Restart to apply changes to a live session."]})]})]})})}function lZ({projectId:e}){let[i,r]=(0,_.useState)(!1),[s,n]=(0,_.useState)(null);return(0,_.useEffect)(()=>{let i=!1;return fetch(`/api/project/${encodeURIComponent(e)}/agent-models`).then(e=>e.ok?e.json():null).then(e=>{i||!e||n((e.agents||[]).map(e=>({id:e.agent_id,backend:e.backend})))}).catch(()=>{}),()=>{i=!0}},[e]),(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)("div",{className:"flex flex-col border border-border",children:[(0,p.jsxs)("div",{className:"flex items-center justify-between h-7 px-3 shrink-0 border-b border-border",children:[(0,p.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,p.jsx)("span",{className:"text-[11px] text-text-muted uppercase tracking-wider",children:"Agent Models"}),(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"Agent Models"})," — configure which LLM model and reasoning effort each agent uses. Changes require an agent restart to take effect."]})]}),(0,p.jsx)("button",{type:"button",onClick:()=>r(!0),className:"px-2 py-0.5 text-[10px] text-text-muted border border-border hover:text-accent hover:border-accent/40 transition-colors",children:"Configure →"})]}),s&&s.length>0&&(0,p.jsx)("div",{className:"px-3 py-1 text-[10px] text-text-muted truncate",title:s.map(e=>`${e.id}: ${e.backend}`).join(" · "),children:s.map((e,i)=>(0,p.jsxs)("span",{children:[i>0&&(0,p.jsx)("span",{className:"text-text-muted/60",children:" · "}),(0,p.jsx)("span",{className:"text-text",children:e.id}),(0,p.jsxs)("span",{children:[": ",e.backend]})]},e.id))})]}),i&&(0,p.jsx)(lQ,{projectId:e,onClose:()=>r(!1)})]})}function l0({projectId:e}){return(0,p.jsxs)("div",{className:"flex flex-col h-full min-h-0",children:[(0,p.jsx)(m,{label:"Operator Features",tooltip:(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"Operator Features"})," — tools for running autonomous overnight batches. Includes the Scheduled Trigger, Telegram Bridge, Loop Guard, Project History, and Agent Models."]})}),(0,p.jsxs)("div",{className:"flex-1 min-h-0 flex flex-col lg:flex-row gap-2 p-2 overflow-auto lg:overflow-hidden",children:[(0,p.jsx)("div",{className:"lg:flex-1 lg:min-w-[280px] lg:min-h-0",children:(0,p.jsx)(lH,{projectId:e})}),(0,p.jsx)("div",{className:"hidden lg:block w-px self-stretch bg-border"}),(0,p.jsxs)("div",{className:"lg:flex-1 lg:min-h-0 lg:overflow-y-auto flex flex-col gap-2",children:[(0,p.jsx)(lZ,{projectId:e}),(0,p.jsx)(l$,{projectId:e}),(0,p.jsx)(lq,{projectId:e}),(0,p.jsx)(lV,{projectId:e}),(0,p.jsx)(lY,{projectId:e})]})]})]})}e.s(["default",0,function({projectId:e}){let i=(0,_.useRef)(null),[r,s]=(0,_.useState)(.5),[n,o]=(0,_.useState)(.5),a=(0,_.useRef)(null),[l,h]=(0,_.useState)({}),[c,d]=(0,_.useState)(!1),u=(0,_.useRef)(!1);(0,_.useEffect)(()=>{u.current=!1,d(!1)},[e]),(0,_.useEffect)(()=>{u.current||fetch("/api/config").then(e=>e.ok?e.json():null).then(i=>{if(!i)return;let r=(i.projects||[]).find(i=>i.id===e);r?.bridge_filter_agents_only&&d(!0),u.current=!0}).catch(()=>{})},[e]);let f=(0,_.useCallback)(()=>{d(i=>{let r=!i;return fetch("/api/config").then(e=>e.ok?e.json():null).then(i=>{if(!i)return;let s=(i.projects||[]).find(i=>i.id===e);if(s)return s.bridge_filter_agents_only=r,fetch("/api/config",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)})}).catch(()=>{}),r})},[e]),v=(0,_.useMemo)(()=>(0,p.jsx)("button",{type:"button",onClick:f,title:c?"Showing agent messages only — click to show all":"Showing all messages — click to hide system/status noise",className:`px-1.5 py-0.5 text-[10px] border transition-colors ${c?"border-accent/50 text-accent bg-accent/10 hover:bg-accent/20":"border-border text-text-muted hover:text-text hover:border-accent"}`,children:c?"Agents ●":"All ○"}),[c,f]);(0,_.useEffect)(()=>{let i=()=>{fetch("/api/agents").then(e=>e.ok?e.json():{}).then(i=>{let r={};for(let[s,n]of Object.entries(i))s.startsWith(`${e}/`)&&(r[s.split("/")[1]]=n.state);h(r)}).catch(()=>{})};i();let r=setInterval(i,5e3);return()=>clearInterval(r)},[e]);let x=(0,_.useCallback)((e,i)=>Math.min((i-4-150)/i,Math.max(150/i,e)),[]);(0,_.useEffect)(()=>{let e=e=>{if(!a.current||!i.current)return;let r=i.current.getBoundingClientRect();"col"===a.current?s(x((e.clientX-r.left)/r.width,r.width)):o(x((e.clientY-r.top)/r.height,r.height))},r=()=>{a.current=null,document.body.style.cursor="",document.body.style.userSelect=""};return window.addEventListener("mousemove",e),window.addEventListener("mouseup",r),()=>{window.removeEventListener("mousemove",e),window.removeEventListener("mouseup",r)}},[x]);let b=e=>{a.current=e,document.body.style.cursor="col"===e?"col-resize":"row-resize",document.body.style.userSelect="none"},y=`${100*r}% 4px 1fr`,S=`${100*n}% 4px 1fr`;return(0,p.jsxs)("div",{ref:i,className:"w-full h-full",style:{display:"grid",gridTemplateColumns:y,gridTemplateRows:S},children:[(0,p.jsxs)("div",{className:"flex flex-col overflow-hidden border-2 border-accent",children:[(0,p.jsx)(m,{label:"AgentChattr — primary chat",tooltip:(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"Primary Chat"})," — live chat between you and the 4 AI agents. Messages you type here trigger agent actions. Use @mentions to address specific agents."]}),children:v}),(0,p.jsx)("div",{className:"flex-1 min-h-0",children:(0,p.jsx)(N,{projectId:e,filterSystem:c})}),(0,p.jsx)(i7,{projectId:e})]}),(0,p.jsx)("div",{className:"bg-border cursor-col-resize hover:bg-accent-dim transition-colors",onMouseDown:()=>b("col")}),(0,p.jsx)("div",{className:"flex flex-col overflow-hidden",children:(0,p.jsx)(lA,{projectId:e,agentStates:l,onStatusChange:(e,i)=>{h(r=>({...r,[e]:i}))}})}),(0,p.jsx)("div",{className:"bg-border cursor-row-resize hover:bg-accent-dim transition-colors",onMouseDown:()=>b("row")}),(0,p.jsx)("div",{className:"bg-border cursor-move",onMouseDown:()=>b("col")}),(0,p.jsx)("div",{className:"bg-border cursor-row-resize hover:bg-accent-dim transition-colors",onMouseDown:()=>b("row")}),(0,p.jsxs)("div",{className:"flex flex-col overflow-hidden",children:[(0,p.jsx)(m,{label:"GitHub",tooltip:(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"GitHub"}),"— open issues and pull requests on this project's repo. Click any item to open it on GitHub. The batch progress panel tracks the active batch's lifecycle from queued to merged."]})}),(0,p.jsx)("div",{className:"flex-1 min-h-0",children:(0,p.jsx)(i5,{projectId:e})})]}),(0,p.jsx)("div",{className:"bg-border cursor-col-resize hover:bg-accent-dim transition-colors",onMouseDown:()=>b("col")}),(0,p.jsx)(l0,{projectId:e})]})}],55601)},73766,e=>{e.n(e.i(55601))}]);
|
|
27
|
+
This file looks like it was already imported. Re-import will duplicate every message. Continue anyway?`))return void s(null);u=!0,e=await f({allow_duplicate:!0}),i=await e.json().catch(()=>null)}if(!e.ok)throw Error(i&&i.error||`HTTP ${e.status}`);l(i)}catch(e){o(e.message||String(e))}finally{s(null),i.current&&(i.current.value="")}};return(0,p.jsxs)("div",{className:"border border-border rounded p-2 text-[11px] font-mono",children:[(0,p.jsxs)("div",{className:"flex items-center gap-1.5 text-text-muted uppercase tracking-wider mb-1.5",children:["Project History",(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"Project History"})," — export or import the full AgentChattr chat history for this project. Useful for backup, migration, or resuming after a fresh install."]})]}),(0,p.jsxs)("div",{className:"flex items-center gap-1.5 flex-wrap",children:[(0,p.jsx)("button",{type:"button",onClick:y,disabled:null!==r,className:"px-2 py-0.5 text-[10px] text-accent border border-accent/40 rounded hover:bg-accent/10 transition-colors disabled:opacity-30 disabled:cursor-not-allowed",title:"Download a JSON snapshot of this project's chat history",children:"export"===r?"…":`Export ${e} chat`}),(0,p.jsx)("button",{type:"button",onClick:()=>{o(null),l(null),i.current?.click()},disabled:null!==r,className:"px-2 py-0.5 text-[10px] text-text-muted border border-border rounded hover:text-text hover:border-accent transition-colors disabled:opacity-30 disabled:cursor-not-allowed",title:"Restore a previously exported chat history (JSON)",children:"import"===r?"Importing…":"Import history…"}),(0,p.jsx)("input",{ref:i,type:"file",accept:"application/json,.json",className:"hidden",onChange:e=>{let i=e.target.files&&e.target.files[0];i&&S(i)}})]}),n&&(0,p.jsx)("div",{className:"mt-1 text-[10px] text-red-400",children:n}),a&&(0,p.jsxs)("div",{className:"mt-1 text-[10px] text-text-muted",children:["Imported ",a.imported," / ",a.total,a.skipped>0&&` \xb7 skipped ${a.skipped}`,a.errors.length>0&&` \xb7 ${a.errors.length} errors`]}),(0,p.jsxs)("label",{className:"mt-2 flex items-center gap-1.5 text-[10px] text-text-muted cursor-pointer select-none",children:[(0,p.jsx)("input",{type:"checkbox",checked:d,disabled:f,onChange:e=>{let i=e.target.checked;u(i),v(i).catch(()=>u(!i))}}),"Auto-restore newest snapshot after AC restart"]}),h.length>0&&(0,p.jsxs)("div",{className:"mt-2 border-t border-border/50 pt-1.5",children:[(0,p.jsx)("div",{className:"text-[9px] text-text-muted uppercase tracking-wider mb-0.5",children:"Auto-snapshots (before restart)"}),h.map(e=>{let i=new Date(e.mtime).toLocaleString();return(0,p.jsxs)("div",{className:"flex items-center gap-1.5 text-[10px] py-0.5",children:[(0,p.jsx)("span",{className:"text-text-muted tabular-nums flex-1 truncate",title:e.name,children:i}),(0,p.jsxs)("span",{className:"text-text-muted tabular-nums shrink-0",children:[Math.round(e.size/1024),"KB"]}),(0,p.jsx)("button",{type:"button",onClick:()=>b(e.name),disabled:null!==r,className:"px-1.5 py-0.5 text-[10px] text-accent border border-accent/40 rounded hover:bg-accent/10 transition-colors disabled:opacity-30 disabled:cursor-not-allowed",title:`Restore ${e.name}`,children:"restore"===r?"…":"Restore"})]},e.name)})]})]})}let lG=["minimal","low","medium","high"],lX={codex:[{value:"",label:"(CLI default)"},{value:"gpt-5.4",label:"gpt-5.4"},{value:"gpt-5",label:"gpt-5"},{value:"gpt-4o",label:"gpt-4o"}],claude:[{value:"",label:"(CLI default)"},{value:"claude-opus-4-7",label:"claude-opus-4-7"},{value:"claude-opus-4-6",label:"claude-opus-4-6"},{value:"claude-sonnet-4-6",label:"claude-sonnet-4-6"},{value:"claude-haiku-4-5-20251001",label:"claude-haiku-4-5"}]};function lJ(e){return lX[e]||[{value:"",label:"(CLI default)"}]}function lQ({projectId:e,onClose:i}){let[r,s]=(0,_.useState)(null),[n,o]=(0,_.useState)(null),[a,l]=(0,_.useState)(null),[h,c]=(0,_.useState)(new Set),d=(0,_.useCallback)(async()=>{try{let i=await fetch(`/api/project/${encodeURIComponent(e)}/agent-models`);if(!i.ok)throw Error(`${i.status}`);let r=await i.json();s(r.agents||[]),o(null)}catch(e){o(e.message)}},[e]);(0,_.useEffect)(()=>{d()},[d]),(0,_.useEffect)(()=>{let e=e=>{"Escape"===e.key&&i()};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[i]);let u=async(i,r)=>{l(i),o(null);try{let s=await fetch(`/api/project/${encodeURIComponent(e)}/agent-models/${i}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)}),n=await s.json();if(!s.ok||!1===n.ok)throw Error(n.error||`${s.status}`);c(e=>{let r=new Set(e);return r.add(i),r}),await d()}catch(e){o(e.message)}finally{l(null)}},f=async i=>{l(i),o(null);try{let r=await fetch(`/api/agents/${encodeURIComponent(e)}/${encodeURIComponent(i)}/restart`,{method:"POST"}),s=await r.json();if(!r.ok||!1===s.ok)throw Error(s.error||`${r.status}`);c(e=>{let r=new Set(e);return r.delete(i),r})}catch(e){o(e.message)}finally{l(null)}};return(0,p.jsx)("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",onClick:i,role:"dialog","aria-modal":"true","aria-labelledby":"agent-models-title",children:(0,p.jsxs)("div",{className:"relative mx-4 w-full max-w-[520px] max-h-[90vh] overflow-auto rounded-lg border border-white/10 bg-neutral-950 p-6 shadow-2xl",onClick:e=>e.stopPropagation(),children:[(0,p.jsx)("button",{type:"button",onClick:i,"aria-label":"Close",className:"absolute right-3 top-3 rounded p-1 text-neutral-400 hover:bg-white/5 hover:text-white",children:(0,p.jsx)("svg",{width:"18",height:"18",viewBox:"0 0 20 20",fill:"none",stroke:"currentColor",strokeWidth:"1.8",children:(0,p.jsx)("path",{d:"M4 4l12 12M16 4L4 16",strokeLinecap:"round"})})}),(0,p.jsxs)("div",{className:"flex items-center justify-between mb-3",children:[(0,p.jsx)("h2",{id:"agent-models-title",className:"text-base font-semibold text-white",children:"Agent Models"}),n&&(0,p.jsxs)("span",{className:"text-[10px] text-error max-w-[60%] truncate ml-2",title:n,children:["err: ",n]})]}),(0,p.jsxs)("div",{className:"flex flex-col gap-1.5",children:[!r&&(0,p.jsx)("div",{className:"text-[11px] text-text-muted",children:"Loading…"}),r&&0===r.length&&(0,p.jsx)("div",{className:"text-[11px] text-text-muted",children:"No agents configured."}),r&&r.map(e=>(0,p.jsxs)("div",{className:"flex items-center gap-1.5 flex-wrap",children:[(0,p.jsx)("span",{className:"text-[11px] text-text font-semibold w-16 shrink-0",children:e.agent_id}),(0,p.jsx)("span",{className:"text-[10px] text-text-muted w-12 shrink-0",children:e.backend}),h.has(e.agent_id)&&(0,p.jsx)("span",{className:"text-[9px] text-[#ffcc00] border border-[#ffcc00]/40 px-1 py-[1px] shrink-0",title:"Config changed — running session is still on the old model/effort. Click Restart to apply.",children:"restart required"}),(0,p.jsxs)("select",{value:e.model,disabled:a===e.agent_id,onChange:i=>u(e.agent_id,{model:i.target.value}),className:"flex-1 min-w-[140px] bg-transparent border border-border px-1 py-0.5 text-[11px] font-mono text-text outline-none focus:border-accent cursor-pointer disabled:opacity-50",children:[lJ(e.backend).map(e=>(0,p.jsx)("option",{value:e.value,className:"bg-bg-surface",children:e.label},e.value)),e.model&&!lJ(e.backend).some(i=>i.value===e.model)&&(0,p.jsxs)("option",{value:e.model,className:"bg-bg-surface",children:[e.model," (custom)"]})]}),e.reasoning_supported?(0,p.jsxs)("select",{value:e.reasoning_effort||"",disabled:a===e.agent_id,onChange:i=>u(e.agent_id,{reasoning_effort:i.target.value}),className:"bg-transparent border border-border px-1 py-0.5 text-[11px] text-text outline-none focus:border-accent cursor-pointer disabled:opacity-50",children:[(0,p.jsx)("option",{value:"",className:"bg-bg-surface",children:"(default)"}),lG.map(e=>(0,p.jsx)("option",{value:e,className:"bg-bg-surface",children:e},e))]}):(0,p.jsx)("span",{className:"text-[10px] text-text-muted w-16 text-center",children:"—"}),(0,p.jsx)("button",{type:"button",onClick:()=>f(e.agent_id),disabled:a===e.agent_id,title:"Restart this agent to pick up the new model / reasoning setting",className:"shrink-0 px-1.5 py-0.5 text-[10px] text-text-muted border border-border hover:text-accent hover:border-accent/40 disabled:opacity-50 transition-colors",children:a===e.agent_id?"…":"Restart"})]},e.agent_id)),(0,p.jsxs)("p",{className:"mt-2 text-[10px] text-text-muted leading-snug",children:["Codex reasoning effort defaults to ",(0,p.jsx)("code",{className:"text-text",children:"medium"})," for new projects. Blank model falls back to the CLI default. Click Restart to apply changes to a live session."]})]})]})})}function lZ({projectId:e}){let[i,r]=(0,_.useState)(!1),[s,n]=(0,_.useState)(null);return(0,_.useEffect)(()=>{let i=!1;return fetch(`/api/project/${encodeURIComponent(e)}/agent-models`).then(e=>e.ok?e.json():null).then(e=>{i||!e||n((e.agents||[]).map(e=>({id:e.agent_id,backend:e.backend})))}).catch(()=>{}),()=>{i=!0}},[e]),(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)("div",{className:"flex flex-col border border-border",children:[(0,p.jsxs)("div",{className:"flex items-center justify-between h-7 px-3 shrink-0 border-b border-border",children:[(0,p.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,p.jsx)("span",{className:"text-[11px] text-text-muted uppercase tracking-wider",children:"Agent Models"}),(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"Agent Models"})," — configure which LLM model and reasoning effort each agent uses. Changes require an agent restart to take effect."]})]}),(0,p.jsx)("button",{type:"button",onClick:()=>r(!0),className:"px-2 py-0.5 text-[10px] text-text-muted border border-border hover:text-accent hover:border-accent/40 transition-colors",children:"Configure →"})]}),s&&s.length>0&&(0,p.jsx)("div",{className:"px-3 py-1 text-[10px] text-text-muted truncate",title:s.map(e=>`${e.id}: ${e.backend}`).join(" · "),children:s.map((e,i)=>(0,p.jsxs)("span",{children:[i>0&&(0,p.jsx)("span",{className:"text-text-muted/60",children:" · "}),(0,p.jsx)("span",{className:"text-text",children:e.id}),(0,p.jsxs)("span",{children:[": ",e.backend]})]},e.id))})]}),i&&(0,p.jsx)(lQ,{projectId:e,onClose:()=>r(!1)})]})}function l0({projectId:e}){return(0,p.jsxs)("div",{className:"flex flex-col h-full min-h-0",children:[(0,p.jsx)(m,{label:"Operator Features",tooltip:(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"Operator Features"})," — tools for running autonomous overnight batches. Includes the Scheduled Trigger, Telegram Bridge, Loop Guard, Project History, and Agent Models."]})}),(0,p.jsxs)("div",{className:"flex-1 min-h-0 flex flex-col lg:flex-row gap-2 p-2 overflow-auto lg:overflow-hidden",children:[(0,p.jsx)("div",{className:"lg:flex-1 lg:min-w-[280px] lg:min-h-0",children:(0,p.jsx)(lH,{projectId:e})}),(0,p.jsx)("div",{className:"hidden lg:block w-px self-stretch bg-border"}),(0,p.jsxs)("div",{className:"lg:flex-1 lg:min-h-0 lg:overflow-y-auto flex flex-col gap-2",children:[(0,p.jsx)(lZ,{projectId:e}),(0,p.jsx)(l$,{projectId:e}),(0,p.jsx)(lq,{projectId:e}),(0,p.jsx)(lV,{projectId:e}),(0,p.jsx)(lY,{projectId:e})]})]})]})}e.s(["default",0,function({projectId:e}){let i=(0,_.useRef)(null),[r,s]=(0,_.useState)(.5),[n,o]=(0,_.useState)(.5),a=(0,_.useRef)(null),[l,h]=(0,_.useState)({}),[c,d]=(0,_.useState)(!1),u=(0,_.useRef)(!1);(0,_.useEffect)(()=>{u.current=!1,d(!1)},[e]),(0,_.useEffect)(()=>{u.current||fetch("/api/config").then(e=>e.ok?e.json():null).then(i=>{if(!i)return;let r=(i.projects||[]).find(i=>i.id===e);r?.bridge_filter_agents_only&&d(!0),u.current=!0}).catch(()=>{})},[e]);let f=(0,_.useCallback)(()=>{d(i=>{let r=!i;return fetch("/api/config").then(e=>e.ok?e.json():null).then(i=>{if(!i)return;let s=(i.projects||[]).find(i=>i.id===e);if(s)return s.bridge_filter_agents_only=r,fetch("/api/config",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)})}).catch(()=>{}),r})},[e]),v=(0,_.useMemo)(()=>(0,p.jsx)("button",{type:"button",onClick:f,title:c?"Showing agent messages only — click to show all":"Showing all messages — click to hide system/status noise",className:`px-1.5 py-0.5 text-[10px] border transition-colors ${c?"border-accent/50 text-accent bg-accent/10 hover:bg-accent/20":"border-border text-text-muted hover:text-text hover:border-accent"}`,children:c?"Filter system log: on":"Filter system log: off"}),[c,f]);(0,_.useEffect)(()=>{let i=()=>{fetch("/api/agents").then(e=>e.ok?e.json():{}).then(i=>{let r={};for(let[s,n]of Object.entries(i))s.startsWith(`${e}/`)&&(r[s.split("/")[1]]=n.state);h(r)}).catch(()=>{})};i();let r=setInterval(i,5e3);return()=>clearInterval(r)},[e]);let x=(0,_.useCallback)((e,i)=>Math.min((i-4-150)/i,Math.max(150/i,e)),[]);(0,_.useEffect)(()=>{let e=e=>{if(!a.current||!i.current)return;let r=i.current.getBoundingClientRect();"col"===a.current?s(x((e.clientX-r.left)/r.width,r.width)):o(x((e.clientY-r.top)/r.height,r.height))},r=()=>{a.current=null,document.body.style.cursor="",document.body.style.userSelect=""};return window.addEventListener("mousemove",e),window.addEventListener("mouseup",r),()=>{window.removeEventListener("mousemove",e),window.removeEventListener("mouseup",r)}},[x]);let b=e=>{a.current=e,document.body.style.cursor="col"===e?"col-resize":"row-resize",document.body.style.userSelect="none"},y=`${100*r}% 4px 1fr`,S=`${100*n}% 4px 1fr`;return(0,p.jsxs)("div",{ref:i,className:"w-full h-full",style:{display:"grid",gridTemplateColumns:y,gridTemplateRows:S},children:[(0,p.jsxs)("div",{className:"flex flex-col overflow-hidden border-2 border-accent",children:[(0,p.jsx)(m,{label:"AgentChattr — primary chat",tooltip:(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"Primary Chat"})," — live chat between you and the 4 AI agents. Messages you type here trigger agent actions. Use @mentions to address specific agents."]}),children:v}),(0,p.jsx)("div",{className:"flex-1 min-h-0",children:(0,p.jsx)(N,{projectId:e,filterSystem:c})}),(0,p.jsx)(i7,{projectId:e})]}),(0,p.jsx)("div",{className:"bg-border cursor-col-resize hover:bg-accent-dim transition-colors",onMouseDown:()=>b("col")}),(0,p.jsx)("div",{className:"flex flex-col overflow-hidden",children:(0,p.jsx)(lA,{projectId:e,agentStates:l,onStatusChange:(e,i)=>{h(r=>({...r,[e]:i}))}})}),(0,p.jsx)("div",{className:"bg-border cursor-row-resize hover:bg-accent-dim transition-colors",onMouseDown:()=>b("row")}),(0,p.jsx)("div",{className:"bg-border cursor-move",onMouseDown:()=>b("col")}),(0,p.jsx)("div",{className:"bg-border cursor-row-resize hover:bg-accent-dim transition-colors",onMouseDown:()=>b("row")}),(0,p.jsxs)("div",{className:"flex flex-col overflow-hidden",children:[(0,p.jsx)(m,{label:"GitHub",tooltip:(0,p.jsxs)(g,{children:[(0,p.jsx)("b",{children:"GitHub"}),"— open issues and pull requests on this project's repo. Click any item to open it on GitHub. The batch progress panel tracks the active batch's lifecycle from queued to merged."]})}),(0,p.jsx)("div",{className:"flex-1 min-h-0",children:(0,p.jsx)(i5,{projectId:e})})]}),(0,p.jsx)("div",{className:"bg-border cursor-col-resize hover:bg-accent-dim transition-colors",onMouseDown:()=>b("col")}),(0,p.jsx)(l0,{projectId:e})]})}],55601)},73766,e=>{e.n(e.i(55601))}]);
|