arcane-agents 1.1.0 → 1.2.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.
package/README.md CHANGED
@@ -476,6 +476,36 @@ server:
476
476
  port: 7600
477
477
  ```
478
478
 
479
+ ## Named Sessions
480
+
481
+ Named sessions let you run independent instances of Arcane Agents, each with its
482
+ own worker state (SQLite DB) and tmux session. Config (projects, runtimes,
483
+ shortcuts) stays shared.
484
+
485
+ ### Usage
486
+
487
+ ```bash
488
+ # Start the default session (same as before)
489
+ arcane-agents start
490
+
491
+ # Start a named session with separate state
492
+ arcane-agents start --session side-project
493
+ arcane-agents start -s experiments
494
+
495
+ # List all sessions that have been created
496
+ arcane-agents sessions list
497
+
498
+ # Delete a named session and all its data
499
+ arcane-agents sessions delete side-project
500
+ ```
501
+
502
+ ### How it works
503
+
504
+ - The default session uses `~/.local/state/arcane-agents/arcane-agents.db` (unchanged).
505
+ - Named sessions store their DB under `~/.local/state/arcane-agents/sessions/<name>/arcane-agents.db`.
506
+ - Each named session gets its own tmux session (`arcane-agents-<name>` by default).
507
+ - Config paths are shared across all sessions.
508
+
479
509
  ## Development Commands
480
510
 
481
511
  ```bash
@@ -80,4 +80,4 @@ WARNING: This link could potentially be dangerous`)){let s=window.open();if(s){t
80
80
  `,t)}paste(e){this._core.paste(e)}refresh(e,t){this._verifyIntegers(e,t),this._core.refresh(e,t)}reset(){this._core.reset()}clearTextureAtlas(){this._core.clearTextureAtlas()}loadAddon(e){this._addonManager.loadAddon(this,e)}static get strings(){return{get promptLabel(){return sh.get()},set promptLabel(e){sh.set(e)},get tooMuchOutput(){return nh.get()},set tooMuchOutput(e){nh.set(e)}}}_verifyIntegers(...e){for(Jr of e)if(Jr===1/0||isNaN(Jr)||Jr%1!==0)throw new Error("This API only accepts integers")}_verifyPositiveIntegers(...e){for(Jr of e)if(Jr&&(Jr===1/0||isNaN(Jr)||Jr%1!==0||Jr<0))throw new Error("This API only accepts positive integers")}};const Aw=`
81
81
  `,Ow={background:"#1a1b26",foreground:"#c0caf5",cursor:"#c0caf5",cursorAccent:"#1a1b26",selectionBackground:"rgba(122, 162, 247, 0.28)",selectionInactiveBackground:"rgba(122, 162, 247, 0.2)",black:"#15161e",red:"#f7768e",green:"#9ece6a",yellow:"#e0af68",blue:"#7aa2f7",magenta:"#bb9af7",cyan:"#7dcfff",white:"#a9b1d6",brightBlack:"#414868",brightRed:"#f7768e",brightGreen:"#9ece6a",brightYellow:"#e0af68",brightBlue:"#7aa2f7",brightMagenta:"#bb9af7",brightCyan:"#7dcfff",brightWhite:"#c0caf5"};function zw({workerId:e,workerName:t,focusRequestKey:s}){const n=k.useRef(null),l=k.useRef(null),c=k.useRef(null),h=k.useRef(null),f=k.useRef(null),_=k.useRef(void 0),d=k.useCallback(()=>{l.current?.focus()},[]),g=k.useCallback(()=>{const w=h.current,v=l.current;!w||!v||w.readyState===WebSocket.OPEN&&w.send(JSON.stringify({type:"resize",cols:v.cols,rows:v.rows}))},[]),y=k.useCallback(()=>{const w=n.current,v=c.current,S=l.current;if(!w||!v||!S)return!1;const E=w.clientWidth,P=w.clientHeight;if(E<8||P<8||w.getClientRects().length===0)return!1;try{v.fit()}catch{return!1}return S.cols>0&&S.rows>0},[]),C=k.useCallback((w=16)=>{f.current&&cancelAnimationFrame(f.current);const v=S=>{if(y()){g();return}S<=0||(f.current=requestAnimationFrame(()=>{v(S-1)}))};f.current=requestAnimationFrame(()=>{v(w)})},[y,g]);return k.useEffect(()=>{if(!n.current)return;const w=new Iw({cursorBlink:!1,cursorStyle:"block",cursorInactiveStyle:"block",fontSize:13,lineHeight:1,theme:Ow}),v=new E0;w.loadAddon(v),w.attachCustomKeyEventHandler(E=>{if(!Ww(E))return!0;E.preventDefault(),E.stopPropagation();const P=h.current;return P?.readyState===WebSocket.OPEN&&P.send(Aw),!1}),w.open(n.current),w.writeln("Select an agent to connect its terminal."),l.current=w,c.current=v,C();const S=new ResizeObserver(()=>{C()});return S.observe(n.current),()=>{S.disconnect(),f.current&&(cancelAnimationFrame(f.current),f.current=null),h.current?.close(),w.dispose(),l.current=null,c.current=null}},[C]),k.useEffect(()=>{const w=l.current;if(!w)return;if(h.current?.close(),h.current=null,w.clear(),C(),!e){w.writeln("Select an agent to connect its terminal.");return}w.writeln(`Connecting to ${t??e}...`);const v=window.location.protocol==="https:"?"wss":"ws",S=new WebSocket(`${v}://${window.location.host}/api/terminal/${e}`);h.current=S;const E=w.onData(P=>{S.readyState===WebSocket.OPEN&&S.send(P)});return S.addEventListener("open",()=>{g(),C(24)}),S.addEventListener("message",P=>{typeof P.data=="string"?w.write(P.data):P.data instanceof Blob&&P.data.text().then(z=>{w.write(z)})}),S.addEventListener("close",()=>{w.writeln(`\r
82
82
  [terminal disconnected]`)}),S.addEventListener("error",()=>{w.writeln(`\r
83
- [terminal connection error]`)}),()=>{E.dispose(),S.close(),h.current===S&&(h.current=null)}},[C,g,e,t]),k.useEffect(()=>{if(!e||s===void 0||_.current===s)return;_.current=s,d();const w=setTimeout(()=>{d()},0);return()=>{clearTimeout(w)}},[s,d,e]),M.jsx("div",{className:"terminal-panel",ref:n})}function Ww(e){return e.type==="keydown"&&e.key==="Enter"&&e.shiftKey&&!e.ctrlKey&&!e.metaKey&&!e.altKey}function Fw({activeWorkers:e,selectedWorkers:t,terminalWorker:s,terminalFocused:n,selectedGroupActiveIndex:l,setSelectedGroupActiveIndex:c,setFocusedSelectedWorkerId:h,rallyCommandInputRef:f,rallyCommandDraft:_,rallyCommandSending:d,rallyCommandResultText:g,onRallyCommandDraftChange:y,onSendRallyCommand:C,rosterEntries:w,completionPendingWorkerIds:v,rosterActiveIndex:S,setRosterActiveIndex:E,onActivateRosterIndex:P,onOpenSelectedInTerminal:z,terminalFocusToken:H}){const I=k.useMemo(()=>new Set(v),[v]),G=v.length;return M.jsxs("div",{className:`terminal-column${s?" terminal-column-selected":""}${s&&n?" terminal-column-focused":""}`,children:[M.jsxs("div",{className:"terminal-header",children:[M.jsx("div",{className:"terminal-header-title",children:t.length>1&&!s?`${t.length} selected agents`:s?`${s.displayName??s.name} (${s.status})`:`Agents (${e.length})`}),!s&&G>0?M.jsxs("div",{className:"terminal-ready-chip",title:"Agents finished but not yet reviewed in terminal",children:["✦ ",G," ready"]}):null,s?M.jsx("button",{className:"terminal-open-external",onClick:()=>{z()},disabled:s.status==="stopped",title:"Open in external terminal",type:"button",children:"↗"}):null]}),t.length>1&&!s?M.jsxs("div",{className:"worker-roster",children:[M.jsx("div",{className:"worker-roster-section-label",children:"Selected Group"}),t.map((j,oe)=>M.jsx("button",{className:`worker-roster-item ${oe===l?"active":""}`,onMouseEnter:()=>c(oe),onClick:()=>{c(oe),h(j.id)},type:"button",children:M.jsxs("div",{className:"worker-roster-main",children:[M.jsx("img",{className:"worker-roster-avatar",src:`/api/assets/characters/${encodeURIComponent(j.avatarType)}/rotations/south.png`,alt:"",loading:"lazy","aria-hidden":"true"}),M.jsxs("div",{className:"worker-roster-text",children:[M.jsxs("div",{className:"worker-roster-name-row",children:[M.jsx("div",{className:"worker-roster-name",children:j.displayName??j.name}),I.has(j.id)?M.jsx("span",{className:"worker-complete-badge",title:"Finished and waiting for review",children:"READY"}):null]}),M.jsxs("div",{className:"worker-roster-meta",children:[j.projectId," · ",j.runtimeId," · ",j.status]}),j.activityText?M.jsx("div",{className:"worker-roster-activity",children:j.activityText}):null]})]})},j.id)),M.jsxs("form",{className:"rally-command-card",onSubmit:j=>{j.preventDefault(),C()},children:[M.jsxs("div",{className:"rally-command-header",children:[M.jsx("div",{className:"rally-command-title",children:"Rally Command"}),M.jsxs("div",{className:"rally-command-count",children:[t.length," agents"]})]}),M.jsx("textarea",{ref:f,className:"input rally-command-input",value:_,onChange:j=>{y(j.target.value)},onKeyDown:j=>{const oe=!j.shiftKey&&!j.ctrlKey&&!j.metaKey&&!j.altKey,Z=(j.ctrlKey||j.metaKey)&&!j.shiftKey&&!j.altKey;j.key==="Enter"&&(oe||Z)&&(j.preventDefault(),C())},placeholder:"Type once, send to all selected agents (use $NAME for per-agent names)...",disabled:d,rows:3}),M.jsxs("div",{className:"rally-command-actions",children:[M.jsx("div",{className:"rally-command-hint",children:"Enter sends, Shift+Enter adds a new line, $NAME inserts each agent's name"}),M.jsx("button",{className:"bar-btn",type:"submit",disabled:d||_.length===0,children:d?"Sending...":`Send to ${t.length}`})]}),g?M.jsx("div",{className:"rally-command-result",children:g}):null]})]}):s?M.jsx(zw,{workerId:s.id,workerName:s.displayName??s.name,focusRequestKey:H}):M.jsx("div",{className:"worker-roster",children:w.length===0?M.jsx("div",{className:"worker-roster-empty",children:"No active agents yet. Summon one from the bottom bar."}):w.map((j,oe)=>M.jsxs("div",{children:[j.kind==="shortcut"&&(oe===0||w[oe-1]?.kind!=="shortcut")?M.jsx("div",{className:"worker-roster-section-label",children:"Summon"}):null,j.kind==="worker"?M.jsx("button",{className:`worker-roster-item ${oe===S?"active":""}`,onMouseEnter:()=>E(oe),onClick:()=>P(oe),type:"button",children:M.jsxs("div",{className:"worker-roster-main",children:[M.jsx("img",{className:"worker-roster-avatar",src:`/api/assets/characters/${encodeURIComponent(j.worker.avatarType)}/rotations/south.png`,alt:"",loading:"lazy","aria-hidden":"true"}),M.jsxs("div",{className:"worker-roster-text",children:[M.jsxs("div",{className:"worker-roster-name-row",children:[M.jsx("div",{className:"worker-roster-name",children:j.worker.displayName??j.worker.name}),I.has(j.worker.id)?M.jsx("span",{className:"worker-complete-badge",title:"Finished and waiting for review",children:"READY"}):null]}),M.jsxs("div",{className:"worker-roster-meta",children:[j.worker.projectId," · ",j.worker.runtimeId," · ",j.worker.status]}),j.worker.activityText?M.jsx("div",{className:"worker-roster-activity",children:j.worker.activityText}):null]})]})}):M.jsx("button",{className:`worker-roster-item worker-roster-item-summon ${oe===S?"active":""}`,onMouseEnter:()=>E(oe),onClick:()=>P(oe),type:"button",children:M.jsxs("div",{className:"worker-roster-main",children:[j.shortcut.avatar?M.jsx("img",{className:"worker-roster-avatar worker-roster-summon-avatar",src:`/api/assets/characters/${encodeURIComponent(j.shortcut.avatar)}/rotations/south.png`,alt:"",loading:"lazy","aria-hidden":"true"}):M.jsx("div",{className:"worker-roster-summon-glyph","aria-hidden":"true",children:"+"}),M.jsxs("div",{className:"worker-roster-text",children:[M.jsx("div",{className:"worker-roster-name",children:j.shortcut.label}),M.jsxs("div",{className:"worker-roster-meta",children:[j.shortcut.project," · ",j.shortcut.runtime]}),M.jsx("div",{className:"worker-roster-activity",children:rv(j.shortcut.hotkeys)})]})]})})]},j.kind==="worker"?j.worker.id:`shortcut-${j.shortcutIndex}-${j.shortcut.label}`))})]})}function Hw(e,t){if(t.killConfirmWorkerIds.length>0)return Al(e)?(e.preventDefault(),t.confirmKillSelection(),!0):(e.preventDefault(),t.closeKillConfirm(),!0);if(!t.isEditableTarget(e.target)&&!t.isTerminalTarget(e.target)){const s=t.findMatchingShortcutIndexes(t.shortcutHotkeyBindings,e);if(s.length>0){e.preventDefault(),e.stopPropagation();for(const n of s)t.runSpawn({shortcutIndex:n});return!0}}if(e.key==="Escape")return t.renameModalOpen?(e.preventDefault(),t.closeRenameModal(),!0):t.batchSpawnDialogOpen?(e.preventDefault(),t.setBatchSpawnDialogOpen(!1),!0):t.shortcutsOverlayOpen?(e.preventDefault(),t.setShortcutsOverlayOpen(!1),!0):t.paletteOpen||t.spawnDialogOpen?(e.preventDefault(),t.setPaletteOpen(!1),t.setSpawnDialogOpen(!1),!0):(t.isTerminalTarget(e.target)||t.selectedWorkerId&&(e.preventDefault(),t.applySelection([])),!0);if(t.isTerminalEscapeShortcut(e)){if(!t.renameModalOpen&&!t.batchSpawnDialogOpen&&!t.shortcutsOverlayOpen&&!t.paletteOpen&&!t.spawnDialogOpen&&t.selectedWorkers.length>1&&t.focusedSelectedWorkerId){const n=t.escapeTerminalFocus();return e.preventDefault(),n&&e.stopPropagation(),t.setFocusedSelectedWorkerId(void 0),!0}if(t.escapeTerminalFocus())return e.preventDefault(),e.stopPropagation(),!0;if(!t.renameModalOpen&&!t.batchSpawnDialogOpen&&!t.shortcutsOverlayOpen&&!t.paletteOpen&&!t.spawnDialogOpen&&t.selectedWorkerIds.length>0){if(e.preventDefault(),t.selectedWorkerId){const n=t.rosterEntries.findIndex(l=>l.kind==="worker"&&l.worker.id===t.selectedWorkerId);n>=0&&t.setRosterActiveIndex(n)}t.applySelection([])}return!0}return e.key==="?"&&!e.ctrlKey&&!e.metaKey&&!e.altKey?((!t.isEditableTarget(e.target)||t.shortcutsOverlayOpen)&&(e.preventDefault(),t.setShortcutsOverlayOpen(s=>!s)),!0):(e.code==="BracketLeft"||e.code==="BracketRight"||e.key==="="&&!e.shiftKey)&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target)&&!t.isTerminalTarget(e.target)?(e.preventDefault(),e.code==="BracketLeft"?t.nudgeMapColumnRatio(e.shiftKey?-1:-t.mapColumnRatioStep):e.code==="BracketRight"?t.nudgeMapColumnRatio(e.shiftKey?1:t.mapColumnRatioStep):t.resetMapColumnRatio(),!0):!1}function jw(e,t){if(e.key==="Tab"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target))return t.isTerminalTarget(e.target)?!0:(e.preventDefault(),t.selectedWorkers.length>1?(t.cycleSelectedGroupFocus(e.shiftKey?-1:1),!0):(t.cycleSelection(e.shiftKey?-1:1),!0));if(e.code==="Period"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target))return t.isTerminalTarget(e.target)||(e.preventDefault(),t.cycleIdleSelection(e.shiftKey?-1:1)),!0;if(e.code==="Comma"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target))return t.isTerminalTarget(e.target)||(e.preventDefault(),t.cycleIdleSelection(-1)),!0;const s=t.parseControlGroupDigit(e);if(s===void 0)return!1;if((e.ctrlKey||e.metaKey)&&!e.altKey&&t.selectedWorkerIds.length>0)return e.preventDefault(),t.setControlGroups(n=>{const l=new Set(t.selectedWorkerIds),c=n[s]??[],h=new Set(c);if(c.length===t.selectedWorkerIds.length&&t.selectedWorkerIds.every(d=>h.has(d))){const d={...n};return delete d[s],d}const _={...n};for(const[d,g]of Object.entries(_)){const y=Number(d);!Number.isInteger(y)||y<0||y>9||y===s||!Array.isArray(g)||(_[y]=g.filter(C=>!l.has(C)))}return _[s]=[...t.selectedWorkerIds],_}),!0;if(!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey&&!t.isEditableTarget(e.target)){const n=t.controlGroupByDigitRef.current[s]??[];if(n.length===0)return!0;const l=new Set(t.activeWorkers.map(h=>h.id)),c=n.filter(h=>l.has(h));if(c.length===0)return t.setControlGroups(h=>{if(!(s in h))return h;const f={...h};return delete f[s],f}),!0;e.preventDefault(),t.applySelection(c,{center:c.length===1})}return!0}function $w(e,t){if(t.isEditableTarget(e.target))return!0;const s=e.key.toLowerCase();if((s==="k"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(t.inSelectedGroupView?e.shiftKey:t.selectedWorkerIds.length===1||!e.shiftKey)||e.key==="Delete")&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&t.selectedWorkerIds.length>0)return e.preventDefault(),t.onKillSelected(),!0;if(t.selectedWorkers.length>1&&!t.isTerminalTarget(e.target)){if(t.inSelectedGroupView&&s==="c"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey)return t.focusRallyCommandInput()&&e.preventDefault(),!0;if((s==="j"||s==="k")&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey){e.preventDefault();const l=s==="j"?1:-1,c=t.clampNumber(t.selectedGroupActiveIndex+l,0,t.selectedWorkers.length-1),h=t.selectedWorkers[c];return t.setSelectedGroupActiveIndex(c),t.focusedSelectedWorkerId&&h&&t.setFocusedSelectedWorkerId(h.id),!0}if(Al(e)){const l=t.selectedWorkers.find(c=>c.id===t.focusedSelectedWorkerId)??t.selectedWorkers[t.selectedGroupActiveIndex]??t.selectedWorkers[0];return l&&(e.preventDefault(),t.setFocusedSelectedWorkerId(l.id),t.requestTerminalFocus()),!0}}if(t.selectedWorkerIds.length===0&&t.rosterEntries.length>0&&!t.isTerminalTarget(e.target)){if(s==="k"&&e.shiftKey&&!e.ctrlKey&&!e.metaKey&&!e.altKey)return e.preventDefault(),t.onKillRosterActive(),!0;if(s==="n"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey&&t.firstSummonEntryIndex!==void 0)return e.preventDefault(),t.setRosterActiveIndex(t.firstSummonEntryIndex),!0;if((s==="j"||s==="k")&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey)return e.preventDefault(),t.setRosterActiveIndex(l=>{const c=s==="j"?1:-1;return t.clampNumber(l+c,0,t.rosterEntries.length-1)}),!0;if(Al(e))return e.preventDefault(),t.onActivateRosterIndex(t.rosterActiveIndex),!0}if(s==="r"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&t.selectedWorkers.length>0){e.preventDefault();const l=t.selectedWorkers.length>1?t.selectedWorkers.find(c=>c.id===t.focusedSelectedWorkerId):void 0;return t.openRenameForWorkers(l?[l]:t.selectedWorkers),!0}return s==="m"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&t.selectedWorkers.length>0?(e.preventDefault(),t.onToggleMovementModeSelected(),!0):s==="s"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey&&t.selectedWorkers.length>1?(e.preventDefault(),t.onScatterSelected(),!0):Al(e)&&t.selectedWorkerId?(e.preventDefault(),t.requestTerminalFocus(),!0):e.key!=="/"||e.metaKey||e.ctrlKey||e.altKey?!1:(e.preventDefault(),t.setPaletteOpen(!0),t.setSpawnDialogOpen(!1),t.setShortcutsOverlayOpen(!1),!0)}function Al(e){return e.key==="Enter"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey}function Kw(e){const t=k.useRef(e);t.current=e,k.useEffect(()=>{const s=n=>{const l=t.current;Uw(n,l)||Hw(n,l)||jw(n,l)||$w(n,l)};return window.addEventListener("keydown",s,!0),()=>window.removeEventListener("keydown",s,!0)},[])}function Uw(e,t){return!(!t.isTerminalTarget(e.target)||t.killConfirmWorkerIds.length>0||t.renameModalOpen||t.batchSpawnDialogOpen||t.shortcutsOverlayOpen||t.paletteOpen||t.spawnDialogOpen||e.key==="Escape"||t.isTerminalEscapeShortcut(e))}async function wi(e,t){const s=await fetch(e,{headers:{"Content-Type":"application/json",...t?.headers??{}},...t});if(!s.ok){const n=await s.json().catch(()=>({error:s.statusText}));throw new Error(n.error??`Request failed: ${s.status}`)}if(s.status!==204)return await s.json()}function Vw(){return wi("/api/config")}async function Qp(){return(await wi("/api/workers")).workers}function Jp(e){return wi("/api/workers/spawn",{method:"POST",body:JSON.stringify(e)})}function Yw(e){return wi(`/api/workers/${e}/stop`,{method:"POST"})}function qw(e,t,s){return wi(`/api/workers/${e}/position`,{method:"PATCH",body:JSON.stringify({x:t,y:s})})}function Zp(e,t){return wi(`/api/workers/${e}/rename`,{method:"PATCH",body:JSON.stringify({displayName:t})})}function Xw(e,t){return wi(`/api/workers/${e}/movement-mode`,{method:"PATCH",body:JSON.stringify({movementMode:t})})}function Gw(e){return wi(`/api/workers/${e}/open-terminal`,{method:"POST"})}function e_(e,t,s=!0){return wi("/api/workers/broadcast-input",{method:"POST",body:JSON.stringify({workerIds:e,text:t,submit:s})})}const Qw=3e4;function Jw(e){const[t,s]=k.useState(null),[n,l]=k.useState([]),[c,h]=k.useState(!1);return k.useEffect(()=>{Promise.all([Vw(),Qp()]).then(([f,_])=>{s(f),l(_),h(!0)}).catch(f=>{const _=f instanceof Error?f.message:"Failed to load Arcane Agents data";e(_)})},[e]),k.useEffect(()=>{let f=null,_=null,d=null,g=!1,y=!1;const C=async()=>{if(!(g||y)){y=!0;try{const E=await Qp();if(g)return;l(E),h(!0)}catch{}finally{y=!1}}},w=()=>{document.visibilityState==="visible"&&C()},v=()=>{C()};function S(){if(g)return;const E=window.location.protocol==="https:"?"wss":"ws";f=new WebSocket(`${E}://${window.location.host}/api/ws`),f.addEventListener("open",()=>{e(void 0),C()}),f.addEventListener("message",P=>{const z=JSON.parse(String(P.data));if(z.type==="init"){s(z.config),l(z.workers),h(!0);return}if(z.type==="worker-created"||z.type==="worker-updated"){l(H=>Ss(H,z.worker));return}z.type==="worker-removed"&&l(H=>H.filter(I=>I.id!==z.workerId))}),f.addEventListener("error",()=>{e("Realtime connection failed. Retrying...")}),f.addEventListener("close",()=>{g||(_=setTimeout(S,2e3))})}return d=setInterval(()=>{C()},Qw),window.addEventListener("focus",v),document.addEventListener("visibilitychange",w),S(),()=>{g=!0,_&&clearTimeout(_),d&&clearInterval(d),window.removeEventListener("focus",v),document.removeEventListener("visibilitychange",w),f?.close()}},[e]),{config:t,workers:n,setWorkers:l,workersHydrated:c}}function Zw(e,t){const[s,n]=k.useState(()=>Gg()),l=k.useRef(s),[c,h]=k.useState(()=>Jg());return k.useEffect(()=>{l.current=s,Qg(s)},[s]),k.useEffect(()=>{Zg(c)},[c]),k.useEffect(()=>{if(!t)return;const f=new Set(e.map(_=>_.id));n(_=>{let d=!1;const g={..._};for(const[y,C]of Object.entries(g)){if(!Array.isArray(C)||C.length===0){delete g[Number(y)],d=!0;continue}const w=C.filter(v=>f.has(v));w.length!==C.length&&(w.length===0?delete g[Number(y)]:g[Number(y)]=w,d=!0)}return d?g:_})},[e,t]),{controlGroups:s,setControlGroups:n,controlGroupByDigitRef:l,mapColumnRatio:c,setMapColumnRatio:f=>{h(_=>{const d=vi(f,lo,ao);return d===_?_:d})},nudgeMapColumnRatio:f=>{h(_=>vi(_+f,lo,ao))},resetMapColumnRatio:()=>{h(Jn)}}}function e1(e,t,s){const[n,l]=k.useState([]),[c,h]=k.useState(0),[f,_]=k.useState(void 0),[d,g]=k.useState(void 0),[y,C]=k.useState(0),[w,v]=k.useState(0),[S,E]=k.useState(void 0),P=n.length===1?n[0]:void 0,z=k.useMemo(()=>new Set(n),[n]),H=k.useMemo(()=>e.filter(b=>z.has(b.id)),[e,z]),I=k.useMemo(()=>e.filter(b=>b.status==="idle"),[e]),G=k.useCallback((b,W)=>{const B=Array.from(new Set(b));l(B),E(void 0);const A=B.length===1?B[0]:void 0;W?.center&&A&&(_(A),h(q=>q+1)),W?.focusTerminal&&A&&g(q=>(q??0)+1)},[]),j=k.useCallback(()=>{g(b=>(b??0)+1)},[]),oe=k.useCallback(b=>{G(b?[b]:[])},[G]),Z=k.useCallback(b=>{G(b)},[G]),ee=k.useCallback(b=>{G([b],{focusTerminal:!0})},[G]),se=k.useCallback(b=>{if(e.length===0)return;const W=e.findIndex(N=>N.id===P),A=((W>=0?W:b>0?-1:0)+b+e.length)%e.length,q=e[A];q&&G([q.id],{center:!0})},[e,G,P]),U=k.useCallback(b=>{if(I.length===0)return;const W=I.findIndex(N=>N.id===P),A=((W>=0?W:b>0?-1:0)+b+I.length)%I.length,q=I[A];q&&G([q.id],{center:!0})},[G,I,P]),D=k.useCallback(b=>{if(H.length<=1)return;const W=S?H.findIndex(N=>N.id===S):vi(w,0,H.length-1),A=((W>=0?W:b>0?-1:0)+b+H.length)%H.length,q=H[A];q&&(v(A),E(q.id))},[S,w,H]);return k.useEffect(()=>{if(t.length===0){C(0);return}if(P){const b=t.findIndex(W=>W.kind==="worker"&&W.worker.id===P);b>=0&&C(b);return}C(b=>vi(b,0,t.length-1))},[t,P]),k.useEffect(()=>{if(H.length<=1){v(0),E(void 0),s?.();return}if(S){const b=H.findIndex(W=>W.id===S);if(b>=0){v(b);return}E(void 0)}v(b=>vi(b,0,H.length-1))},[S,s,H]),k.useEffect(()=>{const b=new Set(e.map(W=>W.id));l(W=>W.filter(B=>b.has(B)))},[e]),{selectedWorkerIds:n,setSelectedWorkerIds:l,selectedWorkerId:P,selectedWorkers:H,mapCenterToken:c,mapCenterWorkerId:f,terminalFocusToken:d,rosterActiveIndex:y,setRosterActiveIndex:C,selectedGroupActiveIndex:w,setSelectedGroupActiveIndex:v,focusedSelectedWorkerId:S,setFocusedSelectedWorkerId:E,applySelection:G,requestTerminalFocus:j,onSelectWorker:oe,onSelectionChange:Z,onActivateWorker:ee,cycleSelection:se,cycleIdleSelection:U,cycleSelectedGroupFocus:D}}function t1(e){const[t,s]=k.useState(!1);return k.useEffect(()=>{s(!1),document.activeElement instanceof HTMLElement&&document.activeElement.closest(".terminal-panel")&&document.activeElement.blur()},[e]),k.useEffect(()=>{const n=()=>{s(Ul(document.activeElement))},l=()=>{setTimeout(n,0)},c=()=>{s(!1)};return window.addEventListener("focusin",n,!0),window.addEventListener("focusout",l,!0),window.addEventListener("blur",c),n(),()=>{window.removeEventListener("focusin",n,!0),window.removeEventListener("focusout",l,!0),window.removeEventListener("blur",c)}},[]),t}function r1({workers:e,reviewedWorkerId:t}){const[s,n]=k.useState([]),l=k.useRef(new Map),c=k.useRef(t);return k.useEffect(()=>{c.current=t},[t]),k.useEffect(()=>{const h=new Set(e.map(g=>g.id)),f=new Set,_=new Set;for(const g of e)if(l.current.get(g.id)==="working"&&g.status==="idle"){if(g.id===c.current)continue;_.add(g.id)}else g.status!=="idle"&&f.add(g.id);const d=new Map;for(const g of e)d.set(g.id,g.status);l.current=d,n(g=>{const C=[...g.filter(w=>h.has(w)&&!f.has(w))];for(const w of _)C.includes(w)||C.push(w);return C.length===g.length&&C.every((w,v)=>w===g[v])?g:C})},[e]),k.useEffect(()=>{t&&n(h=>h.filter(f=>f!==t))},[t]),{pendingCompletionWorkerIds:s}}function i1(e){const[t,s]=k.useState([]),n=k.useCallback(c=>{s(h=>[{worker:c,startedAtMs:Date.now()},...h.filter(f=>f.worker.id!==c.id)])},[]),l=k.useCallback(c=>{s(h=>h.filter(f=>f.worker.id!==c))},[]);return k.useEffect(()=>{const c=setInterval(()=>{const h=Date.now();s(f=>f.filter(_=>h-_.startedAtMs<e))},80);return()=>{clearInterval(c)}},[e]),{fadingWorkers:t,queueWorkerFade:n,removeWorkerFade:l}}const s1=1600,Zc=["move","selected"],n1={move:["move.mp3","move_variant_1.mp3","move_variant_2.mp3","move_variant_3.mp3"],selected:["selected.mp3","selected_variant_1.mp3","selected_variant_2.mp3","selected_variant_3.mp3"]};function o1({config:e,workers:t,workersHydrated:s,selectedWorkerIds:n}){const l=e?.audio.enableSound??!0,c=k.useRef(new Map),h=k.useRef(new Set),f=k.useRef(new Map),_=k.useRef(!1),d=k.useRef(!1),g=k.useRef(new Map),y=k.useRef(new Set),C=k.useRef(new Map),w=k.useRef(new Map),v=k.useRef(new Set),S=k.useRef(null);k.useEffect(()=>{f.current=new Map(t.map(U=>[U.id,U]))},[t]);const E=k.useCallback((U,D)=>`/api/assets/characters/${encodeURIComponent(U)}/voice-lines/${D}.mp3`,[]),P=k.useCallback((U,D)=>`/api/assets/characters/${encodeURIComponent(U)}/voice-lines/${encodeURIComponent(D)}`,[]),z=k.useCallback((U,D)=>{const b=g.current.get(U)?.[D]??[];return b.length>0?b:n1[D].map(W=>P(U,W))},[P]),H=k.useCallback(U=>{if(v.current.has(U))return;v.current.add(U);const D=new Audio(U);D.preload="auto",D.addEventListener("canplay",()=>{w.current.set(U,!0)},{once:!0}),D.addEventListener("error",()=>{w.current.set(U,!1)},{once:!0}),D.load()},[]),I=k.useCallback(U=>{if(!l||w.current.get(U)===!1)return;const D=S.current;D&&(D.pause(),D.currentTime=0);const b=new Audio(U);b.preload="auto",b.addEventListener("canplay",()=>{w.current.set(U,!0)},{once:!0}),b.addEventListener("error",()=>{w.current.set(U,!1)},{once:!0}),S.current=b,b.play().catch(()=>{})},[l]),G=k.useCallback((U,D)=>{I(E(U.avatarType,D))},[I,E]),j=k.useCallback(U=>{if(!l)return;const D=U.filter(A=>w.current.get(A)===!0),b=U.filter(A=>w.current.get(A)!==!1),W=D.length>0?D:b,B=t_(W);B&&I(B)},[I,l]),oe=k.useCallback(U=>{const D=z(U.avatarType,"selected");j(D)},[j,z]),Z=k.useCallback(U=>{const D=z(U.avatarType,"move");j(D)},[j,z]),ee=k.useCallback(async U=>{try{const D=await fetch(`/api/avatars/${encodeURIComponent(U)}/voice-lines`);if(!D.ok)return;const b=await D.json();if(!Array.isArray(b.files))return;const W=b.files.filter(A=>typeof A=="string"&&A.toLowerCase().endsWith(".mp3")).sort((A,q)=>A.localeCompare(q)),B={move:[],selected:[]};for(const A of Zc)B[A]=W.filter(q=>q.toLowerCase().startsWith(A)).map(q=>P(U,q));g.current.set(U,B);for(const A of Zc)for(const q of B[A])H(q)}catch{y.current.delete(U)}},[H,P]);k.useEffect(()=>{const U=Array.from(new Set(t.map(D=>D.avatarType)));for(const D of U)y.current.has(D)||(y.current.add(D),ee(D))},[ee,t]),k.useEffect(()=>{if(!l)return;const U=Array.from(new Set(t.map(b=>b.avatarType))),D=["arrive","attention","complete","death"];for(const b of U){for(const W of D)H(E(b,W));for(const W of Zc)for(const B of z(b,W))H(B)}},[H,E,z,l,t]),k.useEffect(()=>{if(!s)return;const U=new Map(t.map(B=>[B.id,B])),D=c.current,b=C.current,W=performance.now();if(!_.current){_.current=!0,c.current=U;return}for(const B of t){const A=D.get(B.id);if(!A){G(B,"arrive"),b.set(B.id,W+s1);continue}l1(A.status,B.status)&&G(B,"attention"),a1(A.status,B.status)&&!h1(B)&&G(B,"complete")}for(const[B,A]of D.entries())U.has(B)||(G(A,"death"),b.delete(B));c.current=U},[G,t,s]),k.useEffect(()=>{if(!s)return;const U=new Set(n),D=h.current;if(!d.current){d.current=!0,h.current=U;return}const b=n.filter(N=>!D.has(N)),W=C.current,B=performance.now(),A=b.filter(N=>{const K=W.get(N);return K===void 0?!0:K<=B?(W.delete(N),!0):!1}),q=t_(A);if(q){const N=f.current.get(q);N&&oe(N)}h.current=U},[oe,n,s]);const se=k.useCallback(U=>{const D=f.current.get(U);D&&Z(D)},[Z]);return k.useEffect(()=>()=>{const U=S.current;U&&(U.pause(),U.currentTime=0,S.current=null)},[]),{playMoveVoiceLine:se}}function l1(e,t){return e!=="attention"&&t==="attention"}function a1(e,t){return e==="working"&&t==="idle"}const c1=1e4;function h1(e){return Date.now()-new Date(e.createdAt).getTime()<c1}function t_(e){if(e.length===0)return;const t=Math.floor(Math.random()*e.length);return e[t]}function u1({workers:e,activeWorkers:t,renameModalOpen:s,setRenameModalOpen:n,renameTargetWorkerIds:l,setRenameTargetWorkerIds:c,killConfirmWorkerIds:h,setKillConfirmWorkerIds:f,setRenameDraft:_}){const d=k.useMemo(()=>{if(l.length===0)return[];const v=new Map(e.map(S=>[S.id,S]));return l.map(S=>v.get(S)).filter(S=>!!S)},[l,e]),g=k.useMemo(()=>{if(h.length===0)return[];const v=new Map(e.map(S=>[S.id,S]));return h.map(S=>v.get(S)).filter(S=>!!S)},[h,e]),y=k.useCallback(()=>{n(!1),c([])},[n,c]),C=k.useCallback(()=>{f([])},[f]),w=k.useCallback(v=>{v.length!==0&&(_(v.length===1?v[0].displayName??v[0].name:""),c(v.map(S=>S.id)),n(!0))},[_,n,c]);return k.useEffect(()=>{if(!s||l.length===0)return;const v=new Set(t.map(S=>S.id));l.some(S=>v.has(S))||y()},[t,y,s,l]),k.useEffect(()=>{if(h.length===0)return;const v=new Set(t.map(S=>S.id));h.some(S=>v.has(S))||C()},[t,C,h]),{renameTargetWorkers:d,killConfirmWorkers:g,closeRenameModal:y,closeKillConfirm:C,openRenameForWorkers:w}}function d1({workers:e,selectedWorkerIds:t,setSelectedWorkerIds:s,rosterEntries:n,rosterActiveIndex:l,setKillConfirmWorkerIds:c,closeKillConfirm:h,killConfirmWorkerIds:f,queueWorkerFade:_,removeWorkerFade:d,setWorkers:g,showError:y}){const C=k.useCallback(async E=>{const P=e.find(z=>z.id===E);if(P){h(),_(P);try{const z=await Yw(E);g(H=>H.filter(I=>I.id!==z.workerId)),s(H=>H.filter(I=>I!==z.workerId))}catch(z){d(E),y(z)}}},[h,_,d,s,g,y,e]),w=k.useCallback(()=>{t.length!==0&&c(t)},[t,c]),v=k.useCallback(()=>{const E=n[l];!E||E.kind!=="worker"||c([E.worker.id])},[l,n,c]),S=k.useCallback(()=>{if(f.length===0)return;const E=[...f];h();for(const P of E)C(P)},[h,f,C]);return{onKillSelected:w,onKillRosterActive:v,confirmKillSelection:S}}function f1({setWorkers:e,selectedWorkers:t,terminalWorkerId:s,applySelection:n,setSpawnDialogOpen:l,setPaletteOpen:c,renameTargetWorkerIds:h,closeRenameModal:f,showError:_}){const d=k.useCallback(async S=>{try{const E=t.map(H=>H.id),P="shortcutIndex"in S&&E.length>0?{...S,spawnNearWorkerIds:E}:S,z=await Jp(P);e(H=>Ss(H,z)),n([z.id],{center:!0}),l(!1),c(!1)}catch(E){_(E)}},[n,t,c,l,e,_]),g=k.useCallback(async(S,E)=>{const P=[];let z;for(let H=0;H<S.length;H++){E(H,S.length);const I=S[H],G={...I.input,displayName:I.displayName,spawnNearWorkerIds:z?[z]:t.map(j=>j.id).slice(0,1)};try{const j=await Jp(G);e(oe=>Ss(oe,j)),P.push(j.id),z=j.id}catch(j){_(j);break}}E(P.length,S.length),P.length>0&&n(P,{center:!0})},[n,t,e,_]),y=k.useCallback(async S=>{const E=[...h];if(E.length===0){f();return}try{if(E.length===1){const P=await Zp(E[0],S);e(z=>Ss(z,P))}else{const P=S.trim(),z=await Promise.all(E.map((H,I)=>Zp(H,P.length>0?`${P} ${I+1}`:"")));e(H=>{let I=H;for(const G of z)I=Ss(I,G);return I})}f()}catch(P){_(P)}},[f,h,e,_]),C=k.useCallback(async()=>{if(t.length===0)return;const S=t.every(E=>E.movementMode==="hold")?"wander":"hold";try{const E=await Promise.all(t.map(P=>Xw(P.id,S)));e(P=>{let z=P;for(const H of E)z=Ss(z,H);return z})}catch(E){_(E)}},[t,e,_]),w=k.useCallback(async()=>{if(s)try{await Gw(s)}catch(S){_(S)}},[_,s]),v=k.useCallback((S,E)=>{qw(S,E.x,E.y).then(P=>{e(z=>Ss(z,P))}).catch(P=>{_(P)})},[e,_]);return{runSpawn:d,runBatchSpawn:g,submitRename:y,onToggleMovementModeSelected:C,onOpenSelectedInTerminal:w,onPositionCommit:v}}function p1({selectedWorkers:e,rallyCommandDraft:t,setRallyCommandDraft:s,rallyCommandSending:n,setRallyCommandSending:l,rallyCommandResultText:c,setRallyCommandResultText:h,showError:f}){const _=k.useCallback(async()=>{if(n)return;const g=e.map(y=>y.id);if(!(g.length<=1)){if(t.length===0){h("Enter a command to broadcast.");return}l(!0),h(void 0);try{const C=t.includes("$NAME")?sv(await Promise.all(e.map(async w=>{const v=t.replace(/\$NAME/g,w.displayName??w.name);try{return await e_([w.id],v,!0)}catch(S){return{requestedCount:1,deliveredWorkerIds:[],skippedWorkerIds:[],failed:[{workerId:w.id,error:S instanceof Error?S.message:"Failed to send input"}]}}}))):await e_(g,t,!0);s(""),h(iv(C))}catch(y){f(y)}finally{l(!1)}}},[t,n,e,s,h,l,f]),d=k.useCallback(g=>{s(g),c&&h(void 0)},[c,s,h]);return{onSendRallyCommand:_,onRallyCommandDraftChange:d}}function _1({workers:e,activeWorkers:t,setWorkers:s,selectedWorkers:n,selectedWorkerIds:l,setSelectedWorkerIds:c,focusedSelectedWorkerId:h,terminalWorkerId:f,rosterEntries:_,rosterActiveIndex:d,setRosterActiveIndex:g,applySelection:y,setSpawnDialogOpen:C,setPaletteOpen:w,renameModalOpen:v,setRenameModalOpen:S,renameTargetWorkerIds:E,setRenameTargetWorkerIds:P,setRenameDraft:z,killConfirmWorkerIds:H,setKillConfirmWorkerIds:I,rallyCommandDraft:G,setRallyCommandDraft:j,rallyCommandSending:oe,setRallyCommandSending:Z,rallyCommandResultText:ee,setRallyCommandResultText:se,queueWorkerFade:U,removeWorkerFade:D,setErrorText:b}){const W=k.useCallback(xe=>{b(xe instanceof Error?xe.message:"Unknown request failure")},[b]),{renameTargetWorkers:B,killConfirmWorkers:A,closeRenameModal:q,closeKillConfirm:N,openRenameForWorkers:K}=u1({workers:e,activeWorkers:t,renameModalOpen:v,setRenameModalOpen:S,renameTargetWorkerIds:E,setRenameTargetWorkerIds:P,killConfirmWorkerIds:H,setKillConfirmWorkerIds:I,setRenameDraft:z}),{runSpawn:Q,runBatchSpawn:T,submitRename:V,onToggleMovementModeSelected:de,onOpenSelectedInTerminal:fe,onPositionCommit:he}=f1({setWorkers:s,selectedWorkers:n,terminalWorkerId:f,applySelection:y,setSpawnDialogOpen:C,setPaletteOpen:w,renameTargetWorkerIds:E,closeRenameModal:q,showError:W}),{onKillSelected:pe,onKillRosterActive:ge,confirmKillSelection:Re}=d1({workers:e,selectedWorkerIds:l,setSelectedWorkerIds:c,rosterEntries:_,rosterActiveIndex:d,setKillConfirmWorkerIds:I,closeKillConfirm:N,killConfirmWorkerIds:H,queueWorkerFade:U,removeWorkerFade:D,setWorkers:s,showError:W}),{onSendRallyCommand:ye,onRallyCommandDraftChange:Fe}=p1({selectedWorkers:n,rallyCommandDraft:G,setRallyCommandDraft:j,rallyCommandSending:oe,setRallyCommandSending:Z,rallyCommandResultText:ee,setRallyCommandResultText:se,showError:W}),We=k.useCallback(xe=>{const Ke=_[xe];if(Ke){if(g(xe),Ke.kind==="worker"){y([Ke.worker.id],{center:!0});return}Q({shortcutIndex:Ke.shortcutIndex})}},[y,_,Q,g]),je=k.useCallback(()=>{if(n.length===0)return;const xe=n.length>1?n.find(Ke=>Ke.id===h):void 0;K(xe?[xe]:n)},[h,K,n]);return{renameTargetWorkers:B,killConfirmWorkers:A,runSpawn:Q,runBatchSpawn:T,closeRenameModal:q,closeKillConfirm:N,openRenameForWorkers:K,submitRename:V,onKillSelected:pe,onKillRosterActive:ge,confirmKillSelection:Re,onToggleMovementModeSelected:de,onActivateRosterIndex:We,onOpenSelectedInTerminal:fe,onSendRallyCommand:ye,onRallyCommandDraftChange:Fe,onRenameSelected:je,onPositionCommit:he}}function m1(e){const t=[];return e.forEach((s,n)=>{for(const l of s.hotkeys??[]){const c=y1(l);c&&t.push({shortcutIndex:n,hotkey:c})}}),t}function g1(e,t){const s=[],n=new Set;for(const l of e)v1(l.hotkey,t)&&(n.has(l.shortcutIndex)||(n.add(l.shortcutIndex),s.push(l.shortcutIndex)));return s}function v1(e,t){return t.ctrlKey!==e.ctrl||t.metaKey!==e.meta||t.altKey!==e.alt||t.shiftKey!==e.shift?!1:C1(t.key)===e.key?!0:e.code?t.code===e.code:!1}function y1(e){const t=S1(e);if(t.length===0)return;let s=!1,n=!1,l=!1,c=!1,h;for(const _ of t){const d=_.trim().toLowerCase();if(d){if(d==="ctrl"||d==="control"){s=!0;continue}if(d==="cmd"||d==="meta"||d==="super"){n=!0;continue}if(d==="alt"||d==="option"){l=!0;continue}if(d==="shift"){c=!0;continue}if(h)return;h=_}}if(!h)return;const f=w1(h);if(f)return{key:f.key,code:f.code,ctrl:s,meta:n,alt:l,shift:c}}function S1(e){const t=e.trim().replace(/\s+/g,"");if(!t)return[];if(t.includes("+"))return t.split("+").filter(l=>l.length>0);const s=t.toLowerCase();return s.includes("ctrl-")||s.includes("control-")||s.includes("cmd-")||s.includes("meta-")||s.includes("super-")||s.includes("alt-")||s.includes("option-")||s.includes("shift-")?t.split("-").filter(l=>l.length>0):[t]}function w1(e){const t=e.trim();if(!t)return;const s=t.toLowerCase();if(s==="space"||s==="spacebar")return{key:" ",code:"Space"};if(s==="esc")return{key:"escape",code:"Escape"};if(s==="return")return{key:"enter",code:"Enter"};if(s==="up")return{key:"arrowup",code:"ArrowUp"};if(s==="down")return{key:"arrowdown",code:"ArrowDown"};if(s==="left")return{key:"arrowleft",code:"ArrowLeft"};if(s==="right")return{key:"arrowright",code:"ArrowRight"};if(/^key[a-z]$/.test(s)){const n=s.slice(3);return{key:n,code:`Key${n.toUpperCase()}`}}if(/^digit[0-9]$/.test(s)){const n=s.slice(5);return{key:n,code:`Digit${n}`}}if(/^numpad[0-9]$/.test(s)){const n=s.slice(6);return{key:n,code:`Numpad${n}`}}if(/^f[0-9]{1,2}$/.test(s))return{key:s,code:s.toUpperCase()};if(t.length===1){if(/^[a-z]$/i.test(t)){const n=t.toLowerCase();return{key:n,code:`Key${n.toUpperCase()}`}}return/^[0-9]$/.test(t)?{key:t}:{key:t}}return{key:s}}function C1(e){const t=e.toLowerCase();return t==="spacebar"?" ":t}function k1(){const[e,t]=k.useState(!1),[s,n]=k.useState(!1),[l,c]=k.useState(!1),[h,f]=k.useState(!1),[_,d]=k.useState(!1),[g,y]=k.useState([]),[C,w]=k.useState(""),[v,S]=k.useState([]),[E,P]=k.useState(""),[z,H]=k.useState(!1),[I,G]=k.useState(void 0),[j,oe]=k.useState(void 0),[Z,ee]=k.useState([]),[se,U]=k.useState(0),D=k.useRef(null),{config:b,workers:W,setWorkers:B,workersHydrated:A}=Jw(oe),{fadingWorkers:q,queueWorkerFade:N,removeWorkerFade:K}=i1(Xg),Q=k.useMemo(()=>W.filter(te=>te.status!=="stopped"),[W]),{controlGroups:T,setControlGroups:V,controlGroupByDigitRef:de,mapColumnRatio:fe,setMapColumnRatio:he,nudgeMapColumnRatio:pe,resetMapColumnRatio:ge}=Zw(Q,A),Re=k.useRef(null),ye=k.useRef(void 0),[Fe,We]=k.useState(!1),je=k.useMemo(()=>b?.shortcuts??[],[b]),xe=k.useMemo(()=>m1(je),[je]),Ke=k.useMemo(()=>[...Q.map(te=>({kind:"worker",worker:te})),...je.map((te,Ce)=>({kind:"shortcut",shortcut:te,shortcutIndex:Ce}))],[Q,je]),jr=k.useMemo(()=>{const te=Ke.findIndex(Ce=>Ce.kind==="shortcut");return te>=0?te:void 0},[Ke]),at=k.useCallback(()=>{P(""),G(void 0)},[]),{selectedWorkerIds:zt,setSelectedWorkerIds:_r,selectedWorkerId:ct,selectedWorkers:Xe,mapCenterToken:Kt,mapCenterWorkerId:$r,terminalFocusToken:Zi,rosterActiveIndex:ii,setRosterActiveIndex:Ci,selectedGroupActiveIndex:ki,setSelectedGroupActiveIndex:si,focusedSelectedWorkerId:Ut,setFocusedSelectedWorkerId:es,applySelection:Dr,requestTerminalFocus:mr,onSelectWorker:Ae,onSelectionChange:Ts,onActivateWorker:ts,cycleSelection:gr,cycleIdleSelection:vr,cycleSelectedGroupFocus:rs}=e1(Q,Ke,at),yr=k.useMemo(()=>Q.find(te=>te.id===ct),[Q,ct]),Sr=k.useMemo(()=>{if(Ut)return Xe.find(te=>te.id===Ut)},[Ut,Xe]),Kr=yr??(Xe.length>1?Sr:void 0),Et=Kr?.id,or=Xe.length>1&&!Kr,bi=t1(Et),{pendingCompletionWorkerIds:is}=r1({workers:Q,reviewedWorkerId:Et}),{playMoveVoiceLine:xi}=o1({config:b,workers:Q,workersHydrated:A,selectedWorkerIds:zt}),Ur=k.useCallback(()=>{const te=document.activeElement;return!(te instanceof HTMLElement)||!te.closest(".terminal-panel")?!1:(te.blur(),document.querySelector(".map-canvas")?.focus(),!0)},[]),Ps=k.useCallback(()=>{const te=D.current;if(!te)return!1;te.focus();const Ce=te.value.length;return te.setSelectionRange(Ce,Ce),!0},[]),Ei=k.useCallback(()=>{if(Xe.length<2)return;const te=Xe.reduce((Qe,mt)=>Qe+mt.position.x,0)/Xe.length,Ce=Xe.reduce((Qe,mt)=>Qe+mt.position.y,0)/Xe.length,Le=80+Xe.length*20,De=Xe.map(Qe=>({workerId:Qe.id,target:{x:te+(Math.random()-.5)*Le*2,y:Ce+(Math.random()-.5)*Le*2}}));ee(De),U(Qe=>Qe+1)},[Xe]),ss=k.useCallback(te=>{const Ce=Re.current;if(!Ce)return;const Le=Ce.getBoundingClientRect(),De=Math.max(1,Le.width-Bc),Qe=te-Le.left-Bc/2,mt=vi(Qe/De,lo,ao);he(mt)},[he]);k.useEffect(()=>{if(Fe)return document.body.classList.add("split-pane-dragging"),()=>{document.body.classList.remove("split-pane-dragging")}},[Fe]);const{renameTargetWorkers:ni,killConfirmWorkers:ns,runSpawn:lr,runBatchSpawn:ln,closeRenameModal:Vr,closeKillConfirm:Nr,openRenameForWorkers:Mi,submitRename:X,onKillSelected:Y,onKillRosterActive:le,confirmKillSelection:ue,onToggleMovementModeSelected:_e,onActivateRosterIndex:Ne,onOpenSelectedInTerminal:Oe,onSendRallyCommand:ht,onRallyCommandDraftChange:tt,onRenameSelected:Ue,onPositionCommit:Jt}=_1({workers:W,activeWorkers:Q,setWorkers:B,selectedWorkers:Xe,selectedWorkerIds:zt,setSelectedWorkerIds:_r,focusedSelectedWorkerId:Ut,terminalWorkerId:Et,rosterEntries:Ke,rosterActiveIndex:ii,setRosterActiveIndex:Ci,applySelection:Dr,setSpawnDialogOpen:t,setPaletteOpen:n,renameModalOpen:_,setRenameModalOpen:d,renameTargetWorkerIds:v,setRenameTargetWorkerIds:S,setRenameDraft:w,killConfirmWorkerIds:g,setKillConfirmWorkerIds:y,rallyCommandDraft:E,setRallyCommandDraft:P,rallyCommandSending:z,setRallyCommandSending:H,rallyCommandResultText:I,setRallyCommandResultText:G,queueWorkerFade:N,removeWorkerFade:K,setErrorText:oe});return Kw({activeWorkers:Q,applySelection:Dr,batchSpawnDialogOpen:l,clampNumber:vi,closeKillConfirm:Nr,closeRenameModal:Vr,confirmKillSelection:ue,controlGroupByDigitRef:de,cycleIdleSelection:vr,cycleSelectedGroupFocus:rs,cycleSelection:gr,escapeTerminalFocus:Ur,findMatchingShortcutIndexes:g1,firstSummonEntryIndex:jr,focusRallyCommandInput:Ps,focusedSelectedWorkerId:Ut,inSelectedGroupView:or,isEditableTarget:$h,isTerminalEscapeShortcut:ov,isTerminalTarget:ev,killConfirmWorkerIds:g,mapColumnRatioStep:jh,nudgeMapColumnRatio:pe,onActivateRosterIndex:Ne,onKillRosterActive:le,onKillSelected:Y,onScatterSelected:Ei,onToggleMovementModeSelected:_e,openRenameForWorkers:Mi,paletteOpen:s,parseControlGroupDigit:nv,renameModalOpen:_,requestTerminalFocus:mr,resetMapColumnRatio:ge,rosterActiveIndex:ii,rosterEntries:Ke,runSpawn:lr,selectedGroupActiveIndex:ki,selectedWorkerId:ct,selectedWorkerIds:zt,selectedWorkers:Xe,setBatchSpawnDialogOpen:c,setControlGroups:V,setFocusedSelectedWorkerId:es,setPaletteOpen:n,setRosterActiveIndex:Ci,setSelectedGroupActiveIndex:si,setShortcutsOverlayOpen:f,setSpawnDialogOpen:t,shortcutHotkeyBindings:xe,shortcutsOverlayOpen:h,spawnDialogOpen:e}),M.jsxs("div",{ref:Re,className:"app-shell",style:{gridTemplateColumns:`minmax(0px, ${fe.toFixed(3)}fr) ${Bc}px minmax(0px, ${(1-fe).toFixed(3)}fr)`},children:[M.jsxs("div",{className:"map-column",children:[M.jsx(S0,{workers:Q,fadingWorkers:q,selectedWorkerId:ct,selectedWorkerIds:zt,focusedSelectedWorkerId:Ut,terminalFocusedSelected:!!(ct&&bi),terminalFocusedWorkerId:bi?Et:void 0,controlGroups:T,completionPendingWorkerIds:is,onSelect:Ae,onSelectionChange:Ts,onActivateWorker:ts,onMoveOrderIssued:xi,onPositionCommit:Jt,externalMoveOrders:Z,externalMoveOrderToken:se,centerOnWorkerId:$r,centerRequestKey:Kt}),M.jsx(lv,{shortcuts:b?.shortcuts??[],selectedWorker:yr,selectedWorkers:Xe,onSpawnShortcut:te=>{lr({shortcutIndex:te})},onOpenSpawnDialog:()=>{t(!0),n(!1)},onOpenPalette:()=>{n(!0),t(!1)},onDeselect:()=>Ae(void 0),onKillSelected:()=>{Y()},onRenameSelected:()=>{Ue()},onToggleMovementMode:()=>{_e()},onScatterSelected:()=>{Ei()}})]}),M.jsx("div",{className:`layout-divider${Fe?" layout-divider-active":""}`,role:"separator","aria-label":"Resize map and terminal columns","aria-orientation":"vertical",onPointerDown:te=>{te.button===0&&(te.preventDefault(),ye.current=te.pointerId,We(!0),te.currentTarget.setPointerCapture(te.pointerId),ss(te.clientX))},onPointerMove:te=>{ye.current===te.pointerId&&(te.preventDefault(),ss(te.clientX))},onPointerUp:te=>{ye.current===te.pointerId&&(te.preventDefault(),te.currentTarget.hasPointerCapture(te.pointerId)&&te.currentTarget.releasePointerCapture(te.pointerId),ye.current=void 0,We(!1))},onPointerCancel:te=>{ye.current===te.pointerId&&(te.currentTarget.hasPointerCapture(te.pointerId)&&te.currentTarget.releasePointerCapture(te.pointerId),ye.current=void 0,We(!1))}}),M.jsx(Fw,{activeWorkers:Q,selectedWorkers:Xe,terminalWorker:Kr,terminalFocused:bi,selectedGroupActiveIndex:ki,setSelectedGroupActiveIndex:si,setFocusedSelectedWorkerId:es,rallyCommandInputRef:D,rallyCommandDraft:E,rallyCommandSending:z,rallyCommandResultText:I,onRallyCommandDraftChange:tt,onSendRallyCommand:ht,rosterEntries:Ke,completionPendingWorkerIds:is,rosterActiveIndex:ii,setRosterActiveIndex:Ci,onActivateRosterIndex:Ne,onOpenSelectedInTerminal:Oe,terminalFocusToken:Zi}),b?M.jsx(k0,{open:e,projects:b.projects,runtimes:b.runtimes,onClose:()=>t(!1),onSpawn:(te,Ce)=>{lr({projectId:te,runtimeId:Ce})}}):null,b?M.jsx(cv,{open:s,config:b,onClose:()=>n(!1),onSpawnShortcut:te=>{lr({shortcutIndex:te})},onSpawnProjectRuntime:(te,Ce)=>{lr({projectId:te,runtimeId:Ce})},onOpenBatchSpawn:()=>{n(!1),c(!0)}}):null,b?M.jsx(av,{open:l,config:b,onClose:()=>c(!1),onBatchSpawn:ln}):null,M.jsx(C0,{open:h,onClose:()=>{f(!1)}}),M.jsx(hv,{workerIds:g,workers:ns,onClose:Nr,onConfirm:ue}),M.jsx(w0,{open:_,targetWorkerIds:v,targetWorkers:ni,initialDraft:C,onClose:Vr,onSubmit:X}),j?M.jsx("div",{className:"error-toast",onClick:()=>oe(void 0),children:j}):null]})}const em=document.getElementById("root");if(!em)throw new Error("Missing #root container");qg.createRoot(em).render(M.jsx(k.StrictMode,{children:M.jsx(k1,{})}));
83
+ [terminal connection error]`)}),()=>{E.dispose(),S.close(),h.current===S&&(h.current=null)}},[C,g,e,t]),k.useEffect(()=>{if(!e||s===void 0||_.current===s)return;_.current=s,d();const w=setTimeout(()=>{d()},0);return()=>{clearTimeout(w)}},[s,d,e]),M.jsx("div",{className:"terminal-panel",ref:n})}function Ww(e){return e.type==="keydown"&&e.key==="Enter"&&e.shiftKey&&!e.ctrlKey&&!e.metaKey&&!e.altKey}function Fw({activeWorkers:e,selectedWorkers:t,terminalWorker:s,terminalFocused:n,selectedGroupActiveIndex:l,setSelectedGroupActiveIndex:c,setFocusedSelectedWorkerId:h,rallyCommandInputRef:f,rallyCommandDraft:_,rallyCommandSending:d,rallyCommandResultText:g,onRallyCommandDraftChange:y,onSendRallyCommand:C,rosterEntries:w,completionPendingWorkerIds:v,rosterActiveIndex:S,setRosterActiveIndex:E,onActivateRosterIndex:P,onOpenSelectedInTerminal:z,terminalFocusToken:H}){const I=k.useMemo(()=>new Set(v),[v]),G=v.length;return M.jsxs("div",{className:`terminal-column${s?" terminal-column-selected":""}${s&&n?" terminal-column-focused":""}`,children:[M.jsxs("div",{className:"terminal-header",children:[M.jsx("div",{className:"terminal-header-title",children:t.length>1&&!s?`${t.length} selected agents`:s?`${s.displayName??s.name} (${s.status})`:`Agents (${e.length})`}),!s&&G>0?M.jsxs("div",{className:"terminal-ready-chip",title:"Agents finished but not yet reviewed in terminal",children:["✦ ",G," ready"]}):null,s?M.jsx("button",{className:"terminal-open-external",onClick:()=>{z()},disabled:s.status==="stopped",title:"Open in external terminal",type:"button",children:"↗"}):null]}),t.length>1&&!s?M.jsxs("div",{className:"worker-roster",children:[M.jsx("div",{className:"worker-roster-section-label",children:"Selected Group"}),t.map((j,oe)=>M.jsx("button",{className:`worker-roster-item ${oe===l?"active":""}`,onMouseEnter:()=>c(oe),onClick:()=>{c(oe),h(j.id)},type:"button",children:M.jsxs("div",{className:"worker-roster-main",children:[M.jsx("img",{className:"worker-roster-avatar",src:`/api/assets/characters/${encodeURIComponent(j.avatarType)}/rotations/south.png`,alt:"",loading:"lazy","aria-hidden":"true"}),M.jsxs("div",{className:"worker-roster-text",children:[M.jsxs("div",{className:"worker-roster-name-row",children:[M.jsx("div",{className:"worker-roster-name",children:j.displayName??j.name}),I.has(j.id)?M.jsx("span",{className:"worker-complete-badge",title:"Finished and waiting for review",children:"READY"}):null]}),M.jsxs("div",{className:"worker-roster-meta",children:[j.projectId," · ",j.runtimeId," · ",j.status]}),j.activityText?M.jsx("div",{className:"worker-roster-activity",children:j.activityText}):null]})]})},j.id)),M.jsxs("form",{className:"rally-command-card",onSubmit:j=>{j.preventDefault(),C()},children:[M.jsxs("div",{className:"rally-command-header",children:[M.jsx("div",{className:"rally-command-title",children:"Rally Command"}),M.jsxs("div",{className:"rally-command-count",children:[t.length," agents"]})]}),M.jsx("textarea",{ref:f,className:"input rally-command-input",value:_,onChange:j=>{y(j.target.value)},onKeyDown:j=>{const oe=!j.shiftKey&&!j.ctrlKey&&!j.metaKey&&!j.altKey,Z=(j.ctrlKey||j.metaKey)&&!j.shiftKey&&!j.altKey;j.key==="Enter"&&(oe||Z)&&(j.preventDefault(),C())},placeholder:"Type once, send to all selected agents (use $NAME for per-agent names)...",disabled:d,rows:3}),M.jsxs("div",{className:"rally-command-actions",children:[M.jsx("div",{className:"rally-command-hint",children:"Enter sends, Shift+Enter adds a new line, $NAME inserts each agent's name"}),M.jsx("button",{className:"bar-btn",type:"submit",disabled:d||_.length===0,children:d?"Sending...":`Send to ${t.length}`})]}),g?M.jsx("div",{className:"rally-command-result",children:g}):null]})]}):s?M.jsx(zw,{workerId:s.id,workerName:s.displayName??s.name,focusRequestKey:H}):M.jsx("div",{className:"worker-roster",children:w.length===0?M.jsx("div",{className:"worker-roster-empty",children:"No active agents yet. Summon one from the bottom bar."}):w.map((j,oe)=>M.jsxs("div",{children:[j.kind==="shortcut"&&(oe===0||w[oe-1]?.kind!=="shortcut")?M.jsx("div",{className:"worker-roster-section-label",children:"Summon"}):null,j.kind==="worker"?M.jsx("button",{className:`worker-roster-item ${oe===S?"active":""}`,onMouseEnter:()=>E(oe),onClick:()=>P(oe),type:"button",children:M.jsxs("div",{className:"worker-roster-main",children:[M.jsx("img",{className:"worker-roster-avatar",src:`/api/assets/characters/${encodeURIComponent(j.worker.avatarType)}/rotations/south.png`,alt:"",loading:"lazy","aria-hidden":"true"}),M.jsxs("div",{className:"worker-roster-text",children:[M.jsxs("div",{className:"worker-roster-name-row",children:[M.jsx("div",{className:"worker-roster-name",children:j.worker.displayName??j.worker.name}),I.has(j.worker.id)?M.jsx("span",{className:"worker-complete-badge",title:"Finished and waiting for review",children:"READY"}):null]}),M.jsxs("div",{className:"worker-roster-meta",children:[j.worker.projectId," · ",j.worker.runtimeId," · ",j.worker.status]}),j.worker.activityText?M.jsx("div",{className:"worker-roster-activity",children:j.worker.activityText}):null]})]})}):M.jsx("button",{className:`worker-roster-item worker-roster-item-summon ${oe===S?"active":""}`,onMouseEnter:()=>E(oe),onClick:()=>P(oe),type:"button",children:M.jsxs("div",{className:"worker-roster-main",children:[j.shortcut.avatar?M.jsx("img",{className:"worker-roster-avatar worker-roster-summon-avatar",src:`/api/assets/characters/${encodeURIComponent(j.shortcut.avatar)}/rotations/south.png`,alt:"",loading:"lazy","aria-hidden":"true"}):M.jsx("div",{className:"worker-roster-summon-glyph","aria-hidden":"true",children:"+"}),M.jsxs("div",{className:"worker-roster-text",children:[M.jsx("div",{className:"worker-roster-name",children:j.shortcut.label}),M.jsxs("div",{className:"worker-roster-meta",children:[j.shortcut.project," · ",j.shortcut.runtime]}),M.jsx("div",{className:"worker-roster-activity",children:rv(j.shortcut.hotkeys)})]})]})})]},j.kind==="worker"?j.worker.id:`shortcut-${j.shortcutIndex}-${j.shortcut.label}`))})]})}function Hw(e,t){if(t.killConfirmWorkerIds.length>0)return Al(e)?(e.preventDefault(),t.confirmKillSelection(),!0):(e.preventDefault(),t.closeKillConfirm(),!0);if(!t.isEditableTarget(e.target)&&!t.isTerminalTarget(e.target)){const s=t.findMatchingShortcutIndexes(t.shortcutHotkeyBindings,e);if(s.length>0){e.preventDefault(),e.stopPropagation();for(const n of s)t.runSpawn({shortcutIndex:n});return!0}}if(e.key==="Escape")return t.renameModalOpen?(e.preventDefault(),t.closeRenameModal(),!0):t.batchSpawnDialogOpen?(e.preventDefault(),t.setBatchSpawnDialogOpen(!1),!0):t.shortcutsOverlayOpen?(e.preventDefault(),t.setShortcutsOverlayOpen(!1),!0):t.paletteOpen||t.spawnDialogOpen?(e.preventDefault(),t.setPaletteOpen(!1),t.setSpawnDialogOpen(!1),!0):(t.isTerminalTarget(e.target)||t.selectedWorkerId&&(e.preventDefault(),t.applySelection([])),!0);if(t.isTerminalEscapeShortcut(e)){if(!t.renameModalOpen&&!t.batchSpawnDialogOpen&&!t.shortcutsOverlayOpen&&!t.paletteOpen&&!t.spawnDialogOpen&&t.selectedWorkers.length>1&&t.focusedSelectedWorkerId){const n=t.escapeTerminalFocus();return e.preventDefault(),n&&e.stopPropagation(),t.setFocusedSelectedWorkerId(void 0),!0}if(t.escapeTerminalFocus())return e.preventDefault(),e.stopPropagation(),!0;if(!t.renameModalOpen&&!t.batchSpawnDialogOpen&&!t.shortcutsOverlayOpen&&!t.paletteOpen&&!t.spawnDialogOpen&&t.selectedWorkerIds.length>0){if(e.preventDefault(),t.selectedWorkerId){const n=t.rosterEntries.findIndex(l=>l.kind==="worker"&&l.worker.id===t.selectedWorkerId);n>=0&&t.setRosterActiveIndex(n)}t.applySelection([])}return!0}return e.key==="?"&&!e.ctrlKey&&!e.metaKey&&!e.altKey?((!t.isEditableTarget(e.target)||t.shortcutsOverlayOpen)&&(e.preventDefault(),t.setShortcutsOverlayOpen(s=>!s)),!0):(e.code==="BracketLeft"||e.code==="BracketRight"||e.key==="="&&!e.shiftKey)&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target)&&!t.isTerminalTarget(e.target)?(e.preventDefault(),e.code==="BracketLeft"?t.nudgeMapColumnRatio(e.shiftKey?-1:-t.mapColumnRatioStep):e.code==="BracketRight"?t.nudgeMapColumnRatio(e.shiftKey?1:t.mapColumnRatioStep):t.resetMapColumnRatio(),!0):!1}function jw(e,t){if(e.key==="Tab"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target))return t.isTerminalTarget(e.target)?!0:(e.preventDefault(),t.selectedWorkers.length>1?(t.cycleSelectedGroupFocus(e.shiftKey?-1:1),!0):(t.cycleSelection(e.shiftKey?-1:1),!0));if(e.code==="Period"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target))return t.isTerminalTarget(e.target)||(e.preventDefault(),t.cycleIdleSelection(e.shiftKey?-1:1)),!0;if(e.code==="Comma"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target))return t.isTerminalTarget(e.target)||(e.preventDefault(),t.cycleIdleSelection(-1)),!0;const s=t.parseControlGroupDigit(e);if(s===void 0)return!1;if((e.ctrlKey||e.metaKey)&&!e.altKey&&t.selectedWorkerIds.length>0)return e.preventDefault(),t.setControlGroups(n=>{const l=new Set(t.selectedWorkerIds),c=n[s]??[],h=new Set(c);if(c.length===t.selectedWorkerIds.length&&t.selectedWorkerIds.every(d=>h.has(d))){const d={...n};return delete d[s],d}const _={...n};for(const[d,g]of Object.entries(_)){const y=Number(d);!Number.isInteger(y)||y<0||y>9||y===s||!Array.isArray(g)||(_[y]=g.filter(C=>!l.has(C)))}return _[s]=[...t.selectedWorkerIds],_}),!0;if(!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey&&!t.isEditableTarget(e.target)){const n=t.controlGroupByDigitRef.current[s]??[];if(n.length===0)return!0;const l=new Set(t.activeWorkers.map(h=>h.id)),c=n.filter(h=>l.has(h));if(c.length===0)return t.setControlGroups(h=>{if(!(s in h))return h;const f={...h};return delete f[s],f}),!0;e.preventDefault(),t.applySelection(c,{center:c.length===1})}return!0}function $w(e,t){if(t.isEditableTarget(e.target))return!0;const s=e.key.toLowerCase();if((s==="k"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(t.inSelectedGroupView?e.shiftKey:t.selectedWorkerIds.length===1||!e.shiftKey)||e.key==="Delete")&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&t.selectedWorkerIds.length>0)return e.preventDefault(),t.onKillSelected(),!0;if(t.selectedWorkers.length>1&&!t.isTerminalTarget(e.target)){if(t.inSelectedGroupView&&s==="c"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey)return t.focusRallyCommandInput()&&e.preventDefault(),!0;if((s==="j"||s==="k")&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey){e.preventDefault();const l=s==="j"?1:-1,c=t.clampNumber(t.selectedGroupActiveIndex+l,0,t.selectedWorkers.length-1),h=t.selectedWorkers[c];return t.setSelectedGroupActiveIndex(c),t.focusedSelectedWorkerId&&h&&t.setFocusedSelectedWorkerId(h.id),!0}if(Al(e)){const l=t.selectedWorkers.find(c=>c.id===t.focusedSelectedWorkerId)??t.selectedWorkers[t.selectedGroupActiveIndex]??t.selectedWorkers[0];return l&&(e.preventDefault(),t.setFocusedSelectedWorkerId(l.id),t.requestTerminalFocus()),!0}}if(t.selectedWorkerIds.length===0&&t.rosterEntries.length>0&&!t.isTerminalTarget(e.target)){if(s==="k"&&e.shiftKey&&!e.ctrlKey&&!e.metaKey&&!e.altKey)return e.preventDefault(),t.onKillRosterActive(),!0;if(s==="n"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey&&t.firstSummonEntryIndex!==void 0)return e.preventDefault(),t.setRosterActiveIndex(t.firstSummonEntryIndex),!0;if((s==="j"||s==="k")&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey)return e.preventDefault(),t.setRosterActiveIndex(l=>{const c=s==="j"?1:-1;return t.clampNumber(l+c,0,t.rosterEntries.length-1)}),!0;if(Al(e))return e.preventDefault(),t.onActivateRosterIndex(t.rosterActiveIndex),!0}if(s==="r"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&t.selectedWorkers.length>0){e.preventDefault();const l=t.selectedWorkers.length>1?t.selectedWorkers.find(c=>c.id===t.focusedSelectedWorkerId):void 0;return t.openRenameForWorkers(l?[l]:t.selectedWorkers),!0}return s==="m"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&t.selectedWorkers.length>0?(e.preventDefault(),t.onToggleMovementModeSelected(),!0):s==="s"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey&&t.selectedWorkers.length>1?(e.preventDefault(),t.onScatterSelected(),!0):Al(e)&&t.selectedWorkerId?(e.preventDefault(),t.requestTerminalFocus(),!0):e.key!=="/"||e.metaKey||e.ctrlKey||e.altKey?!1:(e.preventDefault(),t.setPaletteOpen(!0),t.setSpawnDialogOpen(!1),t.setShortcutsOverlayOpen(!1),!0)}function Al(e){return e.key==="Enter"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey}function Kw(e){const t=k.useRef(e);t.current=e,k.useEffect(()=>{const s=n=>{const l=t.current;Uw(n,l)||Hw(n,l)||jw(n,l)||$w(n,l)};return window.addEventListener("keydown",s,!0),()=>window.removeEventListener("keydown",s,!0)},[])}function Uw(e,t){return!(!t.isTerminalTarget(e.target)||t.killConfirmWorkerIds.length>0||t.renameModalOpen||t.batchSpawnDialogOpen||t.shortcutsOverlayOpen||t.paletteOpen||t.spawnDialogOpen||e.key==="Escape"||t.isTerminalEscapeShortcut(e))}async function wi(e,t){const s=await fetch(e,{headers:{"Content-Type":"application/json",...t?.headers??{}},...t});if(!s.ok){const n=await s.json().catch(()=>({error:s.statusText}));throw new Error(n.error??`Request failed: ${s.status}`)}if(s.status!==204)return await s.json()}function Vw(){return wi("/api/config")}async function Qp(){return(await wi("/api/workers")).workers}function Jp(e){return wi("/api/workers/spawn",{method:"POST",body:JSON.stringify(e)})}function Yw(e){return wi(`/api/workers/${e}/stop`,{method:"POST"})}function qw(e,t,s){return wi(`/api/workers/${e}/position`,{method:"PATCH",body:JSON.stringify({x:t,y:s})})}function Zp(e,t){return wi(`/api/workers/${e}/rename`,{method:"PATCH",body:JSON.stringify({displayName:t})})}function Xw(e,t){return wi(`/api/workers/${e}/movement-mode`,{method:"PATCH",body:JSON.stringify({movementMode:t})})}function Gw(e){return wi(`/api/workers/${e}/open-terminal`,{method:"POST"})}function e_(e,t,s=!0){return wi("/api/workers/broadcast-input",{method:"POST",body:JSON.stringify({workerIds:e,text:t,submit:s})})}const Qw=3e4;function Jw(e){const[t,s]=k.useState(null),[n,l]=k.useState([]),[c,h]=k.useState(!1);return k.useEffect(()=>{Promise.all([Vw(),Qp()]).then(([f,_])=>{s(f),l(_),h(!0)}).catch(f=>{const _=f instanceof Error?f.message:"Failed to load Arcane Agents data";e(_)})},[e]),k.useEffect(()=>{let f=null,_=null,d=null,g=!1,y=!1;const C=async()=>{if(!(g||y)){y=!0;try{const E=await Qp();if(g)return;l(E),h(!0)}catch{}finally{y=!1}}},w=()=>{document.visibilityState==="visible"&&C()},v=()=>{C()};function S(){if(g)return;const E=window.location.protocol==="https:"?"wss":"ws";f=new WebSocket(`${E}://${window.location.host}/api/ws`),f.addEventListener("open",()=>{e(void 0),C()}),f.addEventListener("message",P=>{const z=JSON.parse(String(P.data));if(z.type==="init"){s(z.config),l(z.workers),h(!0);return}if(z.type==="worker-created"||z.type==="worker-updated"){l(H=>Ss(H,z.worker));return}z.type==="worker-removed"&&l(H=>H.filter(I=>I.id!==z.workerId))}),f.addEventListener("error",()=>{e("Realtime connection failed. Retrying...")}),f.addEventListener("close",()=>{g||(_=setTimeout(S,2e3))})}return d=setInterval(()=>{C()},Qw),window.addEventListener("focus",v),document.addEventListener("visibilitychange",w),S(),()=>{g=!0,_&&clearTimeout(_),d&&clearInterval(d),window.removeEventListener("focus",v),document.removeEventListener("visibilitychange",w),f?.close()}},[e]),{config:t,workers:n,setWorkers:l,workersHydrated:c}}function Zw(e,t){const[s,n]=k.useState(()=>Gg()),l=k.useRef(s),[c,h]=k.useState(()=>Jg());return k.useEffect(()=>{l.current=s,Qg(s)},[s]),k.useEffect(()=>{Zg(c)},[c]),k.useEffect(()=>{if(!t)return;const f=new Set(e.map(_=>_.id));n(_=>{let d=!1;const g={..._};for(const[y,C]of Object.entries(g)){if(!Array.isArray(C)||C.length===0){delete g[Number(y)],d=!0;continue}const w=C.filter(v=>f.has(v));w.length!==C.length&&(w.length===0?delete g[Number(y)]:g[Number(y)]=w,d=!0)}return d?g:_})},[e,t]),{controlGroups:s,setControlGroups:n,controlGroupByDigitRef:l,mapColumnRatio:c,setMapColumnRatio:f=>{h(_=>{const d=vi(f,lo,ao);return d===_?_:d})},nudgeMapColumnRatio:f=>{h(_=>vi(_+f,lo,ao))},resetMapColumnRatio:()=>{h(Jn)}}}function e1(e,t,s){const[n,l]=k.useState([]),[c,h]=k.useState(0),[f,_]=k.useState(void 0),[d,g]=k.useState(void 0),[y,C]=k.useState(0),[w,v]=k.useState(0),[S,E]=k.useState(void 0),P=n.length===1?n[0]:void 0,z=k.useMemo(()=>new Set(n),[n]),H=k.useMemo(()=>e.filter(b=>z.has(b.id)),[e,z]),I=k.useMemo(()=>e.filter(b=>b.status==="idle"),[e]),G=k.useCallback((b,W)=>{const B=Array.from(new Set(b));l(B),E(void 0);const A=B.length===1?B[0]:void 0;W?.center&&A&&(_(A),h(q=>q+1)),W?.focusTerminal&&A?g(q=>(q??0)+1):g(void 0)},[]),j=k.useCallback(()=>{g(b=>(b??0)+1)},[]),oe=k.useCallback(b=>{G(b?[b]:[])},[G]),Z=k.useCallback(b=>{G(b)},[G]),ee=k.useCallback(b=>{G([b],{focusTerminal:!0})},[G]),se=k.useCallback(b=>{if(e.length===0)return;const W=e.findIndex(N=>N.id===P),A=((W>=0?W:b>0?-1:0)+b+e.length)%e.length,q=e[A];q&&G([q.id],{center:!0})},[e,G,P]),U=k.useCallback(b=>{if(I.length===0)return;const W=I.findIndex(N=>N.id===P),A=((W>=0?W:b>0?-1:0)+b+I.length)%I.length,q=I[A];q&&G([q.id],{center:!0})},[G,I,P]),D=k.useCallback(b=>{if(H.length<=1)return;const W=S?H.findIndex(N=>N.id===S):vi(w,0,H.length-1),A=((W>=0?W:b>0?-1:0)+b+H.length)%H.length,q=H[A];q&&(v(A),E(q.id))},[S,w,H]);return k.useEffect(()=>{if(t.length===0){C(0);return}if(P){const b=t.findIndex(W=>W.kind==="worker"&&W.worker.id===P);b>=0&&C(b);return}C(b=>vi(b,0,t.length-1))},[t,P]),k.useEffect(()=>{if(H.length<=1){v(0),E(void 0),s?.();return}if(S){const b=H.findIndex(W=>W.id===S);if(b>=0){v(b);return}E(void 0)}v(b=>vi(b,0,H.length-1))},[S,s,H]),k.useEffect(()=>{const b=new Set(e.map(W=>W.id));l(W=>W.filter(B=>b.has(B)))},[e]),{selectedWorkerIds:n,setSelectedWorkerIds:l,selectedWorkerId:P,selectedWorkers:H,mapCenterToken:c,mapCenterWorkerId:f,terminalFocusToken:d,rosterActiveIndex:y,setRosterActiveIndex:C,selectedGroupActiveIndex:w,setSelectedGroupActiveIndex:v,focusedSelectedWorkerId:S,setFocusedSelectedWorkerId:E,applySelection:G,requestTerminalFocus:j,onSelectWorker:oe,onSelectionChange:Z,onActivateWorker:ee,cycleSelection:se,cycleIdleSelection:U,cycleSelectedGroupFocus:D}}function t1(e){const[t,s]=k.useState(!1);return k.useEffect(()=>{s(!1),document.activeElement instanceof HTMLElement&&document.activeElement.closest(".terminal-panel")&&document.activeElement.blur()},[e]),k.useEffect(()=>{const n=()=>{s(Ul(document.activeElement))},l=()=>{setTimeout(n,0)},c=()=>{s(!1)};return window.addEventListener("focusin",n,!0),window.addEventListener("focusout",l,!0),window.addEventListener("blur",c),n(),()=>{window.removeEventListener("focusin",n,!0),window.removeEventListener("focusout",l,!0),window.removeEventListener("blur",c)}},[]),t}function r1({workers:e,reviewedWorkerId:t}){const[s,n]=k.useState([]),l=k.useRef(new Map),c=k.useRef(t);return k.useEffect(()=>{c.current=t},[t]),k.useEffect(()=>{const h=new Set(e.map(g=>g.id)),f=new Set,_=new Set;for(const g of e)if(l.current.get(g.id)==="working"&&g.status==="idle"){if(g.id===c.current)continue;_.add(g.id)}else g.status!=="idle"&&f.add(g.id);const d=new Map;for(const g of e)d.set(g.id,g.status);l.current=d,n(g=>{const C=[...g.filter(w=>h.has(w)&&!f.has(w))];for(const w of _)C.includes(w)||C.push(w);return C.length===g.length&&C.every((w,v)=>w===g[v])?g:C})},[e]),k.useEffect(()=>{t&&n(h=>h.filter(f=>f!==t))},[t]),{pendingCompletionWorkerIds:s}}function i1(e){const[t,s]=k.useState([]),n=k.useCallback(c=>{s(h=>[{worker:c,startedAtMs:Date.now()},...h.filter(f=>f.worker.id!==c.id)])},[]),l=k.useCallback(c=>{s(h=>h.filter(f=>f.worker.id!==c))},[]);return k.useEffect(()=>{const c=setInterval(()=>{const h=Date.now();s(f=>f.filter(_=>h-_.startedAtMs<e))},80);return()=>{clearInterval(c)}},[e]),{fadingWorkers:t,queueWorkerFade:n,removeWorkerFade:l}}const s1=1600,Zc=["move","selected"],n1={move:["move.mp3","move_variant_1.mp3","move_variant_2.mp3","move_variant_3.mp3"],selected:["selected.mp3","selected_variant_1.mp3","selected_variant_2.mp3","selected_variant_3.mp3"]};function o1({config:e,workers:t,workersHydrated:s,selectedWorkerIds:n}){const l=e?.audio.enableSound??!0,c=k.useRef(new Map),h=k.useRef(new Set),f=k.useRef(new Map),_=k.useRef(!1),d=k.useRef(!1),g=k.useRef(new Map),y=k.useRef(new Set),C=k.useRef(new Map),w=k.useRef(new Map),v=k.useRef(new Set),S=k.useRef(null);k.useEffect(()=>{f.current=new Map(t.map(U=>[U.id,U]))},[t]);const E=k.useCallback((U,D)=>`/api/assets/characters/${encodeURIComponent(U)}/voice-lines/${D}.mp3`,[]),P=k.useCallback((U,D)=>`/api/assets/characters/${encodeURIComponent(U)}/voice-lines/${encodeURIComponent(D)}`,[]),z=k.useCallback((U,D)=>{const b=g.current.get(U)?.[D]??[];return b.length>0?b:n1[D].map(W=>P(U,W))},[P]),H=k.useCallback(U=>{if(v.current.has(U))return;v.current.add(U);const D=new Audio(U);D.preload="auto",D.addEventListener("canplay",()=>{w.current.set(U,!0)},{once:!0}),D.addEventListener("error",()=>{w.current.set(U,!1)},{once:!0}),D.load()},[]),I=k.useCallback(U=>{if(!l||w.current.get(U)===!1)return;const D=S.current;D&&(D.pause(),D.currentTime=0);const b=new Audio(U);b.preload="auto",b.addEventListener("canplay",()=>{w.current.set(U,!0)},{once:!0}),b.addEventListener("error",()=>{w.current.set(U,!1)},{once:!0}),S.current=b,b.play().catch(()=>{})},[l]),G=k.useCallback((U,D)=>{I(E(U.avatarType,D))},[I,E]),j=k.useCallback(U=>{if(!l)return;const D=U.filter(A=>w.current.get(A)===!0),b=U.filter(A=>w.current.get(A)!==!1),W=D.length>0?D:b,B=t_(W);B&&I(B)},[I,l]),oe=k.useCallback(U=>{const D=z(U.avatarType,"selected");j(D)},[j,z]),Z=k.useCallback(U=>{const D=z(U.avatarType,"move");j(D)},[j,z]),ee=k.useCallback(async U=>{try{const D=await fetch(`/api/avatars/${encodeURIComponent(U)}/voice-lines`);if(!D.ok)return;const b=await D.json();if(!Array.isArray(b.files))return;const W=b.files.filter(A=>typeof A=="string"&&A.toLowerCase().endsWith(".mp3")).sort((A,q)=>A.localeCompare(q)),B={move:[],selected:[]};for(const A of Zc)B[A]=W.filter(q=>q.toLowerCase().startsWith(A)).map(q=>P(U,q));g.current.set(U,B);for(const A of Zc)for(const q of B[A])H(q)}catch{y.current.delete(U)}},[H,P]);k.useEffect(()=>{const U=Array.from(new Set(t.map(D=>D.avatarType)));for(const D of U)y.current.has(D)||(y.current.add(D),ee(D))},[ee,t]),k.useEffect(()=>{if(!l)return;const U=Array.from(new Set(t.map(b=>b.avatarType))),D=["arrive","attention","complete","death"];for(const b of U){for(const W of D)H(E(b,W));for(const W of Zc)for(const B of z(b,W))H(B)}},[H,E,z,l,t]),k.useEffect(()=>{if(!s)return;const U=new Map(t.map(B=>[B.id,B])),D=c.current,b=C.current,W=performance.now();if(!_.current){_.current=!0,c.current=U;return}for(const B of t){const A=D.get(B.id);if(!A){G(B,"arrive"),b.set(B.id,W+s1);continue}l1(A.status,B.status)&&G(B,"attention"),a1(A.status,B.status)&&!h1(B)&&G(B,"complete")}for(const[B,A]of D.entries())U.has(B)||(G(A,"death"),b.delete(B));c.current=U},[G,t,s]),k.useEffect(()=>{if(!s)return;const U=new Set(n),D=h.current;if(!d.current){d.current=!0,h.current=U;return}const b=n.filter(N=>!D.has(N)),W=C.current,B=performance.now(),A=b.filter(N=>{const K=W.get(N);return K===void 0?!0:K<=B?(W.delete(N),!0):!1}),q=t_(A);if(q){const N=f.current.get(q);N&&oe(N)}h.current=U},[oe,n,s]);const se=k.useCallback(U=>{const D=f.current.get(U);D&&Z(D)},[Z]);return k.useEffect(()=>()=>{const U=S.current;U&&(U.pause(),U.currentTime=0,S.current=null)},[]),{playMoveVoiceLine:se}}function l1(e,t){return e!=="attention"&&t==="attention"}function a1(e,t){return e==="working"&&t==="idle"}const c1=1e4;function h1(e){return Date.now()-new Date(e.createdAt).getTime()<c1}function t_(e){if(e.length===0)return;const t=Math.floor(Math.random()*e.length);return e[t]}function u1({workers:e,activeWorkers:t,renameModalOpen:s,setRenameModalOpen:n,renameTargetWorkerIds:l,setRenameTargetWorkerIds:c,killConfirmWorkerIds:h,setKillConfirmWorkerIds:f,setRenameDraft:_}){const d=k.useMemo(()=>{if(l.length===0)return[];const v=new Map(e.map(S=>[S.id,S]));return l.map(S=>v.get(S)).filter(S=>!!S)},[l,e]),g=k.useMemo(()=>{if(h.length===0)return[];const v=new Map(e.map(S=>[S.id,S]));return h.map(S=>v.get(S)).filter(S=>!!S)},[h,e]),y=k.useCallback(()=>{n(!1),c([])},[n,c]),C=k.useCallback(()=>{f([])},[f]),w=k.useCallback(v=>{v.length!==0&&(_(v.length===1?v[0].displayName??v[0].name:""),c(v.map(S=>S.id)),n(!0))},[_,n,c]);return k.useEffect(()=>{if(!s||l.length===0)return;const v=new Set(t.map(S=>S.id));l.some(S=>v.has(S))||y()},[t,y,s,l]),k.useEffect(()=>{if(h.length===0)return;const v=new Set(t.map(S=>S.id));h.some(S=>v.has(S))||C()},[t,C,h]),{renameTargetWorkers:d,killConfirmWorkers:g,closeRenameModal:y,closeKillConfirm:C,openRenameForWorkers:w}}function d1({workers:e,selectedWorkerIds:t,setSelectedWorkerIds:s,rosterEntries:n,rosterActiveIndex:l,setKillConfirmWorkerIds:c,closeKillConfirm:h,killConfirmWorkerIds:f,queueWorkerFade:_,removeWorkerFade:d,setWorkers:g,showError:y}){const C=k.useCallback(async E=>{const P=e.find(z=>z.id===E);if(P){h(),_(P);try{const z=await Yw(E);g(H=>H.filter(I=>I.id!==z.workerId)),s(H=>H.filter(I=>I!==z.workerId))}catch(z){d(E),y(z)}}},[h,_,d,s,g,y,e]),w=k.useCallback(()=>{t.length!==0&&c(t)},[t,c]),v=k.useCallback(()=>{const E=n[l];!E||E.kind!=="worker"||c([E.worker.id])},[l,n,c]),S=k.useCallback(()=>{if(f.length===0)return;const E=[...f];h();for(const P of E)C(P)},[h,f,C]);return{onKillSelected:w,onKillRosterActive:v,confirmKillSelection:S}}function f1({setWorkers:e,selectedWorkers:t,terminalWorkerId:s,applySelection:n,setSpawnDialogOpen:l,setPaletteOpen:c,renameTargetWorkerIds:h,closeRenameModal:f,showError:_}){const d=k.useCallback(async S=>{try{const E=t.map(H=>H.id),P="shortcutIndex"in S&&E.length>0?{...S,spawnNearWorkerIds:E}:S,z=await Jp(P);e(H=>Ss(H,z)),n([z.id],{center:!0}),l(!1),c(!1)}catch(E){_(E)}},[n,t,c,l,e,_]),g=k.useCallback(async(S,E)=>{const P=[];let z;for(let H=0;H<S.length;H++){E(H,S.length);const I=S[H],G={...I.input,displayName:I.displayName,spawnNearWorkerIds:z?[z]:t.map(j=>j.id).slice(0,1)};try{const j=await Jp(G);e(oe=>Ss(oe,j)),P.push(j.id),z=j.id}catch(j){_(j);break}}E(P.length,S.length),P.length>0&&n(P,{center:!0})},[n,t,e,_]),y=k.useCallback(async S=>{const E=[...h];if(E.length===0){f();return}try{if(E.length===1){const P=await Zp(E[0],S);e(z=>Ss(z,P))}else{const P=S.trim(),z=await Promise.all(E.map((H,I)=>Zp(H,P.length>0?`${P} ${I+1}`:"")));e(H=>{let I=H;for(const G of z)I=Ss(I,G);return I})}f()}catch(P){_(P)}},[f,h,e,_]),C=k.useCallback(async()=>{if(t.length===0)return;const S=t.every(E=>E.movementMode==="hold")?"wander":"hold";try{const E=await Promise.all(t.map(P=>Xw(P.id,S)));e(P=>{let z=P;for(const H of E)z=Ss(z,H);return z})}catch(E){_(E)}},[t,e,_]),w=k.useCallback(async()=>{if(s)try{await Gw(s)}catch(S){_(S)}},[_,s]),v=k.useCallback((S,E)=>{qw(S,E.x,E.y).then(P=>{e(z=>Ss(z,P))}).catch(P=>{_(P)})},[e,_]);return{runSpawn:d,runBatchSpawn:g,submitRename:y,onToggleMovementModeSelected:C,onOpenSelectedInTerminal:w,onPositionCommit:v}}function p1({selectedWorkers:e,rallyCommandDraft:t,setRallyCommandDraft:s,rallyCommandSending:n,setRallyCommandSending:l,rallyCommandResultText:c,setRallyCommandResultText:h,showError:f}){const _=k.useCallback(async()=>{if(n)return;const g=e.map(y=>y.id);if(!(g.length<=1)){if(t.length===0){h("Enter a command to broadcast.");return}l(!0),h(void 0);try{const C=t.includes("$NAME")?sv(await Promise.all(e.map(async w=>{const v=t.replace(/\$NAME/g,w.displayName??w.name);try{return await e_([w.id],v,!0)}catch(S){return{requestedCount:1,deliveredWorkerIds:[],skippedWorkerIds:[],failed:[{workerId:w.id,error:S instanceof Error?S.message:"Failed to send input"}]}}}))):await e_(g,t,!0);s(""),h(iv(C))}catch(y){f(y)}finally{l(!1)}}},[t,n,e,s,h,l,f]),d=k.useCallback(g=>{s(g),c&&h(void 0)},[c,s,h]);return{onSendRallyCommand:_,onRallyCommandDraftChange:d}}function _1({workers:e,activeWorkers:t,setWorkers:s,selectedWorkers:n,selectedWorkerIds:l,setSelectedWorkerIds:c,focusedSelectedWorkerId:h,terminalWorkerId:f,rosterEntries:_,rosterActiveIndex:d,setRosterActiveIndex:g,applySelection:y,setSpawnDialogOpen:C,setPaletteOpen:w,renameModalOpen:v,setRenameModalOpen:S,renameTargetWorkerIds:E,setRenameTargetWorkerIds:P,setRenameDraft:z,killConfirmWorkerIds:H,setKillConfirmWorkerIds:I,rallyCommandDraft:G,setRallyCommandDraft:j,rallyCommandSending:oe,setRallyCommandSending:Z,rallyCommandResultText:ee,setRallyCommandResultText:se,queueWorkerFade:U,removeWorkerFade:D,setErrorText:b}){const W=k.useCallback(xe=>{b(xe instanceof Error?xe.message:"Unknown request failure")},[b]),{renameTargetWorkers:B,killConfirmWorkers:A,closeRenameModal:q,closeKillConfirm:N,openRenameForWorkers:K}=u1({workers:e,activeWorkers:t,renameModalOpen:v,setRenameModalOpen:S,renameTargetWorkerIds:E,setRenameTargetWorkerIds:P,killConfirmWorkerIds:H,setKillConfirmWorkerIds:I,setRenameDraft:z}),{runSpawn:Q,runBatchSpawn:T,submitRename:V,onToggleMovementModeSelected:de,onOpenSelectedInTerminal:fe,onPositionCommit:he}=f1({setWorkers:s,selectedWorkers:n,terminalWorkerId:f,applySelection:y,setSpawnDialogOpen:C,setPaletteOpen:w,renameTargetWorkerIds:E,closeRenameModal:q,showError:W}),{onKillSelected:pe,onKillRosterActive:ge,confirmKillSelection:Re}=d1({workers:e,selectedWorkerIds:l,setSelectedWorkerIds:c,rosterEntries:_,rosterActiveIndex:d,setKillConfirmWorkerIds:I,closeKillConfirm:N,killConfirmWorkerIds:H,queueWorkerFade:U,removeWorkerFade:D,setWorkers:s,showError:W}),{onSendRallyCommand:ye,onRallyCommandDraftChange:Fe}=p1({selectedWorkers:n,rallyCommandDraft:G,setRallyCommandDraft:j,rallyCommandSending:oe,setRallyCommandSending:Z,rallyCommandResultText:ee,setRallyCommandResultText:se,showError:W}),We=k.useCallback(xe=>{const Ke=_[xe];if(Ke){if(g(xe),Ke.kind==="worker"){y([Ke.worker.id],{center:!0});return}Q({shortcutIndex:Ke.shortcutIndex})}},[y,_,Q,g]),je=k.useCallback(()=>{if(n.length===0)return;const xe=n.length>1?n.find(Ke=>Ke.id===h):void 0;K(xe?[xe]:n)},[h,K,n]);return{renameTargetWorkers:B,killConfirmWorkers:A,runSpawn:Q,runBatchSpawn:T,closeRenameModal:q,closeKillConfirm:N,openRenameForWorkers:K,submitRename:V,onKillSelected:pe,onKillRosterActive:ge,confirmKillSelection:Re,onToggleMovementModeSelected:de,onActivateRosterIndex:We,onOpenSelectedInTerminal:fe,onSendRallyCommand:ye,onRallyCommandDraftChange:Fe,onRenameSelected:je,onPositionCommit:he}}function m1(e){const t=[];return e.forEach((s,n)=>{for(const l of s.hotkeys??[]){const c=y1(l);c&&t.push({shortcutIndex:n,hotkey:c})}}),t}function g1(e,t){const s=[],n=new Set;for(const l of e)v1(l.hotkey,t)&&(n.has(l.shortcutIndex)||(n.add(l.shortcutIndex),s.push(l.shortcutIndex)));return s}function v1(e,t){return t.ctrlKey!==e.ctrl||t.metaKey!==e.meta||t.altKey!==e.alt||t.shiftKey!==e.shift?!1:C1(t.key)===e.key?!0:e.code?t.code===e.code:!1}function y1(e){const t=S1(e);if(t.length===0)return;let s=!1,n=!1,l=!1,c=!1,h;for(const _ of t){const d=_.trim().toLowerCase();if(d){if(d==="ctrl"||d==="control"){s=!0;continue}if(d==="cmd"||d==="meta"||d==="super"){n=!0;continue}if(d==="alt"||d==="option"){l=!0;continue}if(d==="shift"){c=!0;continue}if(h)return;h=_}}if(!h)return;const f=w1(h);if(f)return{key:f.key,code:f.code,ctrl:s,meta:n,alt:l,shift:c}}function S1(e){const t=e.trim().replace(/\s+/g,"");if(!t)return[];if(t.includes("+"))return t.split("+").filter(l=>l.length>0);const s=t.toLowerCase();return s.includes("ctrl-")||s.includes("control-")||s.includes("cmd-")||s.includes("meta-")||s.includes("super-")||s.includes("alt-")||s.includes("option-")||s.includes("shift-")?t.split("-").filter(l=>l.length>0):[t]}function w1(e){const t=e.trim();if(!t)return;const s=t.toLowerCase();if(s==="space"||s==="spacebar")return{key:" ",code:"Space"};if(s==="esc")return{key:"escape",code:"Escape"};if(s==="return")return{key:"enter",code:"Enter"};if(s==="up")return{key:"arrowup",code:"ArrowUp"};if(s==="down")return{key:"arrowdown",code:"ArrowDown"};if(s==="left")return{key:"arrowleft",code:"ArrowLeft"};if(s==="right")return{key:"arrowright",code:"ArrowRight"};if(/^key[a-z]$/.test(s)){const n=s.slice(3);return{key:n,code:`Key${n.toUpperCase()}`}}if(/^digit[0-9]$/.test(s)){const n=s.slice(5);return{key:n,code:`Digit${n}`}}if(/^numpad[0-9]$/.test(s)){const n=s.slice(6);return{key:n,code:`Numpad${n}`}}if(/^f[0-9]{1,2}$/.test(s))return{key:s,code:s.toUpperCase()};if(t.length===1){if(/^[a-z]$/i.test(t)){const n=t.toLowerCase();return{key:n,code:`Key${n.toUpperCase()}`}}return/^[0-9]$/.test(t)?{key:t}:{key:t}}return{key:s}}function C1(e){const t=e.toLowerCase();return t==="spacebar"?" ":t}function k1(){const[e,t]=k.useState(!1),[s,n]=k.useState(!1),[l,c]=k.useState(!1),[h,f]=k.useState(!1),[_,d]=k.useState(!1),[g,y]=k.useState([]),[C,w]=k.useState(""),[v,S]=k.useState([]),[E,P]=k.useState(""),[z,H]=k.useState(!1),[I,G]=k.useState(void 0),[j,oe]=k.useState(void 0),[Z,ee]=k.useState([]),[se,U]=k.useState(0),D=k.useRef(null),{config:b,workers:W,setWorkers:B,workersHydrated:A}=Jw(oe),{fadingWorkers:q,queueWorkerFade:N,removeWorkerFade:K}=i1(Xg),Q=k.useMemo(()=>W.filter(te=>te.status!=="stopped"),[W]),{controlGroups:T,setControlGroups:V,controlGroupByDigitRef:de,mapColumnRatio:fe,setMapColumnRatio:he,nudgeMapColumnRatio:pe,resetMapColumnRatio:ge}=Zw(Q,A),Re=k.useRef(null),ye=k.useRef(void 0),[Fe,We]=k.useState(!1),je=k.useMemo(()=>b?.shortcuts??[],[b]),xe=k.useMemo(()=>m1(je),[je]),Ke=k.useMemo(()=>[...Q.map(te=>({kind:"worker",worker:te})),...je.map((te,Ce)=>({kind:"shortcut",shortcut:te,shortcutIndex:Ce}))],[Q,je]),jr=k.useMemo(()=>{const te=Ke.findIndex(Ce=>Ce.kind==="shortcut");return te>=0?te:void 0},[Ke]),at=k.useCallback(()=>{P(""),G(void 0)},[]),{selectedWorkerIds:zt,setSelectedWorkerIds:_r,selectedWorkerId:ct,selectedWorkers:Xe,mapCenterToken:Kt,mapCenterWorkerId:$r,terminalFocusToken:Zi,rosterActiveIndex:ii,setRosterActiveIndex:Ci,selectedGroupActiveIndex:ki,setSelectedGroupActiveIndex:si,focusedSelectedWorkerId:Ut,setFocusedSelectedWorkerId:es,applySelection:Dr,requestTerminalFocus:mr,onSelectWorker:Ae,onSelectionChange:Ts,onActivateWorker:ts,cycleSelection:gr,cycleIdleSelection:vr,cycleSelectedGroupFocus:rs}=e1(Q,Ke,at),yr=k.useMemo(()=>Q.find(te=>te.id===ct),[Q,ct]),Sr=k.useMemo(()=>{if(Ut)return Xe.find(te=>te.id===Ut)},[Ut,Xe]),Kr=yr??(Xe.length>1?Sr:void 0),Et=Kr?.id,or=Xe.length>1&&!Kr,bi=t1(Et),{pendingCompletionWorkerIds:is}=r1({workers:Q,reviewedWorkerId:Et}),{playMoveVoiceLine:xi}=o1({config:b,workers:Q,workersHydrated:A,selectedWorkerIds:zt}),Ur=k.useCallback(()=>{const te=document.activeElement;return!(te instanceof HTMLElement)||!te.closest(".terminal-panel")?!1:(te.blur(),document.querySelector(".map-canvas")?.focus(),!0)},[]),Ps=k.useCallback(()=>{const te=D.current;if(!te)return!1;te.focus();const Ce=te.value.length;return te.setSelectionRange(Ce,Ce),!0},[]),Ei=k.useCallback(()=>{if(Xe.length<2)return;const te=Xe.reduce((Qe,mt)=>Qe+mt.position.x,0)/Xe.length,Ce=Xe.reduce((Qe,mt)=>Qe+mt.position.y,0)/Xe.length,Le=80+Xe.length*20,De=Xe.map(Qe=>({workerId:Qe.id,target:{x:te+(Math.random()-.5)*Le*2,y:Ce+(Math.random()-.5)*Le*2}}));ee(De),U(Qe=>Qe+1)},[Xe]),ss=k.useCallback(te=>{const Ce=Re.current;if(!Ce)return;const Le=Ce.getBoundingClientRect(),De=Math.max(1,Le.width-Bc),Qe=te-Le.left-Bc/2,mt=vi(Qe/De,lo,ao);he(mt)},[he]);k.useEffect(()=>{if(Fe)return document.body.classList.add("split-pane-dragging"),()=>{document.body.classList.remove("split-pane-dragging")}},[Fe]);const{renameTargetWorkers:ni,killConfirmWorkers:ns,runSpawn:lr,runBatchSpawn:ln,closeRenameModal:Vr,closeKillConfirm:Nr,openRenameForWorkers:Mi,submitRename:X,onKillSelected:Y,onKillRosterActive:le,confirmKillSelection:ue,onToggleMovementModeSelected:_e,onActivateRosterIndex:Ne,onOpenSelectedInTerminal:Oe,onSendRallyCommand:ht,onRallyCommandDraftChange:tt,onRenameSelected:Ue,onPositionCommit:Jt}=_1({workers:W,activeWorkers:Q,setWorkers:B,selectedWorkers:Xe,selectedWorkerIds:zt,setSelectedWorkerIds:_r,focusedSelectedWorkerId:Ut,terminalWorkerId:Et,rosterEntries:Ke,rosterActiveIndex:ii,setRosterActiveIndex:Ci,applySelection:Dr,setSpawnDialogOpen:t,setPaletteOpen:n,renameModalOpen:_,setRenameModalOpen:d,renameTargetWorkerIds:v,setRenameTargetWorkerIds:S,setRenameDraft:w,killConfirmWorkerIds:g,setKillConfirmWorkerIds:y,rallyCommandDraft:E,setRallyCommandDraft:P,rallyCommandSending:z,setRallyCommandSending:H,rallyCommandResultText:I,setRallyCommandResultText:G,queueWorkerFade:N,removeWorkerFade:K,setErrorText:oe});return Kw({activeWorkers:Q,applySelection:Dr,batchSpawnDialogOpen:l,clampNumber:vi,closeKillConfirm:Nr,closeRenameModal:Vr,confirmKillSelection:ue,controlGroupByDigitRef:de,cycleIdleSelection:vr,cycleSelectedGroupFocus:rs,cycleSelection:gr,escapeTerminalFocus:Ur,findMatchingShortcutIndexes:g1,firstSummonEntryIndex:jr,focusRallyCommandInput:Ps,focusedSelectedWorkerId:Ut,inSelectedGroupView:or,isEditableTarget:$h,isTerminalEscapeShortcut:ov,isTerminalTarget:ev,killConfirmWorkerIds:g,mapColumnRatioStep:jh,nudgeMapColumnRatio:pe,onActivateRosterIndex:Ne,onKillRosterActive:le,onKillSelected:Y,onScatterSelected:Ei,onToggleMovementModeSelected:_e,openRenameForWorkers:Mi,paletteOpen:s,parseControlGroupDigit:nv,renameModalOpen:_,requestTerminalFocus:mr,resetMapColumnRatio:ge,rosterActiveIndex:ii,rosterEntries:Ke,runSpawn:lr,selectedGroupActiveIndex:ki,selectedWorkerId:ct,selectedWorkerIds:zt,selectedWorkers:Xe,setBatchSpawnDialogOpen:c,setControlGroups:V,setFocusedSelectedWorkerId:es,setPaletteOpen:n,setRosterActiveIndex:Ci,setSelectedGroupActiveIndex:si,setShortcutsOverlayOpen:f,setSpawnDialogOpen:t,shortcutHotkeyBindings:xe,shortcutsOverlayOpen:h,spawnDialogOpen:e}),M.jsxs("div",{ref:Re,className:"app-shell",style:{gridTemplateColumns:`minmax(0px, ${fe.toFixed(3)}fr) ${Bc}px minmax(0px, ${(1-fe).toFixed(3)}fr)`},children:[M.jsxs("div",{className:"map-column",children:[M.jsx(S0,{workers:Q,fadingWorkers:q,selectedWorkerId:ct,selectedWorkerIds:zt,focusedSelectedWorkerId:Ut,terminalFocusedSelected:!!(ct&&bi),terminalFocusedWorkerId:bi?Et:void 0,controlGroups:T,completionPendingWorkerIds:is,onSelect:Ae,onSelectionChange:Ts,onActivateWorker:ts,onMoveOrderIssued:xi,onPositionCommit:Jt,externalMoveOrders:Z,externalMoveOrderToken:se,centerOnWorkerId:$r,centerRequestKey:Kt}),M.jsx(lv,{shortcuts:b?.shortcuts??[],selectedWorker:yr,selectedWorkers:Xe,onSpawnShortcut:te=>{lr({shortcutIndex:te})},onOpenSpawnDialog:()=>{t(!0),n(!1)},onOpenPalette:()=>{n(!0),t(!1)},onDeselect:()=>Ae(void 0),onKillSelected:()=>{Y()},onRenameSelected:()=>{Ue()},onToggleMovementMode:()=>{_e()},onScatterSelected:()=>{Ei()}})]}),M.jsx("div",{className:`layout-divider${Fe?" layout-divider-active":""}`,role:"separator","aria-label":"Resize map and terminal columns","aria-orientation":"vertical",onPointerDown:te=>{te.button===0&&(te.preventDefault(),ye.current=te.pointerId,We(!0),te.currentTarget.setPointerCapture(te.pointerId),ss(te.clientX))},onPointerMove:te=>{ye.current===te.pointerId&&(te.preventDefault(),ss(te.clientX))},onPointerUp:te=>{ye.current===te.pointerId&&(te.preventDefault(),te.currentTarget.hasPointerCapture(te.pointerId)&&te.currentTarget.releasePointerCapture(te.pointerId),ye.current=void 0,We(!1))},onPointerCancel:te=>{ye.current===te.pointerId&&(te.currentTarget.hasPointerCapture(te.pointerId)&&te.currentTarget.releasePointerCapture(te.pointerId),ye.current=void 0,We(!1))}}),M.jsx(Fw,{activeWorkers:Q,selectedWorkers:Xe,terminalWorker:Kr,terminalFocused:bi,selectedGroupActiveIndex:ki,setSelectedGroupActiveIndex:si,setFocusedSelectedWorkerId:es,rallyCommandInputRef:D,rallyCommandDraft:E,rallyCommandSending:z,rallyCommandResultText:I,onRallyCommandDraftChange:tt,onSendRallyCommand:ht,rosterEntries:Ke,completionPendingWorkerIds:is,rosterActiveIndex:ii,setRosterActiveIndex:Ci,onActivateRosterIndex:Ne,onOpenSelectedInTerminal:Oe,terminalFocusToken:Zi}),b?M.jsx(k0,{open:e,projects:b.projects,runtimes:b.runtimes,onClose:()=>t(!1),onSpawn:(te,Ce)=>{lr({projectId:te,runtimeId:Ce})}}):null,b?M.jsx(cv,{open:s,config:b,onClose:()=>n(!1),onSpawnShortcut:te=>{lr({shortcutIndex:te})},onSpawnProjectRuntime:(te,Ce)=>{lr({projectId:te,runtimeId:Ce})},onOpenBatchSpawn:()=>{n(!1),c(!0)}}):null,b?M.jsx(av,{open:l,config:b,onClose:()=>c(!1),onBatchSpawn:ln}):null,M.jsx(C0,{open:h,onClose:()=>{f(!1)}}),M.jsx(hv,{workerIds:g,workers:ns,onClose:Nr,onConfirm:ue}),M.jsx(w0,{open:_,targetWorkerIds:v,targetWorkers:ni,initialDraft:C,onClose:Vr,onSubmit:X}),j?M.jsx("div",{className:"error-toast",onClick:()=>oe(void 0),children:j}):null]})}const em=document.getElementById("root");if(!em)throw new Error("Missing #root container");qg.createRoot(em).render(M.jsx(k.StrictMode,{children:M.jsx(k1,{})}));
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Arcane Agents</title>
7
- <script type="module" crossorigin src="/assets/index-HfXsReQG.js"></script>
7
+ <script type="module" crossorigin src="/assets/index-DNXJVqF0.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/assets/index-CWU29xaz.css">
9
9
  </head>
10
10
  <body>
@@ -13,12 +13,16 @@ const statusMonitor_1 = require("../status/statusMonitor");
13
13
  const tmuxAdapter_1 = require("../tmux/tmuxAdapter");
14
14
  const realtimeHub_1 = require("../ws/realtimeHub");
15
15
  const terminalBridge_1 = require("../ws/terminalBridge");
16
- async function createServerContext() {
17
- const paths = (0, loadConfig_1.getArcaneAgentsPaths)();
16
+ async function createServerContext(sessionName) {
17
+ const paths = (0, loadConfig_1.getArcaneAgentsPaths)(sessionName);
18
18
  node_fs_1.default.mkdirSync(paths.configDir, { recursive: true });
19
19
  node_fs_1.default.mkdirSync(paths.stateDir, { recursive: true });
20
20
  node_fs_1.default.mkdirSync(paths.cacheDir, { recursive: true });
21
21
  const baseConfig = (0, loadConfig_1.loadResolvedConfig)(paths);
22
+ if ((0, loadConfig_1.isNonDefaultSession)(sessionName)) {
23
+ const baseTmuxName = baseConfig.backend.tmux.sessionName;
24
+ baseConfig.backend.tmux.sessionName = `${baseTmuxName}-${sessionName}`;
25
+ }
22
26
  const discoveryService = new discovery_1.DiscoveryService();
23
27
  const initialDiscovery = await discoveryService.discover(baseConfig);
24
28
  for (const warning of initialDiscovery.warnings) {
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.bootstrap = bootstrap;
7
7
  const node_http_1 = __importDefault(require("node:http"));
8
8
  const httpApp_1 = require("./bootstrap/httpApp");
9
+ const loadConfig_1 = require("./config/loadConfig");
9
10
  const serverContext_1 = require("./bootstrap/serverContext");
10
11
  const shutdown_1 = require("./bootstrap/shutdown");
11
12
  const websocketUpgrade_1 = require("./bootstrap/websocketUpgrade");
@@ -61,10 +62,15 @@ function renderStartupBanner() {
61
62
  })
62
63
  .join("\n");
63
64
  }
64
- async function bootstrap() {
65
+ async function bootstrap(sessionName) {
65
66
  console.log(renderStartupBanner());
66
- console.log("[arcane-agents] launching Arcane Agents...");
67
- const context = await (0, serverContext_1.createServerContext)();
67
+ if ((0, loadConfig_1.isNonDefaultSession)(sessionName)) {
68
+ console.log(`[arcane-agents] launching Arcane Agents (session: ${sessionName})...`);
69
+ }
70
+ else {
71
+ console.log("[arcane-agents] launching Arcane Agents...");
72
+ }
73
+ const context = await (0, serverContext_1.createServerContext)(sessionName);
68
74
  context.statusMonitor.start();
69
75
  const app = (0, httpApp_1.createHttpApp)(context);
70
76
  const server = node_http_1.default.createServer(app);
@@ -7,13 +7,41 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const node_child_process_1 = require("node:child_process");
8
8
  const node_fs_1 = __importDefault(require("node:fs"));
9
9
  const node_path_1 = __importDefault(require("node:path"));
10
+ const node_readline_1 = __importDefault(require("node:readline"));
10
11
  const bootstrapApp_1 = require("./bootstrapApp");
11
12
  const loadConfig_1 = require("./config/loadConfig");
12
13
  const appRoot_1 = require("./utils/appRoot");
13
14
  const path_1 = require("./utils/path");
15
+ function extractSessionFlag(args) {
16
+ const remaining = [...args];
17
+ let sessionName;
18
+ for (let i = 0; i < remaining.length; i++) {
19
+ if (remaining[i] === "--session" || remaining[i] === "-s") {
20
+ const value = remaining[i + 1];
21
+ if (!value || value.startsWith("-")) {
22
+ console.error("[arcane-agents] --session requires a name argument.");
23
+ process.exit(1);
24
+ }
25
+ sessionName = value;
26
+ remaining.splice(i, 2);
27
+ break;
28
+ }
29
+ const eqMatch = remaining[i].match(/^(?:--session|-s)=(.+)$/);
30
+ if (eqMatch) {
31
+ sessionName = eqMatch[1];
32
+ remaining.splice(i, 1);
33
+ break;
34
+ }
35
+ }
36
+ if (sessionName !== undefined && !/^[a-zA-Z0-9_-]+$/.test(sessionName)) {
37
+ console.error("[arcane-agents] session name must only contain letters, digits, hyphens, and underscores.");
38
+ process.exit(1);
39
+ }
40
+ return { sessionName, remainingArgs: remaining };
41
+ }
14
42
  async function runCli() {
15
43
  (0, appRoot_1.setAppRoot)((0, appRoot_1.resolveAppRoot)());
16
- const args = process.argv.slice(2);
44
+ const { sessionName, remainingArgs: args } = extractSessionFlag(process.argv.slice(2));
17
45
  const firstArg = args[0];
18
46
  if (firstArg === "--help" || firstArg === "-h") {
19
47
  printHelp();
@@ -26,13 +54,15 @@ async function runCli() {
26
54
  const [command = "start", ...commandArgs] = args;
27
55
  switch (command) {
28
56
  case "start":
29
- return runStart();
57
+ return runStart(sessionName);
30
58
  case "init":
31
59
  return runInit(commandArgs);
32
60
  case "config":
33
61
  return runConfig(commandArgs);
34
62
  case "doctor":
35
63
  return runDoctor();
64
+ case "sessions":
65
+ return runSessions(commandArgs);
36
66
  case "help":
37
67
  printHelp();
38
68
  return 0;
@@ -45,11 +75,11 @@ async function runCli() {
45
75
  return 1;
46
76
  }
47
77
  }
48
- async function runStart() {
78
+ async function runStart(sessionName) {
49
79
  if (!process.env.NODE_ENV) {
50
80
  process.env.NODE_ENV = "production";
51
81
  }
52
- const paths = (0, loadConfig_1.getArcaneAgentsPaths)();
82
+ const paths = (0, loadConfig_1.getArcaneAgentsPaths)(sessionName);
53
83
  let configResult;
54
84
  try {
55
85
  configResult = ensureStarterConfig(paths);
@@ -63,7 +93,7 @@ async function runStart() {
63
93
  console.log(`[arcane-agents] no config found; wrote starter config to ${paths.configPath}`);
64
94
  console.log("[arcane-agents] next: edit it with 'arcane-agents config edit'.");
65
95
  }
66
- await (0, bootstrapApp_1.bootstrap)();
96
+ await (0, bootstrapApp_1.bootstrap)(sessionName);
67
97
  return 0;
68
98
  }
69
99
  function runInit(args) {
@@ -235,6 +265,85 @@ Commands:
235
265
  help Show this config help message
236
266
  `);
237
267
  }
268
+ async function runSessions(args) {
269
+ const [subcommand = "list", ...subcommandArgs] = args;
270
+ switch (subcommand) {
271
+ case "list":
272
+ break;
273
+ case "delete":
274
+ return runSessionsDelete(subcommandArgs);
275
+ default:
276
+ console.error(`[arcane-agents] unknown sessions command '${subcommand}'.`);
277
+ console.log("Usage: arcane-agents sessions [list|delete <name>]");
278
+ return 1;
279
+ }
280
+ const defaultPaths = (0, loadConfig_1.getArcaneAgentsPaths)();
281
+ const sessionsDir = node_path_1.default.join(defaultPaths.stateDir, "sessions");
282
+ const defaultDbPath = defaultPaths.dbPath;
283
+ const sessions = [];
284
+ if (node_fs_1.default.existsSync(defaultDbPath)) {
285
+ sessions.push("default");
286
+ }
287
+ if (node_fs_1.default.existsSync(sessionsDir)) {
288
+ try {
289
+ const entries = node_fs_1.default.readdirSync(sessionsDir, { withFileTypes: true });
290
+ for (const entry of entries) {
291
+ if (entry.isDirectory()) {
292
+ const dbPath = node_path_1.default.join(sessionsDir, entry.name, "arcane-agents.db");
293
+ if (node_fs_1.default.existsSync(dbPath)) {
294
+ sessions.push(entry.name);
295
+ }
296
+ }
297
+ }
298
+ }
299
+ catch {
300
+ // no-op
301
+ }
302
+ }
303
+ if (sessions.length === 0) {
304
+ console.log("[arcane-agents] no sessions found.");
305
+ }
306
+ else {
307
+ console.log("[arcane-agents] sessions:");
308
+ for (const session of sessions) {
309
+ console.log(` ${session}`);
310
+ }
311
+ }
312
+ return 0;
313
+ }
314
+ async function runSessionsDelete(args) {
315
+ const name = args[0];
316
+ if (!name) {
317
+ console.error("[arcane-agents] usage: arcane-agents sessions delete <name>");
318
+ return 1;
319
+ }
320
+ if (name === "default") {
321
+ console.error("[arcane-agents] cannot delete the default session.");
322
+ return 1;
323
+ }
324
+ const sessionDir = (0, loadConfig_1.getArcaneAgentsPaths)(name).stateDir;
325
+ if (!node_fs_1.default.existsSync(sessionDir)) {
326
+ console.error(`[arcane-agents] session '${name}' not found.`);
327
+ return 1;
328
+ }
329
+ const answer = await promptConfirm(`Delete session '${name}' and all its data (${sessionDir})? [y/N] `);
330
+ if (!answer) {
331
+ console.log("[arcane-agents] aborted.");
332
+ return 0;
333
+ }
334
+ node_fs_1.default.rmSync(sessionDir, { recursive: true, force: true });
335
+ console.log(`[arcane-agents] deleted session '${name}'.`);
336
+ return 0;
337
+ }
338
+ function promptConfirm(question) {
339
+ const rl = node_readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
340
+ return new Promise((resolve) => {
341
+ rl.question(question, (answer) => {
342
+ rl.close();
343
+ resolve(answer.trim().toLowerCase() === "y");
344
+ });
345
+ });
346
+ }
238
347
  function runDoctor() {
239
348
  const checks = [];
240
349
  const nodeVersion = process.versions.node;
@@ -357,9 +466,10 @@ function printHelp() {
357
466
  console.log(`Arcane Agents CLI
358
467
 
359
468
  Usage:
360
- arcane-agents [start]
469
+ arcane-agents [start] [--session <name>]
361
470
  arcane-agents init [--force]
362
471
  arcane-agents config [path|show|edit]
472
+ arcane-agents sessions [list|delete <name>]
363
473
  arcane-agents doctor
364
474
  arcane-agents --help
365
475
  arcane-agents --version
@@ -368,10 +478,16 @@ Commands:
368
478
  start Start the Arcane Agents server
369
479
  init Write ~/.config/arcane-agents/config.yaml from config.example.yaml
370
480
  config Print, show, or edit config files
481
+ sessions List or delete named sessions
371
482
  doctor Check dependencies and runtime command availability
372
483
  help Show this help message
373
484
  version Print CLI version
374
485
 
486
+ Options:
487
+ --session <name>, -s <name>
488
+ Run with a named session (separate DB and tmux session).
489
+ Default session uses the standard paths for backwards compatibility.
490
+
375
491
  Config paths:
376
492
  primary: ${paths.configPath}
377
493
  local override: ${paths.localOverridePath}
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isNonDefaultSession = isNonDefaultSession;
6
7
  exports.getArcaneAgentsPaths = getArcaneAgentsPaths;
7
8
  exports.loadResolvedConfig = loadResolvedConfig;
8
9
  const node_fs_1 = __importDefault(require("node:fs"));
@@ -10,9 +11,16 @@ const node_path_1 = __importDefault(require("node:path"));
10
11
  const yaml_1 = __importDefault(require("yaml"));
11
12
  const schema_1 = require("./schema");
12
13
  const path_1 = require("../utils/path");
13
- function getArcaneAgentsPaths() {
14
+ function isNonDefaultSession(sessionName) {
15
+ return sessionName !== undefined && sessionName !== "default";
16
+ }
17
+ function getArcaneAgentsPaths(sessionName) {
14
18
  const configDir = (0, path_1.resolveUserPath)("~/.config/arcane-agents");
15
- const stateDir = (0, path_1.resolveUserPath)("~/.local/state/arcane-agents");
19
+ const baseStateDir = (0, path_1.resolveUserPath)("~/.local/state/arcane-agents");
20
+ const isNamedSession = sessionName !== undefined && sessionName !== "default";
21
+ const stateDir = isNamedSession
22
+ ? node_path_1.default.join(baseStateDir, "sessions", sessionName)
23
+ : baseStateDir;
16
24
  return {
17
25
  configDir,
18
26
  configPath: node_path_1.default.join(configDir, "config.yaml"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-agents",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Local-first visual control room for tmux-backed coding agents",
5
5
  "bin": {
6
6
  "arcane-agents": "dist/server/server/cli.js"