@geminixiang/mikan 1.0.0-beta.76 → 1.0.0-beta.78

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 (535) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/adapters/commands/login.d.ts.map +1 -1
  3. package/dist/adapters/commands/login.js +0 -9
  4. package/dist/adapters/commands/login.js.map +1 -1
  5. package/dist/adapters/commands/manifest.d.ts +0 -19
  6. package/dist/adapters/commands/manifest.d.ts.map +1 -1
  7. package/dist/adapters/commands/manifest.js +0 -34
  8. package/dist/adapters/commands/manifest.js.map +1 -1
  9. package/dist/adapters/commands/model.d.ts +0 -5
  10. package/dist/adapters/commands/model.d.ts.map +1 -1
  11. package/dist/adapters/commands/model.js +0 -5
  12. package/dist/adapters/commands/model.js.map +1 -1
  13. package/dist/adapters/commands/new.d.ts.map +1 -1
  14. package/dist/adapters/commands/new.js +0 -2
  15. package/dist/adapters/commands/new.js.map +1 -1
  16. package/dist/adapters/commands/registry.d.ts +0 -1
  17. package/dist/adapters/commands/registry.d.ts.map +1 -1
  18. package/dist/adapters/commands/registry.js +0 -8
  19. package/dist/adapters/commands/registry.js.map +1 -1
  20. package/dist/adapters/commands/session-view.d.ts.map +1 -1
  21. package/dist/adapters/commands/session-view.js +0 -2
  22. package/dist/adapters/commands/session-view.js.map +1 -1
  23. package/dist/adapters/commands/types.d.ts +0 -29
  24. package/dist/adapters/commands/types.d.ts.map +1 -1
  25. package/dist/adapters/commands/types.js.map +1 -1
  26. package/dist/adapters/commands/utils.d.ts +0 -3
  27. package/dist/adapters/commands/utils.d.ts.map +1 -1
  28. package/dist/adapters/commands/utils.js +0 -3
  29. package/dist/adapters/commands/utils.js.map +1 -1
  30. package/dist/adapters/discord/bot.d.ts +0 -4
  31. package/dist/adapters/discord/bot.d.ts.map +1 -1
  32. package/dist/adapters/discord/bot.js +5 -41
  33. package/dist/adapters/discord/bot.js.map +1 -1
  34. package/dist/adapters/discord/components.d.ts +0 -23
  35. package/dist/adapters/discord/components.d.ts.map +1 -1
  36. package/dist/adapters/discord/components.js +0 -23
  37. package/dist/adapters/discord/components.js.map +1 -1
  38. package/dist/adapters/discord/context.d.ts.map +1 -1
  39. package/dist/adapters/discord/context.js +1 -11
  40. package/dist/adapters/discord/context.js.map +1 -1
  41. package/dist/adapters/discord/format.d.ts +0 -1
  42. package/dist/adapters/discord/format.d.ts.map +1 -1
  43. package/dist/adapters/discord/format.js +0 -37
  44. package/dist/adapters/discord/format.js.map +1 -1
  45. package/dist/adapters/github/bot.d.ts +0 -44
  46. package/dist/adapters/github/bot.d.ts.map +1 -1
  47. package/dist/adapters/github/bot.js +1 -120
  48. package/dist/adapters/github/bot.js.map +1 -1
  49. package/dist/adapters/github/client.d.ts +0 -57
  50. package/dist/adapters/github/client.d.ts.map +1 -1
  51. package/dist/adapters/github/client.js +0 -59
  52. package/dist/adapters/github/client.js.map +1 -1
  53. package/dist/adapters/github/context.d.ts.map +1 -1
  54. package/dist/adapters/github/context.js +1 -8
  55. package/dist/adapters/github/context.js.map +1 -1
  56. package/dist/adapters/github/github-ops.d.ts +0 -55
  57. package/dist/adapters/github/github-ops.d.ts.map +1 -1
  58. package/dist/adapters/github/github-ops.js +0 -59
  59. package/dist/adapters/github/github-ops.js.map +1 -1
  60. package/dist/adapters/github/ids.d.ts +0 -27
  61. package/dist/adapters/github/ids.d.ts.map +1 -1
  62. package/dist/adapters/github/ids.js +0 -29
  63. package/dist/adapters/github/ids.js.map +1 -1
  64. package/dist/adapters/github/repo.d.ts +0 -25
  65. package/dist/adapters/github/repo.d.ts.map +1 -1
  66. package/dist/adapters/github/repo.js +0 -49
  67. package/dist/adapters/github/repo.js.map +1 -1
  68. package/dist/adapters/github/tool-pack.d.ts +0 -10
  69. package/dist/adapters/github/tool-pack.d.ts.map +1 -1
  70. package/dist/adapters/github/tool-pack.js +0 -10
  71. package/dist/adapters/github/tool-pack.js.map +1 -1
  72. package/dist/adapters/github/tools/checks.d.ts +0 -6
  73. package/dist/adapters/github/tools/checks.d.ts.map +1 -1
  74. package/dist/adapters/github/tools/checks.js +0 -7
  75. package/dist/adapters/github/tools/checks.js.map +1 -1
  76. package/dist/adapters/github/tools/issue.d.ts +0 -5
  77. package/dist/adapters/github/tools/issue.d.ts.map +1 -1
  78. package/dist/adapters/github/tools/issue.js +0 -5
  79. package/dist/adapters/github/tools/issue.js.map +1 -1
  80. package/dist/adapters/github/tools/pr.d.ts +0 -7
  81. package/dist/adapters/github/tools/pr.d.ts.map +1 -1
  82. package/dist/adapters/github/tools/pr.js +0 -7
  83. package/dist/adapters/github/tools/pr.js.map +1 -1
  84. package/dist/adapters/github/tools/read.d.ts +0 -6
  85. package/dist/adapters/github/tools/read.d.ts.map +1 -1
  86. package/dist/adapters/github/tools/read.js +0 -8
  87. package/dist/adapters/github/tools/read.js.map +1 -1
  88. package/dist/adapters/github/tools/review-reply.d.ts +0 -5
  89. package/dist/adapters/github/tools/review-reply.d.ts.map +1 -1
  90. package/dist/adapters/github/tools/review-reply.js +0 -5
  91. package/dist/adapters/github/tools/review-reply.js.map +1 -1
  92. package/dist/adapters/github/tools/sync.d.ts +0 -6
  93. package/dist/adapters/github/tools/sync.d.ts.map +1 -1
  94. package/dist/adapters/github/tools/sync.js +0 -6
  95. package/dist/adapters/github/tools/sync.js.map +1 -1
  96. package/dist/adapters/github/types.d.ts +0 -88
  97. package/dist/adapters/github/types.d.ts.map +1 -1
  98. package/dist/adapters/github/types.js.map +1 -1
  99. package/dist/adapters/github/webhook.d.ts +0 -13
  100. package/dist/adapters/github/webhook.d.ts.map +1 -1
  101. package/dist/adapters/github/webhook.js +0 -16
  102. package/dist/adapters/github/webhook.js.map +1 -1
  103. package/dist/adapters/index.d.ts +3 -4
  104. package/dist/adapters/index.d.ts.map +1 -1
  105. package/dist/adapters/index.js +4 -4
  106. package/dist/adapters/index.js.map +1 -1
  107. package/dist/adapters/intake.d.ts +0 -17
  108. package/dist/adapters/intake.d.ts.map +1 -1
  109. package/dist/adapters/intake.js +6 -32
  110. package/dist/adapters/intake.js.map +1 -1
  111. package/dist/adapters/markdown-tables.d.ts +0 -10
  112. package/dist/adapters/markdown-tables.d.ts.map +1 -1
  113. package/dist/adapters/markdown-tables.js +1 -42
  114. package/dist/adapters/markdown-tables.js.map +1 -1
  115. package/dist/adapters/progressive-renderer.d.ts.map +1 -1
  116. package/dist/adapters/progressive-renderer.js +3 -25
  117. package/dist/adapters/progressive-renderer.js.map +1 -1
  118. package/dist/adapters/shared.d.ts +0 -57
  119. package/dist/adapters/shared.d.ts.map +1 -1
  120. package/dist/adapters/shared.js +0 -71
  121. package/dist/adapters/shared.js.map +1 -1
  122. package/dist/adapters/slack/assistant.d.ts +0 -49
  123. package/dist/adapters/slack/assistant.d.ts.map +1 -1
  124. package/dist/adapters/slack/assistant.js +0 -97
  125. package/dist/adapters/slack/assistant.js.map +1 -1
  126. package/dist/adapters/slack/auto-reply-context.d.ts +0 -7
  127. package/dist/adapters/slack/auto-reply-context.d.ts.map +1 -1
  128. package/dist/adapters/slack/auto-reply-context.js +0 -7
  129. package/dist/adapters/slack/auto-reply-context.js.map +1 -1
  130. package/dist/adapters/slack/blocks.d.ts +0 -9
  131. package/dist/adapters/slack/blocks.d.ts.map +1 -1
  132. package/dist/adapters/slack/blocks.js +0 -20
  133. package/dist/adapters/slack/blocks.js.map +1 -1
  134. package/dist/adapters/slack/bot.d.ts +0 -116
  135. package/dist/adapters/slack/bot.d.ts.map +1 -1
  136. package/dist/adapters/slack/bot.js +7 -231
  137. package/dist/adapters/slack/bot.js.map +1 -1
  138. package/dist/adapters/slack/context.d.ts.map +1 -1
  139. package/dist/adapters/slack/context.js +1 -3
  140. package/dist/adapters/slack/context.js.map +1 -1
  141. package/dist/adapters/slack/jev-context.d.ts +0 -13
  142. package/dist/adapters/slack/jev-context.d.ts.map +1 -1
  143. package/dist/adapters/slack/jev-context.js +0 -12
  144. package/dist/adapters/slack/jev-context.js.map +1 -1
  145. package/dist/adapters/slack/markdown.d.ts +0 -1
  146. package/dist/adapters/slack/markdown.d.ts.map +1 -1
  147. package/dist/adapters/slack/markdown.js +0 -5
  148. package/dist/adapters/slack/markdown.js.map +1 -1
  149. package/dist/adapters/slack/persona.d.ts +0 -9
  150. package/dist/adapters/slack/persona.d.ts.map +1 -1
  151. package/dist/adapters/slack/persona.js +0 -9
  152. package/dist/adapters/slack/persona.js.map +1 -1
  153. package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
  154. package/dist/adapters/slack/response-lifecycle.js +1 -45
  155. package/dist/adapters/slack/response-lifecycle.js.map +1 -1
  156. package/dist/adapters/slack/session.d.ts +2 -1
  157. package/dist/adapters/slack/session.d.ts.map +1 -1
  158. package/dist/adapters/slack/session.js +2 -2
  159. package/dist/adapters/slack/session.js.map +1 -1
  160. package/dist/adapters/slack/stream-limits.d.ts +0 -25
  161. package/dist/adapters/slack/stream-limits.d.ts.map +1 -1
  162. package/dist/adapters/slack/stream-limits.js +0 -34
  163. package/dist/adapters/slack/stream-limits.js.map +1 -1
  164. package/dist/adapters/slack/task-intent.d.ts +0 -13
  165. package/dist/adapters/slack/task-intent.d.ts.map +1 -1
  166. package/dist/adapters/slack/task-intent.js +0 -7
  167. package/dist/adapters/slack/task-intent.js.map +1 -1
  168. package/dist/adapters/slack/task-status.d.ts +0 -1
  169. package/dist/adapters/slack/task-status.d.ts.map +1 -1
  170. package/dist/adapters/slack/task-status.js +1 -9
  171. package/dist/adapters/slack/task-status.js.map +1 -1
  172. package/dist/adapters/slack/tool-pack.d.ts +0 -6
  173. package/dist/adapters/slack/tool-pack.d.ts.map +1 -1
  174. package/dist/adapters/slack/tool-pack.js +0 -6
  175. package/dist/adapters/slack/tool-pack.js.map +1 -1
  176. package/dist/adapters/slack/tools/blockkit.d.ts.map +1 -1
  177. package/dist/adapters/slack/tools/blockkit.js +0 -11
  178. package/dist/adapters/slack/tools/blockkit.js.map +1 -1
  179. package/dist/adapters/slack/types.d.ts +0 -19
  180. package/dist/adapters/slack/types.d.ts.map +1 -1
  181. package/dist/adapters/slack/types.js.map +1 -1
  182. package/dist/adapters/slack/update-diagnostics.d.ts +0 -1
  183. package/dist/adapters/slack/update-diagnostics.d.ts.map +1 -1
  184. package/dist/adapters/slack/update-diagnostics.js +1 -7
  185. package/dist/adapters/slack/update-diagnostics.js.map +1 -1
  186. package/dist/adapters/telegram/bot.d.ts +0 -6
  187. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  188. package/dist/adapters/telegram/bot.js +1 -50
  189. package/dist/adapters/telegram/bot.js.map +1 -1
  190. package/dist/adapters/telegram/context.d.ts.map +1 -1
  191. package/dist/adapters/telegram/context.js +1 -8
  192. package/dist/adapters/telegram/context.js.map +1 -1
  193. package/dist/adapters/types.d.ts +0 -57
  194. package/dist/adapters/types.d.ts.map +1 -1
  195. package/dist/adapters/types.js.map +1 -1
  196. package/dist/adapters/web/admin/client-assets.d.ts +4 -0
  197. package/dist/adapters/web/admin/client-assets.d.ts.map +1 -0
  198. package/dist/adapters/web/admin/client-assets.js +1765 -0
  199. package/dist/adapters/web/admin/client-assets.js.map +1 -0
  200. package/dist/adapters/web/admin/portal.d.ts +0 -10
  201. package/dist/adapters/web/admin/portal.d.ts.map +1 -1
  202. package/dist/adapters/web/admin/portal.js +6 -1909
  203. package/dist/adapters/web/admin/portal.js.map +1 -1
  204. package/dist/adapters/web/admin/types.d.ts +0 -3
  205. package/dist/adapters/web/admin/types.d.ts.map +1 -1
  206. package/dist/adapters/web/admin/types.js.map +1 -1
  207. package/dist/adapters/web/login/oauth.d.ts.map +1 -1
  208. package/dist/adapters/web/login/oauth.js +3 -7
  209. package/dist/adapters/web/login/oauth.js.map +1 -1
  210. package/dist/adapters/web/login/portal.d.ts +0 -10
  211. package/dist/adapters/web/login/portal.d.ts.map +1 -1
  212. package/dist/adapters/web/login/portal.js +4 -37
  213. package/dist/adapters/web/login/portal.js.map +1 -1
  214. package/dist/adapters/web/login/types.d.ts +0 -5
  215. package/dist/adapters/web/login/types.d.ts.map +1 -1
  216. package/dist/adapters/web/login/types.js.map +1 -1
  217. package/dist/adapters/web/portal-shell.d.ts +0 -12
  218. package/dist/adapters/web/portal-shell.d.ts.map +1 -1
  219. package/dist/adapters/web/portal-shell.js +8 -25
  220. package/dist/adapters/web/portal-shell.js.map +1 -1
  221. package/dist/adapters/web/server.d.ts +0 -1
  222. package/dist/adapters/web/server.d.ts.map +1 -1
  223. package/dist/adapters/web/server.js +0 -2
  224. package/dist/adapters/web/server.js.map +1 -1
  225. package/dist/adapters/web/session-view/portal.d.ts.map +1 -1
  226. package/dist/adapters/web/session-view/portal.js +13 -24
  227. package/dist/adapters/web/session-view/portal.js.map +1 -1
  228. package/dist/adapters/web/session-view/types.d.ts.map +1 -1
  229. package/dist/adapters/web/session-view/types.js.map +1 -1
  230. package/dist/adapters/web/token-store.d.ts +0 -2
  231. package/dist/adapters/web/token-store.d.ts.map +1 -1
  232. package/dist/adapters/web/token-store.js +0 -2
  233. package/dist/adapters/web/token-store.js.map +1 -1
  234. package/dist/cli/arg-grammar.d.ts +0 -3
  235. package/dist/cli/arg-grammar.d.ts.map +1 -1
  236. package/dist/cli/arg-grammar.js +0 -3
  237. package/dist/cli/arg-grammar.js.map +1 -1
  238. package/dist/cli/boot.d.ts.map +1 -1
  239. package/dist/cli/boot.js +3 -12
  240. package/dist/cli/boot.js.map +1 -1
  241. package/dist/cli/download.d.ts.map +1 -1
  242. package/dist/cli/download.js +2 -6
  243. package/dist/cli/download.js.map +1 -1
  244. package/dist/cli/office.d.ts.map +1 -1
  245. package/dist/cli/office.js +0 -31
  246. package/dist/cli/office.js.map +1 -1
  247. package/dist/cli/onboard.d.ts +0 -2
  248. package/dist/cli/onboard.d.ts.map +1 -1
  249. package/dist/cli/onboard.js +0 -30
  250. package/dist/cli/onboard.js.map +1 -1
  251. package/dist/cli/process-lifecycle.d.ts +0 -5
  252. package/dist/cli/process-lifecycle.d.ts.map +1 -1
  253. package/dist/cli/process-lifecycle.js +0 -6
  254. package/dist/cli/process-lifecycle.js.map +1 -1
  255. package/dist/cli/sandbox.d.ts +3 -0
  256. package/dist/cli/sandbox.d.ts.map +1 -0
  257. package/dist/cli/sandbox.js +79 -0
  258. package/dist/cli/sandbox.js.map +1 -0
  259. package/dist/cli/sessions.d.ts.map +1 -1
  260. package/dist/cli/sessions.js +0 -11
  261. package/dist/cli/sessions.js.map +1 -1
  262. package/dist/cli/types.d.ts +2 -6
  263. package/dist/cli/types.d.ts.map +1 -1
  264. package/dist/cli/types.js.map +1 -1
  265. package/dist/env-manifest.d.ts +0 -21
  266. package/dist/env-manifest.d.ts.map +1 -1
  267. package/dist/env-manifest.js +0 -11
  268. package/dist/env-manifest.js.map +1 -1
  269. package/dist/events/index.d.ts +0 -51
  270. package/dist/events/index.d.ts.map +1 -1
  271. package/dist/events/index.js +0 -42
  272. package/dist/events/index.js.map +1 -1
  273. package/dist/events/scheduler.d.ts +0 -8
  274. package/dist/events/scheduler.d.ts.map +1 -1
  275. package/dist/events/scheduler.js +0 -8
  276. package/dist/events/scheduler.js.map +1 -1
  277. package/dist/file-guards.d.ts +0 -15
  278. package/dist/file-guards.d.ts.map +1 -1
  279. package/dist/file-guards.js +2 -26
  280. package/dist/file-guards.js.map +1 -1
  281. package/dist/harness/execution-env.d.ts +0 -15
  282. package/dist/harness/execution-env.d.ts.map +1 -1
  283. package/dist/harness/execution-env.js +4 -55
  284. package/dist/harness/execution-env.js.map +1 -1
  285. package/dist/harness/http.d.ts +0 -12
  286. package/dist/harness/http.d.ts.map +1 -1
  287. package/dist/harness/http.js +0 -34
  288. package/dist/harness/http.js.map +1 -1
  289. package/dist/harness/index.d.ts +4 -7
  290. package/dist/harness/index.d.ts.map +1 -1
  291. package/dist/harness/index.js +4 -7
  292. package/dist/harness/index.js.map +1 -1
  293. package/dist/harness/jev.d.ts +0 -28
  294. package/dist/harness/jev.d.ts.map +1 -1
  295. package/dist/harness/jev.js +0 -45
  296. package/dist/harness/jev.js.map +1 -1
  297. package/dist/harness/mcp-result.d.ts +20 -0
  298. package/dist/harness/mcp-result.d.ts.map +1 -0
  299. package/dist/harness/mcp-result.js +171 -0
  300. package/dist/harness/mcp-result.js.map +1 -0
  301. package/dist/harness/mcp.d.ts +0 -19
  302. package/dist/harness/mcp.d.ts.map +1 -1
  303. package/dist/harness/mcp.js +7 -66
  304. package/dist/harness/mcp.js.map +1 -1
  305. package/dist/harness/models.d.ts +0 -19
  306. package/dist/harness/models.d.ts.map +1 -1
  307. package/dist/harness/models.js +0 -52
  308. package/dist/harness/models.js.map +1 -1
  309. package/dist/harness/open-connector.d.ts +0 -12
  310. package/dist/harness/open-connector.d.ts.map +1 -1
  311. package/dist/harness/open-connector.js +0 -22
  312. package/dist/harness/open-connector.js.map +1 -1
  313. package/dist/harness/presenter.d.ts +1 -0
  314. package/dist/harness/presenter.d.ts.map +1 -1
  315. package/dist/harness/presenter.js +16 -66
  316. package/dist/harness/presenter.js.map +1 -1
  317. package/dist/harness/prompt.d.ts +0 -11
  318. package/dist/harness/prompt.d.ts.map +1 -1
  319. package/dist/harness/prompt.js +1 -18
  320. package/dist/harness/prompt.js.map +1 -1
  321. package/dist/harness/runner.d.ts.map +1 -1
  322. package/dist/harness/runner.js +8 -28
  323. package/dist/harness/runner.js.map +1 -1
  324. package/dist/harness/session.d.ts +11 -28
  325. package/dist/harness/session.d.ts.map +1 -1
  326. package/dist/harness/session.js +100 -38
  327. package/dist/harness/session.js.map +1 -1
  328. package/dist/harness/skills.d.ts +0 -22
  329. package/dist/harness/skills.d.ts.map +1 -1
  330. package/dist/harness/skills.js +0 -20
  331. package/dist/harness/skills.js.map +1 -1
  332. package/dist/harness/subagent-profiles.d.ts +0 -4
  333. package/dist/harness/subagent-profiles.d.ts.map +1 -1
  334. package/dist/harness/subagent-profiles.js +0 -29
  335. package/dist/harness/subagent-profiles.js.map +1 -1
  336. package/dist/harness/subagent.d.ts +0 -25
  337. package/dist/harness/subagent.d.ts.map +1 -1
  338. package/dist/harness/subagent.js +1 -70
  339. package/dist/harness/subagent.js.map +1 -1
  340. package/dist/harness/tools/event.d.ts.map +1 -1
  341. package/dist/harness/tools/event.js +0 -9
  342. package/dist/harness/tools/event.js.map +1 -1
  343. package/dist/harness/tools/generate-image.d.ts +0 -6
  344. package/dist/harness/tools/generate-image.d.ts.map +1 -1
  345. package/dist/harness/tools/generate-image.js.map +1 -1
  346. package/dist/harness/tools/host-fn-tool.d.ts +0 -17
  347. package/dist/harness/tools/host-fn-tool.d.ts.map +1 -1
  348. package/dist/harness/tools/host-fn-tool.js +0 -16
  349. package/dist/harness/tools/host-fn-tool.js.map +1 -1
  350. package/dist/harness/tools/index.d.ts +1 -7
  351. package/dist/harness/tools/index.d.ts.map +1 -1
  352. package/dist/harness/tools/index.js +3 -14
  353. package/dist/harness/tools/index.js.map +1 -1
  354. package/dist/harness/tools/jev-browser.d.ts +4 -94
  355. package/dist/harness/tools/jev-browser.d.ts.map +1 -1
  356. package/dist/harness/tools/jev-browser.js +203 -92
  357. package/dist/harness/tools/jev-browser.js.map +1 -1
  358. package/dist/harness/tools/jev.d.ts +0 -8
  359. package/dist/harness/tools/jev.d.ts.map +1 -1
  360. package/dist/harness/tools/jev.js +0 -11
  361. package/dist/harness/tools/jev.js.map +1 -1
  362. package/dist/harness/tools/pi-tools.d.ts +1 -11
  363. package/dist/harness/tools/pi-tools.d.ts.map +1 -1
  364. package/dist/harness/tools/pi-tools.js +1 -30
  365. package/dist/harness/tools/pi-tools.js.map +1 -1
  366. package/dist/harness/tools/react.d.ts +0 -5
  367. package/dist/harness/tools/react.d.ts.map +1 -1
  368. package/dist/harness/tools/react.js +0 -5
  369. package/dist/harness/tools/react.js.map +1 -1
  370. package/dist/harness/tools/secret-redaction.d.ts +0 -25
  371. package/dist/harness/tools/secret-redaction.d.ts.map +1 -1
  372. package/dist/harness/tools/secret-redaction.js +3 -36
  373. package/dist/harness/tools/secret-redaction.js.map +1 -1
  374. package/dist/harness/tools/subagent.d.ts +0 -34
  375. package/dist/harness/tools/subagent.d.ts.map +1 -1
  376. package/dist/harness/tools/subagent.js +0 -92
  377. package/dist/harness/tools/subagent.js.map +1 -1
  378. package/dist/harness/tools/task.d.ts +0 -1
  379. package/dist/harness/tools/task.d.ts.map +1 -1
  380. package/dist/harness/tools/task.js +0 -1
  381. package/dist/harness/tools/task.js.map +1 -1
  382. package/dist/harness/tools/types.d.ts +0 -16
  383. package/dist/harness/tools/types.d.ts.map +1 -1
  384. package/dist/harness/tools/types.js.map +1 -1
  385. package/dist/harness/types.d.ts +1 -77
  386. package/dist/harness/types.d.ts.map +1 -1
  387. package/dist/harness/types.js.map +1 -1
  388. package/dist/index.d.ts.map +1 -1
  389. package/dist/index.js +0 -2
  390. package/dist/index.js.map +1 -1
  391. package/dist/log.d.ts.map +1 -1
  392. package/dist/log.js +0 -13
  393. package/dist/log.js.map +1 -1
  394. package/dist/main.js +7 -52
  395. package/dist/main.js.map +1 -1
  396. package/dist/memory-capture/index.d.ts +16 -0
  397. package/dist/memory-capture/index.d.ts.map +1 -0
  398. package/dist/memory-capture/index.js +194 -0
  399. package/dist/memory-capture/index.js.map +1 -0
  400. package/dist/memory-capture/types.d.ts +27 -0
  401. package/dist/memory-capture/types.d.ts.map +1 -0
  402. package/dist/memory-capture/types.js.map +1 -0
  403. package/dist/observability/index.d.ts +0 -9
  404. package/dist/observability/index.d.ts.map +1 -1
  405. package/dist/observability/index.js +0 -9
  406. package/dist/observability/index.js.map +1 -1
  407. package/dist/observability/instrument.js +0 -5
  408. package/dist/observability/instrument.js.map +1 -1
  409. package/dist/observability/otel.js +0 -1
  410. package/dist/observability/otel.js.map +1 -1
  411. package/dist/observability/sentry.d.ts +0 -18
  412. package/dist/observability/sentry.d.ts.map +1 -1
  413. package/dist/observability/sentry.js +0 -36
  414. package/dist/observability/sentry.js.map +1 -1
  415. package/dist/observability/types.d.ts +1 -10
  416. package/dist/observability/types.d.ts.map +1 -1
  417. package/dist/observability/types.js.map +1 -1
  418. package/dist/office/index.d.ts +0 -76
  419. package/dist/office/index.d.ts.map +1 -1
  420. package/dist/office/index.js +4 -154
  421. package/dist/office/index.js.map +1 -1
  422. package/dist/office/projection.d.ts +0 -29
  423. package/dist/office/projection.d.ts.map +1 -1
  424. package/dist/office/projection.js +5 -41
  425. package/dist/office/projection.js.map +1 -1
  426. package/dist/office/types.d.ts +0 -66
  427. package/dist/office/types.d.ts.map +1 -1
  428. package/dist/office/types.js.map +1 -1
  429. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  430. package/dist/runtime/conversation-runtime.js +22 -83
  431. package/dist/runtime/conversation-runtime.js.map +1 -1
  432. package/dist/runtime/session-lifecycle.d.ts +0 -10
  433. package/dist/runtime/session-lifecycle.d.ts.map +1 -1
  434. package/dist/runtime/session-lifecycle.js +0 -12
  435. package/dist/runtime/session-lifecycle.js.map +1 -1
  436. package/dist/runtime/types.d.ts +2 -29
  437. package/dist/runtime/types.d.ts.map +1 -1
  438. package/dist/runtime/types.js.map +1 -1
  439. package/dist/sandbox/cloudflare.d.ts.map +1 -1
  440. package/dist/sandbox/cloudflare.js +2 -1
  441. package/dist/sandbox/cloudflare.js.map +1 -1
  442. package/dist/sandbox/container.d.ts.map +1 -1
  443. package/dist/sandbox/container.js +21 -15
  444. package/dist/sandbox/container.js.map +1 -1
  445. package/dist/sandbox/host.d.ts.map +1 -1
  446. package/dist/sandbox/host.js +0 -2
  447. package/dist/sandbox/host.js.map +1 -1
  448. package/dist/sandbox/identity.d.ts +0 -28
  449. package/dist/sandbox/identity.d.ts.map +1 -1
  450. package/dist/sandbox/identity.js +0 -28
  451. package/dist/sandbox/identity.js.map +1 -1
  452. package/dist/sandbox/index.d.ts +0 -18
  453. package/dist/sandbox/index.d.ts.map +1 -1
  454. package/dist/sandbox/index.js +2 -19
  455. package/dist/sandbox/index.js.map +1 -1
  456. package/dist/sandbox/layout.d.ts +7 -0
  457. package/dist/sandbox/layout.d.ts.map +1 -0
  458. package/dist/sandbox/layout.js +13 -0
  459. package/dist/sandbox/layout.js.map +1 -0
  460. package/dist/sandbox/provisioner.d.ts +24 -65
  461. package/dist/sandbox/provisioner.d.ts.map +1 -1
  462. package/dist/sandbox/provisioner.js +213 -125
  463. package/dist/sandbox/provisioner.js.map +1 -1
  464. package/dist/sandbox/types.d.ts +0 -39
  465. package/dist/sandbox/types.d.ts.map +1 -1
  466. package/dist/sandbox/types.js.map +1 -1
  467. package/dist/sandbox/utils.d.ts +2 -5
  468. package/dist/sandbox/utils.d.ts.map +1 -1
  469. package/dist/sandbox/utils.js +31 -31
  470. package/dist/sandbox/utils.js.map +1 -1
  471. package/dist/sessions/chat-history-sync.d.ts +0 -6
  472. package/dist/sessions/chat-history-sync.d.ts.map +1 -1
  473. package/dist/sessions/chat-history-sync.js +1 -33
  474. package/dist/sessions/chat-history-sync.js.map +1 -1
  475. package/dist/sessions/history-line.d.ts +0 -20
  476. package/dist/sessions/history-line.d.ts.map +1 -1
  477. package/dist/sessions/history-line.js +0 -20
  478. package/dist/sessions/history-line.js.map +1 -1
  479. package/dist/sessions/migrate-common.d.ts +0 -15
  480. package/dist/sessions/migrate-common.d.ts.map +1 -1
  481. package/dist/sessions/migrate-common.js +0 -19
  482. package/dist/sessions/migrate-common.js.map +1 -1
  483. package/dist/sessions/migrate-pi-084.d.ts.map +1 -1
  484. package/dist/sessions/migrate-pi-084.js +0 -7
  485. package/dist/sessions/migrate-pi-084.js.map +1 -1
  486. package/dist/sessions/migrate-v3.d.ts +0 -7
  487. package/dist/sessions/migrate-v3.d.ts.map +1 -1
  488. package/dist/sessions/migrate-v3.js +1 -67
  489. package/dist/sessions/migrate-v3.js.map +1 -1
  490. package/dist/sessions/session-key.d.ts +3 -17
  491. package/dist/sessions/session-key.d.ts.map +1 -1
  492. package/dist/sessions/session-key.js +8 -35
  493. package/dist/sessions/session-key.js.map +1 -1
  494. package/dist/sessions/session-store.d.ts +1 -7
  495. package/dist/sessions/session-store.d.ts.map +1 -1
  496. package/dist/sessions/session-store.js +8 -50
  497. package/dist/sessions/session-store.js.map +1 -1
  498. package/dist/sessions/store.d.ts +0 -60
  499. package/dist/sessions/store.d.ts.map +1 -1
  500. package/dist/sessions/store.js +0 -113
  501. package/dist/sessions/store.js.map +1 -1
  502. package/dist/sessions/types.d.ts +0 -17
  503. package/dist/sessions/types.d.ts.map +1 -1
  504. package/dist/sessions/types.js.map +1 -1
  505. package/dist/settings/apply.d.ts +0 -6
  506. package/dist/settings/apply.d.ts.map +1 -1
  507. package/dist/settings/apply.js +0 -29
  508. package/dist/settings/apply.js.map +1 -1
  509. package/dist/settings/index.d.ts +0 -53
  510. package/dist/settings/index.d.ts.map +1 -1
  511. package/dist/settings/index.js +0 -77
  512. package/dist/settings/index.js.map +1 -1
  513. package/dist/settings/migrate.d.ts +0 -5
  514. package/dist/settings/migrate.d.ts.map +1 -1
  515. package/dist/settings/migrate.js +0 -17
  516. package/dist/settings/migrate.js.map +1 -1
  517. package/dist/types.d.ts +9 -159
  518. package/dist/types.d.ts.map +1 -1
  519. package/dist/types.js.map +1 -1
  520. package/dist/vault/index.d.ts +2 -36
  521. package/dist/vault/index.d.ts.map +1 -1
  522. package/dist/vault/index.js +135 -65
  523. package/dist/vault/index.js.map +1 -1
  524. package/dist/vault/types.d.ts +0 -15
  525. package/dist/vault/types.d.ts.map +1 -1
  526. package/dist/vault/types.js.map +1 -1
  527. package/package.json +1 -1
  528. package/dist/dream/index.d.ts +0 -20
  529. package/dist/dream/index.d.ts.map +0 -1
  530. package/dist/dream/index.js +0 -264
  531. package/dist/dream/index.js.map +0 -1
  532. package/dist/dream/types.d.ts +0 -30
  533. package/dist/dream/types.d.ts.map +0 -1
  534. package/dist/dream/types.js.map +0 -1
  535. /package/dist/{dream → memory-capture}/types.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n ChatToolResult,\n MessagingBot,\n ConversationContext,\n ConversationEvent,\n MessagingEventHandler,\n OfficeAddress,\n SubagentProgressSnapshot,\n} from \"./index.js\";\nimport type { Attachment } from \"../types.js\";\n\nexport type ChatResponseErrorOperation =\n | \"respond\"\n | \"replace_response\"\n | \"respond_diagnostic\"\n | \"set_working\";\n\nexport interface ChatResponseErrorContext {\n platform: string;\n conversationId: string;\n messageId: string;\n sessionKey: string;\n conversationKind: string;\n operation: ChatResponseErrorOperation;\n channelId?: string;\n chatId?: number;\n responseMessageId?: string | number | null;\n threadTs?: string;\n replyTargetId?: string;\n replyToId?: number | null;\n isThreaded?: boolean;\n extra?: Record<string, unknown>;\n}\n\nexport type ChatResponseErrorReporter = (\n err: unknown,\n operation: ChatResponseErrorOperation,\n extra?: Record<string, unknown>,\n) => void;\n\nexport interface RetryOptions {\n /** Predicate that returns true when an error is worth retrying (rate limit, transient 5xx, etc.). */\n isRateLimited: (err: Error) => boolean;\n /** Total attempts including the first call. */\n maxAttempts?: number;\n baseDelayMs?: number;\n}\n\nexport interface ResolveStopTargetInput {\n handler: MessagingEventHandler;\n /** The office whose runtime state may be stopped. */\n address: OfficeAddress;\n /** Session key derived from the current message; checked first when present. */\n sessionKey?: string;\n}\n\nexport interface HandleTooLongInput {\n text: string;\n operation: \"render\" | \"replace\";\n options: { createOverflowLink?: () => string } | undefined;\n responseId: string | null;\n write: (text: string) => Promise<void>;\n getResponseId: () => string | null;\n}\n\ninterface ProgressiveStreamTransport {\n start(text: string): Promise<string>;\n append(messageId: string, delta: string): Promise<void>;\n stop(messageId: string): Promise<void>;\n /**\n * Withhold an append until this many characters have accumulated. A\n * streaming transport is usually rate-limited per call rather than per byte,\n * so forwarding every token spends the budget on latency nobody perceives.\n * The final render flushes whatever is left before stopping, so buffering\n * can never drop text. Omit for no buffering.\n */\n minDeltaChars?: number;\n}\n\nexport interface ProgressiveRendererPlatform {\n label: string;\n maxLength: number;\n /**\n * Shortest gap between redraws of a streaming response, measured from the\n * end of the previous one. Platforms meter edits per channel at different\n * rates; the default suits the tightest of them. Zero redraws on every\n * delta, which only a platform with no such limit can afford.\n */\n flushIntervalMs?: number;\n initialResponseId?: string | null;\n formatContinuation: (partNum: number) => string;\n errorPrefix: string;\n sanitize?: (text: string) => string;\n workingIndicator?: string;\n formatProvisional?: (text: string, working: boolean) => string;\n prepareSource?: (text: string, working: boolean) => string;\n onWorkingChanged?: (working: boolean, responseId: string | null) => Promise<void>;\n setTyping?: (isTyping: boolean, responseId: string | null) => Promise<void>;\n /** Terminal hook. Awaited, so it may finish sending before the run ends. */\n onFinish?: (text: string, responseId: string | null) => void | Promise<void>;\n /** Whether `respond` calls are complete messages that should be logged immediately. */\n logIntermediateResponses?: boolean;\n /** Whether the platform accepts incremental response deltas. */\n supportsDeltas?: boolean;\n /** Native stream operations, when this reply target supports them. */\n stream?: ProgressiveStreamTransport;\n /** Re-render a finished response when the provisional stream lacked structure. */\n needsCanonicalRender?: (text: string) => boolean;\n formatSubagentProgress?: (progress: SubagentProgressSnapshot) => string;\n typing?: {\n send: () => Promise<unknown>;\n intervalMs: number;\n stopOnSend?: boolean;\n };\n formatToolResult: (result: ChatToolResult) => string;\n /** Stable platform context plus the current response identity for error reporting. */\n responseErrorContext?: (\n responseId: string | null,\n ) => Omit<ChatResponseErrorContext, \"operation\" | \"extra\">;\n notifySendFailure?: (errorMessage: string) => Promise<void>;\n post: (text: string) => Promise<string>;\n update: (id: string, text: string) => Promise<void>;\n postExtra: (text: string, responseId: string | null) => Promise<string | number | void>;\n postDiagnostic?: (\n text: string,\n options: { style?: \"muted\" | \"error\" },\n responseId: string | null,\n ) => Promise<Array<string | number>>;\n delete?: (id: string) => Promise<void>;\n deleteExtra?: (id: string | number) => Promise<void>;\n logBotResponse?: (text: string, id: string) => void;\n uploadFile?: (filePath: string, title?: string) => Promise<void>;\n uploadFallbackNote?: (name: string) => string;\n react?: (emoji: string) => Promise<void>;\n /**\n * Whether a send failure is a length rejection, as opposed to a rate limit\n * or an outage. Without this the fallback swallows every error, so a\n * transient 429 gets reported to the reader as \"message too long\" and the\n * response is truncated for no reason.\n */\n isTooLongError?: (err: unknown) => boolean;\n /** Handle a Slack-style length rejection and return the canonical fallback text. */\n handleTooLong?: (input: HandleTooLongInput) => Promise<{ text: string; prefixLength?: number }>;\n}\n\n/** How intake resolved a message. Callers branch on this instead of passing callbacks. */\nexport type MessageIntakeOutcome = \"magic-word\" | \"not-triggered\" | \"rejected-busy\" | \"enqueued\";\n\n/** Platform policy for the magic-word step, stated as data. */\nexport interface MagicWordIntakeOptions {\n /**\n * Raw user-typed text to match when `eventBase.text` is decorated for the\n * agent (e.g. GitHub review-comment framing). Defaults to `eventBase.text`.\n */\n text?: string;\n /**\n * Whether the message addressed the bot (mention, DM, slash form). The idle\n * \"Nothing running.\" reply is suppressed for unaddressed messages.\n */\n addressed: boolean;\n /**\n * How far a stop may widen when the direct session key is not running:\n * \"top-level\" widens to the single running scoped session only from the\n * persistent conversation session, \"always\" widens unconditionally,\n * \"never\" stops only exact matches.\n */\n scopeFallback: \"top-level\" | \"always\" | \"never\";\n}\n\n/** One platform file to save into an office's attachments directory. */\nexport interface IncomingAttachment {\n /** Original file name as the platform reports it. */\n name: string;\n /** Epoch ms used in the stored filename; defaults to the time of saving. */\n timestampMs?: number;\n /** Fetch the file and write it to destPath (the parent directory exists). */\n download(destPath: string): Promise<void>;\n}\n\n/** Outcome of saving incoming attachments; error policy stays with the adapter. */\nexport interface SavedAttachments {\n saved: Attachment[];\n failed: { name: string; error: unknown }[];\n}\n\nexport interface MessageIntakeOptions<TEvent extends ConversationEvent> {\n eventBase: TEvent;\n /** Explicit DM, mention, command or supported thread continuation. */\n addressed: boolean;\n magicWord: MagicWordIntakeOptions;\n /**\n * \"reject\" bounces a new message while its session is already running\n * (with an \"Already working\" reply); \"queue\" lines it up behind the run.\n */\n busyPolicy: \"queue\" | \"reject\";\n logEntryBase: Record<string, unknown>;\n log?: (entry: Record<string, unknown>) => void;\n processAttachments: () => Promise<unknown[]>;\n queueKey: string;\n enqueue: (queueKey: string, work: () => Promise<void>) => void;\n handler: MessagingEventHandler;\n bot: MessagingBot;\n createContext: (event: TEvent) => ConversationContext;\n deferAttachmentsUntilRun?: boolean;\n}\n\nexport interface MarkdownTable {\n headers: string[];\n rows: string[][];\n /** Source line span, so a caller can slice the prose around it. */\n startLine: number;\n endLine: number;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n ChatToolResult,\n MessagingBot,\n ConversationContext,\n ConversationEvent,\n MessagingEventHandler,\n OfficeAddress,\n SubagentProgressSnapshot,\n} from \"./index.js\";\nimport type { Attachment } from \"../types.js\";\n\nexport type ChatResponseErrorOperation =\n | \"respond\"\n | \"replace_response\"\n | \"respond_diagnostic\"\n | \"set_working\";\n\nexport interface ChatResponseErrorContext {\n platform: string;\n conversationId: string;\n messageId: string;\n sessionKey: string;\n conversationKind: string;\n operation: ChatResponseErrorOperation;\n channelId?: string;\n chatId?: number;\n responseMessageId?: string | number | null;\n threadTs?: string;\n replyTargetId?: string;\n replyToId?: number | null;\n isThreaded?: boolean;\n extra?: Record<string, unknown>;\n}\n\nexport type ChatResponseErrorReporter = (\n err: unknown,\n operation: ChatResponseErrorOperation,\n extra?: Record<string, unknown>,\n) => void;\n\nexport interface RetryOptions {\n isRateLimited: (err: Error) => boolean;\n maxAttempts?: number;\n baseDelayMs?: number;\n}\n\nexport interface ResolveStopTargetInput {\n handler: MessagingEventHandler;\n address: OfficeAddress;\n sessionKey?: string;\n}\n\nexport interface HandleTooLongInput {\n text: string;\n operation: \"render\" | \"replace\";\n options: { createOverflowLink?: () => string } | undefined;\n responseId: string | null;\n write: (text: string) => Promise<void>;\n getResponseId: () => string | null;\n}\n\ninterface ProgressiveStreamTransport {\n start(text: string): Promise<string>;\n append(messageId: string, delta: string): Promise<void>;\n stop(messageId: string): Promise<void>;\n minDeltaChars?: number;\n}\n\nexport interface ProgressiveRendererPlatform {\n label: string;\n maxLength: number;\n flushIntervalMs?: number;\n initialResponseId?: string | null;\n formatContinuation: (partNum: number) => string;\n errorPrefix: string;\n sanitize?: (text: string) => string;\n workingIndicator?: string;\n formatProvisional?: (text: string, working: boolean) => string;\n prepareSource?: (text: string, working: boolean) => string;\n onWorkingChanged?: (working: boolean, responseId: string | null) => Promise<void>;\n setTyping?: (isTyping: boolean, responseId: string | null) => Promise<void>;\n onFinish?: (text: string, responseId: string | null) => void | Promise<void>;\n logIntermediateResponses?: boolean;\n supportsDeltas?: boolean;\n stream?: ProgressiveStreamTransport;\n needsCanonicalRender?: (text: string) => boolean;\n formatSubagentProgress?: (progress: SubagentProgressSnapshot) => string;\n typing?: {\n send: () => Promise<unknown>;\n intervalMs: number;\n stopOnSend?: boolean;\n };\n formatToolResult: (result: ChatToolResult) => string;\n responseErrorContext?: (\n responseId: string | null,\n ) => Omit<ChatResponseErrorContext, \"operation\" | \"extra\">;\n notifySendFailure?: (errorMessage: string) => Promise<void>;\n post: (text: string) => Promise<string>;\n update: (id: string, text: string) => Promise<void>;\n postExtra: (text: string, responseId: string | null) => Promise<string | number | void>;\n postDiagnostic?: (\n text: string,\n options: { style?: \"muted\" | \"error\" },\n responseId: string | null,\n ) => Promise<Array<string | number>>;\n delete?: (id: string) => Promise<void>;\n deleteExtra?: (id: string | number) => Promise<void>;\n logBotResponse?: (text: string, id: string) => void;\n uploadFile?: (filePath: string, title?: string) => Promise<void>;\n uploadFallbackNote?: (name: string) => string;\n react?: (emoji: string) => Promise<void>;\n isTooLongError?: (err: unknown) => boolean;\n handleTooLong?: (input: HandleTooLongInput) => Promise<{ text: string; prefixLength?: number }>;\n}\n\nexport type MessageIntakeOutcome = \"magic-word\" | \"not-triggered\" | \"rejected-busy\" | \"enqueued\";\n\nexport interface MagicWordIntakeOptions {\n text?: string;\n addressed: boolean;\n scopeFallback: \"top-level\" | \"always\" | \"never\";\n}\n\nexport interface IncomingAttachment {\n name: string;\n timestampMs?: number;\n download(destPath: string): Promise<void>;\n}\n\nexport interface SavedAttachments {\n saved: Attachment[];\n failed: { name: string; error: unknown }[];\n}\n\nexport interface MessageIntakeOptions<TEvent extends ConversationEvent> {\n eventBase: TEvent;\n addressed: boolean;\n magicWord: MagicWordIntakeOptions;\n busyPolicy: \"queue\" | \"reject\";\n logEntryBase: Record<string, unknown>;\n log?: (entry: Record<string, unknown>) => void;\n processAttachments: () => Promise<unknown[]>;\n queueKey: string;\n enqueue: (queueKey: string, work: () => Promise<void>) => void;\n handler: MessagingEventHandler;\n bot: MessagingBot;\n createContext: (event: TEvent) => ConversationContext;\n deferAttachmentsUntilRun?: boolean;\n}\n\nexport interface MarkdownTable {\n headers: string[];\n rows: string[][];\n startLine: number;\n endLine: number;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ export declare const adminViewFunctionsScript = " let activeConversationKey = defaultConversationKey;\n function scopeOf(key) {\n const sep = key.indexOf(':');\n return { platform: key.slice(0, sep), conversationId: key.slice(sep + 1) };\n }\n function scopeQuery() {\n const scope = scopeOf(activeConversationKey);\n return 'conversationId=' + encodeURIComponent(scope.conversationId) +\n '&platform=' + encodeURIComponent(scope.platform);\n }\n function scopeBody() {\n const scope = scopeOf(activeConversationKey);\n return { conversationId: scope.conversationId, platform: scope.platform };\n }\n let availableModels = [];\n let modelsLoaded = false;\n let mcpPresets = [];\n let mcpServersByScope = { conversation: {}, global: {} };\n let pendingMcpInstall = null;\n\n\n function escHtml(str) {\n return String(str).replace(/[&<>\"']/g, (c) => (\n {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]\n ));\n }\n const escAttr = escHtml;\n async function copyToClipboard(text) {\n try { await navigator.clipboard.writeText(text); } catch { prompt('Copy this link:', text); }\n }\n document.addEventListener('click', (event) => {\n const target = event.target instanceof Element\n ? event.target.closest('[data-admin-action]')\n : null;\n if (!target) return;\n switch (target.dataset.adminAction) {\n case 'preview-file':\n void previewFile(target.dataset.filePath || '');\n break;\n case 'copy-link':\n void copyToClipboard(target.dataset.copyText || '');\n break;\n case 'delete-event':\n void deleteEvent(target.dataset.eventName || '', target);\n break;\n case 'select-conversation':\n setActiveConversation(target.dataset.conversationId || '');\n switchTab('conversation');\n break;\n case 'toggle-timeline-filter':\n toggleTimelineFilter(target.dataset.filterKey || '');\n break;\n }\n });\n async function apiGet(path) {\n const url = path + (path.includes('?') ? '&' : '?') + 'token=' + encodeURIComponent(adminToken);\n const r = await fetch(url);\n const data = await r.json();\n if (!r.ok) throw new Error(data.error || ('HTTP ' + r.status));\n return data;\n }\n async function apiPost(path, body) {\n const r = await fetch(path, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ token: adminToken, ...body }),\n });\n const data = await r.json();\n if (!r.ok) throw new Error(data.error || ('HTTP ' + r.status));\n return data;\n }\n async function loadModels() {\n try {\n const data = await apiGet('/admin/api/models');\n availableModels = Array.isArray(data.models) ? data.models : [];\n } catch (err) {\n availableModels = [];\n } finally {\n modelsLoaded = true;\n }\n }\n function modelRef(provider, model) {\n return provider && model ? provider + '/' + model : '';\n }\n function parseModelRef(value) {\n const slash = value.indexOf('/');\n if (slash <= 0 || slash === value.length - 1) return { provider: '', model: '' };\n return { provider: value.slice(0, slash), model: value.slice(slash + 1) };\n }\n function renderModelOptions(currentProvider, currentModel) {\n const current = modelRef(currentProvider, currentModel);\n const seen = new Set();\n const groups = {\n available: [],\n unverified: [],\n };\n if (current) {\n seen.add(current);\n groups.available.push('<option value=\"' + escAttr(current) + '\">' + escHtml(current + ' (current)') + '</option>');\n }\n for (const model of availableModels) {\n const ref = modelRef(model.provider, model.id);\n if (!ref || seen.has(ref)) continue;\n seen.add(ref);\n const details = [model.name && model.name !== model.id ? model.name : '', model.reasoning ? 'thinking' : '', Array.isArray(model.input) && model.input.includes('image') ? 'image' : '']\n .filter(Boolean)\n .join(' \u00B7 ');\n const option = '<option value=\"' + escAttr(ref) + '\">' + escHtml(details ? ref + ' \u2014 ' + details : ref) + '</option>';\n if (model.status === 'unverified') groups.unverified.push(option);\n else groups.available.push(option);\n }\n const sections = [];\n if (groups.available.length > 0) sections.push('<optgroup label=\"Available\">' + groups.available.join('') + '</optgroup>');\n if (groups.unverified.length > 0) sections.push('<optgroup label=\"Configured but unverified\">' + groups.unverified.join('') + '</optgroup>');\n if (sections.length === 0) {\n return '<option value=\"\">No available models</option>';\n }\n return sections.join('');\n }\n\n\n const scopeBtns = document.querySelectorAll('.rail-scope-btn');\n const tabPanels = document.querySelectorAll('.tab-panel');\n const railNavConversation = document.getElementById('rail-nav-conversation');\n const railNavGlobal = document.getElementById('rail-nav-global');\n\n function switchTab(tabId) {\n scopeBtns.forEach((btn) => {\n const active = btn.dataset.tab === tabId;\n btn.classList.toggle('active', active);\n btn.setAttribute('aria-selected', active ? 'true' : 'false');\n });\n tabPanels.forEach((panel) => panel.classList.toggle('active', panel.id === 'panel-' + tabId));\n railNavConversation.hidden = tabId !== 'conversation';\n railNavGlobal.hidden = tabId !== 'global';\n if (tabId === 'global') initGlobal();\n const firstPane = (tabId === 'global' ? railNavGlobal : railNavConversation).querySelector('.rail-link');\n if (firstPane) selectPane(tabId, firstPane.dataset.pane);\n }\n scopeBtns.forEach((btn) => btn.addEventListener('click', () => switchTab(btn.dataset.tab)));\n\n\n function paneLoader(key) {\n switch (key) {\n case 'settings': return loadSettings;\n case 'workspace': return loadWorkspace;\n case 'skills': return loadSkills;\n case 'mcp': return loadMcpServers;\n case 'events': return loadConversationEvents;\n case 'g-overview': return loadAllConversations;\n case 'g-usage': return loadTokenUsage;\n case 'g-settings': return loadGlobalSettings;\n case 'g-mcp': return loadMcpServers;\n case 'g-skills': return loadGlobalSkills;\n case 'g-events': return loadEvents;\n default: return undefined;\n }\n }\n const paneLoaded = new Set();\n\n function ensurePaneLoaded(key) {\n if (paneLoaded.has(key)) return;\n const loader = paneLoader(key);\n if (!loader) return;\n paneLoaded.add(key);\n loader();\n }\n\n function selectPane(scope, key) {\n const nav = scope === 'global' ? railNavGlobal : railNavConversation;\n const panel = document.getElementById(scope === 'global' ? 'panel-global' : 'panel-conversation');\n nav.querySelectorAll('.rail-link').forEach((btn) => {\n const active = btn.dataset.pane === key;\n btn.classList.toggle('active', active);\n btn.setAttribute('aria-current', active ? 'page' : 'false');\n });\n panel.querySelectorAll('.pane').forEach((pane) => {\n pane.classList.toggle('active', pane.dataset.pane === key);\n });\n ensurePaneLoaded(key);\n }\n\n function initRailNav() {\n document.querySelectorAll('.rail-link').forEach((btn) => {\n btn.addEventListener('click', () => {\n const nav = btn.closest('.rail-nav');\n const scope = nav.dataset.scope;\n selectPane(scope, btn.dataset.pane);\n });\n });\n selectPane('conversation', 'settings');\n }\n\n\n async function initConvSwitcher() {\n const sel = document.getElementById('conv-switcher');\n try {\n const data = await apiGet('/admin/api/conversations');\n sel.innerHTML = data.conversations.map((c) => {\n const key = c.platform + ':' + c.conversationId;\n const label = (c.label || c.conversationId) + (c.running ? ' (running)' : '');\n const selected = key === defaultConversationKey ? ' selected' : '';\n return '<option value=\"' + escAttr(key) + '\"' + selected + '>' + escHtml(label) + '</option>';\n }).join('');\n sel.addEventListener('change', () => setActiveConversation(sel.value));\n } catch (err) {\n }\n }\n\n function setActiveConversation(key) {\n activeConversationKey = key;\n const sel = document.getElementById('conv-switcher');\n if (sel && sel.value !== key) sel.value = key;\n const activeLink = railNavConversation.querySelector('.rail-link.active');\n ['settings', 'workspace', 'skills', 'mcp', 'events'].forEach((key) => paneLoaded.delete(key));\n if (activeLink) ensurePaneLoaded(activeLink.dataset.pane);\n openLogin(true);\n openSessionView(true);\n }\n\n\n const loadSettings = () => loadSettingsPanel('settings-content', () => '/admin/api/conversation-state?' + scopeQuery(), renderSettings);\n const loadGlobalSettings = () => loadSettingsPanel('global-settings-content', () => '/admin/api/settings/global', renderGlobalSettings);\n\n async function loadSettingsPanel(id, path, render) {\n const container = document.getElementById(id);\n container.innerHTML = '<div class=\"loading-msg\">Loading\u2026</div>';\n if (!modelsLoaded) await loadModels();\n try {\n const data = await apiGet(path());\n container.innerHTML = render(data);\n } catch (err) {\n container.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n function renderConfigCard(title, content, action, buttonLabel, resultId) {\n return '<div class=\"config-block\"><h3 class=\"card-subtitle\">' + title + '</h3>' + content +\n '<button class=\"primary-action-btn\" onclick=\"' + action + '(this)\">' + buttonLabel + '</button>' +\n '<div id=\"' + resultId + '\" class=\"inline-result\" style=\"display:none\"></div></div>';\n }\n\n function renderOptions(choices, selected) {\n return choices.map(([value, label]) =>\n '<option value=\"' + escAttr(value) + '\"' + (selected === value ? ' selected' : '') + '>' + escHtml(label) + '</option>'\n ).join('');\n }\n function renderThinkingOptions(value) {\n return renderOptions(['off','minimal','low','medium','high','xhigh','max'].map((t) => [t, t]), value);\n }\n function renderReplyOptions(slack) {\n return renderOptions(['top-level','thread'].map((m) => [m, m]), (slack && slack.replyMode) || 'top-level');\n }\n function renderSettings(data) {\n const thinkingOpts = renderThinkingOptions(data.thinkingLevel);\n const replyModeOpts = renderReplyOptions(data.slack);\n const globalReplyMode = (data.slack && data.slack.globalReplyMode) || 'top-level';\n const globalModel = [data.globalProvider, data.globalModel].filter(Boolean).join('/');\n const globalModelLabel = globalModel + (data.globalThinkingLevel ? ':' + data.globalThinkingLevel : '');\n return [\n '<div class=\"config-grid\">',\n renderConfigCard('Model', [\n '<div class=\"config-row config-row-stack\"><label>Model</label><select id=\"m-model-ref\">' + renderModelOptions(data.provider, data.model) + '</select></div>',\n '<div class=\"config-row\"><label>Thinking</label><select id=\"m-thinking\">' + thinkingOpts + '</select></div>',\n '<p class=\"muted-note\">Global default: ' + escHtml(globalModelLabel) + '</p>',\n ].join(''), 'saveModel', 'Save model', 'model-save-result'),\n\n renderVisibilityCard(data),\n renderConfigCard('Slack', [\n '<div class=\"config-row\"><label>Reply mode</label><select id=\"m-slack-reply-mode\">' + replyModeOpts + '</select></div>',\n '<p class=\"muted-note\">Global default: ' + escHtml(globalReplyMode) + '</p>',\n ].join(''), 'saveSlack', 'Save Slack', 'slack-save-result'),\n '</div>',\n ].join('');\n }\n\n async function saveModel(btn) {\n const selectedModel = parseModelRef(document.getElementById('m-model-ref').value.trim());\n const provider = selectedModel.provider;\n const model = selectedModel.model;\n const thinkingLevel = document.getElementById('m-thinking').value;\n const result = document.getElementById('model-save-result');\n if (!provider || !model) {\n result.style.display = 'block'; result.className = 'inline-result err';\n result.textContent = 'Provider and model are required';\n return;\n }\n await saveConversationSetting(btn, result, 'model', { provider, model, thinkingLevel }, 'Save model');\n }\n\n async function saveSlack(btn) {\n const replyMode = document.getElementById('m-slack-reply-mode').value;\n const result = document.getElementById('slack-save-result');\n await saveConversationSetting(btn, result, 'slack', { replyMode }, 'Save Slack');\n }\n\n function renderVisibilityCard(data) {\n const hidden = data.officeVisibilityOverride === 'private';\n const canChoose = data.officeVisibilitySource === 'platform' ? data.officeVisibility === 'public' : hidden;\n if (!canChoose) {\n const why = data.officeVisibilitySource === 'platform'\n ? 'This is a DM, a private channel, or a conversation on a platform other than Slack, so its files are hidden from every other office. The platform decides this; it cannot be opened up here.'\n : 'Slack has not reported what kind of conversation this is yet, so it is treated as hidden until it does.';\n return '<div class=\"config-block\"><h3 class=\"card-subtitle\">Who can see the files in this office</h3>' +\n '<p><strong>Hidden</strong> \u2014 only this office.</p><p class=\"muted-note\">' + why + '</p></div>';\n }\n return renderConfigCard('Who can see the files in this office', [\n '<div class=\"config-row\"><label><input type=\"checkbox\" id=\"m-visibility\"' + (hidden ? ' checked' : '') + '> Hide the files in this channel from other offices</label></div>',\n '<p>' + (hidden\n ? '<strong>Hidden</strong> \u2014 other offices cannot read the files in this channel, and it can read shared MEMORY.md and skills but not change them.'\n : '<strong>Shared</strong> \u2014 every other office can read the files in this channel (read-only), and this channel may update shared MEMORY.md and skills.') + '</p>',\n '<p class=\"muted-note\">Slack says this is a public channel, which is why sharing is the default. Hiding is the only change allowed; nothing can be made more visible than Slack allows.</p>',\n ].join(''), 'saveVisibility', 'Save', 'mount-save-result');\n }\n\n async function saveVisibility(btn) {\n const visibility = document.getElementById('m-visibility').checked ? 'private' : 'default';\n const result = document.getElementById('mount-save-result');\n await saveConversationSetting(btn, result, 'visibility', { visibility }, 'Save visibility', loadSettings);\n }\n\n function saveConversationSetting(btn, result, setting, values, label, onSaved) {\n return saveSetting(btn, result, 'conversations/' + setting, { ...scopeBody(), ...values }, label, onSaved);\n }\n\n async function saveSetting(btn, result, path, values, label, onSaved) {\n btn.disabled = true; btn.textContent = 'Saving\u2026'; result.style.display = 'none';\n try {\n await apiPost('/admin/api/' + path, values);\n result.style.display = 'block'; result.className = 'inline-result ok'; result.textContent = 'Saved \u2713';\n if (onSaved) onSaved();\n } catch (err) {\n result.style.display = 'block'; result.className = 'inline-result err'; result.textContent = err.message;\n } finally {\n btn.disabled = false; btn.textContent = label;\n }\n }\n\n\n async function loadWorkspace() {\n const treeEl = document.getElementById('workspace-tree');\n const previewEl = document.getElementById('workspace-preview');\n treeEl.innerHTML = '<div class=\"loading-msg\">Loading\u2026</div>';\n previewEl.innerHTML = '<div class=\"placeholder-msg\">Click a file to preview</div>';\n try {\n const data = await apiGet('/admin/api/workspace/tree?' + scopeQuery());\n if (!data.tree) {\n treeEl.innerHTML = '<div class=\"empty-state\">No files</div>';\n return;\n }\n treeEl.innerHTML = '<ul class=\"tree-root\">' + renderTreeChildren(data.tree) + '</ul>';\n } catch (err) {\n treeEl.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n function renderTreeChildren(node) {\n if (node.type === 'file') {\n return '<li><button class=\"tree-file\" data-admin-action=\"preview-file\" data-file-path=\"' + escAttr(node.path) + '\">' + escHtml(node.name) + '</button></li>';\n }\n if (!node.children || node.children.length === 0) {\n return '<li><span class=\"tree-dir empty\">' + escHtml(node.name || '.') + '/</span></li>';\n }\n const inner = node.children.map((c) =>\n c.type === 'file'\n ? '<li><button class=\"tree-file\" data-admin-action=\"preview-file\" data-file-path=\"' + escAttr(c.path) + '\">' + escHtml(c.name) + '</button></li>'\n : '<li><details open><summary class=\"tree-dir\">' + escHtml(c.name) + '/</summary><ul>' + renderTreeChildren(c) + '</ul></details></li>'\n ).join('');\n return inner;\n }\n\n function renderPreviewFileResult(previewEl, label, data) {\n if (data.binary) {\n previewEl.innerHTML = '<div class=\"preview-meta\">' + escHtml(label) + ' \u00B7 ' + data.size + ' bytes \u00B7 binary</div><div class=\"placeholder-msg\">Binary file \u2014 preview not available</div>';\n return;\n }\n previewEl.innerHTML =\n '<div class=\"preview-meta\">' + escHtml(label) + ' \u00B7 ' + data.size + ' bytes</div>' +\n '<pre class=\"preview-body\">' + escHtml(data.content || '') + '</pre>';\n }\n\n async function previewFile(path) {\n const previewEl = document.getElementById('workspace-preview');\n previewEl.innerHTML = '<div class=\"loading-msg\">Loading ' + escHtml(path) + '\u2026</div>';\n try {\n const data = await apiGet('/admin/api/workspace/file?' + scopeQuery() + '&path=' + encodeURIComponent(path));\n renderPreviewFileResult(previewEl, path, data);\n } catch (err) {\n previewEl.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n\n async function loadScopePanels(prefix, endpoint, render) {\n const convEl = document.getElementById(prefix + '-conv-content');\n const globalEl = document.getElementById(prefix + '-global-content');\n if (convEl) convEl.innerHTML = '<div class=\"loading-msg\">Loading\u2026</div>';\n if (globalEl) globalEl.innerHTML = '<div class=\"loading-msg\">Loading\u2026</div>';\n try {\n const data = await apiGet('/admin/api/' + endpoint + '?' + scopeQuery());\n render(data, convEl, globalEl);\n } catch (err) {\n if (convEl) convEl.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n if (globalEl) globalEl.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n\n function mcpMessage(scope, text, kind) {\n const el = document.getElementById(scope === 'global' ? 'mcp-global-msg' : 'mcp-conv-msg');\n if (!el) return;\n el.style.display = 'block';\n el.className = 'status-msg status-msg-' + kind;\n el.textContent = text;\n }\n\n function mcpTransport(server) {\n return server.command\n ? server.command + (server.args && server.args.length ? ' ' + server.args.join(' ') : '')\n : (server.url || '');\n }\n\n function renderMcpServer(scope, name, server) {\n const transport = (server.command ? 'stdio: ' : 'http: ') + mcpTransport(server);\n const keys = [];\n if (server.envKeys && server.envKeys.length) keys.push('env: ' + server.envKeys.join(', '));\n if (server.headerKeys && server.headerKeys.length) keys.push('headers: ' + server.headerKeys.join(', '));\n return '<article class=\"mcp-preset mcp-installed-card\">' +\n '<div class=\"mcp-preset-top\">' +\n '<span class=\"mcp-preset-category\">' + (server.command ? 'STDIO' : 'HTTP') + '</span>' +\n (server.disabled ? '<span class=\"mcp-badge mcp-badge-warn\">disabled</span>' : '<span class=\"mcp-badge mcp-badge-ok\">enabled</span>') +\n '</div>' +\n '<h3>' + escHtml(name) + '</h3>' +\n '<p class=\"mcp-preset-meta mcp-installed-transport\">' + escHtml(transport) + '</p>' +\n (keys.length ? '<div class=\"mcp-preset-meta\">' + escHtml(keys.join(' \u00B7 ')) + '</div>' : '') +\n '<div class=\"mcp-preset-actions\">' +\n '<button class=\"mcp-btn\" data-mcp-action=\"test\" data-mcp-scope=\"' + scope + '\" data-mcp-name=\"' + escAttr(name) + '\">Test</button>' +\n '<button class=\"mcp-btn\" data-mcp-action=\"toggle\" data-mcp-scope=\"' + scope + '\" data-mcp-name=\"' + escAttr(name) + '\">' + (server.disabled ? 'Enable' : 'Disable') + '</button>' +\n '<button class=\"mcp-btn mcp-btn-danger\" data-mcp-action=\"remove\" data-mcp-scope=\"' + scope + '\" data-mcp-name=\"' + escAttr(name) + '\">Remove</button>' +\n '</div>' +\n '</article>';\n }\n\n function renderMcpPreset(scope, preset, servers) {\n const installed = Boolean(servers[preset.serverName]);\n const transport = preset.server.command ? 'Runs on host' : 'Remote service';\n return '<article class=\"mcp-preset\">' +\n '<div class=\"mcp-preset-top\">' +\n '<span class=\"mcp-preset-category\">' + escHtml(preset.category) + '</span>' +\n (installed ? '<span class=\"mcp-badge mcp-badge-ok\">Installed here</span>' : '') +\n '</div>' +\n '<h3>' + escHtml(preset.name) + '</h3>' +\n '<p>' + escHtml(preset.description) + '</p>' +\n '<div class=\"mcp-preset-meta\">' + escHtml(transport) + ' \u00B7 <code>' + escHtml(preset.serverName) + '</code></div>' +\n '<div class=\"mcp-preset-actions\">' +\n '<a href=\"' + escAttr(preset.sourceUrl) + '\" target=\"_blank\" rel=\"noopener\">Source \u2197</a>' +\n '<button class=\"primary-action-btn\" data-mcp-action=\"preset\" data-mcp-scope=\"' + scope + '\" data-mcp-preset=\"' + escAttr(preset.id) + '\">' + (installed ? 'Review' : 'Install') + '</button>' +\n '</div>' +\n '</article>';\n }\n\n function renderMcpScope(el, scope, servers) {\n const presets = mcpPresets.map((preset) => renderMcpPreset(scope, preset, servers)).join('');\n const rows = Object.entries(servers).map(([name, server]) => renderMcpServer(scope, name, server)).join('');\n el.innerHTML =\n '<div class=\"mcp-market-head\"><div><h3>Explore presets</h3><p>Reviewed recipes that install into this scope.</p></div><span>' + mcpPresets.length + ' available</span></div>' +\n '<div class=\"mcp-preset-grid\">' + presets + '</div>' +\n '<details class=\"mcp-installed\" open><summary>Installed in this scope</summary>' +\n '<div class=\"mcp-preset-grid\">' +\n rows +\n '<button type=\"button\" class=\"mcp-add-card\" data-mcp-action=\"open-custom\" data-mcp-scope=\"' + scope + '\">' +\n '<span class=\"mcp-add-card-plus\">+</span><span>\u65B0\u589E server</span>' +\n '</button>' +\n '</div>' +\n '</details>';\n }\n\n function renderMcpGuidedForm(scope) {\n return '<div class=\"mcp-guided-grid\">' +\n '<label class=\"mcp-field\"><span>Server \u540D\u7A31</span><input id=\"mcp-' + scope + '-g-name\" class=\"form-input mcp-json\" placeholder=\"\u4F8B\u5982 github\" autocomplete=\"off\" /></label>' +\n '<div class=\"mcp-transport-toggle\">' +\n '<label><input type=\"radio\" name=\"mcp-' + scope + '-g-transport\" value=\"stdio\" checked /> \u672C\u6A5F\u6307\u4EE4 (stdio)</label>' +\n '<label><input type=\"radio\" name=\"mcp-' + scope + '-g-transport\" value=\"http\" /> \u9060\u7AEF\u670D\u52D9 (HTTP)</label>' +\n '</div>' +\n '<div id=\"mcp-' + scope + '-g-stdio\" class=\"mcp-transport-fields\">' +\n '<label class=\"mcp-field\"><span>\u6307\u4EE4</span><input id=\"mcp-' + scope + '-g-command\" class=\"form-input mcp-json\" placeholder=\"npx\" autocomplete=\"off\" /></label>' +\n '<label class=\"mcp-field\"><span>\u53C3\u6578\uFF08\u4EE5\u9017\u865F\u5206\u9694\uFF09</span><input id=\"mcp-' + scope + '-g-args\" class=\"form-input mcp-json\" placeholder=\"-y, @modelcontextprotocol/server-github\" autocomplete=\"off\" /></label>' +\n '</div>' +\n '<div id=\"mcp-' + scope + '-g-http\" class=\"mcp-transport-fields\" style=\"display:none\">' +\n '<label class=\"mcp-field\"><span>URL</span><input id=\"mcp-' + scope + '-g-url\" class=\"form-input mcp-json\" placeholder=\"https://mcp.example.com/mcp\" autocomplete=\"off\" /></label>' +\n '</div>' +\n '<div class=\"mcp-kv-block\">' +\n '<div class=\"mcp-kv-head\"><span id=\"mcp-' + scope + '-g-kv-label\">\u74B0\u5883\u8B8A\u6578 (env)</span><button type=\"button\" class=\"mcp-btn\" data-mcp-kv-add=\"' + scope + '\">+ \u65B0\u589E\u4E00\u5217</button></div>' +\n '<div id=\"mcp-' + scope + '-g-kv\" class=\"mcp-kv-rows\"></div>' +\n '</div>' +\n '</div>';\n }\n\n function renderMcpKvRow() {\n return '<div class=\"mcp-kv-row\">' +\n '<input class=\"form-input mcp-json\" placeholder=\"KEY\" data-kv-key autocomplete=\"off\" />' +\n '<input class=\"form-input mcp-json\" type=\"password\" placeholder=\"value\" data-kv-value autocomplete=\"off\" />' +\n '<button type=\"button\" class=\"mcp-btn mcp-btn-danger\" data-mcp-kv-remove>\u79FB\u9664</button>' +\n '</div>';\n }\n\n function addMcpGuidedRow(scope) {\n const container = document.getElementById('mcp-' + scope + '-g-kv');\n if (container) container.insertAdjacentHTML('beforeend', renderMcpKvRow());\n }\n\n function updateMcpGuidedTransport(scope) {\n const checked = document.querySelector('input[name=\"mcp-' + scope + '-g-transport\"]:checked');\n const transport = checked ? checked.value : 'stdio';\n const stdioEl = document.getElementById('mcp-' + scope + '-g-stdio');\n const httpEl = document.getElementById('mcp-' + scope + '-g-http');\n const kvLabel = document.getElementById('mcp-' + scope + '-g-kv-label');\n if (stdioEl) stdioEl.style.display = transport === 'stdio' ? '' : 'none';\n if (httpEl) httpEl.style.display = transport === 'http' ? '' : 'none';\n if (kvLabel) kvLabel.textContent = transport === 'stdio' ? '\u74B0\u5883\u8B8A\u6578 (env)' : 'HTTP Headers';\n }\n\n let pendingMcpCustomScope = null;\n\n function openMcpCustomDialog(scope) {\n pendingMcpCustomScope = scope;\n const content = document.getElementById('mcp-custom-dialog-content');\n content.innerHTML =\n '<div class=\"mcp-add-mode\">' +\n '<button type=\"button\" class=\"mcp-mode-btn active\" data-mcp-mode=\"guided\">\u5F15\u5C0E\u5F0F\u8868\u55AE</button>' +\n '<button type=\"button\" class=\"mcp-mode-btn\" data-mcp-mode=\"json\">\u8CBC\u4E0A JSON</button>' +\n '</div>' +\n '<div id=\"mcp-custom-guided-panel\" class=\"mcp-panel\">' + renderMcpGuidedForm('custom') + '</div>' +\n '<div id=\"mcp-custom-json-panel\" class=\"mcp-panel\" style=\"display:none\">' +\n '<p class=\"mcp-manual-hint\">\u8CBC\u4E0A MCP server \u6587\u4EF6\u7D66\u7684 <code>mcpServers</code> JSON\uFF0C\u539F\u6A23\u4FDD\u5B58\u5230\u9019\u500B scope\u3002remote \u8D70 Streamable HTTP\uFF0Clocal \u8D70 stdio\u3002</p>' +\n '<textarea id=\"mcp-custom-json\" class=\"form-input mcp-json\" spellcheck=\"false\" rows=\"9\" placeholder=\"' + escAttr(MCP_JSON_PLACEHOLDER) + '\"></textarea>' +\n '</div>';\n const dialogError = document.getElementById('mcp-custom-dialog-error');\n dialogError.style.display = 'none';\n dialogError.textContent = '';\n addMcpGuidedRow('custom');\n document.getElementById('mcp-custom-dialog').showModal();\n }\n\n function closeMcpCustomDialog() {\n pendingMcpCustomScope = null;\n document.getElementById('mcp-custom-dialog').close();\n }\n\n function switchMcpCustomMode(mode) {\n const guidedEl = document.getElementById('mcp-custom-guided-panel');\n const jsonEl = document.getElementById('mcp-custom-json-panel');\n if (guidedEl) guidedEl.style.display = mode === 'guided' ? '' : 'none';\n if (jsonEl) jsonEl.style.display = mode === 'json' ? '' : 'none';\n document.querySelectorAll('#mcp-custom-dialog-content [data-mcp-mode]').forEach((b) => {\n b.classList.toggle('active', b.dataset.mcpMode === mode);\n });\n }\n\n async function submitMcpCustomDialog(btn) {\n const scope = pendingMcpCustomScope;\n if (!scope) return;\n const dialogError = document.getElementById('mcp-custom-dialog-error');\n dialogError.style.display = 'none';\n dialogError.textContent = '';\n const jsonMode = document.getElementById('mcp-custom-json-panel').style.display !== 'none';\n let json;\n if (jsonMode) {\n const raw = document.getElementById('mcp-custom-json').value.trim();\n if (!raw) { dialogError.textContent = '\u8ACB\u8CBC\u4E0A mcpServers JSON'; dialogError.style.display = 'block'; return; }\n json = raw;\n } else {\n const nameEl = document.getElementById('mcp-custom-g-name');\n const name = nameEl ? nameEl.value.trim() : '';\n if (!name) { dialogError.textContent = '\u8ACB\u8F38\u5165 server \u540D\u7A31'; dialogError.style.display = 'block'; return; }\n const checked = document.querySelector('input[name=\"mcp-custom-g-transport\"]:checked');\n const transport = checked ? checked.value : 'stdio';\n const kv = {};\n document.querySelectorAll('#mcp-custom-g-kv .mcp-kv-row').forEach((row) => {\n const keyInput = row.querySelector('[data-kv-key]');\n const valueInput = row.querySelector('[data-kv-value]');\n const key = keyInput ? keyInput.value.trim() : '';\n if (key) kv[key] = valueInput ? valueInput.value : '';\n });\n let entry;\n if (transport === 'stdio') {\n const commandEl = document.getElementById('mcp-custom-g-command');\n const command = commandEl ? commandEl.value.trim() : '';\n if (!command) { dialogError.textContent = '\u8ACB\u8F38\u5165\u6307\u4EE4'; dialogError.style.display = 'block'; return; }\n const argsEl = document.getElementById('mcp-custom-g-args');\n const argsRaw = argsEl ? argsEl.value.trim() : '';\n const args = argsRaw ? argsRaw.split(',').map((s) => s.trim()).filter(Boolean) : [];\n entry = Object.assign({ command: command }, args.length ? { args: args } : {}, Object.keys(kv).length ? { env: kv } : {});\n } else {\n const urlEl = document.getElementById('mcp-custom-g-url');\n const url = urlEl ? urlEl.value.trim() : '';\n if (!url) { dialogError.textContent = '\u8ACB\u8F38\u5165 URL'; dialogError.style.display = 'block'; return; }\n entry = Object.assign({ url: url }, Object.keys(kv).length ? { headers: kv } : {});\n }\n json = JSON.stringify({ mcpServers: { [name]: entry } });\n }\n btn.disabled = true;\n btn.textContent = '\u5132\u5B58\u4E2D\u2026';\n try {\n const data = await apiPost('/admin/api/mcp-servers/mutate', {\n action: 'import', scope: scope, json: json, ...scopeBody(),\n });\n const results = Array.isArray(data.results) ? data.results : [];\n const failed = results.filter((r) => r.error).length;\n closeMcpCustomDialog();\n mcpMessage(scope, failed ? '\u5DF2\u5132\u5B58\uFF0C\u4F46 ' + failed + ' \u500B server \u9023\u7DDA\u5931\u6557\uFF0C\u898B\u4E0A\u65B9\u9078\u9805\u7DAD\u8B77\u3002' : '\u5DF2\u5132\u5B58\u4E26\u9023\u7DDA\u6210\u529F\u3002', failed ? 'err' : 'ok');\n await loadMcpServers();\n } catch (err) {\n dialogError.textContent = err.message;\n dialogError.style.display = 'block';\n } finally {\n btn.disabled = false;\n btn.textContent = '\u65B0\u589E\u4E26\u6E2C\u8A66\u9023\u7DDA';\n }\n }\n\n const MCP_JSON_PLACEHOLDER = JSON.stringify({\n mcpServers: {\n browserless: {\n type: 'http',\n url: 'https://mcp.browserless.io/mcp',\n headers: { Authorization: 'Bearer YOUR_API_TOKEN' },\n },\n },\n }, null, 2);\n\n const loadMcpServers = () => loadScopePanels('mcp', 'mcp-servers', (data, convEl, globalEl) => {\n mcpPresets = Array.isArray(data.presets) ? data.presets : [];\n mcpServersByScope = { conversation: data.conversation || {}, global: data.global || {} };\n if (convEl) renderMcpScope(convEl, 'conversation', mcpServersByScope.conversation);\n if (globalEl) renderMcpScope(globalEl, 'global', mcpServersByScope.global);\n });\n\n async function mutateMcpServer(scope, action, name, extra) {\n mcpMessage(scope, action === 'test' ? '\u9023\u7DDA\u6E2C\u8A66\u4E2D\u2026' : '\u5132\u5B58\u4E26\u6E2C\u8A66\u9023\u7DDA\u4E2D\u2026', 'busy');\n try {\n const data = await apiPost('/admin/api/mcp-servers/mutate', {\n action: action,\n scope: scope,\n ...(name ? { name: name } : {}),\n ...(extra || {}),\n ...scopeBody(),\n });\n const results = Array.isArray(data.results) ? data.results : [];\n const failed = results.filter((r) => r.error).length;\n if (action === 'test') {\n mcpMessage(scope, failed ? '\u2717 ' + name + ': ' + results[0].error : '\u2713 ' + name + ': ' + results[0].tools + ' tool(s)', failed ? 'err' : 'ok');\n return;\n }\n if (action === 'remove' || action === 'toggle') {\n mcpMessage(scope, '\u5B8C\u6210\u3002\u65B0\u8A2D\u5B9A\u5728\u4E0B\u4E00\u6B21\u56DE\u61C9\u751F\u6548\u3002', 'ok');\n } else {\n mcpMessage(scope, failed ? '\u5DF2\u5132\u5B58\uFF0C\u4F46 ' + failed + ' \u500B server \u9023\u7DDA\u5931\u6557\uFF0C\u898B\u4E0B\u65B9\u932F\u8AA4\u3002' : '\u5DF2\u5132\u5B58\u4E26\u9023\u7DDA\u6210\u529F\u3002', failed ? 'err' : 'ok');\n }\n await loadMcpServers();\n } catch (err) {\n mcpMessage(scope, err.message, 'err');\n }\n }\n\n function openMcpPreset(scope, presetId) {\n const preset = mcpPresets.find((item) => item.id === presetId);\n if (!preset) return;\n pendingMcpInstall = { scope: scope, preset: preset };\n const local = Boolean(preset.server.command);\n const installed = Boolean(mcpServersByScope[scope][preset.serverName]);\n const credentials = preset.credentials.map((credential, index) =>\n '<label class=\"mcp-credential\"><span>' + escHtml(credential.label) + (credential.required ? ' *' : '') + '</span>' +\n '<input data-mcp-credential=\"' + index + '\" type=\"' + (credential.secret ? 'password' : 'text') + '\" autocomplete=\"off\" />' +\n '<small>' + escHtml(credential.description) + '</small></label>'\n ).join('');\n document.getElementById('mcp-dialog-title').textContent = preset.name;\n const dialogError = document.getElementById('mcp-dialog-error');\n dialogError.style.display = 'none';\n dialogError.textContent = '';\n document.getElementById('mcp-dialog-content').innerHTML =\n '<p class=\"mcp-dialog-desc\">' + escHtml(preset.description) + '</p>' +\n '<div class=\"mcp-install-preview\"><span>' + (local ? 'Host command' : 'Remote endpoint') + '</span><code>' + escHtml(mcpTransport(preset.server)) + '</code></div>' +\n '<div class=\"mcp-security-note ' + (local ? 'local' : 'remote') + '\">' +\n (local\n ? '<strong>Host code execution.</strong> This command runs outside the conversation sandbox with the mikan process user permissions.'\n : '<strong>External service.</strong> Tool calls and selected data will be sent to this remote origin.') +\n '</div>' +\n (installed ? '<div class=\"mcp-replace-note\">Installing again replaces the existing <code>' + escHtml(preset.serverName) + '</code> entry in this scope.</div>' : '') +\n (credentials || '<p class=\"mcp-no-credentials\">No credentials required.</p>') +\n '<p class=\"mcp-secret-note\">These values are stored in host-private settings and are not shown to the model or sandbox.</p>' +\n '<a class=\"mcp-setup-link\" href=\"' + escAttr(preset.setupUrl) + '\" target=\"_blank\" rel=\"noopener\">Setup documentation \u2197</a>';\n document.getElementById('mcp-install-dialog').showModal();\n }\n\n function closeMcpInstall() {\n pendingMcpInstall = null;\n document.getElementById('mcp-install-dialog').close();\n }\n\n async function installMcpPreset(btn) {\n if (!pendingMcpInstall) return;\n const { scope, preset } = pendingMcpInstall;\n const credentials = {};\n document.querySelectorAll('[data-mcp-credential]').forEach((input) => {\n const descriptor = preset.credentials[Number(input.dataset.mcpCredential)];\n if (descriptor) credentials[descriptor.key] = input.value;\n });\n btn.disabled = true;\n btn.textContent = 'Installing\u2026';\n try {\n const data = await apiPost('/admin/api/mcp-servers/mutate', {\n action: 'install', scope: scope, presetId: preset.id, credentials: credentials, ...scopeBody(),\n });\n closeMcpInstall();\n const result = (Array.isArray(data.results) ? data.results : [])[0];\n if (result && result.error) mcpMessage(scope, '\u2717 ' + preset.name + ' saved but failed to connect: ' + result.error, 'err');\n else mcpMessage(scope, '\u2713 ' + preset.name + ' installed, ' + (result ? result.tools : 0) + ' tool(s).', 'ok');\n await loadMcpServers();\n } catch (err) {\n const dialogError = document.getElementById('mcp-dialog-error');\n dialogError.textContent = err.message;\n dialogError.style.display = 'block';\n } finally {\n btn.disabled = false;\n btn.textContent = 'Install preset';\n }\n }\n\n document.addEventListener('click', (event) => {\n const modeBtn = event.target.closest('#mcp-custom-dialog-content [data-mcp-mode]');\n if (modeBtn) { switchMcpCustomMode(modeBtn.dataset.mcpMode); return; }\n const kvAddBtn = event.target.closest('[data-mcp-kv-add]');\n if (kvAddBtn) { addMcpGuidedRow(kvAddBtn.dataset.mcpKvAdd); return; }\n const kvRemoveBtn = event.target.closest('[data-mcp-kv-remove]');\n if (kvRemoveBtn) { kvRemoveBtn.closest('.mcp-kv-row').remove(); return; }\n const transportRadio = event.target.closest('input[name^=\"mcp-\"][name$=\"-g-transport\"]');\n if (transportRadio) {\n const match = /^mcp-(.+)-g-transport$/.exec(transportRadio.name);\n if (match) updateMcpGuidedTransport(match[1]);\n return;\n }\n const btn = event.target.closest('[data-mcp-action]');\n if (!btn) return;\n if (btn.dataset.mcpAction === 'open-custom') { openMcpCustomDialog(btn.dataset.mcpScope); return; }\n if (btn.dataset.mcpAction === 'preset') { openMcpPreset(btn.dataset.mcpScope, btn.dataset.mcpPreset); return; }\n void mutateMcpServer(btn.dataset.mcpScope, btn.dataset.mcpAction, btn.dataset.mcpName);\n });\n\n let skillsCache = [];\n\n function renderSkillsInto(containerId, skills, allowedSource) {\n const container = document.getElementById(containerId);\n if (!container) return;\n const filtered = allowedSource ? skills.filter((s) => s.source === allowedSource) : skills;\n if (filtered.length === 0) {\n container.innerHTML = '<div class=\"empty-state\">No skills available</div>';\n return;\n }\n container.innerHTML = '<div class=\"skills-list\">' +\n filtered.map((s) =>\n '<div class=\"skill-row\">' +\n '<button class=\"skill-row-btn\" data-skill-source=\"' + escAttr(s.source) + '\" data-skill-directory=\"' + escAttr(s.directory) + '\" data-skill-name=\"' + escAttr(s.name) + '\">' +\n '<div class=\"skill-name\">' + escHtml(s.name) + '<span class=\"skill-source skill-source-' + s.source + '\">' + s.source + '</span></div>' +\n (s.description ? '<div class=\"skill-desc\">' + escHtml(s.description) + '</div>' : '') +\n '</button>' +\n '<div class=\"skill-row-actions\">' +\n '<button class=\"mcp-btn\" data-skill-edit-source=\"' + escAttr(s.source) + '\" data-skill-edit-directory=\"' + escAttr(s.directory) + '\">Edit</button>' +\n '<button class=\"mcp-btn mcp-btn-danger\" data-skill-delete-source=\"' + escAttr(s.source) + '\" data-skill-delete-directory=\"' + escAttr(s.directory) + '\" data-skill-delete-name=\"' + escAttr(s.name) + '\">Delete</button>' +\n '</div>' +\n '</div>'\n ).join('') + '</div>';\n }\n\n async function loadSkills() {\n const container = document.getElementById('skills-content');\n const previewEl = document.getElementById('skills-preview');\n container.innerHTML = '<div class=\"loading-msg\">Loading\u2026</div>';\n if (previewEl) previewEl.innerHTML = '<div class=\"placeholder-msg\">Click a skill to preview SKILL.md</div>';\n try {\n const data = await apiGet('/admin/api/skills?' + scopeQuery());\n skillsCache = Array.isArray(data.skills) ? data.skills : [];\n renderSkillsInto('skills-content', skillsCache);\n renderSkillsInto('global-skills-content', skillsCache, 'global');\n } catch (err) {\n container.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n const loadGlobalSkills = loadSkills;\n\n async function previewSkillInto(previewId, source, directory, name) {\n const previewEl = document.getElementById(previewId);\n if (!previewEl) return;\n if (!source || !directory) {\n previewEl.innerHTML = '<div class=\"err-msg\">Missing skill source or directory</div>';\n return;\n }\n previewEl.innerHTML = '<div class=\"loading-msg\">Loading ' + escHtml(name || directory) + '\u2026</div>';\n try {\n const data = await apiGet('/admin/api/skills/file?' + scopeQuery() + '&source=' + encodeURIComponent(source) + '&directory=' + encodeURIComponent(directory));\n renderPreviewFileResult(previewEl, source + '/' + directory + '/SKILL.md', data);\n } catch (err) {\n previewEl.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n async function deleteSkill(source, directory, name) {\n if (!confirm('Delete skill \"' + (name || directory) + '\" (' + source + ')?')) return;\n try {\n await apiPost('/admin/api/skills/mutate', { action: 'delete', source: source, directory: directory, ...scopeBody() });\n await loadSkills();\n } catch (err) {\n alert(err.message);\n }\n }\n\n function bindSkillListEvents(containerId, previewId) {\n const el = document.getElementById(containerId);\n if (!el) return;\n el.addEventListener('click', (event) => {\n const previewBtn = event.target.closest('[data-skill-source]');\n if (previewBtn) {\n previewSkillInto(previewId, previewBtn.dataset.skillSource, previewBtn.dataset.skillDirectory, previewBtn.dataset.skillName);\n return;\n }\n const editBtn = event.target.closest('[data-skill-edit-source]');\n if (editBtn) {\n const skill = skillsCache.find((s) => s.source === editBtn.dataset.skillEditSource && s.directory === editBtn.dataset.skillEditDirectory);\n if (skill) void openSkillDialog(skill.source, skill);\n return;\n }\n const deleteBtn = event.target.closest('[data-skill-delete-source]');\n if (deleteBtn) {\n void deleteSkill(deleteBtn.dataset.skillDeleteSource, deleteBtn.dataset.skillDeleteDirectory, deleteBtn.dataset.skillDeleteName);\n }\n });\n }\n bindSkillListEvents('skills-content', 'skills-preview');\n bindSkillListEvents('global-skills-content', 'global-skills-preview');\n\n let editingSkill = null;\n let pendingSkillScope = null;\n\n async function openSkillDialog(scope, existing) {\n editingSkill = existing ? { source: existing.source, directory: existing.directory } : null;\n document.getElementById('skill-dialog-eyebrow').textContent = scope === 'global' ? 'Global skill' : 'Conversation skill';\n document.getElementById('skill-dialog-title').textContent = existing ? 'Edit skill' : 'New skill';\n const dirInput = document.getElementById('skill-dialog-directory');\n dirInput.value = existing ? existing.directory : '';\n dirInput.disabled = Boolean(existing);\n document.getElementById('skill-dialog-name').value = existing ? existing.name : '';\n document.getElementById('skill-dialog-description').value = existing ? existing.description : '';\n const contentEl = document.getElementById('skill-dialog-content');\n contentEl.value = '';\n const dialogError = document.getElementById('skill-dialog-error');\n dialogError.style.display = 'none';\n dialogError.textContent = '';\n pendingSkillScope = scope;\n if (existing) {\n contentEl.value = 'Loading\u2026';\n try {\n const data = await apiGet('/admin/api/skills/file?' + scopeQuery() + '&source=' + encodeURIComponent(existing.source) + '&directory=' + encodeURIComponent(existing.directory));\n contentEl.value = data.binary ? '' : (data.content || '').replace(/^---[\\s\\S]*?---\\n?/, '').trim();\n } catch (err) {\n contentEl.value = '';\n dialogError.textContent = 'Failed to load current content: ' + err.message;\n dialogError.style.display = 'block';\n }\n }\n document.getElementById('skill-dialog').showModal();\n }\n\n function closeSkillDialog() {\n editingSkill = null;\n pendingSkillScope = null;\n document.getElementById('skill-dialog').close();\n }\n\n async function submitSkillDialog(btn) {\n const dialogError = document.getElementById('skill-dialog-error');\n dialogError.style.display = 'none';\n dialogError.textContent = '';\n const directory = document.getElementById('skill-dialog-directory').value.trim();\n const name = document.getElementById('skill-dialog-name').value.trim() || directory;\n const description = document.getElementById('skill-dialog-description').value.trim();\n const content = document.getElementById('skill-dialog-content').value;\n if (!directory) { dialogError.textContent = 'Directory is required'; dialogError.style.display = 'block'; return; }\n if (!description) { dialogError.textContent = 'Description is required'; dialogError.style.display = 'block'; return; }\n const source = editingSkill ? editingSkill.source : pendingSkillScope;\n btn.disabled = true;\n btn.textContent = 'Saving\u2026';\n try {\n await apiPost('/admin/api/skills/mutate', {\n action: 'save', source: source, directory: directory, name: name, description: description, content: content, ...scopeBody(),\n });\n closeSkillDialog();\n await loadSkills();\n } catch (err) {\n dialogError.textContent = err.message;\n dialogError.style.display = 'block';\n } finally {\n btn.disabled = false;\n btn.textContent = 'Save';\n }\n }\n\n\n const openLogin = (silent) => openPortalLink('vault', 'login', silent);\n const openSessionView = (silent) => openPortalLink('session', 'session', silent);\n\n async function openPortalLink(resultId, kind, silent) {\n const result = document.getElementById(resultId + '-link-result');\n if (silent) { result.style.display = 'none'; return; }\n result.style.display = 'block'; result.className = 'link-result loading'; result.textContent = 'Generating link\u2026';\n try {\n const data = await apiPost('/admin/api/conversations/' + kind + '-link', scopeBody());\n result.className = 'link-result ok';\n result.innerHTML =\n (kind === 'login' ? '<span class=\"link-vault\">vault: <code>' + escHtml(data.vaultId) + '</code></span>' : '') +\n '<a href=\"' + escAttr(data.url) + '\" target=\"_blank\" rel=\"noopener\">' + escHtml(data.url) + '</a>' +\n '<button class=\"copy-link-btn\" data-admin-action=\"copy-link\" data-copy-text=\"' + escAttr(data.url) + '\">Copy</button>';\n window.open(data.url, '_blank', 'noopener');\n } catch (err) {\n result.className = 'link-result err'; result.textContent = err.message;\n }\n }\n\n\n const loadConversationEvents = () => loadEventList(true);\n const loadEvents = () => loadEventList(false);\n\n async function loadEventList(conversation) {\n const container = document.getElementById(conversation ? 'events-content' : 'global-events-content');\n if (!container) return;\n container.innerHTML = '<div class=\"loading-msg\">Loading\u2026</div>';\n try {\n const data = await apiGet(conversation ? '/admin/api/conversations/events?' + scopeQuery() : '/admin/api/events');\n if (data.events.length === 0) {\n container.innerHTML = '<div class=\"empty-state\">' + (conversation ? '\u6C92\u6709\u95DC\u806F\u6B64\u5C0D\u8A71\u7684 event' : 'No events scheduled') + '</div>';\n return;\n }\n container.innerHTML = '<div class=\"events-list\">' +\n data.events.map((e) => renderEventRow(e, conversation)).join('') + '</div>';\n } catch (err) {\n container.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n function renderEventRow(e, allowDelete) {\n const meta = [e.type, e.platform, e.conversationId, e.schedule || e.at]\n .filter(Boolean).map(escHtml).join(' \u00B7 ');\n const preview = e.text ? '<div class=\"event-text\">' + escHtml(e.text.length > 240 ? e.text.slice(0, 237) + '\u2026' : e.text) + '</div>' : '';\n const deleteBtn = allowDelete\n ? '<button class=\"event-delete-btn\" data-admin-action=\"delete-event\" data-event-name=\"' + escAttr(e.name) + '\">Delete</button>'\n : '';\n return '<div class=\"event-row\">' +\n '<div class=\"event-row-top\">' +\n '<div class=\"event-name\"><code>' + escHtml(e.name) + '</code></div>' +\n deleteBtn +\n '</div>' +\n '<div class=\"event-meta\">' + meta + '</div>' +\n preview +\n '</div>';\n }\n\n async function deleteEvent(name, btn) {\n if (!confirm('Delete event \"' + name + '\"?')) return;\n btn.disabled = true; btn.textContent = 'Deleting\u2026';\n try {\n await apiPost('/admin/api/conversations/events/delete', {\n ...scopeBody(), name,\n });\n await loadConversationEvents();\n } catch (err) {\n btn.disabled = false; btn.textContent = 'Delete';\n alert(err.message);\n }\n }\n\n\n function initGlobal() {}\n\n async function loadAllConversations() {\n const container = document.getElementById('all-conv-content');\n container.innerHTML = '<div class=\"loading-msg\">Loading\u2026</div>';\n try {\n const data = await apiGet('/admin/api/conversations');\n if (data.conversations.length === 0) {\n container.innerHTML = '<div class=\"empty-state\">No conversations found</div>';\n return;\n }\n container.innerHTML = '<div class=\"conv-list\">' + data.conversations.map((c) => {\n const last = c.lastActivityAt ? new Date(c.lastActivityAt).toLocaleString() : '\u2014';\n return '<button class=\"conv-row-btn\" data-admin-action=\"select-conversation\" data-conversation-id=\"' + escAttr(c.conversationId) + '\">' +\n '<span class=\"conv-id\">' + escHtml(c.label || c.conversationId) + '</span>' +\n (c.running ? '<span class=\"status-pill running\">running</span>' : '') +\n '<span class=\"conv-last\">' + escHtml(last) + '</span>' +\n '</button>';\n }).join('') + '</div>';\n } catch (err) {\n container.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n async function loadSessionUsage() {\n const container = document.getElementById('session-usage-content');\n container.innerHTML = '<div class=\"loading-msg\">Loading\u2026</div>';\n try {\n const data = await apiGet('/admin/api/session-usage');\n if (data.sessions.length === 0) {\n container.innerHTML = '<div class=\"empty-state\">No token usage found</div>';\n return;\n }\n container.innerHTML = '<div class=\"usage-table-wrap\"><table class=\"usage-table\"><thead><tr><th>#</th><th>Channel</th><th>Session</th><th>Updated</th><th>Input</th><th>Output</th><th>Cache Read</th><th>Cache Write</th><th>Total</th><th>Cost</th></tr></thead><tbody>' +\n data.sessions.map((s, i) => '<tr>' +\n '<td>' + (i + 1) + '</td>' +\n '<td>' + escHtml(s.label || s.conversationId) + '</td>' +\n '<td><code>' + escHtml(s.fileName) + '</code></td>' +\n '<td>' + escHtml(new Date(s.updatedAt).toLocaleString()) + '</td>' +\n '<td>' + fmtNum(s.input) + '</td>' +\n '<td>' + fmtNum(s.output) + '</td>' +\n '<td>' + fmtNum(s.cacheRead) + '</td>' +\n '<td>' + fmtNum(s.cacheWrite) + '</td>' +\n '<td><strong>' + fmtNum(s.total) + '</strong></td>' +\n '<td>' + (s.cost > 0 ? '$' + Number(s.cost).toFixed(4) : '\u2014') + '</td>' +\n '</tr>').join('') + '</tbody></table></div>';\n } catch (err) {\n container.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n function fmtNum(value) {\n return Number(value || 0).toLocaleString('en-US');\n }\n\n let timelineConvLoaded = false;\n let timelineData = null;\n let timelineFilter = null;\n async function ensureTimelineConvOptions() {\n if (timelineConvLoaded) return;\n const sel = document.getElementById('timeline-conv');\n const prev = sel.value;\n const data = await apiGet('/admin/api/conversations');\n if (!data.conversations.length) {\n sel.innerHTML = '<option value=\"\">No conversations</option>';\n timelineConvLoaded = true;\n return;\n }\n const want = prev || defaultConversationKey;\n sel.innerHTML = data.conversations.map((c) => {\n const key = c.platform + ':' + c.conversationId;\n return '<option value=\"' + escAttr(key) + '\"' +\n (key === want ? ' selected' : '') + '>' +\n escHtml(c.label || c.conversationId) + '</option>';\n }).join('');\n timelineConvLoaded = true;\n }\n\n async function loadTokenUsage() {\n loadSessionUsage();\n timelineConvLoaded = false;\n await loadUsageTimeline();\n }\n\n async function loadUsageTimeline() {\n const container = document.getElementById('usage-timeline-content');\n try {\n await ensureTimelineConvOptions();\n const conv = document.getElementById('timeline-conv').value;\n if (!conv) {\n container.innerHTML = '<div class=\"empty-state\">No conversations found</div>';\n return;\n }\n container.innerHTML = '<div class=\"loading-msg\">Loading\u2026</div>';\n const convScope = scopeOf(conv);\n const data = await apiGet('/admin/api/conversation-usage?conversationId=' +\n encodeURIComponent(convScope.conversationId) +\n '&platform=' + encodeURIComponent(convScope.platform));\n timelineData = data;\n container.innerHTML = renderUsageTimeline(data);\n } catch (err) {\n container.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n function tlCard(label, value) {\n return '<div class=\"tl-card\"><div class=\"tl-card-label\">' + label +\n '</div><div class=\"tl-card-value\">' + value + '</div></div>';\n }\n\n const TL_SERIES = [\n { key: 'cacheRead', seg: 'tl-cache-read', sw: 'sw-cache-read', label: 'Cache read' },\n { key: 'cacheWrite', seg: 'tl-cache-write', sw: 'sw-cache-write', label: 'Cache write' },\n { key: 'input', seg: 'tl-input', sw: 'sw-input', label: 'Input' },\n { key: 'output', seg: 'tl-output', sw: 'sw-output', label: 'Output' },\n ];\n\n function toggleTimelineFilter(key) {\n timelineFilter = timelineFilter === key ? null : key;\n if (timelineData) {\n document.getElementById('usage-timeline-content').innerHTML = renderUsageTimeline(timelineData);\n }\n }\n\n function renderUsageTimeline(data) {\n const buckets = data.buckets || [];\n const totals = data.totals || { total: 0, cost: 0, cacheRead: 0 };\n const cacheHit = totals.total > 0 ? Math.round((totals.cacheRead / totals.total) * 100) : 0;\n const cards = '<div class=\"tl-cards\">' +\n tlCard('Total cost', totals.cost > 0 ? '$' + Number(totals.cost).toFixed(4) : '\u2014') +\n tlCard('Total tokens', fmtNum(totals.total)) +\n tlCard('Cache hit', cacheHit + '%') +\n '</div>';\n\n if (totals.total === 0) {\n const emptyNote = data.hasOlder\n ? '<div class=\"tl-note\">No usage in the last 14 days \u00B7 earlier activity exists</div>'\n : '<div class=\"empty-state\">No token usage in the last 14 days</div>';\n return cards + emptyNote;\n }\n\n const active = TL_SERIES.find((s) => s.key === timelineFilter) || null;\n const valueOf = (b) => active ? (b[active.key] || 0) : b.total;\n const max = Math.max(1, ...buckets.map(valueOf));\n const px = (v) => Math.round((v / max) * 180);\n\n const legend = '<div class=\"tl-legend\">' + TL_SERIES.map((s) => {\n const cls = 'tl-legend-item' +\n (active && active.key === s.key ? ' active' : (active ? ' dim' : ''));\n return '<span class=\"' + cls + '\" data-admin-action=\"toggle-timeline-filter\" data-filter-key=\"' + escAttr(s.key) + '\">' +\n '<i class=\"sw ' + s.sw + '\"></i>' + s.label + '</span>';\n }).join('') + '</div>';\n\n const bars = buckets.map((b) => {\n const val = valueOf(b);\n const tip = active\n ? b.date + ' \u00B7 ' + active.label + ': ' + fmtNum(b[active.key] || 0) + ' tokens'\n : b.date + ' \u00B7 ' + fmtNum(b.total) + ' tokens' +\n (b.cost > 0 ? ' \u00B7 $' + Number(b.cost).toFixed(4) : '');\n let inner;\n if (val <= 0) {\n inner = '<span class=\"tl-empty\"></span>';\n } else if (active) {\n inner = '<span class=\"tl-seg ' + active.seg + '\" style=\"height:' + px(val) + 'px\"></span>';\n } else {\n inner = '<span class=\"tl-seg tl-output\" style=\"height:' + px(b.output) + 'px\"></span>' +\n '<span class=\"tl-seg tl-input\" style=\"height:' + px(b.input) + 'px\"></span>' +\n '<span class=\"tl-seg tl-cache-write\" style=\"height:' + px(b.cacheWrite) + 'px\"></span>' +\n '<span class=\"tl-seg tl-cache-read\" style=\"height:' + px(b.cacheRead) + 'px\"></span>';\n }\n return '<div class=\"tl-bar\">' +\n '<span class=\"tl-tip\">' + escHtml(tip) + '</span>' +\n '<div class=\"tl-fill\">' + inner + '</div>' +\n '</div>';\n }).join('');\n\n const axis = buckets.length\n ? '<div class=\"tl-axis\"><span>' + escHtml(buckets[0].date.slice(5)) +\n '</span><span>' + escHtml(buckets[buckets.length - 1].date.slice(5)) + '</span></div>'\n : '';\n const note = data.hasOlder\n ? '<div class=\"tl-note\">Showing last 14 days \u00B7 earlier activity not shown</div>'\n : '<div class=\"tl-note\">Showing last 14 days</div>';\n const peak = '<div class=\"tl-peak\" style=\"bottom:180px\"><span class=\"tl-peak-label\">' +\n fmtNum(max) + ' tokens</span></div>';\n return cards + legend + '<div class=\"tl-chart\">' + peak + bars + '</div>' + axis + note;\n }\n\n function renderGlobalSettings(data) {\n const thinkingOpts = renderThinkingOptions(data.thinkingLevel);\n const replyModeOpts = renderReplyOptions(data.slack);\n return [\n '<div class=\"config-grid\">',\n renderConfigCard('Default model', [\n '<div class=\"config-row config-row-stack\"><label>Model</label><select id=\"g-model-ref\">' + renderModelOptions(data.provider, data.model) + '</select></div>',\n '<div class=\"config-row\"><label>Thinking</label><select id=\"g-thinking\">' + thinkingOpts + '</select></div>',\n ].join(''), 'saveGlobalModel', 'Save model', 'g-model-result'),\n renderConfigCard('Sandbox limits', [\n '<div class=\"config-row\"><label>CPUs</label><input id=\"g-cpus\" placeholder=\"0.5\" value=\"' + escAttr(data.sandboxCpus || '') + '\"></div>',\n '<div class=\"config-row\"><label>Memory</label><input id=\"g-mem\" placeholder=\"1g\" value=\"' + escAttr(data.sandboxMemory || '') + '\"></div>',\n '<div class=\"config-row\"><label>Boost CPUs</label><input id=\"g-bcpus\" placeholder=\"2\" value=\"' + escAttr(data.sandboxBoostCpus || '') + '\"></div>',\n '<div class=\"config-row\"><label>Boost Mem</label><input id=\"g-bmem\" placeholder=\"4g\" value=\"' + escAttr(data.sandboxBoostMemory || '') + '\"></div>',\n ].join(''), 'saveGlobalSandbox', 'Save sandbox', 'g-sandbox-result'),\n renderConfigCard('Slack', [\n '<div class=\"config-row\"><label>Reply mode</label><select id=\"g-slack-reply-mode\">' + replyModeOpts + '</select></div>',\n ].join(''), 'saveGlobalSlack', 'Save Slack', 'g-slack-result'),\n '</div>',\n ].join('');\n }\n\n async function saveGlobalModel(btn) {\n const selectedModel = parseModelRef(document.getElementById('g-model-ref').value.trim());\n const provider = selectedModel.provider;\n const model = selectedModel.model;\n const thinkingLevel = document.getElementById('g-thinking').value;\n const result = document.getElementById('g-model-result');\n if (!provider || !model) {\n result.style.display = 'block'; result.className = 'inline-result err';\n result.textContent = 'Provider and model are required'; return;\n }\n btn.disabled = true; btn.textContent = 'Saving\u2026'; result.style.display = 'none';\n try {\n await apiPost('/admin/api/settings/model', { provider, model, thinkingLevel });\n result.style.display = 'block'; result.className = 'inline-result ok'; result.textContent = 'Saved \u2713';\n } catch (err) {\n result.style.display = 'block'; result.className = 'inline-result err'; result.textContent = err.message;\n } finally {\n btn.disabled = false; btn.textContent = 'Save model';\n }\n }\n\n async function saveGlobalSandbox(btn) {\n const cpus = document.getElementById('g-cpus').value.trim();\n const memory = document.getElementById('g-mem').value.trim();\n const boostCpus = document.getElementById('g-bcpus').value.trim();\n const boostMemory = document.getElementById('g-bmem').value.trim();\n const result = document.getElementById('g-sandbox-result');\n await saveSetting(btn, result, 'settings/sandbox', { cpus, memory, boostCpus, boostMemory }, 'Save sandbox');\n }\n\n async function saveGlobalSlack(btn) {\n const replyMode = document.getElementById('g-slack-reply-mode').value;\n const result = document.getElementById('g-slack-result');\n await saveSetting(btn, result, 'settings/slack', { replyMode }, 'Save Slack');\n }\n\n ";
2
+ export declare const adminViewStartupScript = " initConvSwitcher();\n initRailNav();\n ";
3
+ export declare const adminViewStyles = "\n\n .shell { max-width: 1180px; }\n\n\n\n .settings-shell {\n display: grid;\n grid-template-columns: 216px 1fr;\n align-items: start;\n gap: 22px;\n }\n\n .settings-rail {\n position: sticky;\n top: 28px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .rail-scope {\n display: flex;\n padding: 3px;\n border: 1px solid var(--border);\n border-radius: 11px;\n background: rgba(0,0,0,0.03);\n gap: 2px;\n }\n .rail-scope-btn {\n flex: 1;\n padding: 7px 8px;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--muted);\n font: 600 0.72rem/1.2 'DM Sans', sans-serif;\n letter-spacing: 0.01em;\n cursor: pointer;\n transition: background 140ms, color 140ms;\n }\n .rail-scope-btn:hover { color: var(--text); }\n .rail-scope-btn.active { background: var(--surface); color: var(--text); box-shadow: 0 1px 3px rgba(0,0,0,0.1); }\n .rail-scope-btn:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }\n\n .rail-nav { display: flex; flex-direction: column; gap: 1px; }\n .rail-nav[hidden] { display: none; }\n .rail-link {\n display: flex; align-items: center; gap: 10px;\n padding: 8px 10px; border: none; border-radius: 9px;\n background: transparent; color: var(--muted); text-align: left;\n font: 500 0.86rem/1.2 'DM Sans', sans-serif; cursor: pointer;\n transition: background 120ms, color 120ms;\n }\n .rail-link svg { flex-shrink: 0; opacity: 0.75; }\n .rail-link:hover { background: rgba(0,0,0,0.045); color: var(--text); }\n .rail-link:hover svg { opacity: 1; }\n .rail-link.active {\n background: var(--text); color: #fafafa; font-weight: 600;\n }\n .rail-link.active svg { opacity: 1; }\n .rail-link:focus-visible { outline: 2px solid var(--text); outline-offset: 1px; }\n\n .settings-panels { min-width: 0; }\n .tab-panel { display: none; flex-direction: column; gap: 0; }\n .tab-panel.active { display: flex; }\n\n\n\n .pane { display: none; padding: 30px 0; border-bottom: 1px solid var(--border); }\n .pane:last-child { border-bottom: none; }\n .pane.active { display: block; animation: pane-in 180ms ease; }\n @keyframes pane-in { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }\n\n .pane-head {\n display: flex; align-items: flex-start; justify-content: space-between;\n gap: 16px; margin-bottom: 22px; flex-wrap: wrap;\n }\n .pane-title {\n font-family: 'Lora', Georgia, serif; font-size: 1.32rem; font-weight: 600;\n letter-spacing: -0.01em; line-height: 1.25; margin-bottom: 5px;\n }\n .pane-desc { color: var(--muted); font-size: 0.88rem; line-height: 1.55; max-width: 52ch; }\n .pane-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }\n .pane-body { display: flex; flex-direction: column; gap: 14px; }\n\n .card-desc { color: var(--muted); font-size: 0.9rem; line-height: 1.55; margin-bottom: 12px; }\n\n .link-result {\n margin-top: 12px; padding: 10px 14px; border-radius: 10px;\n display: flex; gap: 10px; align-items: center; flex-wrap: wrap;\n font-size: 0.84rem;\n }\n .link-result.ok { background: var(--ok-bg); border: 1px solid var(--ok-border); }\n .link-result.err { background: var(--err-bg); border: 1px solid var(--err-border); color: var(--err-text); }\n .link-result.loading { background: rgba(0,0,0,0.025); border: 1px solid var(--border); color: var(--muted); }\n .link-result a {\n color: var(--ok-text);\n font-family: 'JetBrains Mono', ui-monospace, monospace;\n font-size: 0.78rem; word-break: break-all; flex: 1; min-width: 0;\n }\n .link-vault { color: var(--muted); font-size: 0.78rem; flex-shrink: 0; }\n .copy-link-btn {\n padding: 5px 12px; border: 1px solid var(--ok-border); border-radius: 7px;\n background: rgba(255,255,255,0.7); color: var(--ok-text);\n font: 500 0.78rem/1.2 'DM Sans', sans-serif;\n cursor: pointer; flex-shrink: 0;\n }\n\n .config-grid {\n display: grid; grid-template-columns: 1fr 1fr; gap: 18px;\n }\n .config-block { display: flex; flex-direction: column; gap: 10px; }\n\n .config-block .primary-action-btn { align-self: flex-start; min-width: 132px; justify-content: center; }\n .config-row { display: grid; grid-template-columns: 110px 1fr; gap: 10px; align-items: center; }\n .config-row.config-row-stack { grid-template-columns: 1fr; }\n .config-row label { font-size: 0.82rem; color: var(--muted); }\n .config-row input, .config-row select, .config-row textarea {\n padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px;\n font-family: inherit; font-size: 0.84rem; width: 100%;\n }\n .config-row textarea {\n font-family: 'JetBrains Mono', ui-monospace, monospace;\n resize: vertical;\n }\n .toggle { display: inline-flex; align-items: center; gap: 8px; font-size: 0.84rem; }\n\n .inline-result {\n padding: 8px 12px; border-radius: 8px; font-size: 0.82rem; margin-top: 4px;\n }\n .inline-result.ok { background: var(--ok-bg); color: var(--ok-text); border: 1px solid var(--ok-border); }\n .inline-result.err { background: var(--err-bg); color: var(--err-text); border: 1px solid var(--err-border); }\n\n .refresh-btn {\n flex-shrink: 0; padding: 6px 12px;\n border: 1px solid var(--border); border-radius: 10px;\n background: rgba(0,0,0,0.025); color: var(--muted);\n font: 500 0.8rem/1.2 'DM Sans', sans-serif; cursor: pointer;\n transition: background 120ms, color 120ms;\n }\n .refresh-btn:hover { background: rgba(0,0,0,0.06); color: var(--text); }\n\n\n\n .workspace-split {\n display: grid; grid-template-columns: 260px 1fr; gap: 14px;\n min-height: 360px;\n }\n .workspace-tree {\n border: 1px solid var(--border); border-radius: 12px; padding: 10px;\n background: rgba(0,0,0,0.02); overflow: auto; max-height: 480px;\n font-family: 'JetBrains Mono', ui-monospace, monospace;\n font-size: 0.78rem;\n }\n .workspace-tree ul { list-style: none; padding-left: 12px; margin: 0; }\n .workspace-tree .tree-root { padding-left: 0; }\n .workspace-tree details { margin: 1px 0; }\n .workspace-tree summary { cursor: pointer; padding: 2px 4px; border-radius: 4px; }\n .workspace-tree summary:hover { background: rgba(0,0,0,0.05); }\n .tree-dir { color: var(--text); font-weight: 600; }\n .tree-dir.empty { color: var(--subtle); font-weight: 400; }\n .tree-file {\n display: block; width: 100%; text-align: left;\n background: transparent; border: none; cursor: pointer;\n padding: 2px 4px; border-radius: 4px;\n font-family: inherit; font-size: inherit; color: var(--muted);\n }\n .tree-file:hover { background: rgba(0,0,0,0.05); color: var(--text); }\n\n .workspace-preview {\n border: 1px solid var(--border); border-radius: 12px;\n background: #fff; padding: 12px; overflow: auto; max-height: 480px;\n }\n .preview-meta {\n font-size: 0.74rem; color: var(--subtle);\n margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px solid var(--border);\n font-family: 'JetBrains Mono', ui-monospace, monospace;\n }\n .preview-body {\n margin: 0; white-space: pre-wrap; word-break: break-word;\n font-family: 'JetBrains Mono', ui-monospace, monospace;\n font-size: 0.78rem; color: var(--text);\n }\n .placeholder-msg { color: var(--subtle); font-size: 0.86rem; padding: 24px 8px; text-align: center; }\n\n\n\n .skills-list { display: flex; flex-direction: column; gap: 8px; }\n .skill-row {\n padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px;\n background: rgba(0,0,0,0.02);\n display: flex; flex-direction: column; align-items: stretch; gap: 6px;\n }\n .skill-row-btn {\n flex: 1 1 auto; min-width: 0; text-align: left; cursor: pointer; font-family: inherit;\n background: transparent; border: none; padding: 0;\n }\n .skill-row-btn:hover .skill-name { color: var(--accent); }\n .skill-row-actions { display: flex; gap: 6px; flex-shrink: 0; justify-content: flex-end; }\n .skill-name {\n font-weight: 650; font-size: 0.9rem; color: var(--text);\n display: flex; align-items: center; gap: 8px; flex-wrap: wrap;\n }\n .skill-source {\n padding: 1px 8px; border-radius: 999px; font-size: 0.7rem;\n font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;\n }\n .skill-source-global { background: rgba(59,130,246,0.1); color: #1d4ed8; }\n .skill-source-conversation { background: rgba(217,119,6,0.1); color: var(--accent); }\n .skill-desc { color: var(--muted); font-size: 0.82rem; margin-top: 4px; line-height: 1.5; }\n\n\n\n .form-input {\n flex: 1 1 240px; min-width: 0; padding: 8px 10px;\n border: 1px solid var(--border); border-radius: 8px; background: var(--card);\n font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 0.8rem; color: var(--text);\n }\n .status-msg {\n padding: 8px 12px; border-radius: 8px; margin-bottom: 12px;\n font-size: 0.82rem; line-height: 1.5; word-break: break-word;\n }\n .status-msg-ok { background: rgba(22,163,74,0.1); color: #15803d; }\n .status-msg-err { background: rgba(220,38,38,0.1); color: #b91c1c; }\n .status-msg-busy { background: rgba(0,0,0,0.05); color: var(--muted); }\n .mcp-btn {\n padding: 4px 10px; border: 1px solid var(--border); border-radius: 7px;\n background: var(--card); color: var(--text); font-size: 0.76rem; cursor: pointer;\n }\n .mcp-btn:hover { background: rgba(0,0,0,0.05); }\n .mcp-btn-danger { color: #b91c1c; }\n .mcp-badge {\n padding: 1px 8px; border-radius: 999px; font-size: 0.7rem;\n font-weight: 600; letter-spacing: 0.03em;\n }\n .mcp-badge-ok { background: rgba(22,163,74,0.1); color: #15803d; }\n .mcp-badge-warn { background: rgba(217,119,6,0.12); color: var(--accent); }\n\n .mcp-market-head {\n display: flex; align-items: end; justify-content: space-between; gap: 16px;\n margin-bottom: 12px;\n }\n .mcp-market-head h3 { margin: 0; font-size: 1rem; }\n .mcp-market-head p { margin: 3px 0 0; color: var(--muted); font-size: 0.8rem; }\n .mcp-market-head > span {\n color: var(--subtle); font: 500 0.72rem/1.2 'JetBrains Mono', ui-monospace, monospace;\n }\n .mcp-preset-grid {\n display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px;\n }\n .mcp-preset {\n min-width: 0; padding: 14px; border: 1px solid var(--border); border-radius: 12px;\n background:\n linear-gradient(145deg, rgba(255,255,255,0.85), rgba(247,245,238,0.72)),\n var(--card);\n display: flex; flex-direction: column; min-height: 190px;\n }\n .mcp-preset-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }\n .mcp-preset-category {\n color: var(--accent); font-size: 0.66rem; font-weight: 700;\n letter-spacing: 0.09em; text-transform: uppercase;\n }\n .mcp-preset h3 { margin: 14px 0 5px; font-size: 1.06rem; letter-spacing: -0.02em; }\n .mcp-preset p { margin: 0; color: var(--muted); font-size: 0.8rem; line-height: 1.5; }\n .mcp-preset-meta {\n margin-top: 12px; color: var(--subtle); font-size: 0.72rem;\n }\n .mcp-preset-meta code { color: var(--text); background: transparent; }\n .mcp-preset-actions {\n margin-top: auto; padding-top: 16px; display: flex; align-items: center;\n justify-content: space-between; gap: 10px;\n }\n .mcp-preset-actions a, .mcp-setup-link {\n color: var(--muted); font-size: 0.76rem; text-decoration: none;\n }\n .mcp-preset-actions a:hover, .mcp-setup-link:hover { color: var(--text); text-decoration: underline; }\n .mcp-preset-actions .primary-action-btn { padding: 6px 12px; }\n .mcp-installed {\n margin-top: 14px; border-top: 1px solid var(--border); padding-top: 12px;\n }\n .mcp-installed > summary {\n cursor: pointer; color: var(--muted); font-size: 0.8rem; font-weight: 600;\n margin-bottom: 10px;\n }\n .mcp-installed-card { min-height: 150px; }\n .mcp-installed-transport {\n font-family: 'JetBrains Mono', ui-monospace, monospace; word-break: break-all;\n }\n .mcp-add-card {\n min-width: 0; min-height: 190px; border: 1.5px dashed var(--border); border-radius: 12px;\n background: transparent; color: var(--muted); font-size: 0.86rem; font-weight: 600;\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n gap: 8px; cursor: pointer;\n }\n .mcp-add-card:hover { background: rgba(0,0,0,0.03); color: var(--text); border-color: var(--text); }\n .mcp-add-card-plus { font-size: 1.8rem; line-height: 1; font-weight: 400; }\n .mcp-manual-hint { color: var(--muted); font-size: 0.8rem; margin: 0 0 10px; line-height: 1.5; }\n .mcp-add-mode { display: flex; gap: 8px; margin: 0 0 12px; }\n .mcp-mode-btn {\n padding: 6px 12px; border: 1px solid var(--border); border-radius: 999px;\n background: var(--card); font-size: 0.8rem; cursor: pointer; color: var(--muted);\n }\n .mcp-mode-btn.active { background: var(--text); color: #fafafa; border-color: var(--text); }\n .mcp-guided-grid { display: grid; gap: 12px; }\n .mcp-field { display: grid; gap: 5px; }\n .mcp-field span { font-size: 0.78rem; font-weight: 650; color: var(--muted); }\n .mcp-transport-toggle { display: flex; gap: 16px; font-size: 0.85rem; align-items: center; }\n .mcp-transport-toggle label { display: flex; align-items: center; gap: 6px; cursor: pointer; }\n .mcp-transport-fields { display: grid; gap: 12px; }\n .mcp-kv-block { display: grid; gap: 8px; }\n .mcp-kv-head { display: flex; align-items: center; justify-content: space-between; }\n .mcp-kv-head span { font-size: 0.78rem; font-weight: 650; color: var(--muted); }\n .mcp-kv-rows { display: grid; gap: 6px; }\n .mcp-kv-row { display: flex; gap: 8px; }\n .mcp-kv-row .form-input { flex: 1 1 auto; }\n .mcp-json {\n width: 100%; box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.8rem; line-height: 1.45; resize: vertical;\n }\n .mcp-verify { margin-top: 10px; display: grid; gap: 6px; }\n .mcp-verify .inline-result { word-break: break-word; }\n\n .mcp-dialog {\n width: min(620px, calc(100vw - 28px)); max-height: calc(100vh - 40px);\n border: 1px solid var(--border); border-radius: 18px; padding: 22px;\n color: var(--text); background: #fbfaf6; box-shadow: 0 28px 90px rgba(18,18,16,0.24);\n\n position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); margin: 0;\n }\n .mcp-dialog::backdrop { background: rgba(20,20,18,0.5); backdrop-filter: blur(3px); }\n .mcp-dialog-head {\n display: flex; align-items: flex-start; justify-content: space-between; gap: 18px;\n padding-bottom: 14px; border-bottom: 1px solid var(--border);\n }\n .mcp-dialog-head .card-title { margin: 4px 0 0; }\n .mcp-dialog-close {\n border: 0; background: transparent; color: var(--muted); cursor: pointer;\n font-size: 1.6rem; line-height: 1; padding: 2px 5px;\n }\n .mcp-dialog-desc { color: var(--muted); line-height: 1.55; font-size: 0.88rem; }\n .mcp-install-preview {\n display: grid; gap: 6px; padding: 12px; border-radius: 10px;\n background: #1d211f; color: #f5f2e9;\n }\n .mcp-install-preview span {\n color: #aeb8b1; font-size: 0.66rem; font-weight: 700;\n letter-spacing: 0.08em; text-transform: uppercase;\n }\n .mcp-install-preview code {\n color: #f5f2e9; background: transparent; word-break: break-all; font-size: 0.78rem;\n }\n .mcp-security-note, .mcp-replace-note {\n margin-top: 12px; padding: 10px 12px; border-radius: 9px;\n font-size: 0.78rem; line-height: 1.5;\n }\n .mcp-security-note.local {\n background: rgba(220,38,38,0.08); border: 1px solid rgba(185,28,28,0.18); color: #991b1b;\n }\n .mcp-security-note.remote {\n background: rgba(59,130,246,0.08); border: 1px solid rgba(29,78,216,0.16); color: #1e40af;\n }\n .mcp-replace-note { background: rgba(217,119,6,0.1); color: #92400e; }\n .mcp-credential { display: grid; gap: 5px; margin-top: 14px; }\n .mcp-credential span { font-size: 0.78rem; font-weight: 650; }\n .mcp-credential input {\n width: 100%; padding: 9px 10px; border: 1px solid var(--border); border-radius: 8px;\n background: #fff; color: var(--text); font: 0.8rem/1.3 'JetBrains Mono', ui-monospace, monospace;\n }\n .mcp-credential small, .mcp-secret-note, .mcp-no-credentials {\n color: var(--subtle); font-size: 0.72rem; line-height: 1.45;\n }\n .mcp-secret-note { margin: 14px 0 8px; }\n .mcp-no-credentials { margin: 14px 0 0; }\n #mcp-dialog-error, #mcp-custom-dialog-error { margin-top: 14px; }\n .mcp-dialog-actions {\n display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px;\n padding-top: 14px; border-top: 1px solid var(--border);\n }\n\n\n\n .events-list { display: flex; flex-direction: column; gap: 8px; }\n .event-row {\n padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px;\n background: rgba(0,0,0,0.02);\n }\n .event-row-top {\n display: flex; align-items: center; justify-content: space-between;\n gap: 10px;\n }\n .event-name { min-width: 0; flex: 1; word-break: break-all; }\n .event-name code { font-size: 0.82rem; background: transparent; padding: 0; }\n .event-meta { font-size: 0.74rem; color: var(--muted); margin-top: 3px; }\n .event-text {\n font-size: 0.82rem; color: var(--text); margin-top: 6px;\n font-family: 'JetBrains Mono', ui-monospace, monospace;\n white-space: pre-wrap; word-break: break-word;\n }\n .event-delete-btn {\n flex-shrink: 0; padding: 4px 10px;\n border-radius: 7px; border: 1px solid rgba(185, 28, 28, 0.18);\n background: rgba(0,0,0,0.03); color: var(--err-text);\n font: 500 0.76rem/1.2 'DM Sans', sans-serif; cursor: pointer;\n }\n .event-delete-btn:hover:not(:disabled) {\n background: var(--err-bg); border-color: rgba(185, 28, 28, 0.28);\n }\n .event-delete-btn:disabled { opacity: 0.5; cursor: wait; }\n\n\n\n .conv-list { display: flex; flex-direction: column; gap: 6px; }\n .conv-row-btn {\n display: flex; align-items: center; gap: 12px;\n padding: 10px 14px; border: 1px solid var(--border); border-radius: 10px;\n background: rgba(0,0,0,0.02); cursor: pointer; text-align: left;\n transition: background 120ms, border-color 120ms;\n }\n .conv-row-btn:hover { background: rgba(0,0,0,0.05); border-color: rgba(0,0,0,0.14); }\n .conv-id { flex: 1; font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 0.84rem; }\n .conv-last { color: var(--subtle); font-size: 0.78rem; }\n\n .usage-table-wrap { overflow-x: auto; }\n .usage-table { width: 100%; border-collapse: collapse; font-size: 0.78rem; }\n .usage-table th, .usage-table td {\n padding: 8px 10px; border-bottom: 1px solid var(--border);\n text-align: left; white-space: nowrap;\n }\n .usage-table th {\n color: var(--subtle); font-size: 0.68rem;\n text-transform: uppercase; letter-spacing: 0.08em;\n }\n .usage-table code { font-size: 0.72rem; }\n\n .timeline-controls {\n display: flex; flex-wrap: wrap; gap: 18px; align-items: center; margin-bottom: 16px;\n }\n .timeline-controls label {\n display: flex; align-items: center; gap: 8px;\n font-size: 0.76rem; color: var(--muted);\n }\n .timeline-controls select {\n padding: 6px 10px; border: 1px solid var(--border); border-radius: 8px;\n background: #fff; color: var(--text); font-size: 0.8rem; max-width: 240px;\n }\n .tl-note { margin-top: 8px; font-size: 0.72rem; color: var(--subtle); }\n .tl-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 16px; }\n .tl-card { background: rgba(0,0,0,0.025); border-radius: 10px; padding: 10px 12px; }\n .tl-card-label { font-size: 0.73rem; color: var(--muted); margin-bottom: 4px; }\n .tl-card-value { font-size: 1.35rem; font-weight: 600; color: var(--text); }\n .tl-legend { display: flex; gap: 16px; font-size: 0.73rem; color: var(--muted); margin-bottom: 10px; }\n .tl-legend span { display: inline-flex; align-items: center; gap: 6px; }\n .tl-legend-item { cursor: pointer; transition: opacity 100ms; }\n .tl-legend-item:hover { text-decoration: underline; }\n .tl-legend-item.active { color: var(--text); font-weight: 600; text-decoration: underline; }\n .tl-legend-item.dim { opacity: 0.4; }\n .sw { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }\n .sw-cache-read { background: rgba(0,0,0,0.18); }\n .sw-cache-write { background: #3b6fb0; }\n .sw-input { background: var(--accent); }\n .sw-output { background: var(--ok-text); }\n .tl-chart {\n position: relative;\n display: flex; align-items: flex-end; gap: 6px;\n height: 216px; border-bottom: 1px solid var(--border);\n }\n .tl-peak {\n position: absolute; left: 0; right: 0; height: 0;\n border-top: 1px dashed var(--accent); pointer-events: none;\n }\n .tl-peak-label {\n position: absolute; left: 0; top: -15px;\n font-size: 0.7rem; color: var(--accent); white-space: nowrap;\n }\n .tl-bar {\n position: relative; flex: 1; min-width: 0; height: 180px;\n display: flex; flex-direction: column; justify-content: flex-end;\n border-radius: 6px 6px 0 0; transition: background 80ms ease;\n }\n .tl-bar:hover { background: rgba(0,0,0,0.06); }\n .tl-bar:hover .tl-seg { opacity: 0.55; }\n .tl-fill {\n display: flex; flex-direction: column; justify-content: flex-end;\n border-radius: 3px 3px 0 0; overflow: hidden;\n }\n .tl-tip {\n position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%);\n margin-bottom: 6px; padding: 5px 9px; border-radius: 6px;\n background: var(--text); color: #fafafa; font-size: 0.7rem; line-height: 1.35;\n white-space: nowrap; opacity: 0; pointer-events: none; z-index: 5;\n }\n .tl-bar:hover .tl-tip { opacity: 1; }\n .tl-seg { display: block; width: 100%; }\n .tl-seg.tl-output { background: var(--ok-text); }\n .tl-seg.tl-input { background: var(--accent); }\n .tl-seg.tl-cache-write { background: #3b6fb0; }\n .tl-seg.tl-cache-read { background: rgba(0,0,0,0.18); }\n .tl-empty { display: block; width: 100%; height: 3px; background: var(--border); }\n .tl-axis { display: flex; justify-content: space-between; margin-top: 6px; font-size: 0.7rem; color: var(--subtle); }\n\n .status-pill {\n display: inline-flex; padding: 2px 9px; border-radius: 999px;\n font-size: 0.7rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;\n }\n .status-pill.running { background: var(--ok-bg); color: var(--ok-text); border: 1px solid var(--ok-border); }\n\n @media (max-width: 860px) {\n .settings-shell { grid-template-columns: 1fr; }\n .settings-rail { position: static; }\n\n .rail-nav {\n flex-direction: row; flex-wrap: nowrap; gap: 6px;\n overflow-x: auto; padding-bottom: 6px;\n\n min-width: 0; max-width: 100%;\n scrollbar-width: thin; -webkit-overflow-scrolling: touch;\n }\n .settings-rail { min-width: 0; }\n .rail-nav[hidden] { display: none; }\n .rail-link { flex: 0 0 auto; padding: 7px 11px; }\n .rail-link span { white-space: nowrap; }\n }\n\n @media (max-width: 640px) {\n .config-grid { grid-template-columns: 1fr; }\n .config-row { grid-template-columns: 1fr; gap: 4px; }\n .workspace-split { grid-template-columns: 1fr; }\n .workspace-tree, .workspace-preview { max-height: 260px; }\n .mcp-preset-grid { grid-template-columns: 1fr; }\n .mcp-dialog { padding: 18px; }\n }\n";
4
+ //# sourceMappingURL=client-assets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-assets.d.ts","sourceRoot":"","sources":["../../../../src/adapters/web/admin/client-assets.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB,kt/DA2sClC,CAAC;AAEJ,eAAO,MAAM,sBAAsB,oDAEhC,CAAC;AAEJ,eAAO,MAAM,eAAe,28tBAohB3B,CAAC"}