botmux 2.100.0 → 2.101.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.en.md +33 -7
  2. package/README.md +33 -7
  3. package/dist/adapters/backend/sandbox.d.ts +33 -4
  4. package/dist/adapters/backend/sandbox.d.ts.map +1 -1
  5. package/dist/adapters/backend/sandbox.js +90 -29
  6. package/dist/adapters/backend/sandbox.js.map +1 -1
  7. package/dist/adapters/cli/hermes.d.ts.map +1 -1
  8. package/dist/adapters/cli/hermes.js +6 -0
  9. package/dist/adapters/cli/hermes.js.map +1 -1
  10. package/dist/bot-registry.d.ts +12 -0
  11. package/dist/bot-registry.d.ts.map +1 -1
  12. package/dist/bot-registry.js +10 -3
  13. package/dist/bot-registry.js.map +1 -1
  14. package/dist/cli.d.ts.map +1 -1
  15. package/dist/cli.js +659 -154
  16. package/dist/cli.js.map +1 -1
  17. package/dist/core/cost-calculator.d.ts.map +1 -1
  18. package/dist/core/cost-calculator.js +26 -36
  19. package/dist/core/cost-calculator.js.map +1 -1
  20. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  21. package/dist/core/dashboard-ipc-server.js +37 -2
  22. package/dist/core/dashboard-ipc-server.js.map +1 -1
  23. package/dist/core/session-manager.d.ts +11 -0
  24. package/dist/core/session-manager.d.ts.map +1 -1
  25. package/dist/core/session-manager.js +87 -17
  26. package/dist/core/session-manager.js.map +1 -1
  27. package/dist/core/worker-pool.d.ts.map +1 -1
  28. package/dist/core/worker-pool.js +6 -0
  29. package/dist/core/worker-pool.js.map +1 -1
  30. package/dist/daemon.d.ts +1 -1
  31. package/dist/daemon.d.ts.map +1 -1
  32. package/dist/daemon.js +24 -8
  33. package/dist/daemon.js.map +1 -1
  34. package/dist/dashboard/bot-onboarding.d.ts +6 -0
  35. package/dist/dashboard/bot-onboarding.d.ts.map +1 -1
  36. package/dist/dashboard/bot-onboarding.js +3 -1
  37. package/dist/dashboard/bot-onboarding.js.map +1 -1
  38. package/dist/dashboard/web/bot-onboarding.d.ts.map +1 -1
  39. package/dist/dashboard/web/bot-onboarding.js +24 -3
  40. package/dist/dashboard/web/bot-onboarding.js.map +1 -1
  41. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  42. package/dist/dashboard/web/i18n.js +14 -4
  43. package/dist/dashboard/web/i18n.js.map +1 -1
  44. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  45. package/dist/dashboard/web/sessions.js +5 -19
  46. package/dist/dashboard/web/sessions.js.map +1 -1
  47. package/dist/dashboard-web/app.js +40 -33
  48. package/dist/dashboard-web/chunks/{bot-defaults-page-3EV7K6LJ.js → bot-defaults-page-HOGLABBC.js} +1 -1
  49. package/dist/dashboard-web/chunks/{chunk-NASYW5FD.js → chunk-ECXPBQ3J.js} +1 -1
  50. package/dist/dashboard-web/chunks/chunk-MR57RUVP.js +31 -0
  51. package/dist/dashboard-web/chunks/{connectors-XPZKEHF6.js → connectors-UGTF4Q4I.js} +1 -1
  52. package/dist/dashboard-web/chunks/{groups-page-C54PVKYP.js → groups-page-IKTCMQ4A.js} +1 -1
  53. package/dist/dashboard-web/chunks/{insights-page-BBQYR7WY.js → insights-page-7CCSHEKM.js} +1 -1
  54. package/dist/dashboard-web/chunks/{overview-page-5PJCAMID.js → overview-page-JOJW65P5.js} +1 -1
  55. package/dist/dashboard-web/chunks/{roles-page-APUDLPVM.js → roles-page-E2VN2DLV.js} +1 -1
  56. package/dist/dashboard-web/chunks/{schedules-WG2UKHSO.js → schedules-6QIHJIHX.js} +1 -1
  57. package/dist/dashboard-web/chunks/sessions-page-QN2TARF2.js +238 -0
  58. package/dist/dashboard-web/chunks/{settings-page-OSBZNCXL.js → settings-page-WWTXB577.js} +1 -1
  59. package/dist/dashboard-web/chunks/{skills-page-Y3VUAOYG.js → skills-page-2FAUPQ6H.js} +1 -1
  60. package/dist/dashboard-web/chunks/{team-federation-page-CRH3WZPH.js → team-federation-page-PTBQQSFS.js} +1 -1
  61. package/dist/dashboard-web/chunks/{workflows-UB7OPYGI.js → workflows-ZNBB5KFV.js} +1 -1
  62. package/dist/dashboard.js +86 -1
  63. package/dist/dashboard.js.map +1 -1
  64. package/dist/i18n/en.d.ts.map +1 -1
  65. package/dist/i18n/en.js +2 -0
  66. package/dist/i18n/en.js.map +1 -1
  67. package/dist/i18n/zh.d.ts.map +1 -1
  68. package/dist/i18n/zh.js +2 -0
  69. package/dist/i18n/zh.js.map +1 -1
  70. package/dist/im/lark/event-dispatcher.d.ts +8 -3
  71. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  72. package/dist/im/lark/event-dispatcher.js +34 -9
  73. package/dist/im/lark/event-dispatcher.js.map +1 -1
  74. package/dist/platform/bind.d.ts.map +1 -1
  75. package/dist/platform/bind.js +36 -15
  76. package/dist/platform/bind.js.map +1 -1
  77. package/dist/platform/binding.d.ts +5 -0
  78. package/dist/platform/binding.d.ts.map +1 -1
  79. package/dist/platform/binding.js +11 -0
  80. package/dist/platform/binding.js.map +1 -1
  81. package/dist/platform/platform-http.d.ts +11 -0
  82. package/dist/platform/platform-http.d.ts.map +1 -0
  83. package/dist/platform/platform-http.js +41 -0
  84. package/dist/platform/platform-http.js.map +1 -0
  85. package/dist/platform/tunnel-client.d.ts +14 -0
  86. package/dist/platform/tunnel-client.d.ts.map +1 -1
  87. package/dist/platform/tunnel-client.js +40 -3
  88. package/dist/platform/tunnel-client.js.map +1 -1
  89. package/dist/services/bot-union-ids-store.d.ts +9 -0
  90. package/dist/services/bot-union-ids-store.d.ts.map +1 -0
  91. package/dist/services/bot-union-ids-store.js +59 -0
  92. package/dist/services/bot-union-ids-store.js.map +1 -0
  93. package/dist/services/coco-transcript.d.ts.map +1 -1
  94. package/dist/services/coco-transcript.js +59 -64
  95. package/dist/services/coco-transcript.js.map +1 -1
  96. package/dist/services/jsonl-cursor.d.ts +12 -0
  97. package/dist/services/jsonl-cursor.d.ts.map +1 -1
  98. package/dist/services/jsonl-cursor.js +63 -4
  99. package/dist/services/jsonl-cursor.js.map +1 -1
  100. package/dist/services/platform-team-store.d.ts +37 -0
  101. package/dist/services/platform-team-store.d.ts.map +1 -0
  102. package/dist/services/platform-team-store.js +120 -0
  103. package/dist/services/platform-team-store.js.map +1 -0
  104. package/dist/services/team-groups-store.d.ts +14 -1
  105. package/dist/services/team-groups-store.d.ts.map +1 -1
  106. package/dist/services/team-groups-store.js +28 -1
  107. package/dist/services/team-groups-store.js.map +1 -1
  108. package/dist/setup/bot-config-editor.d.ts +6 -0
  109. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  110. package/dist/setup/bot-config-editor.js +18 -15
  111. package/dist/setup/bot-config-editor.js.map +1 -1
  112. package/dist/setup/interactive-select.d.ts +22 -0
  113. package/dist/setup/interactive-select.d.ts.map +1 -1
  114. package/dist/setup/interactive-select.js +101 -7
  115. package/dist/setup/interactive-select.js.map +1 -1
  116. package/dist/setup/open-platform-automation.d.ts +42 -0
  117. package/dist/setup/open-platform-automation.d.ts.map +1 -1
  118. package/dist/setup/open-platform-automation.js +107 -0
  119. package/dist/setup/open-platform-automation.js.map +1 -1
  120. package/dist/setup/setup-args.d.ts +77 -0
  121. package/dist/setup/setup-args.d.ts.map +1 -0
  122. package/dist/setup/setup-args.js +272 -0
  123. package/dist/setup/setup-args.js.map +1 -0
  124. package/dist/types.d.ts +6 -0
  125. package/dist/types.d.ts.map +1 -1
  126. package/dist/worker.js +2 -0
  127. package/dist/worker.js.map +1 -1
  128. package/package.json +1 -1
  129. package/dist/dashboard-web/chunks/chunk-JXLHRRJD.js +0 -31
  130. package/dist/dashboard-web/chunks/sessions-page-F3U5NFPD.js +0 -238
@@ -1,238 +0,0 @@
1
- import{b as Ut}from"./chunk-NASYW5FD.js";import{a as O}from"./chunk-76J7PNZI.js";import{b as Nt,c as Bt,d as Pt,e as Qe,f as Rt,g as et,h as qt,i as Dt,l as we,m as i,n as l,o as ue,p as ae,q as Ot,r as Kt,t as F,u as pe,v as j,y as Pe}from"./chunk-JXLHRRJD.js";import{a as xn,b as _t,c as jt}from"./chunk-JTSE5VP6.js";import{c as Ze}from"./chunk-7SZRL6FY.js";var De=Ze(xn(),1);var Re=[24,72,168],Nn=new Set(Re);function tt(o){let u=o==="7d"?168:Number(o);return!Number.isFinite(u)||!Nn.has(u)?null:u}function Bn(o){let u=Number(o);return Number.isFinite(u)&&u>0?u:null}function Pn(o,u=Date.now()){return u-o*60*60*1e3}function Rn(o,u,b=Date.now()){if(!o.sessionId||o.status!=="idle"||o.locked||o.pendingRepo||o.tuiPromptActive||o.agentAttention)return!1;let f=Bn(o.lastMessageAt);return f!==null&&f<Pn(u,b)}function Ft(o,u,b=Date.now()){return o.filter(f=>Rn(f,u,b))}var zt=["backlog","todo","in_progress","in_review","done"];function Wt(o){return typeof o=="string"&&zt.includes(o)?o:null}function Vt(o){if(o.status==="closed")return"done";let u=Wt(o.kanbanColumn);return u||(o.pendingRepo||o.tuiPromptActive||o.agentAttention||o.status==="limited"?"in_review":o.status==="starting"||o.status==="working"||o.status==="analyzing"||o.status==="active"?"in_progress":"todo")}var qn=1e15;function oe(o){if(typeof o.kanbanPosition=="number"&&Number.isFinite(o.kanbanPosition))return o.kanbanPosition;let u=typeof o.lastMessageAt=="number"&&Number.isFinite(o.lastMessageAt)?o.lastMessageAt:0;return qn-u}function nt(o,u){return o!==null&&u!==null?(o+u)/2:o!==null?o+1024:u!==null?u-1024:1024}function it(o){return typeof o=="number"&&Number.isFinite(o)?o:null}function Gt(o){let u=it(o);return u===null?"-":u.toLocaleString("en-US")}var en=["claude-code","seed","relay","codex","codex-app","cursor","gemini","opencode","mtr","hermes","mira","pi","copilot","aiden","coco","oh-my-pi","unknown"],Jt=[{id:"needs-you",labelKey:"sessions.board.needsYou",hintKey:"sessions.board.needsYouHint"},{id:"starting",labelKey:"sessions.board.starting",hintKey:"sessions.board.startingHint"},{id:"working",labelKey:"sessions.board.working",hintKey:"sessions.board.workingHint"},{id:"idle",labelKey:"sessions.board.idle",hintKey:"sessions.board.idleHint"}],Yt=[{id:"backlog",labelKey:"sessions.kanban.backlog"},{id:"todo",labelKey:"sessions.kanban.todo"},{id:"in_progress",labelKey:"sessions.kanban.inProgress"},{id:"in_review",labelKey:"sessions.kanban.inReview"},{id:"done",labelKey:"sessions.kanban.done"}],st=50;function Dn(o){let u=(b="")=>`<svg viewBox="0 0 14 14" aria-hidden="true"><circle cx="7" cy="7" r="5.4" fill="none" stroke="currentColor" stroke-width="1.6"${b}/>`;switch(o){case"backlog":return`${u(' stroke-dasharray="1.6 2.1"')}</svg>`;case"in_progress":return`${u()}<path d="M7,7 L7,3.6 A3.4,3.4 0 0 1 7,10.4 Z" fill="currentColor"/></svg>`;case"in_review":return`${u()}<path d="M7,7 L7,3.6 A3.4,3.4 0 1 1 3.6,7 Z" fill="currentColor"/></svg>`;case"done":return'<svg viewBox="0 0 14 14" aria-hidden="true"><circle cx="7" cy="7" r="6.2" fill="currentColor"/><path d="M4.4 7.2 6.2 9 9.7 5.4" fill="none" stroke="var(--surface)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>';default:return`${u()}</svg>`}}function qe(o){return String(o??"unknown").toLowerCase().replace(/[^a-z0-9_-]/g,"-")}var tn=["starting","working","idle","dormant","analyzing","active","limited","closed"];function Me(o){let u=String(o??"unknown"),b=`sessions.status.${u}`,f=i(b);return f===b?u:f}function Se(o){let u=String(o??"unknown");return`<span class="status status-${l(qe(u))}">${l(Me(u))}</span>`}function Xt(o){let u=String(o??"").trim();return u?u.replace(/\\/g,"/").split("/").filter(Boolean).at(-1)??u:"-"}function at(o){if(!o.webPort)return null;if(location.protocol==="https:")return o.proxyPort?`${location.origin}/s/${encodeURIComponent(o.sessionId)}`:null;let u=o.proxyPort??o.webPort,b=o.proxyPort?`/s/${encodeURIComponent(o.sessionId)}`:"";return`http://${location.hostname}:${u}${b}`}var C={pin:'<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M8 14.3s4.2-3.9 4.2-7.3A4.2 4.2 0 0 0 8 2.9a4.2 4.2 0 0 0-4.2 4.1C3.8 10.4 8 14.3 8 14.3z"/><circle cx="8" cy="6.9" r="1.5"/></svg>',openChat:'<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M9.4 2.8h3.8v3.8"/><path d="M13.2 2.8 7.3 8.7"/><path d="M11.5 9.3v2.9a1.2 1.2 0 0 1-1.2 1.2H3.8a1.2 1.2 0 0 1-1.2-1.2V5.7a1.2 1.2 0 0 1 1.2-1.2h2.9"/></svg>',details:'<svg viewBox="0 0 16 16" aria-hidden="true"><rect x="2.2" y="2.9" width="11.6" height="10.2" rx="1.8"/><path d="M9.9 2.9v10.2"/></svg>',terminal:'<svg viewBox="0 0 16 16" aria-hidden="true"><rect x="1.7" y="2.7" width="12.6" height="10.6" rx="2"/><path d="M4.4 6.3 6.4 8.1 4.4 9.9"/><path d="M8.2 10.2h3.4"/></svg>',key:'<svg viewBox="0 0 16 16" aria-hidden="true"><circle cx="6" cy="6.1" r="3"/><path d="M8.1 8.2 13 13.1"/><path d="M11.3 11.4 12.6 10.1"/><path d="M12.7 12.8 13.7 11.8"/></svg>',lock:'<svg viewBox="0 0 16 16" aria-hidden="true"><rect x="3.2" y="7" width="9.6" height="6.2" rx="1.4"/><path d="M5.2 7V5.2a2.8 2.8 0 0 1 5.6 0V7"/></svg>',unlock:'<svg viewBox="0 0 16 16" aria-hidden="true"><rect x="3.2" y="7" width="9.6" height="6.2" rx="1.4"/><path d="M5.2 7V5.2a2.8 2.8 0 0 1 5.2-1.4"/></svg>',close:'<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M4.2 4.2 11.8 11.8"/><path d="M11.8 4.2 4.2 11.8"/></svg>',edit:'<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M10.7 3.3 12.7 5.3 6.3 11.7 3.7 12.3 4.3 9.7 10.7 3.3z"/></svg>',history:'<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M2.2 4.8a2 2 0 0 1 2-2h7.6a2 2 0 0 1 2 2v4.6a2 2 0 0 1-2 2H6.6l-2.9 2.4v-2.4h-.5a2 2 0 0 1-2-2z"/><path d="M5.2 6.2h5.6M5.2 8.4h3.6"/></svg>',restart:'<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M12.4 6.2a4.8 4.8 0 1 0 1.1 3.1"/><path d="M12.4 2.9v3.3H9.1"/></svg>',feishu:'<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M2.6 4.4C6.4 4 10.4 5.4 13.4 8.2 9.8 7 6.4 6.6 3.4 7.4"/><path d="M13.4 8.2C9.6 8.7 6 10 2.9 12 5.6 9 8.8 7.6 13.4 8.2"/></svg>'};function Te(o,u,b,f=""){return`<button type="button" class="card-act${f?" "+f:""}" data-action="${o}" title="${l(b)}" aria-label="${l(b)}">${u}</button>`}function Le(o){return o.locked?i("sessions.unlock"):i("sessions.lock")}function Ce(o){return o.locked?`<span class="session-lock-badge" title="${l(i("sessions.locked"))}">${l(i("sessions.locked"))}</span>`:""}function Zt(o){if(!o)return"";let u=`<a class="term-btn term-open" href="${l(o)}" target="_blank" rel="noopener" title="${l(i("sessions.openTerminal"))}" aria-label="${l(i("sessions.openTerminal"))}">${C.terminal}</a>`;if(!we.authed)return`<span class="term-pill solo">${u}</span>`;let b=`<button type="button" class="term-btn term-write" data-action="write-link" title="${l(i("sessions.writeLinkHint"))}" aria-label="${l(i("sessions.writeLink"))}">${C.key}</button>`;return`<span class="term-pill">${u}${b}</span>`}async function Qt(o,u){let b=window.open("about:blank","_blank");b&&(b.opener=null),u&&(u.disabled=!0);try{let f=await fetch(`/api/sessions/${encodeURIComponent(o.sessionId)}/write-link`),v=await f.json().catch(()=>({}));if(!f.ok||v?.ok===!1||!v?.url){b?.close(),f.status!==401&&alert(`${i("sessions.writeLinkFail")}: ${v?.error??f.status}`);return}b?b.location.href=v.url:window.open(v.url,"_blank","noopener")}catch(f){b?.close(),alert(`${i("sessions.writeLinkFail")}: ${f}`)}finally{u&&(u.disabled=!1)}}function On(o){return o.status==="closed"?null:o.pendingRepo||o.tuiPromptActive||o.agentAttention||o.status==="limited"?"needs-you":o.status==="starting"?"starting":o.status==="working"||o.status==="analyzing"||o.status==="active"?"working":(o.status==="dormant","idle")}function nn(){return`<details class="filter-cli">
2
- <summary>${i("sessions.cli")} \xB7 <b id="cli-filter-count">${i("common.all")}</b></summary>
3
- <div class="filter-cli-pop" role="group" aria-label="${i("sessions.cli")}">
4
- ${en.map(o=>`
5
- <label class="filter-check">
6
- <input type="checkbox" name="cli" value="${l(o)}" checked>
7
- <span>${l(o)}</span>
8
- </label>
9
- `).join("")}
10
- </div>
11
- </details>`}function Kn(o){let u=String(o.status??"unknown"),b=String(o.cliId??"unknown"),f=we.locale==="zh"?"\uFF1A":": ";return[i("sessions.restartConfirmIntro"),"",`${i("sessions.restartConfirmStatus")}${f}${Me(u)}`,`${i("sessions.restartConfirmCli")}${f}${b}`,"",i("sessions.restartConfirmQuestion")].join(`
12
- `)}function ot(o){return o.status!=="closed"&&!o.adopt&&!o.pendingRepo}async function _n(){try{let o=await fetch("/api/groups");if(!o.ok)return[];let u=await o.json();return(Array.isArray(u?.bots)?u.bots:[]).filter(f=>f&&typeof f.larkAppId=="string").map(f=>({larkAppId:f.larkAppId,botName:typeof f.botName=="string"&&f.botName?f.botName:f.larkAppId}))}catch{return[]}}function jn(o){let u=o.map(b=>`
13
- <label class="cs-bot"><input type="checkbox" name="bot" value="${l(b.larkAppId)}"> <span>${l(b.botName)}</span></label>`).join("");return`
14
- <article class="cs-card">
15
- <header><h3>${i("sessions.create.title")}</h3></header>
16
- <form id="cs-form">
17
- <label class="form-row">
18
- <span>${i("sessions.create.content")}</span>
19
- <textarea name="content" rows="5" placeholder="${l(i("sessions.create.contentPlaceholder"))}" required></textarea>
20
- </label>
21
- <fieldset class="cs-bots">
22
- <legend>${i("sessions.create.bots")}</legend>
23
- ${u||`<p class="cs-empty">${i("sessions.create.noBots")}</p>`}
24
- </fieldset>
25
- <fieldset class="cs-mode">
26
- <legend>${i("sessions.create.mode")}</legend>
27
- <label><input type="radio" name="mode" value="lead" checked> ${i("sessions.create.modeLead")}</label>
28
- <label><input type="radio" name="mode" value="all"> ${i("sessions.create.modeAll")}</label>
29
- <small>${i("sessions.create.modeHelp")}</small>
30
- </fieldset>
31
- <div class="cs-lead-row form-row" hidden>
32
- <span>${i("sessions.create.lead")}</span>
33
- <select name="lead"></select>
34
- <small>${i("sessions.create.leadHelp")}</small>
35
- </div>
36
- <fieldset class="cs-column">
37
- <legend>${i("sessions.create.column")}</legend>
38
- <label><input type="radio" name="column" value="in_progress" checked> ${i("sessions.create.columnInProgress")}</label>
39
- <label><input type="radio" name="column" value="backlog"> ${i("sessions.create.columnBacklog")}</label>
40
- <small>${i("sessions.create.columnHelp")}</small>
41
- </fieldset>
42
- <details class="cs-advanced">
43
- <summary>${i("sessions.create.advanced")}</summary>
44
- <label class="form-row">
45
- <span>${i("sessions.create.groupName")}</span>
46
- <input type="text" name="name" maxlength="60" placeholder="${l(i("sessions.create.groupNamePlaceholder"))}">
47
- </label>
48
- <label class="form-row">
49
- <span>${i("sessions.create.workingDir")}</span>
50
- <input type="text" name="bindWorkingDir" placeholder="e.g. ~/projects/foo">
51
- <small>${i("sessions.create.workingDirHelp")}</small>
52
- </label>
53
- </details>
54
- <div class="actions cs-actions">
55
- <button type="submit" class="primary">${i("sessions.create.submit")}</button>
56
- <button type="button" id="cs-cancel">${i("sessions.create.cancel")}</button>
57
- </div>
58
- </form>
59
- </article>`}function Un(o,u){u.onclick=async()=>{u.disabled=!0;try{let b=await _n();if(b.length===0){alert(i("sessions.create.noBots"));return}o.innerHTML=jn(b),o.showModal(),Fn(o,b)}finally{u.disabled=!1}}}function Fn(o,u){let b=o.querySelector("#cs-form"),f=o.querySelector(".cs-lead-row"),v=o.querySelector("select[name=lead]"),me=H=>u.find(R=>R.larkAppId===H)?.botName??H,ie=()=>Array.from(b.querySelectorAll("input[name=bot]:checked")).map(H=>H.value),W=()=>b.querySelector("input[name=mode]:checked")?.value??"all";function G(){let H=ie(),R=v.value;if(H.length===0){v.innerHTML=`<option value="" disabled selected>${l(i("sessions.create.leadPickFirst"))}</option>`,v.disabled=!0;return}v.disabled=!1,v.innerHTML=H.map(J=>`<option value="${l(J)}">${l(me(J))}</option>`).join(""),H.includes(R)&&(v.value=R)}function U(){W()==="lead"?(f.hidden=!1,G()):f.hidden=!0}b.querySelectorAll("input[name=mode]").forEach(H=>H.addEventListener("change",U)),b.querySelectorAll("input[name=bot]").forEach(H=>H.addEventListener("change",()=>{W()==="lead"&&G()})),U(),o.querySelector("#cs-cancel").onclick=()=>o.close(),b.onsubmit=async H=>{H.preventDefault();let R=new FormData(b),J=(R.get("content")??"").trim(),Q=ie(),be=W(),ee=R.get("column")??"in_progress",fe=(R.get("name")??"").trim(),q=(R.get("bindWorkingDir")??"").trim(),T=R.get("lead")??"";if(!J){alert(i("sessions.create.errContent"));return}if(Q.length===0){alert(i("sessions.create.errNoBot"));return}if(be==="lead"&&(!T||!Q.includes(T))){alert(i("sessions.create.errLead"));return}let $=b.querySelector("button[type=submit]");$&&($.disabled=!0,$.textContent=i("sessions.create.submitting"));try{let S=await fetch("/api/sessions/create",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({content:J,larkAppIds:Q,mode:be,column:ee,leadLarkAppId:be==="lead"?T:void 0,name:fe||void 0,bindWorkingDir:q||void 0})}),te=await S.json().catch(()=>null);S.ok&&te?.ok?zn(o,te):S.status!==401&&(alert(`${i("sessions.create.failed")}: ${te?.error??S.status}`),$&&($.disabled=!1,$.textContent=i("sessions.create.submit")))}catch(S){alert(`${i("sessions.create.failed")}: ${S}`),$&&($.disabled=!1,$.textContent=i("sessions.create.submit"))}}}function zn(o,u){let b=typeof u.shareLink=="string"&&u.shareLink?u.shareLink:"",f=Array.isArray(u.failed)?u.failed.length:0,v=Array.isArray(u.spawned)?u.spawned.length:0,me=u.column==="backlog"?i("sessions.create.doneBacklog"):i("sessions.create.doneInProgress"),ie=f>0?`<p class="cs-warn">${i("sessions.create.partialFail",{n:String(f)})}</p>`:"";o.innerHTML=`
60
- <article class="cs-card">
61
- <header><h3>${i("sessions.create.doneTitle")}</h3></header>
62
- <p>${l(me)}\uFF08${v}\uFF09</p>
63
- ${ie}
64
- ${b?`<p><a href="${l(b)}" target="_blank" rel="noopener">${i("sessions.create.openChat")}</a></p>`:""}
65
- <div class="actions"><button type="button" id="cs-done" class="primary">${i("sessions.create.close")}</button></div>
66
- </article>`,o.querySelector("#cs-done").onclick=()=>o.close()}function sn(o){let u=o.querySelector("#sessions-table tbody"),b=o.querySelector("#filters"),f=o.querySelector("#drawer"),v=o.querySelector("#select-all"),me=o.querySelector("#bulk-bar"),ie=o.querySelector("#bulk-count"),W=o.querySelector("#bulk-lock"),G=o.querySelector("#bulk-unlock"),U=o.querySelector("#bulk-close"),H=o.querySelector("#bulk-clear"),R=o.querySelector("#idle-cleanup-bar"),J=o.querySelector("#idle-cleanup-threshold"),Q=o.querySelector("#idle-cleanup-run"),be=o.querySelector("#idle-cleanup-count"),ee=o.querySelector("#idle-cleanup-status"),fe=o.querySelector("#sessions-table"),q=o.querySelector("#sessions-board"),T=o.querySelector("#sessions-kanban"),$=o.querySelector("#term-modal"),S=o.querySelector("#history-modal"),te=o.querySelector("#kanban-groupby"),K=o.querySelector("#kanban-team"),rt=o.querySelector("#kanban-team-stats"),lt=o.querySelectorAll(".sessions-view-toggle [data-view]"),an=o.querySelector("#create-session-btn"),ct=o.querySelector("#create-session-modal");Un(ct,an);let w=new Set,ge="lastMessageAt",re="desc",E=Bt(window.localStorage),_=Pt(window.localStorage),Y=null,Oe="",Ke="",L="",dt=!1,ne=null,_e=!1,he=null,le=new Map,D=qt(window.localStorage),se=null,Ie=null,B=[],je=null,He=!1,Ue=!1,X=(()=>{try{return window.localStorage.getItem(et)??""}catch{return""}})(),Ee=!1,Ae=24;function ut(){return Ae}function on(e){return e===168?"7d":`${e}H`}function pt(e){let t=new Set;if(!e)return t;for(let a of e.groupChats)t.add(a);if(je)for(let[a,n]of je){if(t.has(a))continue;let s=!1;for(let r of e.botIds)if(n.botIds.has(r)){s=!0;break}if(s){for(let r of n.observedNames)if(e.botNames.has(r)){t.add(a);break}}}return t}function rn(){let e=ye();if(E==="kanban"&&D==="team"){let t=B.find(n=>n.key===X)??B[0],a=pt(t);return e.filter(n=>a.has(String(n.chatId)))}return e}function mt(){return Ft(rn(),ut())}function ln(){J.querySelectorAll("button[data-hours]").forEach(e=>{let t=tt(e.dataset.hours)===Ae;e.classList.toggle("active",t),e.setAttribute("aria-pressed",t?"true":"false")})}async function cn(){if(!(Ue||He)){Ue=!0;try{let[e,t,a]=await Promise.all([fetch("/api/team/hosted").then(r=>r.json()).catch(()=>null),fetch("/api/team/remote-roster").then(r=>r.json()).catch(()=>null),fetch("/api/groups").then(r=>r.json()).catch(()=>null)]);Array.isArray(a?.chats)&&(je=new Map(a.chats.map(r=>[String(r.chatId),{botIds:new Set((r.memberBots??[]).filter(c=>c.inChat).map(c=>String(c.larkAppId))),observedNames:new Set((r.observedBotNames??[]).map(c=>String(c)))}])));let n=r=>({ids:new Set(r.map(c=>String(c.larkAppId))),names:new Set(r.map(c=>String(c.name??"")).filter(Boolean))}),s=[];for(let r of e?.teams??[]){let{ids:c,names:d}=n(r.bots??[]);s.push({key:`local:${r.teamId}`,label:r.isDefault?i("team.myHostedTeam"):String(r.name??r.teamId),botIds:c,botNames:d,groupChats:new Set((r.groupChatIds??[]).map(m=>String(m)))})}for(let r of t?.memberships??[]){let{ids:c,names:d}=n(r.roster?.bots??[]);s.push({key:`${r.hubUrl}::${r.teamId}`,label:String(r.teamName??r.teamId??r.hubUrl),botIds:c,botNames:d,groupChats:new Set})}B=s}finally{He=!0,Ue=!1}B.length&&!B.some(e=>e.key===X)&&(X=B[0].key),delete K.dataset.loading,K.disabled=B.length===0,K.innerHTML=B.length?B.map(e=>`<option value="${l(e.key)}"${e.key===X?" selected":""}>${l(e.label)}</option>`).join(""):`<option value="">${l(i("sessions.kanban.noTeam"))}</option>`,L="",h()}}let A=null,ce="",bt=0,Fe=!1,ze=new Map;async function dn(e){let t=ce===e.key&&Date.now()-bt<3e4;if(!(Fe||t)){Fe=!0;try{let n=e.key.startsWith("local:")?`/api/team/board/local/${encodeURIComponent(e.key.slice(6))}`:`/api/team/remote-board?key=${encodeURIComponent(e.key)}`,s=await fetch(n),r=await s.json().catch(()=>({}));if(!s.ok||r?.ok===!1)return;let c=typeof r.deploymentId=="string"?r.deploymentId:null,d=[];ze=new Map;for(let m of Array.isArray(r.reports)?r.reports:[])if(!(c&&m.deploymentId===c))for(let g of Array.isArray(m.sessions)?m.sessions:[]){let M={...g,remoteDeployment:m.deploymentName||m.deploymentId};d.push(M),ze.set(String(g.sessionId),M)}A={board:r.board&&typeof r.board=="object"?r.board:{},remoteRows:d},ce=e.key,bt=Date.now(),L="",h()}catch{}finally{Fe=!1}}}async function un(e,t,a,n,s){try{let c=e.startsWith("local:")?await fetch(`/api/team/board/local/${encodeURIComponent(e.slice(6))}/move`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({sessionId:t,column:a,position:n})}):await fetch("/api/team/remote-board-move",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({key:e,sessionId:t,column:a,position:n})}),d=await c.json().catch(()=>({}));(!c.ok||d?.ok===!1)&&(A&&(s?A.board[t]=s:delete A.board[t]),L="",h(),c.status!==401&&alert(`${i("sessions.kanban.moveFail")}: ${d?.error??c.status}`))}catch(r){A&&(s?A.board[t]=s:delete A.board[t]),L="",h(),alert(`${i("sessions.kanban.moveFail")}: ${r}`)}}function ft(e,t,a){let n=B.find(r=>r.key===X)??B[0];if(!n)return;(!A||ce!==n.key)&&(A={board:{},remoteRows:A?.remoteRows??[]},ce=n.key);let s=A.board[e];A.board[e]={column:t,position:a},un(n.key,e,t,a,s)}function pn(){return _.map(e=>Jt.find(t=>t.id===e)).filter(e=>!!e)}function mn(e,t){let a=_.indexOf(e),n=a+t;if(a<0||n<0||n>=_.length)return;let s=[..._];s.splice(a,1),s.splice(n,0,e),_=s,Qe(window.localStorage,_),h()}function bn(e,t){if(e===t)return;let a=_.indexOf(e),n=_.indexOf(t);if(a<0||n<0)return;let s=[..._];s.splice(a,1),s.splice(n,0,e),_=s,Qe(window.localStorage,_),h()}function fn(e){let t=e.status==="closed",a=w.has(e.sessionId)?"checked":"";return`<tr data-id="${l(e.sessionId)}">
67
- <td><input type="checkbox" class="row-select" ${a} ${t?"disabled":""}></td>
68
- <td>${l(F(e))}</td>
69
- <td><span class="badge cli-${qe(e.cliId)}">${l(e.cliId??"unknown")}</span></td>
70
- <td>${Se(e.status)}${Ce(e)}</td>
71
- <td class="token-cell">${Gt(e.tokenUsage?.in)}</td>
72
- <td class="token-cell">${Gt(e.tokenUsage?.out)}</td>
73
- <td title="${l(String(e.title??""))}">${l(j(e.title??"").slice(0,48))}</td>
74
- <td title="${l(e.workingDir??"")}">${l((e.workingDir??"").slice(-34))}</td>
75
- <td>${ue(e.spawnedAt)}</td>
76
- <td>${ue(e.lastMessageAt)}</td>
77
- <td>${e.adopt?'<span class="badge">adopt</span>':""}</td>
78
- <td><button class="open" type="button">${i("sessions.details")}</button></td>
79
- </tr>`}function gt(e){if(e.scope!=="chat"||!e.feishuChatLink)return null;let t=i("sessions.openChat");return`<a class="card-act" href="${l(e.feishuChatLink)}" target="_blank" rel="noopener" title="${l(t)}" aria-label="${l(t)}">${C.feishu}</a>`}function ht(e){return e.agentAttention?.reason?e.agentAttention.reason:e.agentAttention?i("sessions.board.signalAgent"):e.pendingRepo?i("sessions.board.signalRepo"):e.tuiPromptActive?i("sessions.board.signalPrompt"):e.status==="limited"?i("sessions.board.signalLimited"):""}function gn(e){let t=w.has(e.sessionId),a=j(e.title)||e.sessionId,n=F(e),s=pe(e),r=at(e),c=ht(e),d=Xt(e.workingDir);return`<article class="session-card${t?" selected":""}${e.locked?" locked":""}" data-id="${l(e.sessionId)}" aria-pressed="${t}">
80
- <div class="session-card-top">
81
- ${ae({name:n,larkAppId:e.larkAppId,size:"sm"})}
82
- <div class="session-card-title">
83
- <strong title="${l(String(e.title??a))}">${l(String(a).slice(0,72))}</strong>
84
- <span>${l(n)} \xB7 ${l(s??e.cliId??"unknown")}</span>
85
- </div>
86
- <span class="session-card-status-group">
87
- ${Se(e.status)}
88
- ${Ce(e)}
89
- </span>
90
- </div>
91
- ${d!=="-"||e.adopt?`<div class="session-card-meta">
92
- ${d!=="-"?`<span title="${l(e.workingDir??"")}">${l(d)}</span>`:""}
93
- ${e.adopt?'<span class="badge">adopt</span>':""}
94
- </div>`:""}
95
- <div class="session-card-time">
96
- <span>${e.agentAttention?.at?`${l(i("sessions.board.waiting"))} ${ue(Pe(e))}`:`${l(i("sessions.last"))}: ${ue(e.lastMessageAt)}`}</span>
97
- </div>
98
- ${c?`<div class="session-signal" title="${l(c)}">${l(c)}</div>`:""}
99
- <div class="session-card-actions">
100
- ${gt(e)??Te("locate",C.pin,i("sessions.locate"))}
101
- ${Te("details",C.details,i("sessions.details"))}
102
- ${ot(e)?Te("restart",C.restart,i("sessions.restart")):""}
103
- ${Zt(r)}
104
- ${Te("lock",e.locked?C.unlock:C.lock,Le(e),e.locked?"locked":"")}
105
- ${Te("close",C.close,i("sessions.close"),"danger")}
106
- </div>
107
- </article>`}function hn(e,t,a){let n=a==="needs-you"?Pe(e):Number(e.lastMessageAt??0),s=a==="needs-you"?Pe(t):Number(t.lastMessageAt??0);return n!==s?a==="needs-you"?n-s:s-n:String(e.title??e.sessionId).localeCompare(String(t.title??t.sessionId))}function yn(e){let t=new Map(Jt.map(s=>[s.id,[]]));for(let s of e){let r=On(s);r&&t.get(r).push(s)}let a=pn(),n=a.map((s,r)=>{let c=(t.get(s.id)??[]).sort((d,m)=>hn(d,m,s.id));return`<section class="session-board-column session-board-${s.id}" data-col="${s.id}">
108
- <header draggable="true" title="${l(i("sessions.board.dragHint"))}">
109
- <div>
110
- <h2>${l(i(s.labelKey))}</h2>
111
- <p>${l(i(s.hintKey))}</p>
112
- </div>
113
- <span class="session-board-head-right">
114
- <span class="session-board-move">
115
- <button type="button" data-move-col="${s.id}" data-dir="-1"
116
- aria-label="${l(i("sessions.board.moveLeft"))}" ${r===0?"disabled":""}>\u2039</button>
117
- <button type="button" data-move-col="${s.id}" data-dir="1"
118
- aria-label="${l(i("sessions.board.moveRight"))}" ${r===a.length-1?"disabled":""}>\u203A</button>
119
- </span>
120
- <span class="session-board-count">${c.length}</span>
121
- </span>
122
- </header>
123
- <div class="session-board-list">
124
- ${c.length?c.map(gn).join(""):`<div class="session-board-empty">${i("sessions.board.emptyColumn")}</div>`}
125
- </div>
126
- </section>`}).join("");n!==Oe&&(Oe=n,q.innerHTML=n,q.classList.toggle("board-enter",!dt),dt=!0)}function yt(e){let t=j(e.title)||e.sessionId,a=F(e),n=pe(e),s=Xt(e.workingDir),r=ht(e),c=[n,s!=="-"?s:null].filter(Boolean).join(" \xB7 "),d=String(e.status??"unknown"),m=typeof e.remoteDeployment=="string"?e.remoteDeployment:"";return`<article class="kanban-card${m?" kanban-card-remote":""}${e.locked?" locked":""}" data-id="${l(e.sessionId)}" tabindex="0" role="button" draggable="true">
127
- <div class="kanban-card-top">
128
- <span class="badge cli-${qe(e.cliId)}">${l(e.cliId??"unknown")}</span>
129
- ${e.adopt?'<span class="badge">adopt</span>':""}
130
- ${Ce(e)}
131
- ${m?`<span class="badge kanban-remote-badge" title="${l(i("sessions.kanban.remoteHint",{name:m}))}">${l(m)}</span>`:""}
132
- <span class="kanban-card-top-right">
133
- <span class="kanban-card-dot" data-status="${l(qe(d))}" title="${l(Me(d))}"></span>
134
- ${m?"":`<button type="button" class="card-act kanban-card-act" data-action="history" title="${l(i("sessions.history.title"))}" aria-label="${l(i("sessions.history.title"))}">${C.history}</button>
135
- ${e.feishuChatLink?`<a class="card-act kanban-card-act" href="${l(e.feishuChatLink)}" target="_blank" rel="noopener" title="${l(i("sessions.kanban.openFeishu"))}" aria-label="${l(i("sessions.kanban.openFeishu"))}">${C.feishu}</a>`:""}
136
- <button type="button" class="card-act kanban-card-act${e.locked?" locked":""}" data-action="lock" title="${l(Le(e))}" aria-label="${l(Le(e))}">${e.locked?C.unlock:C.lock}</button>
137
- <button type="button" class="card-act kanban-card-act" data-action="details" title="${l(i("sessions.details"))}" aria-label="${l(i("sessions.details"))}">${C.details}</button>
138
- ${ot(e)?`<button type="button" class="card-act kanban-card-act" data-action="restart" title="${l(i("sessions.restart"))}" aria-label="${l(i("sessions.restart"))}">${C.restart}</button>`:""}`}
139
- </span>
140
- </div>
141
- <p class="kanban-card-title" title="${l(String(e.title??t))}">${l(String(t).slice(0,140))}</p>
142
- ${c?`<p class="kanban-card-desc" title="${l(c)}">${l(c)}</p>`:""}
143
- ${r?`<div class="session-signal" title="${l(r)}">${l(r)}</div>`:""}
144
- <div class="kanban-card-foot">
145
- <span class="kanban-card-owner">${ae({name:a,larkAppId:e.larkAppId,size:"sm"})}<span>${l(a)}</span></span>
146
- <span class="kanban-card-updated">${l(i("sessions.kanban.updated",{time:ue(e.lastMessageAt)}))}</span>
147
- </div>
148
- </article>`}function kt(e){let t=[],a=new Map;for(let r of e){let c=String(r.chatId??r.sessionId),d=a.get(c);d||(d={chatId:c,rows:[]},a.set(c,d),t.push(d)),d.rows.push(r)}let n=[];return{html:t.map(r=>{if(n.push(...r.rows),r.rows.length<2)return yt(r.rows[0]);let c=pe(r.rows[0])??r.chatId;return`<div class="kanban-cluster" data-chat="${l(r.chatId)}">
149
- <header draggable="true" title="${l(c)} \xB7 ${l(i("sessions.kanban.clusterDragHint"))}">
150
- ${Ot({chatId:r.chatId,name:c,size:"sm"})}
151
- <span class="kanban-cluster-name">${l(c)}</span>
152
- <span class="kanban-cluster-count">${r.rows.length}</span>
153
- </header>
154
- ${r.rows.map(yt).join("")}
155
- </div>`}).join(""),flat:n}}function kn(e){let t=new Map;for(let n of e){if(n.status==="closed")continue;let s=String(n.larkAppId||n.botName||"unknown"),r=t.get(s);r||(r={name:F(n),larkAppId:n.larkAppId,rows:[]},t.set(s,r)),r.rows.push(n)}let a=[...t.values()].sort((n,s)=>n.name.localeCompare(s.name));return a.length?a.map(n=>{let s=n.rows.sort((c,d)=>Number(d.lastMessageAt??0)-Number(c.lastMessageAt??0)),{html:r}=kt(s);return`<section class="kanban-column kanban-bot-col" data-bot="${l(n.larkAppId??n.name)}">
156
- <header>
157
- <span class="kanban-col-avatar">${ae({name:n.name,larkAppId:n.larkAppId,size:"sm"})}</span>
158
- <h2>${l(n.name)}</h2>
159
- <span class="kanban-col-count">${s.length}</span>
160
- </header>
161
- <div class="kanban-col-list">${r}</div>
162
- </section>`}).join(""):`<div class="kanban-col-empty">${i("sessions.board.emptyColumn")}</div>`}function $t(e){let t=new Map(Yt.map(n=>[n.id,[]]));for(let n of e)t.get(Vt(n)).push(n);let a=Yt.map(n=>{let s=(t.get(n.id)??[]).sort((m,g)=>oe(m)-oe(g)),r=0;n.id==="done"&&s.length>st&&(r=s.length-st,s=s.slice(0,st));let{html:c,flat:d}=kt(s);return t.set(n.id,d),`<section class="kanban-column kanban-${n.id}" data-col="${n.id}">
163
- <header>
164
- <span class="kanban-col-icon">${Dn(n.id)}</span>
165
- <h2>${l(i(n.labelKey))}</h2>
166
- <span class="kanban-col-count">${s.length+r}</span>
167
- </header>
168
- <div class="kanban-col-list">
169
- ${s.length?c:`<div class="kanban-col-empty">${i("sessions.board.emptyColumn")}</div>`}
170
- ${r?`<div class="kanban-col-more">${l(i("sessions.kanban.moreHidden",{count:r}))}</div>`:""}
171
- </div>
172
- </section>`}).join("");return le=t,a}function $n(e){if(ne||se||_e)return;T.classList.toggle("kanban-mode-bot",D==="bot");let t;if(D==="bot")t=kn(e),le=new Map;else if(D==="team")if(!He)t=`<div class="kanban-loading">${i("sessions.kanban.teamLoading")}</div>`,le=new Map,K.dataset.loading||(K.dataset.loading="1",K.disabled=!0,K.innerHTML=`<option>${l(i("sessions.kanban.teamLoading"))}</option>`),cn();else{let n=B.find(g=>g.key===X)??B[0],s=pt(n),r=n?e.filter(g=>s.has(String(g.chatId))):[];n&&dn(n);let c=(ce===n?.key?A?.board:null)??{},d=(ce===n?.key?A?.remoteRows:null)??[],m=[...r,...d].map(g=>{let M=c[g.sessionId];return M?{...g,kanbanColumn:M.column,kanbanPosition:M.position}:g});rt.textContent=i("sessions.kanban.teamScope",{chats:s.size,sessions:m.length}),t=$t(m)}else t=$t(e);if(t===L)return;L=t;let a=new Map;T.querySelectorAll(".kanban-col-list").forEach(n=>{let s=n.closest(".kanban-column")?.dataset.col;s&&n.scrollTop&&a.set(s,n.scrollTop)}),T.innerHTML=t,a.size&&T.querySelectorAll(".kanban-col-list").forEach(n=>{let s=n.closest(".kanban-column")?.dataset.col,r=s?a.get(s):void 0;r&&(n.scrollTop=r)})}async function vt(e,t,a,n){try{let s=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/board`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({column:t,position:a})}),r=await s.json().catch(()=>({}));(!s.ok||r?.ok===!1)&&(e.kanbanColumn=n.column,e.kanbanPosition=n.position,L="",h(),s.status!==401&&alert(`${i("sessions.kanban.moveFail")}: ${r?.error??s.status}`))}catch(s){e.kanbanColumn=n.column,e.kanbanPosition=n.position,L="",h(),alert(`${i("sessions.kanban.moveFail")}: ${s}`)}}async function wt(e,t){let a=e.title;e.title=t,L="",h();try{let n=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/rename`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({title:t})}),s=await n.json().catch(()=>({}));(!n.ok||s?.ok===!1)&&(e.title=a,L="",h(),n.status!==401&&alert(`${i("sessions.kanban.renameFail")}: ${s?.error??n.status}`))}catch(n){e.title=a,L="",h(),alert(`${i("sessions.kanban.renameFail")}: ${n}`)}}function vn(e){if(e==null||e==="")return"";let t=Number(e),a=Number.isFinite(t)&&t>0?new Date(t):new Date(String(e));return Number.isNaN(a.getTime())?"":a.toLocaleString()}function wn(e,t,a){let n=t.senderType==="user",s=n?t.senderName||(a&&t.senderId===a?i("sessions.history.owner"):i("sessions.history.user")):F(e),r=vn(t.createTime),c=String(t.content??"").trim()||`[${t.msgType??"message"}]`,d=n?t.senderAvatar?`<img class="history-avatar-img" src="${l(String(t.senderAvatar))}" alt="" decoding="async" referrerpolicy="no-referrer">`:`<span class="history-avatar-user" aria-hidden="true">${l(String(s).slice(0,1))}</span>`:ae({name:F(e),larkAppId:e.larkAppId,size:"sm"});return`<div class="history-msg${n?" mine":""}">
173
- ${d}
174
- <div class="history-msg-main">
175
- <div class="history-msg-meta"><span>${l(s)}</span><time>${l(r)}</time></div>
176
- <div class="history-bubble">${l(c)}</div>
177
- </div>
178
- </div>`}async function St(e){let t=F(e);S.innerHTML=`<div class="term-modal-head">
179
- <span class="term-modal-title">
180
- ${ae({name:t,larkAppId:e.larkAppId,size:"sm"})}
181
- <strong title="${l(String(e.title??""))}">${l((j(e.title)||e.sessionId).slice(0,60))}</strong>
182
- <span class="history-scope-tag">${l(i("sessions.history.title"))}</span>
183
- </span>
184
- <span class="term-modal-actions">
185
- <button type="button" id="history-close" class="card-act" title="${l(i("sessions.dismiss"))}" aria-label="${l(i("sessions.dismiss"))}">${C.close}</button>
186
- </span>
187
- </div>
188
- <div class="history-body"><div class="term-modal-loading">${i("sessions.history.loading")}</div></div>`,S.showModal(),S.querySelector("#history-close").onclick=()=>S.close();try{let a=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/history?limit=80`),n=await a.json().catch(()=>({}));if(!S.open)return;let s=S.querySelector(".history-body");if(!a.ok||n?.ok===!1){let c=String(n?.error??a.status),d=c==="not_found_yet"||c==="not_found";s.innerHTML=`<div class="history-error">${l(i("sessions.history.fail"))}: ${l(c)}${d?`<br><span>${l(i("sessions.history.staleHint"))}</span>`:""}</div>`;return}let r=Array.isArray(n.messages)?n.messages:[];if(!r.length){s.innerHTML=`<div class="history-error">${i("sessions.history.empty")}</div>`;return}s.innerHTML=`<div class="history-list">${r.map(c=>wn(e,c,n.ownerOpenId)).join("")}</div>`,s.scrollTop=s.scrollHeight}catch(a){if(!S.open)return;let n=S.querySelector(".history-body");n&&(n.innerHTML=`<div class="history-error">${l(i("sessions.history.fail"))}: ${l(String(a))}</div>`)}}function Tt(e,t){let a=e.querySelector(".kanban-card-title");if(!a||e.querySelector(".kanban-rename-input"))return;_e=!0;let n=document.createElement("input");n.type="text",n.className="kanban-rename-input",n.maxLength=200,n.value=j(t.title)||"",a.replaceWith(n),n.focus(),n.select();let s=!1,r=c=>{if(s)return;s=!0,_e=!1;let d=n.value.trim();c&&d&&d!==(j(t.title)||"")?wt(t,d):(L="",h())};n.addEventListener("keydown",c=>{c.stopPropagation(),c.key==="Enter"?(c.preventDefault(),r(!0)):c.key==="Escape"&&(c.preventDefault(),r(!1))}),n.addEventListener("blur",()=>r(!0)),n.addEventListener("click",c=>c.stopPropagation())}function Lt(e,t){for(let a of e.querySelectorAll(".kanban-card:not(.dragging)")){if(a.closest(".kanban-cluster.dragging"))continue;let n=a.getBoundingClientRect();if(t<n.top+n.height/2)return a}return null}function Ct(){T.querySelectorAll(".drag-over, .dragging, .drop-before").forEach(e=>e.classList.remove("drag-over","dragging","drop-before"))}function Sn(e){let t=$.querySelector(".term-modal-name");if(!t||$.querySelector(".term-modal-name-input"))return;let a=document.createElement("input");a.type="text",a.className="term-modal-name-input",a.maxLength=200,a.value=j(e.title)||"",t.replaceWith(a);let n=()=>{let c=getComputedStyle(a),d=document.createElement("span");d.style.cssText="position:absolute;visibility:hidden;white-space:pre",d.style.fontSize=c.fontSize,d.style.fontFamily=c.fontFamily,d.style.fontWeight=c.fontWeight,d.style.letterSpacing=c.letterSpacing,d.textContent=a.value||" ",document.body.appendChild(d);let m=d.offsetWidth;d.remove();let g=Math.round(window.innerWidth*.6);a.style.width=`${Math.min(Math.max(m+22,80),g)}px`};n(),a.addEventListener("input",n),a.focus(),a.select();let s=!1,r=c=>{if(s)return;s=!0;let d=a.value.trim(),m=j(e.title)||"";if(c&&d&&d!==m){e.title=d;let g=document.createElement("strong");g.className="term-modal-name",g.title=d,g.textContent=d.slice(0,60),a.replaceWith(g),wt(e,d)}else{let g=document.createElement("strong");g.className="term-modal-name",g.title=String(e.title??m),g.textContent=m.slice(0,60),a.replaceWith(g)}};a.addEventListener("keydown",c=>{c.stopPropagation(),c.key==="Enter"?(c.preventDefault(),r(!0)):c.key==="Escape"&&(c.preventDefault(),r(!1))}),a.addEventListener("blur",()=>r(!0))}async function Mt(e){let t=at(e);if(!t){xe(e);return}let a=j(e.title)||e.sessionId,n=e.feishuChatLink?`<a class="card-act" href="${l(e.feishuChatLink)}" target="_blank" rel="noopener" title="${l(i("sessions.kanban.openFeishu"))}" aria-label="${l(i("sessions.kanban.openFeishu"))}">${C.feishu}</a>`:"";$.innerHTML=`<div class="term-modal-head">
189
- <span class="term-modal-title">
190
- ${ae({name:F(e),larkAppId:e.larkAppId,size:"sm"})}
191
- <strong class="term-modal-name" title="${l(String(e.title??a))}">${l(String(a).slice(0,60))}</strong>
192
- <button type="button" id="term-modal-edit" class="card-act" title="${l(i("sessions.kanban.rename"))}" aria-label="${l(i("sessions.kanban.rename"))}">${C.edit}</button>
193
- ${Se(e.status)}
194
- </span>
195
- <span class="term-modal-actions">
196
- ${n}
197
- <a id="term-modal-tab" class="card-act" href="${l(t)}" target="_blank" rel="noopener" title="${l(i("sessions.kanban.openTab"))}" aria-label="${l(i("sessions.kanban.openTab"))}">${C.terminal}</a>
198
- <button type="button" id="term-modal-close" class="card-act" title="${l(i("sessions.dismiss"))}" aria-label="${l(i("sessions.dismiss"))}">${C.close}</button>
199
- </span>
200
- </div>
201
- <div class="term-modal-body"><div class="term-modal-loading">${i("sessions.kanban.terminalLoading")}</div></div>`,$.showModal(),$.querySelector("#term-modal-close").onclick=()=>$.close(),$.querySelector("#term-modal-edit").onclick=()=>Sn(e);let s=t;if(we.authed)try{let d=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/write-link`),m=await d.json().catch(()=>({}));d.ok&&m?.ok!==!1&&m?.url&&(s=m.url)}catch{}if(!$.open)return;let r=$.querySelector(".term-modal-body");r.innerHTML=`<iframe class="term-modal-frame" src="${l(s)}" allow="clipboard-read; clipboard-write"></iframe>`;let c=$.querySelector("#term-modal-tab");c&&(c.href=s)}function ye(){let e=new FormData(b),t=(e.get("q")??"").toLowerCase(),a=e.getAll("cli"),n=a.length>0&&a.length<en.length,s=e.get("status"),r=e.get("adopt"),c=!!e.get("active"),d=E==="kanban",m=[...O.sessions.values()].filter(g=>!n||a.includes(g.cliId??"unknown")).filter(g=>!s||g.status===s).filter(g=>!r||r==="yes"==!!g.adopt).filter(g=>!c||d||g.status!=="closed").filter(g=>!t||JSON.stringify(g).toLowerCase().includes(t));return m.sort(Tn),m}function It(e,t){return t==="spawnedAt"||t==="lastMessageAt"?Number(e[t]??0):t==="tokenIn"?it(e.tokenUsage?.in)??-1:t==="tokenOut"?it(e.tokenUsage?.out)??-1:t==="adopt"?!!e.adopt:String(e[t]??"").toLowerCase()}function Tn(e,t){let a=It(e,ge),n=It(t,ge),s=0;return typeof a=="number"&&typeof n=="number"?s=a-n:typeof a=="boolean"&&typeof n=="boolean"?s=Number(a)-Number(n):s=String(a).localeCompare(String(n)),s===0&&(s=Number(e.lastMessageAt??0)-Number(t.lastMessageAt??0)),re==="asc"?s:-s}function Ln(){fe.querySelectorAll("th[data-sort]").forEach(e=>{let t=e.dataset.sort===ge;e.classList.toggle("sorted",t),e.setAttribute("aria-sort",t?re==="asc"?"ascending":"descending":"none");let a=e.dataset.label??e.textContent?.trim()??"";e.textContent=t?`${a} ${re==="asc"?"\u25B2":"\u25BC"}`:a})}function We(e){me.hidden=w.size===0,ie.textContent=i("sessions.selectedCount",{count:w.size});let t=[...w].map(s=>O.sessions.get(s)).filter(s=>!!s&&s.status!=="closed");W.disabled=!t.some(s=>!s.locked),G.disabled=!t.some(s=>!!s.locked);let a=e.filter(s=>s.status!=="closed");if(a.length===0){v.checked=!1,v.indeterminate=!1,v.disabled=!0;return}v.disabled=!1;let n=a.filter(s=>w.has(s.sessionId)).length;v.checked=n===a.length,v.indeterminate=n>0&&n<a.length}function Ve(){let e=mt().length;be.textContent=i("sessions.idleCleanupCount",{count:e}),Q.disabled=Ee||e===0,R.classList.toggle("is-empty",e===0),ln()}function Cn(){lt.forEach(e=>{let t=e.dataset.view===E;e.classList.toggle("active",t),e.setAttribute("aria-pressed",String(t))}),te.hidden=E!=="kanban",K.hidden=!(E==="kanban"&&D==="team"),rt.hidden=K.hidden||!He,te.querySelectorAll("[data-groupby]").forEach(e=>{let t=e.dataset.groupby===D;e.classList.toggle("active",t),e.setAttribute("aria-pressed",String(t))})}function Mn(){let e=b.querySelector("#cli-filter-count");if(!e)return;let t=[...b.querySelectorAll('input[name="cli"]')],a=t.filter(n=>n.checked).length;e.textContent=a===t.length?i("common.all"):`${a}/${t.length}`,e.classList.toggle("cli-filter-active",a!==t.length)}function h(){let e=ye();for(let n of[...w]){let s=O.sessions.get(n);(!s||s.status==="closed")&&w.delete(n)}let t=e.filter(n=>n.status!=="closed"),a=E==="table"?e:t;if(fe.hidden=E!=="table",q.hidden=E!=="board",T.hidden=E!=="kanban",E==="table"){let n=e.length?e.map(fn).join(""):`<tr><td colspan="12" class="empty">${i("sessions.empty")}</td></tr>`;n!==Ke&&(Ke=n,u.innerHTML=n)}else E==="kanban"?$n(e):yn(t);Cn(),Ln(),Mn(),We(a),Ve()}async function Ht(e,t){t&&(t.disabled=!0,t.textContent=i("sessions.locating"));try{let a=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/locate`,{method:"POST"}),n=await a.json();if(n.ok){if(!t)return;let s=30;t.textContent=i("sessions.cooldown",{seconds:s});let r=setInterval(()=>{s-=1,s<=0?(clearInterval(r),t.disabled=!1,t.textContent=i("sessions.locate")):t.textContent=i("sessions.cooldown",{seconds:s})},1e3)}else alert(`Locate failed: ${n.error??a.status}`),t&&(t.disabled=!1,t.textContent=i("sessions.locate"))}catch(a){alert(`Locate error: ${a}`),t&&(t.disabled=!1,t.textContent=i("sessions.locate"))}}async function Et(e,t){if(!confirm(i("sessions.closeConfirm")))return!1;t&&(t.disabled=!0);try{let a=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/close`,{method:"POST"}),n=await a.json().catch(()=>({}));return!a.ok||n?.ok===!1?(a.status!==401&&alert(`Close failed: ${n?.error??a.status}`),!1):!0}catch(a){return alert(`Close error: ${a}`),!1}finally{t&&(t.disabled=!1)}}function ke(){Oe="",Ke="",L=""}async function Ge(e,t,a){let n=!!e.locked;if(n===t)return!0;e.locked=t,ke(),h(),a&&(a.disabled=!0);try{let s=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/lock`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({locked:t})}),r=await s.json().catch(()=>({}));return!s.ok||r?.ok===!1?(e.locked=n,ke(),h(),s.status!==401&&alert(`${i("sessions.lockFailed")}: ${r?.error??s.status}`),!1):(e.locked=!!r.locked,ke(),h(),!0)}catch(s){return e.locked=n,ke(),h(),alert(`${i("sessions.lockFailed")}: ${s}`),!1}finally{a&&(a.disabled=!1)}}let Je=new Set;async function Ye(e,t){if(Je.has(e.sessionId)||!confirm(Kn(e)))return!1;t&&(t.disabled=!0);try{let a=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/restart`,{method:"POST"}),n=await a.json().catch(()=>({}));return!a.ok||n?.ok===!1?(a.status!==401&&alert(`${i("sessions.restartFailed")}: ${n?.error??a.status}`),!1):(Je.add(e.sessionId),setTimeout(()=>Je.delete(e.sessionId),5e3),!0)}catch(a){return alert(`${i("sessions.restartFailed")}: ${a}`),!1}finally{t&&(t.disabled=!1)}}function In(e){return e===void 0?"\u2014":e<1e3?`${e}ms`:e<6e4?`${(e/1e3).toFixed(1)}s`:`${Math.floor(e/6e4)}m${Math.round(e%6e4/1e3)}s`}function Hn(e){if(!e||e.status!=="ok"){let r=e?.error?.message?l(String(e.error.message)):l(String(e?.status??"error"));return`<p>${i("sessions.insightUnavailable")}: ${r}</p>`}let t=e.agg??{};if(!t.totalSpans)return`<p>${i("sessions.insightEmpty")}</p>`;let a="color:var(--muted,#8f959e)",n=[],s=[];if(e.meta?.asOf&&s.push(l(i("sessions.insightAsOf",{asOf:String(e.meta.asOf)}))),e.meta?.partial&&s.push(l(i("sessions.insightPartial"))),s.length&&n.push(`<p style="font-size:12px;${a}">${s.join(" \xB7 ")}</p>`),n.push(`<p>${l(i("sessions.insightMetrics",{total:String(t.totalSpans??0),failed:String(t.failedSpans??0),slow:String(t.slowSpans??0),rw:t.readWriteRatio===null||t.readWriteRatio===void 0?"\u2014":String(t.readWriteRatio),compactions:String(t.compactions??0)}))}</p>`),Array.isArray(e.suggestions)&&e.suggestions.length){let r=d=>d==="bad"?"\u{1F534}":d==="warn"?"\u{1F7E1}":"\u2139\uFE0F",c=e.suggestions.map(d=>`<li>${r(d.severity)} <b>${l(String(d.title??""))}</b> \u2014 ${l(String(d.action??""))}`+(Array.isArray(d.evidence)&&d.evidence.length?`<br><small style="${a}">${l(d.evidence.join("\uFF1B"))}</small>`:"")+"</li>").join("");n.push(`<details open><summary>${i("sessions.insightSuggestions")}</summary><ul style="padding-left:18px;margin:6px 0">${c}</ul></details>`)}if(Array.isArray(e.spans)&&e.spans.length){let r=m=>m==="error"?"\u{1F534}":m==="running"?"\u23F3":"\u2705",c=[...e.spans].sort((m,g)=>(m.relStartMs??0)-(g.relStartMs??0)).map(m=>{let g=[m.inputSummary,m.outputSummary].filter(Boolean).map(x=>l(String(x))).join(" \u2192 ");return`<tr${m.status==="error"?' style="color:var(--danger,#d33)"':""}><td>${r(m.status)}</td><td><code>${l(String(m.tool??""))}</code></td><td>${l(String(m.phase??""))}</td><td>${In(m.durationMs)}</td><td>#${l(String(m.turnIndex??0))}</td><td>${g}</td></tr>`}).join(""),d=e.meta?.capped?`<p style="font-size:12px;${a}">${l(i("sessions.insightCapped",{shown:String(e.meta.spansReturned??e.spans.length),total:String(e.meta.spansTotal??e.spans.length)}))}</p>`:"";n.push(`<details><summary>${i("sessions.insightSpans")} (${e.spans.length})</summary>${d}<div style="max-height:320px;overflow:auto"><table style="width:100%;font-size:12px"><tbody>${c}</tbody></table></div></details>`)}return n.join("")}function xe(e){let t=e.status==="closed",a=at(e);f.innerHTML=`<article>
202
- <header>
203
- <h3>${l(j(e.title)||e.sessionId)}</h3>
204
- <span class="drawer-status-line">
205
- ${Se(e.status)}
206
- ${Ce(e)}
207
- </span>
208
- <p><code>${l(e.sessionId)}</code> <button data-copy="${l(e.sessionId)}">${i("sessions.copy")}</button></p>
209
- </header>
210
- <p><b>${i("sessions.bot")}:</b> ${l(F(e))} \xB7 <b>${i("sessions.cli")}:</b> ${l(e.cliId??"?")}</p>
211
- ${pe(e)?`<p><b>${i("sessions.chat")}:</b> ${l(pe(e))}</p>`:""}
212
- <p><b>chatId:</b> <code>${l(e.chatId??"")}</code> <button data-copy="${l(e.chatId??"")}">${i("sessions.copy")}</button></p>
213
- <p><b>rootMessageId:</b> <code>${l(e.rootMessageId??"")}</code> <button data-copy="${l(e.rootMessageId??"")}">${i("sessions.copy")}</button></p>
214
- ${e.threadId?`<p><b>threadId:</b> <code>${l(e.threadId)}</code></p>`:""}
215
- <p><b>${i("sessions.workingDir")}:</b> ${l(e.workingDir??"-")}</p>
216
- <div class="actions">
217
- ${gt(e)??`<button id="locate-btn" type="button">${i("sessions.locate")}</button>`}
218
- <button id="history-drawer-btn" type="button">${i("sessions.history.title")}</button>
219
- ${Zt(a)}
220
- ${ot(e)?`<button id="restart-btn" type="button">${i("sessions.restart")}</button>`:""}
221
- ${t?"":`<button id="lock-btn" type="button">${l(Le(e))}</button>`}
222
- ${e.queued&&!t?`<button id="start-btn" type="button" class="primary">${i("sessions.create.start")}</button>`:""}
223
- ${t?`<button id="resume-btn" type="button" class="primary">${i("sessions.resume")}</button>`:""}
224
- ${t?"":`<button id="close-btn" type="button" class="contrast">${i("sessions.close")}</button>`}
225
- <button id="land-btn" type="button">${i("sessions.land")}</button>
226
- ${we.authed?`<button id="insight-btn" type="button">${i("sessions.insight")}</button>`:""}
227
- </div>
228
- <div id="land-area"></div>
229
- <div id="insight-area"></div>
230
- <form method="dialog"><button>${i("sessions.dismiss")}</button></form>
231
- </article>`,f.querySelectorAll("[data-copy]").forEach(y=>{y.onclick=()=>{navigator.clipboard.writeText(y.dataset.copy??""),y.textContent=i("sessions.copied"),setTimeout(()=>{y.textContent=i("sessions.copy")},800)}});let n=f.querySelector("#locate-btn");n&&(n.onclick=()=>{Ht(e,n)});let s=f.querySelector("#history-drawer-btn");s&&(s.onclick=()=>{St(e)});let r=f.querySelector(".term-write");r&&(r.onclick=()=>{Qt(e,r)});let c=f.querySelector("#resume-btn");c&&(c.onclick=async()=>{c.disabled=!0;try{let y=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/resume`,{method:"POST"}),k=await y.json().catch(()=>({}));if(!y.ok||k.ok===!1){alert(`${i("sessions.resumeFailed")}: ${k?.error??y.status}`),c.disabled=!1;return}f.close()}catch(y){alert(`${i("sessions.resumeFailed")}: ${y}`),c.disabled=!1}});let d=f.querySelector("#restart-btn");d&&(d.onclick=async()=>{await Ye(e,d)&&f.close()});let m=f.querySelector("#close-btn");m&&(m.onclick=async()=>{await Et(e,m)&&f.close()});let g=f.querySelector("#lock-btn");g&&(g.onclick=async()=>{let y=!e.locked;if(await Ge(e,y,g)){g.textContent=Le(e);let k=f.querySelector(".drawer-status-line");k&&(k.innerHTML=`${Se(e.status)}${Ce(e)}`)}});let M=f.querySelector("#start-btn");M&&(M.onclick=async()=>{M.disabled=!0;try{let y=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/start`,{method:"POST"}),k=await y.json().catch(()=>({}));if(!y.ok||k.ok===!1){y.status!==401&&alert(`${i("sessions.create.startFailed")}: ${k?.error??y.status}`),M.disabled=!1;return}f.close()}catch(y){alert(`${i("sessions.create.startFailed")}: ${y}`),M.disabled=!1}});let x=f.querySelector("#land-btn"),N=f.querySelector("#land-area");x&&N&&(x.onclick=async()=>{x.disabled=!0,N.innerHTML=`<p>${i("sessions.landLoading")}</p>`;try{let y=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/sandbox-diff`),k=await y.json().catch(()=>({}));if(!k.ok){N.innerHTML=`<p>${i("sessions.landUnavailable")}: ${l(k.error??String(y.status))}</p>`,x.disabled=!1;return}if(k.empty){N.innerHTML=`<p>${i("sessions.landEmpty")}</p>`,x.disabled=!1;return}let V=String(k.patch??""),Ne=V.slice(0,2e4)+(V.length>2e4?`
232
- \u2026(truncated)`:"");N.innerHTML=`
233
- <p><b>${k.files}</b> files (+${k.insertions}/-${k.deletions}) \u2192 <code>${l(String(k.workingDir??""))}</code></p>
234
- <pre style="max-height:320px;overflow:auto;white-space:pre-wrap">${l(Ne)}</pre>
235
- <div class="actions">
236
- <button id="land-apply" type="button" class="primary">${i("sessions.landApply")}</button>
237
- <button id="land-discard" type="button" class="contrast">${i("sessions.landDiscard")}</button>
238
- </div>`;let ve=N.querySelector("#land-apply"),Be=N.querySelector("#land-discard");ve.onclick=async()=>{ve.disabled=!0,Be.disabled=!0;let I=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/sandbox-land/apply`,{method:"POST"}),Z=await I.json().catch(()=>({}));N.innerHTML=Z.ok?`<p>\u2705 ${i("sessions.landApplied")}: ${Z.files} files (+${Z.insertions}/-${Z.deletions}) \u2192 <code>${l(String(Z.workingDir??""))}</code></p>`:`<p>\u274C ${i("sessions.landFailed")}: ${l(Z.error??String(I.status))}</p>`},Be.onclick=async()=>{await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/sandbox-land/discard`,{method:"POST"}),N.innerHTML=`<p>\u{1F5D1} ${i("sessions.landDiscarded")}</p>`}}catch(y){N.innerHTML=`<p>${i("sessions.landUnavailable")}: ${l(String(y))}</p>`,x.disabled=!1}});let de=f.querySelector("#insight-btn"),P=f.querySelector("#insight-area");de&&P&&(de.onclick=async()=>{de.disabled=!0,P.innerHTML=`<p>${i("sessions.insightLoading")}</p>`;try{let y=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/insight?detail=spans`),k=await y.json().catch(()=>({}));!k.ok||!k.report?P.innerHTML=`<p>${i("sessions.insightUnavailable")}: ${l(String(k.error??y.status))}</p>`:P.innerHTML=Hn(k.report)}catch(y){P.innerHTML=`<p>${i("sessions.insightUnavailable")}: ${l(String(y))}</p>`}de.disabled=!1}),f.showModal()}u.addEventListener("click",e=>{let t=e.target;if(t.classList.contains("row-select")){let r=t.closest("tr[data-id]");if(!r)return;t.checked?w.add(r.dataset.id):w.delete(r.dataset.id),We(ye());return}let a=t.closest("td");if(a&&a.querySelector(".row-select"))return;let n=t.closest("tr[data-id]");if(!n)return;let s=O.sessions.get(n.dataset.id);s&&xe(s)}),q.addEventListener("click",e=>{let t=e.target,a=t.closest("button[data-move-col]");if(a){mn(a.dataset.moveCol,Number(a.dataset.dir));return}let n=t.closest(".session-card[data-id]");if(!n)return;let s=O.sessions.get(n.dataset.id);if(!s)return;let r=t.closest("button[data-action]");if(r){let c=r.dataset.action;c==="details"?xe(s):c==="write-link"?Qt(s,r):c==="locate"?Ht(s,r):c==="restart"?Ye(s,r):c==="lock"?Ge(s,!s.locked,r):c==="close"&&Et(s,r).then(d=>{d&&(w.delete(s.sessionId),h())});return}t.closest("a, button, input, label")||(w.has(s.sessionId)?w.delete(s.sessionId):w.add(s.sessionId),n.classList.toggle("selected",w.has(s.sessionId)),n.setAttribute("aria-pressed",String(w.has(s.sessionId))),We(ye().filter(c=>c.status!=="closed")))}),q.addEventListener("dragstart",e=>{let a=e.target.closest(".session-board-column > header[draggable]")?.closest(".session-board-column");a?.dataset.col&&(Y=a.dataset.col,a.classList.add("dragging"),e.dataTransfer&&(e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",Y)))}),q.addEventListener("dragover",e=>{if(!Y)return;e.preventDefault(),e.dataTransfer&&(e.dataTransfer.dropEffect="move");let t=e.target.closest(".session-board-column");q.querySelectorAll(".drag-over").forEach(a=>a.classList.remove("drag-over")),t&&t.dataset.col!==Y&&t.classList.add("drag-over")}),q.addEventListener("drop",e=>{if(!Y)return;e.preventDefault();let t=e.target.closest(".session-board-column"),a=Y;Y=null,t?.dataset.col&&bn(a,t.dataset.col)}),q.addEventListener("dragend",()=>{Y=null,q.querySelectorAll(".drag-over, .dragging").forEach(e=>e.classList.remove("drag-over","dragging"))}),lt.forEach(e=>{e.addEventListener("click",()=>{let t=Nt(e.dataset.view)??"board";t!==E&&(E=t,Rt(window.localStorage,E),h())})}),te.querySelectorAll("[data-groupby]").forEach(e=>{e.addEventListener("click",()=>{let t=e.dataset.groupby,a=t==="bot"?"bot":t==="team"?"team":"flow";a!==D&&(D=a,Dt(window.localStorage,a),L="",h())})}),K.addEventListener("change",()=>{X=K.value;try{window.localStorage.setItem(et,X)}catch{}L="",h()});function $e(){he!==null&&(clearTimeout(he),he=null)}T.addEventListener("click",e=>{let t=e.target,a=t.closest(".kanban-card[data-id]");if(!a)return;let n=O.sessions.get(a.dataset.id);if(!n)return;let s=t.closest("button[data-action]");if(s){s.dataset.action==="details"?xe(n):s.dataset.action==="rename"?Tt(a,n):s.dataset.action==="history"?St(n):s.dataset.action==="restart"?Ye(n,s):s.dataset.action==="lock"&&Ge(n,!n.locked,s);return}t.closest("a, button, input, label")||($e(),he=setTimeout(()=>{he=null,Mt(n)},220))}),T.addEventListener("dblclick",e=>{let t=e.target,a=t.closest(".kanban-card-title"),n=t.closest(".kanban-card[data-id]");if(!a||!n)return;$e();let s=O.sessions.get(n.dataset.id);s&&Tt(n,s)}),T.addEventListener("keydown",e=>{if(e.key!=="Enter"&&e.key!==" ")return;let t=e.target;if(!t.classList?.contains("kanban-card"))return;e.preventDefault();let a=O.sessions.get(t.dataset.id);a&&Mt(a)}),T.addEventListener("dragstart",e=>{if(D==="bot")return;let t=e.target,a=t.closest(".kanban-cluster > header[draggable]");if(a){let s=a.closest(".kanban-cluster"),r=s.closest(".kanban-column")?.dataset.col;if(!s.dataset.chat||!r)return;$e(),se=s.dataset.chat,Ie=r,s.classList.add("dragging"),e.dataTransfer&&(e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",`cluster:${se}`));return}let n=t.closest(".kanban-card[data-id]");n&&($e(),ne=n.dataset.id,n.classList.add("dragging"),e.dataTransfer&&(e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",ne)))}),T.addEventListener("dragover",e=>{if(!ne&&!se)return;e.preventDefault(),e.dataTransfer&&(e.dataTransfer.dropEffect="move");let t=e.target.closest(".kanban-column");T.querySelectorAll(".drag-over").forEach(a=>a.classList.remove("drag-over")),T.querySelectorAll(".drop-before").forEach(a=>a.classList.remove("drop-before")),t&&(t.classList.add("drag-over"),Lt(t,e.clientY)?.classList.add("drop-before"))}),T.addEventListener("drop",e=>{let t=se,a=Ie,n=ne;if(!n&&!t)return;e.preventDefault(),ne=null,se=null,Ie=null,Ct();let s=e.target.closest(".kanban-column"),r=s?.dataset.col;if(!s||!r)return;let c=Lt(s,e.clientY);if(t&&a){let P=(le.get(a)??[]).filter(I=>String(I.chatId)===t).filter(I=>!(I.status==="closed"&&r!=="done"));if(!P.length)return;let y=new Set(P.map(I=>I.sessionId)),k=(le.get(r)??[]).filter(I=>!y.has(I.sessionId)),V=c?k.findIndex(I=>I.sessionId===c.dataset.id):k.length;V<0&&(V=k.length);let Ne=V>0?k[V-1]:null,ve=V<k.length?k[V]:null,Be=nt(Ne?oe(Ne):null,ve?oe(ve):null);P.forEach((I,Z)=>{let Xe=Be+Z*.001;if(D==="team")ft(String(I.sessionId),r,Xe);else{let An={column:I.kanbanColumn,position:I.kanbanPosition};I.kanbanColumn=r,I.kanbanPosition=Xe,vt(I,r,Xe,An)}}),L="",h();return}let d=O.sessions.get(n)??ze.get(n);if(!d||d.status==="closed"&&r!=="done")return;let m=(le.get(r)??[]).filter(P=>P.sessionId!==n),g=c?m.findIndex(P=>P.sessionId===c.dataset.id):m.length;g<0&&(g=m.length);let M=g>0?m[g-1]:null,x=g<m.length?m[g]:null,N=nt(M?oe(M):null,x?oe(x):null);if(D==="team"){ft(String(d.sessionId),r,N),L="",h();return}let de={column:d.kanbanColumn,position:d.kanbanPosition};d.kanbanColumn=r,d.kanbanPosition=N,L="",h(),vt(d,r,N,de)}),T.addEventListener("dragend",()=>{ne=null,se=null,Ie=null,Ct(),L="",h()}),$.addEventListener("click",e=>{e.target===$&&$.close()}),$.addEventListener("close",()=>{$.innerHTML=""}),S.addEventListener("click",e=>{e.target===S&&S.close()}),S.addEventListener("close",()=>{S.innerHTML=""}),v.addEventListener("change",()=>{let e=ye().filter(t=>t.status!=="closed");for(let t of e)v.checked?w.add(t.sessionId):w.delete(t.sessionId);h()}),H.addEventListener("click",()=>{w.clear(),h()}),U.addEventListener("click",async()=>{let e=[...w];if(e.length===0||!confirm(i("sessions.closeBulkConfirm",{count:e.length})))return;U.disabled=!0,H.disabled=!0;let t=U.textContent,a=0,n=0,s=[...e];U.textContent=`0/${e.length}`;async function r(){for(;s.length;){let c=s.shift();try{let d=await fetch(`/api/sessions/${encodeURIComponent(c)}/close`,{method:"POST"}),m=await d.json().catch(()=>({}));(!d.ok||m?.ok===!1)&&(n+=1)}catch{n+=1}finally{a+=1,U.textContent=`${a}/${e.length}`}}}await Promise.all(Array.from({length:Math.min(6,e.length)},()=>r())),U.textContent=t,U.disabled=!1,H.disabled=!1,w.clear(),h(),n>0&&alert(`Failed: ${n}/${e.length}`)});async function At(e){let t=[...w].map(m=>O.sessions.get(m)).filter(m=>!!m&&m.status!=="closed"&&!!m.locked!==e);if(t.length===0)return;W.disabled=!0,G.disabled=!0;let a=e?W.textContent:G.textContent,n=e?W:G,s=0,r=0,c=[...t];n.textContent=`0/${t.length}`;async function d(){for(;c.length;){let m=c.shift(),g=!!m.locked;try{let M=await fetch(`/api/sessions/${encodeURIComponent(m.sessionId)}/lock`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({locked:e})}),x=await M.json().catch(()=>({}));!M.ok||x?.ok===!1?(r+=1,m.locked=g):m.locked=!!x.locked}catch{r+=1,m.locked=g}finally{s+=1,n.textContent=`${s}/${t.length}`}}}await Promise.all(Array.from({length:Math.min(6,t.length)},()=>d())),n.textContent=a,ke(),h(),r>0&&alert(`${i("sessions.lockFailed")}: ${r}/${t.length}`)}W.addEventListener("click",()=>{At(!0)}),G.addEventListener("click",()=>{At(!1)}),J.addEventListener("click",e=>{if(Ee)return;let t=e.target?.closest("button[data-hours]");if(!t)return;let a=tt(t.dataset.hours);!a||a===Ae||(Ae=a,ee.textContent="",Ve())}),Q.addEventListener("click",async()=>{let e=ut(),t=mt();if(t.length===0)return;let a=on(e);if(confirm(i("sessions.idleCleanupConfirm",{count:t.length,threshold:a}))){Ee=!0,Q.disabled=!0,ee.textContent=i("sessions.idleCleanupRunning");try{let n=await fetch("/api/sessions/cleanup-idle",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({olderThanHours:e,sessionIds:t.map(r=>r.sessionId)})}),s=await n.json().catch(()=>({}));if(!n.ok){n.status!==401&&alert(`${i("sessions.idleCleanupFailed")}: ${s?.error??n.status}`),ee.textContent="";return}for(let r of s?.results??[])r?.ok&&r?.sessionId&&w.delete(String(r.sessionId));ee.textContent=i("sessions.idleCleanupDone",{closed:Number(s?.closed??0),failed:Number(s?.failed??0)}),h()}catch(n){alert(`${i("sessions.idleCleanupFailed")}: ${n}`),ee.textContent=""}finally{Ee=!1,Ve()}}}),fe.querySelectorAll("th[data-sort]").forEach(e=>{e.addEventListener("click",()=>{let t=e.dataset.sort;ge===t?re=re==="asc"?"desc":"asc":(ge=t,re=t==="spawnedAt"||t==="lastMessageAt"?"desc":"asc"),h()})}),b.addEventListener("input",h);let En=O.on(h),xt=setInterval(()=>{if(!document.body.contains(T)){clearInterval(xt);return}E==="kanban"&&D==="team"&&(L="",h())},3e4);return h(),Kt().then(h),()=>{En(),clearInterval(xt),$e(),f.close(),$.close(),S.close(),ct.close()}}var p=Ze(jt(),1);function z(o){return(0,p.jsx)("th",{"data-sort":o.sort,"data-label":o.label,children:o.label})}function Wn(){let o=Ut(),u=(0,De.useRef)(null);return(0,De.useEffect)(()=>{if(u.current)return sn(u.current)},[]),(0,p.jsxs)("section",{className:"page",ref:u,children:[(0,p.jsxs)("div",{className:"page-heading",children:[(0,p.jsxs)("div",{children:[(0,p.jsx)("p",{className:"eyebrow",children:o("nav.sessions")}),(0,p.jsx)("h1",{children:o("sessions.title")}),(0,p.jsx)("p",{children:o("sessions.subtitle")})]}),(0,p.jsxs)("div",{className:"sessions-view-controls",children:[(0,p.jsxs)("button",{type:"button",id:"create-session-btn",className:"primary create-session-btn",children:["+ ",o("sessions.create.button")]}),(0,p.jsx)("span",{id:"kanban-team-stats",className:"kanban-team-stats",hidden:!0}),(0,p.jsx)("select",{id:"kanban-team",className:"kanban-team-select","aria-label":o("sessions.kanban.groupTeam"),hidden:!0}),(0,p.jsxs)("div",{className:"segmented kanban-groupby",id:"kanban-groupby",role:"group","aria-label":o("sessions.kanban.groupBy"),hidden:!0,children:[(0,p.jsx)("button",{type:"button","data-groupby":"flow",children:o("sessions.kanban.groupFlow")}),(0,p.jsx)("button",{type:"button","data-groupby":"team",children:o("sessions.kanban.groupTeam")}),(0,p.jsx)("button",{type:"button","data-groupby":"bot",children:o("sessions.kanban.groupBot")})]}),(0,p.jsxs)("div",{className:"segmented sessions-view-toggle",role:"group","aria-label":o("sessions.viewMode"),children:[(0,p.jsx)("button",{type:"button","data-view":"kanban",children:o("sessions.viewKanban")}),(0,p.jsx)("button",{type:"button","data-view":"board",children:o("sessions.viewBoard")}),(0,p.jsx)("button",{type:"button","data-view":"table",children:o("sessions.viewTable")})]})]})]}),(0,p.jsxs)("form",{id:"filters",className:"filters sessions-filters",children:[(0,p.jsx)("input",{type:"search",name:"q",placeholder:o("sessions.search")}),(0,p.jsxs)("select",{name:"status",children:[(0,p.jsx)("option",{value:"",children:o("sessions.anyStatus")}),tn.map(b=>(0,p.jsx)("option",{value:b,children:Me(b)},b))]}),(0,p.jsxs)("select",{name:"adopt",children:[(0,p.jsx)("option",{value:"",children:o("sessions.adoptAny")}),(0,p.jsx)("option",{value:"yes",children:o("sessions.adoptYes")}),(0,p.jsx)("option",{value:"no",children:o("sessions.adoptNo")})]}),(0,p.jsx)("span",{style:{display:"contents"},dangerouslySetInnerHTML:{__html:nn()}}),(0,p.jsxs)("label",{className:"filter-toggle",children:[(0,p.jsx)("input",{type:"checkbox",name:"active",defaultChecked:!0})," ",(0,p.jsx)("span",{children:o("sessions.activeOnly")})]})]}),(0,p.jsxs)("div",{id:"idle-cleanup-bar",className:"idle-cleanup-bar",children:[(0,p.jsxs)("div",{className:"idle-cleanup-summary",children:[(0,p.jsx)("span",{className:"idle-cleanup-dot","aria-hidden":"true"}),(0,p.jsx)("span",{id:"idle-cleanup-count",className:"idle-cleanup-count"})]}),(0,p.jsxs)("div",{className:"idle-cleanup-controls",children:[(0,p.jsx)("span",{className:"idle-cleanup-label",children:o("sessions.idleCleanupOlderThan")}),(0,p.jsx)("div",{id:"idle-cleanup-threshold",className:"idle-cleanup-thresholds",role:"group","aria-label":o("sessions.idleCleanupThreshold"),children:Re.map(b=>(0,p.jsx)("button",{type:"button","data-hours":b,"aria-pressed":b===24?"true":"false",children:b===168?"7d":`${b}H`},b))}),(0,p.jsx)("button",{type:"button",id:"idle-cleanup-run",className:"contrast idle-cleanup-run",children:o("sessions.idleCleanupRun")})]}),(0,p.jsx)("span",{id:"idle-cleanup-status",className:"idle-cleanup-status","aria-live":"polite"})]}),(0,p.jsxs)("div",{id:"bulk-bar",className:"bulk-bar",hidden:!0,children:[(0,p.jsx)("span",{id:"bulk-count"}),(0,p.jsx)("button",{type:"button",id:"bulk-lock",children:o("sessions.lockSelected")}),(0,p.jsx)("button",{type:"button",id:"bulk-unlock",children:o("sessions.unlockSelected")}),(0,p.jsx)("button",{type:"button",id:"bulk-close",className:"contrast",children:o("sessions.closeSelected")}),(0,p.jsx)("button",{type:"button",id:"bulk-clear",children:o("sessions.clearSelection")})]}),(0,p.jsxs)("table",{id:"sessions-table",children:[(0,p.jsx)("thead",{children:(0,p.jsxs)("tr",{children:[(0,p.jsx)("th",{children:(0,p.jsx)("input",{type:"checkbox",id:"select-all",title:o("sessions.activeOnly")})}),(0,p.jsx)(z,{sort:"botName",label:o("sessions.bot")}),(0,p.jsx)(z,{sort:"cliId",label:o("sessions.cli")}),(0,p.jsx)(z,{sort:"status",label:o("sessions.status")}),(0,p.jsx)(z,{sort:"tokenIn",label:o("sessions.tokenIn")}),(0,p.jsx)(z,{sort:"tokenOut",label:o("sessions.tokenOut")}),(0,p.jsx)(z,{sort:"title",label:o("sessions.titleCol")}),(0,p.jsx)(z,{sort:"workingDir",label:o("sessions.workingDir")}),(0,p.jsx)(z,{sort:"spawnedAt",label:o("sessions.created")}),(0,p.jsx)(z,{sort:"lastMessageAt",label:o("sessions.last")}),(0,p.jsx)(z,{sort:"adopt",label:o("sessions.adopt")}),(0,p.jsx)("th",{children:o("sessions.actions")})]})}),(0,p.jsx)("tbody",{})]}),(0,p.jsx)("div",{id:"sessions-board",className:"sessions-board",hidden:!0}),(0,p.jsx)("div",{id:"sessions-kanban",className:"sessions-kanban",hidden:!0}),(0,p.jsx)("dialog",{id:"drawer"}),(0,p.jsx)("dialog",{id:"term-modal",className:"term-modal"}),(0,p.jsx)("dialog",{id:"history-modal",className:"history-modal"}),(0,p.jsx)("dialog",{id:"create-session-modal",className:"create-session-modal"})]})}function rs(o){return _t(o,(0,p.jsx)(Wn,{}))}export{rs as renderSessionsPage};