@lelouchhe/webagent 0.2.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +58 -23
  2. package/bin/webagent.mjs +119 -8
  3. package/config.toml +102 -3
  4. package/dist/index.html +64 -41
  5. package/dist/js/app.GSAIYHML.js +4 -0
  6. package/dist/js/chunk.AJZBJBMO.js +1 -0
  7. package/dist/js/chunk.CGWFHJI2.js +76 -0
  8. package/dist/js/chunk.D4ZYHJAM.js +1 -0
  9. package/dist/js/chunk.VZXGXFNN.js +5 -0
  10. package/dist/js/login.PYIK52HN.js +1 -0
  11. package/dist/js/viewer.6DT53STL.js +1 -0
  12. package/dist/login.html +49 -0
  13. package/dist/share-viewer.00gubshk.css +114 -0
  14. package/dist/share-viewer.html +53 -0
  15. package/dist/styles.012p32dz.css +1443 -0
  16. package/dist/sw.js +79 -27
  17. package/dist/theme-init.js +6 -0
  18. package/lib/agent-detect.js +110 -0
  19. package/lib/atomic-write.js +50 -0
  20. package/lib/attachment-dispatch.js +86 -0
  21. package/lib/attachment-interceptor.js +130 -0
  22. package/lib/attachment-labels.js +139 -0
  23. package/lib/attachments.js +154 -0
  24. package/lib/auth-middleware.js +102 -0
  25. package/lib/auth-store.js +269 -0
  26. package/lib/auth.js +89 -0
  27. package/lib/bootstrap.js +70 -0
  28. package/lib/bridge.js +244 -93
  29. package/lib/client-registry.js +60 -0
  30. package/lib/config.js +127 -9
  31. package/lib/daemon.js +185 -40
  32. package/lib/event-handler.js +209 -90
  33. package/lib/log-fmt.js +67 -0
  34. package/lib/log.js +83 -0
  35. package/lib/message-cleanup.js +48 -0
  36. package/lib/mode-bucket.js +62 -0
  37. package/lib/preflight.js +195 -0
  38. package/lib/push-service.js +338 -45
  39. package/lib/routes.js +1218 -144
  40. package/lib/server.js +159 -32
  41. package/lib/session-manager.js +164 -18
  42. package/lib/session-state.js +160 -0
  43. package/lib/sessions-anchor.js +28 -0
  44. package/lib/share/cleanup.js +45 -0
  45. package/lib/share/routes.js +972 -0
  46. package/lib/share/sanitize.js +179 -0
  47. package/lib/sse-manager.js +94 -8
  48. package/lib/sse-ticket.js +45 -0
  49. package/lib/startup-checks.js +94 -0
  50. package/lib/store.js +654 -24
  51. package/lib/title-service.js +42 -9
  52. package/lib/tokens.js +50 -0
  53. package/lib/types.js +23 -0
  54. package/package.json +38 -4
  55. package/dist/js/app.4FZ67UW4.js +0 -10
  56. package/dist/styles.008ve1hx.css +0 -669
  57. package/lib/shared/constants.js +0 -17
@@ -1,25 +1,34 @@
1
- const TITLE_MODEL = "claude-haiku-4.5";
1
+ import { log } from "./log.js";
2
+ const tlog = log.scope("title");
2
3
  export class TitleService {
3
4
  titleSessionId = null;
4
5
  activeSourceSessions = new Set();
5
6
  cancelledSourceSessions = new Set();
6
7
  defaultCwd;
8
+ modelPatterns;
7
9
  store;
8
10
  sessions;
9
- constructor(store, sessions, defaultCwd) {
11
+ constructor(store, sessions, defaultCwd, modelPatterns = []) {
10
12
  this.store = store;
11
13
  this.sessions = sessions;
12
14
  this.defaultCwd = defaultCwd;
15
+ // Lowercase + drop empty strings for case-insensitive substring match.
16
+ this.modelPatterns = modelPatterns
17
+ .map((p) => p.trim().toLowerCase())
18
+ .filter((p) => p.length > 0);
13
19
  }
14
20
  /** Generate a title for the session (non-blocking, fire-and-forget). */
15
21
  generate(bridge, userMessage, sessionId, onTitle) {
16
- if (this.sessions.sessionHasTitle.has(sessionId) || this.activeSourceSessions.has(sessionId))
22
+ if (this.sessions.sessionHasTitle.has(sessionId) ||
23
+ this.activeSourceSessions.has(sessionId))
17
24
  return;
18
- this._generate(bridge, userMessage, sessionId).then((title) => {
25
+ this._generate(bridge, userMessage, sessionId)
26
+ .then((title) => {
19
27
  if (title && onTitle)
20
28
  onTitle(title);
21
- }).catch((err) => {
22
- console.error(`[title] generation failed:`, err);
29
+ })
30
+ .catch((err) => {
31
+ tlog.error("generation failed", { error: err });
23
32
  });
24
33
  }
25
34
  async _generate(bridge, userMessage, sessionId) {
@@ -38,7 +47,10 @@ export class TitleService {
38
47
  // User may have set a title while generation was in flight
39
48
  if (this.sessions.sessionHasTitle.has(sessionId))
40
49
  return;
41
- const cleaned = title.replace(/^["']|["']$/g, "").trim().slice(0, 30);
50
+ const cleaned = title
51
+ .replace(/^["']|["']$/g, "")
52
+ .trim()
53
+ .slice(0, 30);
42
54
  if (!cleaned)
43
55
  return;
44
56
  this.store.updateSessionTitle(sessionId, cleaned);
@@ -65,9 +77,16 @@ export class TitleService {
65
77
  if (this.titleSessionId)
66
78
  return this.titleSessionId;
67
79
  try {
68
- const id = await bridge.newSession(this.defaultCwd, { silent: true });
80
+ const { sessionId: id, configOptions } = await bridge.newSession(this.defaultCwd, { silent: true });
69
81
  this.sessions.liveSessions.add(id);
70
- await bridge.setConfigOption(id, "model", TITLE_MODEL).catch(() => []);
82
+ // Pick the cheapest available model by matching id substrings against
83
+ // the agent's reported availableModels (`configOptions[id=model].options`).
84
+ // Empty pattern list, no model option, or no match → skip the call and
85
+ // inherit the agent's default model (`currentModelId`).
86
+ const picked = this.pickTitleModel(configOptions);
87
+ if (picked) {
88
+ await bridge.setConfigOption(id, "model", picked).catch(() => []);
89
+ }
71
90
  this.titleSessionId = id;
72
91
  return id;
73
92
  }
@@ -75,4 +94,18 @@ export class TitleService {
75
94
  return null;
76
95
  }
77
96
  }
97
+ /** Find the first available model whose id matches any pattern (case-insensitive). */
98
+ pickTitleModel(configOptions) {
99
+ if (this.modelPatterns.length === 0)
100
+ return null;
101
+ const modelOpt = configOptions.find((c) => c.id === "model");
102
+ if (!modelOpt || modelOpt.options.length === 0)
103
+ return null;
104
+ for (const pattern of this.modelPatterns) {
105
+ const hit = modelOpt.options.find((o) => o.value.toLowerCase().includes(pattern));
106
+ if (hit)
107
+ return hit.value;
108
+ }
109
+ return null;
110
+ }
78
111
  }
package/lib/tokens.js ADDED
@@ -0,0 +1,50 @@
1
+ import { randomBytes } from "node:crypto";
2
+ /**
3
+ * Auth-bearing token generators (single audit surface).
4
+ *
5
+ * This file centralizes generation of all string tokens that act as
6
+ * credentials — i.e. clients hold them, present them to the server,
7
+ * and the server grants access on that basis. New auth-bearing tokens
8
+ * MUST be added here so the security review surface stays one file.
9
+ *
10
+ * Out of scope (intentionally not here):
11
+ * - Internal IDs that aren't auth-bearing (client IDs, request IDs,
12
+ * error correlation IDs) — see sse-manager.ts, routes.ts, etc.
13
+ * - HMAC signing keys (image URL secret) — see server.ts.
14
+ * - UUIDs used as opaque labels (session IDs, msg IDs) — these
15
+ * are protocol identifiers, not credentials.
16
+ *
17
+ * Repo convention: `node:crypto` only. No `nanoid` / `uuid` deps.
18
+ */
19
+ const API_TOKEN_PREFIX = "wat_";
20
+ /**
21
+ * Share viewer token. 24 base64url chars / 144 bits of entropy.
22
+ *
23
+ * Base64url (`[A-Za-z0-9_-]`) is the RFC 4648 standard, has Node
24
+ * built-in support, and is 33% shorter than the hex form at the same
25
+ * entropy. Share links are produced as full URLs by the client
26
+ * (copy-link affordance, no manual retyping), so the double-click
27
+ * selection that hex previously enabled is no longer relevant.
28
+ */
29
+ export function generateShareToken() {
30
+ return randomBytes(18).toString("base64url");
31
+ }
32
+ /**
33
+ * API auth token. "wat_" prefix + 43 base64url chars / 256 bits.
34
+ *
35
+ * Prefix lets secret scanners (GitHub, Slack) match `wat_[A-Za-z0-9_-]{43}`
36
+ * and gives `grep` an obvious search target in logs.
37
+ */
38
+ export function generateApiToken() {
39
+ return API_TOKEN_PREFIX + randomBytes(32).toString("base64url");
40
+ }
41
+ /**
42
+ * Single-use SSE ticket. 32 base64url chars / 192 bits.
43
+ *
44
+ * Lifecycle is short (default 60s TTL, deleted on consume), so length
45
+ * sits between share and API tokens — well above brute-force feasibility,
46
+ * comfortably below "user pastes this manually".
47
+ */
48
+ export function generateSseTicket() {
49
+ return randomBytes(24).toString("base64url");
50
+ }
package/lib/types.js CHANGED
@@ -1,3 +1,26 @@
1
+ import { z } from "zod";
2
+ // --- Inbox message ingress (Stage B primitive) ---
3
+ const FROM_REF_ALLOWED = /^(cron|external):[A-Za-z0-9._\-+/]{1,120}$/;
4
+ export const MessageIngressSchema = z.object({
5
+ from_ref: z.string().min(1).max(128).regex(FROM_REF_ALLOWED, {
6
+ message: "from_ref must start with 'cron:' or 'external:' (reserved values and 'session:<id>' rejected in MVP)",
7
+ }),
8
+ from_label: z.string().max(64).optional(),
9
+ to: z.union([z.literal("user"), z.string().regex(/^session:[^\s]+$/)]),
10
+ deliver: z.enum(["silent", "inapp", "push"]).default("push"),
11
+ dedup_key: z.string().max(128).optional(),
12
+ title: z.string().min(1).max(256),
13
+ body: z.string().max(64 * 1024),
14
+ cwd: z.string().max(4096).optional(),
15
+ });
16
+ export const MessageEventDataSchema = z.object({
17
+ message_id: z.string().min(1),
18
+ from_ref: z.string().min(1),
19
+ from_label: z.string().nullish(),
20
+ title: z.string(),
21
+ body: z.string(),
22
+ cwd: z.string().nullish(),
23
+ });
1
24
  // --- Utility ---
2
25
  export function errorMessage(err) {
3
26
  if (err instanceof Error)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lelouchhe/webagent",
3
- "version": "0.2.6",
3
+ "version": "0.4.0",
4
4
  "description": "A terminal-style web UI for ACP-compatible agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -31,30 +31,64 @@
31
31
  ],
32
32
  "scripts": {
33
33
  "compile": "tsc -p tsconfig.build.json",
34
+ "create-token": "node --experimental-strip-types scripts/create-token.ts",
35
+ "typecheck": "tsc --noEmit",
34
36
  "prepublishOnly": "npm run compile && npm run build",
35
37
  "build": "node scripts/build.js",
36
38
  "start": "node --experimental-strip-types src/server.ts --config config.toml",
39
+ "pretest": "test -f .husky/_/pre-push || npx husky",
37
40
  "test": "npm run compile && node --experimental-strip-types --test test/*.test.ts",
38
- "test:e2e": "playwright test",
41
+ "test:e2e": "rm -rf test/e2e-data && playwright test",
39
42
  "dev": "node scripts/build.js --watch & node --experimental-strip-types --watch src/server.ts --config config.dev.toml",
40
43
  "dev:start": "node scripts/build.js --dev && node --experimental-strip-types src/server.ts --config config.dev.toml &",
41
44
  "dev:stop": "lsof -ti:6801 | xargs kill 2>/dev/null || true",
42
- "screenshots": "playwright test --config scripts/screenshots.config.ts"
45
+ "screenshots": "playwright test --config scripts/screenshots.config.ts",
46
+ "lint": "eslint . --max-warnings 0",
47
+ "lint:fix": "eslint . --fix",
48
+ "format": "prettier --write .",
49
+ "format:check": "prettier --check .",
50
+ "prepare": "husky"
51
+ },
52
+ "lint-staged": {
53
+ "*.{ts,js,mjs,cjs}": [
54
+ "prettier --write",
55
+ "eslint --fix"
56
+ ],
57
+ "*.{json,md,css,html,yml,yaml}": [
58
+ "prettier --write"
59
+ ]
43
60
  },
44
61
  "dependencies": {
45
62
  "@agentclientprotocol/sdk": "^0.14.1",
63
+ "@types/proper-lockfile": "^4.1.4",
46
64
  "@types/web-push": "^3.6.4",
47
65
  "better-sqlite3": "^12.6.2",
66
+ "busboy": "^1.6.0",
67
+ "dompurify": "^3.4.1",
68
+ "file-type": "^22.0.1",
69
+ "highlight.js": "^11.11.1",
70
+ "marked": "^18.0.2",
71
+ "proper-lockfile": "^4.1.2",
48
72
  "smol-toml": "^1.6.0",
49
73
  "web-push": "^3.6.7",
50
74
  "zod": "^4.3.6"
51
75
  },
52
76
  "devDependencies": {
77
+ "@eslint/js": "^10.0.1",
53
78
  "@types/better-sqlite3": "^7.6.13",
79
+ "@types/busboy": "^1.5.4",
80
+ "@types/dompurify": "^3.0.5",
54
81
  "@types/node": "^25.3.3",
55
82
  "esbuild": "^0.27.3",
83
+ "eslint": "^10.2.1",
84
+ "eslint-config-prettier": "^10.1.8",
85
+ "globals": "^17.5.0",
56
86
  "happy-dom": "^20.8.3",
87
+ "husky": "^9.1.7",
88
+ "lint-staged": "^16.4.0",
57
89
  "playwright": "^1.58.2",
58
- "typescript": "^5.9.3"
90
+ "prettier": "^3.8.3",
91
+ "typescript": "^5.9.3",
92
+ "typescript-eslint": "^8.59.0"
59
93
  }
60
94
  }
@@ -1,10 +0,0 @@
1
- var me=class extends Error{name="ApiError";status;constructor(t,s){super(s),this.status=t}};async function W(e,t){let s=await fetch(e,t);if(!s.ok){let a=`HTTP ${s.status}`;try{let r=await s.json();r.error&&(a=String(r.error))}catch{}throw new me(s.status,a)}let i=await s.text();if(i)return JSON.parse(i)}function N(e,t){return W(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})}function Re(e){let t={};return e?.cwd&&(t.cwd=e.cwd),e?.inheritFromSessionId&&(t.inheritFromSessionId=e.inheritFromSessionId),N("/api/v1/sessions",t)}function ge(e){return W("/api/v1/sessions/"+e,{method:"DELETE"})}function ie(){return W("/api/v1/sessions")}function $(e){return W("/api/v1/sessions/"+e)}function he(e,t,s){let i={text:t};return s?.length&&(i.images=s),N("/api/v1/sessions/"+e+"/prompt",i)}function De(e){return N("/api/v1/sessions/"+e+"/cancel",{})}function oe(e,t,s){return N("/api/v1/sessions/"+e+"/permissions/"+t,{optionId:s})}function re(e,t){return N("/api/v1/sessions/"+e+"/permissions/"+t,{denied:!0})}function G(e,t,s){let i=t.replace(/_/g,"-");return W("/api/v1/sessions/"+e+"/"+i,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:s})})}function je(e,t){return W("/api/v1/sessions/"+e+"/title",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:t})})}function Fe(e,t){return N("/api/v1/sessions/"+e+"/bash",{command:t})}function J(e,t,s){let i={visible:t};return s&&(i.sessionId=s),N("/api/beta/clients/"+e+"/visibility",i)}function Ue(){return N("/api/v1/bridge/reload",{})}var T=e=>document.querySelector(e),o={messages:T("#messages"),input:T("#input"),sendBtn:T("#send-btn"),prompt:T("#input-prompt"),status:T("#status"),sessionInfo:T("#session-info"),attachBtn:T("#attach-btn"),fileInput:T("#file-input"),attachPreview:T("#attach-preview"),themeBtn:T("#theme-btn"),slashMenu:T("#slash-menu"),inputArea:T("#input-area"),statusBar:T("#status-bar")},n={eventSource:null,clientId:null,sessionId:null,sessionSwitchGen:0,sessionCwd:null,sessionTitle:null,awaitingNewSession:!1,configOptions:[],currentAssistantEl:null,currentAssistantText:"",currentThinkingEl:null,currentThinkingText:"",busy:!1,pendingImages:[],currentBashEl:null,followMessages:!0,pendingToolCallIds:new Set,pendingPermissionRequestIds:new Set,pendingPromptDone:!1,turnEnded:!1,newTurnStarted:!1,sentMessageForSession:null,sentBashForSession:null,cancelTimeout:1e4,serverVersion:null,agentName:null,agentVersion:null,_cancelTimerId:null,_onCancelTimeout:null,lastEventSeq:0,oldestLoadedSeq:0,hasMoreHistory:!1,loadingOlderEvents:!1,replayInProgress:!1,replayTarget:null,replayQueue:[],unconfirmedPermissions:new Map,agentReloading:!1},bt={disconnected:"is-disconnected",connecting:"is-connecting",connected:"is-connected"};function A(e,t=e){o.status.textContent="",o.status.className=`status-dot ${bt[e]}`,o.status.dataset.state=e,o.status.setAttribute("aria-label",t),o.status.setAttribute("title",t)}function x(e){return n.configOptions.find(t=>t.id===e)}function ae(e){return x(e)?.currentValue??null}function We(e,t){let s=x(e);s&&(s.currentValue=t)}function ye(e){n.configOptions=e,q(),R()}function q(){o.inputArea.classList.remove("plan-mode","autopilot-mode");let e=ae("mode")||"";e.includes("#plan")?o.inputArea.classList.add("plan-mode"):e.includes("#autopilot")&&o.inputArea.classList.add("autopilot-mode")}function R(){if(!o.statusBar)return;let e=ae("model"),t=n.sessionCwd||"";if(o.statusBar.textContent="",t){e&&o.statusBar.appendChild(document.createTextNode(e+" \xB7 "));let s=document.createElement("span");s.className="status-cwd",s.textContent=t,o.statusBar.appendChild(s)}else e&&(o.statusBar.textContent=e)}function h(e){n.busy=e,e?(o.sendBtn.textContent="^C",o.sendBtn.title="Cancel (Ctrl+C)",o.sendBtn.classList.add("cancel"),o.prompt.classList.add("busy")):(o.sendBtn.textContent="\u21B5",o.sendBtn.title="Send (Enter)",o.sendBtn.classList.remove("cancel"),o.prompt.classList.remove("busy"))}function L({cwd:e,inheritFromSessionId:t=n.sessionId}={}){n.awaitingNewSession=!0,Re({cwd:e,inheritFromSessionId:t}).catch(()=>{})}var Ke=[];function Ve(e){Ke.push(e)}function w(){for(let e of Ke)e();o.messages.innerHTML="",n.currentAssistantEl=null,n.currentAssistantText="",n.currentThinkingEl=null,n.currentThinkingText="",n.pendingImages.length=0,n.followMessages=!0,n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.unconfirmedPermissions.clear(),n.pendingPromptDone=!1,n.turnEnded=!1,n.newTurnStarted=!1,n._cancelTimerId=null,n.lastEventSeq=0,n.oldestLoadedSeq=0,n.hasMoreHistory=!1,n.loadingOlderEvents=!1,n.replayInProgress=!1,n.replayQueue=[],o.attachPreview.innerHTML="",o.attachPreview.classList.remove("active"),o.input.disabled=!1,o.sendBtn.disabled=!1,o.input.placeholder="Message or ?",h(!1),n.sessionTitle=null,n.sessionCwd=null,n.configOptions=[],le(null,null),o.statusBar&&(o.statusBar.textContent="")}function Q(){return!n.busy||!n.sessionId?!1:(De(n.sessionId).catch(()=>{}),Y(),n.cancelTimeout>0&&(n._cancelTimerId=setTimeout(()=>{n._cancelTimerId=null,n.busy&&(n.turnEnded=!0,h(!1),n._onCancelTimeout?.())},n.cancelTimeout)),!0)}function Y(){n._cancelTimerId!=null&&(clearTimeout(n._cancelTimerId),n._cancelTimerId=null)}function Ge(){return location.hash.slice(1)||null}function Je(e){history.replaceState(null,"",`#${e}`)}function le(e,t){o.sessionInfo.textContent=t||(e?e.slice(0,8)+"\u2026":""),document.title=t||">_"}A("disconnected");var B="idle",we=[],Tt="11.11.1",Qe=`https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@${Tt}/build`,Et={dark:"github-dark.min.css",light:"github.min.css",auto:""},P=null;function kt(){let e=document.documentElement.getAttribute("data-theme")||"auto";return e==="light"?"light":e==="dark"?"dark":window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark"}function Se(){let e=kt(),t=Et[e],s=`${Qe}/styles/${t}`;if(P){if(P.href===s)return;P.href=s;return}P=document.createElement("link"),P.rel="stylesheet",P.href=s,document.head.appendChild(P)}function Ct(){return new Promise((e,t)=>{let s=document.createElement("script");s.src=`${Qe}/highlight.min.js`,s.onload=()=>e(),s.onerror=()=>t(new Error("Failed to load highlight.js")),document.head.appendChild(s)})}async function xt(){if(B==="ready")return!0;if(B==="loading")return new Promise(e=>{we.push(()=>e(B==="ready"))});B="loading";try{return Se(),await Ct(),B="ready",we.splice(0).forEach(t=>t()),!0}catch{return B="idle",we.splice(0).forEach(t=>t()),!1}}function Lt(e){let t=globalThis.hljs;if(t)for(let s of e.querySelectorAll("pre code"))s.dataset.highlighted||t.highlightElement(s)}function Mt(e){for(let t of e.classList)if(t.startsWith("language-"))return t.slice(9);return""}function $t(e){let t=e.querySelectorAll("pre");for(let s of t){let i=s.querySelector("code");if(!i||s.parentElement?.classList.contains("code-block-wrapper"))continue;let a=Mt(i),r=document.createElement("div");r.className="code-block-wrapper";let l=document.createElement("div");l.className="code-toolbar";let d=document.createElement("button");d.className="copy-btn",d.textContent="cp",d.type="button",l.appendChild(d),s.replaceWith(r),r.appendChild(l),r.appendChild(s)}}var ve=new WeakMap;function Ye(e){let t=e.target.closest(".copy-btn");if(!t)return;let i=t.closest(".code-block-wrapper")?.querySelector("code");if(!i)return;let a=i.textContent||"";navigator.clipboard.writeText(a).then(()=>{let r=ve.get(t);r&&clearTimeout(r),t.textContent="\u2713",t.classList.add("copied");let l=setTimeout(()=>{t.textContent="cp",t.classList.remove("copied"),ve.delete(t)},1500);ve.set(t,l)}).catch(()=>{let r=document.createRange();r.selectNodeContents(i);let l=window.getSelection();l?.removeAllRanges(),l?.addRange(r)})}async function z(e){if(!e.querySelector("pre code"))return;$t(e),await xt()&&Lt(e)}function ze(){B==="ready"&&P&&Se()}typeof window<"u"&&window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{B==="ready"&&P&&Se()});marked.setOptions({breaks:!0,gfm:!0});function ce(e){return DOMPurify.sanitize(marked.parse(e))}function j(e,t){let s=document.createElement("div");return s.className=`msg ${e}`,s.innerHTML=e==="user"?p(t).replace(/\n/g,"<br>"):ce(t),E(s),s}function c(e){let t=document.createElement("div");return t.className="system-msg",t.textContent=e,E(t),t}function H(){let e=n.currentAssistantEl;n.currentAssistantEl=null,n.currentAssistantText="",e&&typeof e.querySelector=="function"&&z(e)}function k(){if(n.currentThinkingEl){let e=n.currentThinkingEl.querySelector("summary");e.textContent="\u283F thought",e.classList.remove("active"),e.style.animation="none",n.currentThinkingEl=null,n.currentThinkingText=""}}var D=null,Pt=80;function be(e){return e.scrollHeight-e.scrollTop-e.clientHeight<Pt}function Ht(){n.followMessages=be(o.messages)}o.messages.addEventListener("scroll",Ht);function _t(){return n.followMessages||be(o.messages)}function E(e,t=!1){if(n.replayTarget)return n.replayTarget.appendChild(e),e;let s=t||_t();return o.messages.appendChild(e),I(s),e}function Xe(){F(),D=document.createElement("div"),D.id="waiting",D.innerHTML='<span class="cursor">\u258C</span>',E(D,!0)}function F(){D&&(D.remove(),D=null)}var Ie=!1;function I(e){let t=o.messages;if(e||n.followMessages){typeof requestAnimationFrame=="function"?Ie||(Ie=!0,requestAnimationFrame(()=>{Ie=!1,t.scrollTop=t.scrollHeight})):t.scrollTop=t.scrollHeight,n.followMessages=!0;return}n.followMessages=be(t)}function p(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}function et(e){if(!e)return"";let t=new Date(e.endsWith("Z")?e:e+"Z"),s=i=>String(i).padStart(2,"0");return`${t.getFullYear()}-${s(t.getMonth()+1)}-${s(t.getDate())} ${s(t.getHours())}:${s(t.getMinutes())}`}function Te(e){if(typeof e=="string"&&e.includes("*** Begin Patch")){let t=e.split(`
2
- `),s=[];for(let i of t)i.startsWith("*** Begin Patch")||i.startsWith("*** End Patch")||(i.startsWith("*** Update File:")||i.startsWith("*** Add File:")||i.startsWith("*** Delete File:")?s.push(`<span class="diff-file">${p(i)}</span>`):i.startsWith("@@")?s.push(`<span class="diff-hunk">${p(i)}</span>`):i.startsWith("-")?s.push(`<span class="diff-del">${p(i)}</span>`):i.startsWith("+")?s.push(`<span class="diff-add">${p(i)}</span>`):s.push(p(i)));return s.join(`
3
- `)}if(e&&typeof e=="object"){let t=[];if(e.path&&t.push(`<span class="diff-file">*** ${p(e.path)}</span>`),e.old_str!=null)for(let s of String(e.old_str).split(`
4
- `))t.push(`<span class="diff-del">- ${p(s)}</span>`);if(e.new_str!=null)for(let s of String(e.new_str).split(`
5
- `))t.push(`<span class="diff-add">+ ${p(s)}</span>`);if(e.file_text!=null)for(let s of String(e.file_text).split(`
6
- `))t.push(`<span class="diff-add">+ ${p(s)}</span>`);return t.length>(e.path?1:0)?t.join(`
7
- `):null}return null}function Z(e,t=!1){let s=document.createElement("div");return s.className="bash-block",s.innerHTML=`<span class="bash-cmd${t?" running":""}">${p(e)}</span><div class="bash-output"></div>`,s.querySelector(".bash-cmd").addEventListener("click",()=>{let i=s.querySelector(".bash-output");i.style.display==="none"?i.style.display="block":i.classList.contains("has-content")&&(i.style.display="none")}),E(s),t&&(n.currentBashEl=s),s}function X(e,t,s){if(!e)return;let i=e.querySelector(".bash-cmd");i.classList.remove("running");let a="";if(s?a=`[signal: ${s}]`:t!==0&&t!=null&&(a=`[exit: ${t}]`),a){let r=document.createElement("span");r.className=`bash-exit ${t===0?"ok":"fail"}`,r.textContent=a,i.after(r)}e===n.currentBashEl&&(n.currentBashEl=null)}var Nt={auto:"\u25D1",light:"\u2600",dark:"\u263E"},Ze=["auto","light","dark"];function tt(){return localStorage.getItem("theme")||"auto"}function nt(e){document.documentElement.setAttribute("data-theme",e),o.themeBtn.textContent=Nt[e],o.themeBtn.title=`Theme: ${e}`,localStorage.setItem("theme",e);for(let t of st)t()}var st=[];function it(e){st.push(e)}o.themeBtn.onclick=()=>{let e=tt();nt(Ze[(Ze.indexOf(e)+1)%3])};nt(tt());var Ee={task_complete:"\u2714",read:"cat",edit:"edit",execute:"exec",search:"find",delete:"rm"},ke="run",Ce={pending:"\u25CB",in_progress:"\u25C9",completed:"\u25CF"};function ot(e){return n.replayTarget?.querySelector(`[id="${e}"]`)??document.getElementById(e)}function qt(e){return n.replayTarget?.querySelector(e)??document.querySelector(e)}var rt="webagent_notify_tip_shown",at="webagent_notify_tip_denied_shown";function Bt(){if(typeof Notification>"u"||n.replayInProgress)return;let e=Notification.permission;if(e!=="granted"){if(e==="denied"){if(localStorage.getItem(at))return;localStorage.setItem(at,"1"),c("tip: notifications are blocked \u2014 allow in browser site settings to enable");return}localStorage.getItem(rt)||(localStorage.setItem(rt,"1"),c("tip: use /notify to enable background notifications"))}}function de(){n.pendingPromptDone&&(n.pendingToolCallIds.size>0||n.pendingPermissionRequestIds.size>0||(F(),k(),H(),h(!1),n.pendingPromptDone=!1,Bt()))}function Ot(){for(let e of n.pendingToolCallIds){let t=document.getElementById(`tc-${e}`);if(!t)continue;t.className="tool-call failed";let s=t.querySelector(".icon");s&&(s.textContent="\u2717")}for(let e of n.pendingPermissionRequestIds){let t=document.querySelector(`.permission[data-request-id="${e}"]`);if(!t||!t.querySelector("button"))continue;let i=t.querySelector(".title")?.textContent||"\u26BF";t.innerHTML=`<span style="opacity:0.5">${p(i)} \u2014 cancelled</span>`}n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear()}function At(){for(let e of n.pendingToolCallIds){let t=document.getElementById(`tc-${e}`);if(!t)continue;t.className="tool-call completed";let s=t.querySelector(".icon");s&&(s.textContent="\u2713")}n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear()}function Le(e){if(Array.isArray(e))return{events:e,streaming:{thinking:!1,assistant:!1}};let t=e;return{events:t.events??[],streaming:t.streaming??{thinking:!1,assistant:!1},total:typeof t.total=="number"?t.total:void 0,hasMore:typeof t.hasMore=="boolean"?t.hasMore:void 0}}var lt=200;async function O(e){n.replayInProgress=!0,n.replayQueue=[];try{let t=await fetch(`/api/v1/sessions/${e}/events?limit=${lt}`);if(!t.ok)return!1;let s=await t.json(),{events:i,streaming:a,hasMore:r}=Le(s),l=document.createDocumentFragment(),d=$e(i);n.replayTarget=l;for(let u=0;u<i.length;u++){let f=JSON.parse(i[u].data);Pe(i[u].type,f,i,u,d)}return n.replayTarget=null,o.messages.style.display="none",o.messages.appendChild(l),o.messages.style.display="",i.length&&(n.lastEventSeq=i[i.length-1].seq,n.oldestLoadedSeq=i[0].seq),n.hasMoreHistory=r===!0,n.hasMoreHistory&&Rt(),ct(),xe(i,a),!0}catch{return!1}finally{n.replayTarget=null,n.replayInProgress=!1,ut()}}function xe(e,t){if(t.thinking&&e.length){for(let s=e.length-1;s>=0;s--)if(e[s].type==="thinking"){let i=JSON.parse(e[s].data),a=o.messages.querySelectorAll(".thinking"),r=a[a.length-1];if(r){n.currentThinkingEl=r,n.currentThinkingText=i.text;let l=r.querySelector("summary");l&&(l.textContent="\u283F thinking...",l.classList.add("active"))}break}}if(t.assistant&&e.length){for(let s=e.length-1;s>=0;s--)if(e[s].type==="assistant_message"){let i=JSON.parse(e[s].data),a=o.messages.querySelectorAll(".msg.assistant"),r=a[a.length-1];r&&(n.currentAssistantEl=r,n.currentAssistantText=i.text);break}}}function ct(){let e=o.messages.querySelector("[data-sync-boundary]");e&&e.removeAttribute("data-sync-boundary");let t=o.messages.lastElementChild;t&&t.setAttribute("data-sync-boundary","")}async function Me(e){n.replayInProgress=!0,n.replayQueue=[];try{let t=`/api/v1/sessions/${e}/events?after=${n.lastEventSeq}`,s=await fetch(t);if(!s.ok)return!1;let i=await s.json(),{events:a,streaming:r}=Le(i),l=o.messages.querySelector("[data-sync-boundary]");if(l)for(;l.nextElementSibling;)l.nextElementSibling.remove();if(n.currentAssistantEl=null,n.currentAssistantText="",n.currentThinkingEl=null,n.currentThinkingText="",n.currentBashEl=null,a.length===0)return xe(a,r),!0;let d=document.createDocumentFragment(),u=$e(a);n.replayTarget=d;for(let f=0;f<a.length;f++){let g=JSON.parse(a[f].data);Pe(a[f].type,g,a,f,u)}return n.replayTarget=null,o.messages.appendChild(d),n.lastEventSeq=a[a.length-1].seq,ct(),xe(a,r),!0}catch{return!1}finally{n.replayTarget=null,n.replayInProgress=!1,ut()}}var ee=null;function Rt(){ue();let e=document.createElement("div");e.id="history-sentinel",e.className="history-sentinel",e.textContent="\u2191 loading\u2026",o.messages.prepend(e),typeof IntersectionObserver=="function"&&(ee=new IntersectionObserver(t=>{t[0]?.isIntersecting&&!n.loadingOlderEvents&&n.hasMoreHistory&&n.sessionId&&Dt(n.sessionId)},{root:o.messages,rootMargin:"200px 0px 0px 0px"}),ee.observe(e))}function ue(){ee&&(ee.disconnect(),ee=null),document.getElementById("history-sentinel")?.remove()}Ve(ue);async function Dt(e){if(n.loadingOlderEvents||!n.hasMoreHistory||n.oldestLoadedSeq<=0)return!1;n.loadingOlderEvents=!0;try{let t=await fetch(`/api/v1/sessions/${e}/events?limit=${lt}&before=${n.oldestLoadedSeq}`);if(!t.ok||e!==n.sessionId)return!1;let s=await t.json(),{events:i,hasMore:a}=Le(s);if(i.length===0)return n.hasMoreHistory=!1,ue(),!0;let r=document.createDocumentFragment(),l=$e(i);n.replayTarget=r;for(let g=0;g<i.length;g++){let S=JSON.parse(i[g].data);Pe(i[g].type,S,i,g,l)}n.replayTarget=null;let d=o.messages,u=d.scrollHeight,f=document.getElementById("history-sentinel");return f?f.after(r):d.prepend(r),d.scrollTop+=d.scrollHeight-u,n.oldestLoadedSeq=i[0].seq,n.hasMoreHistory=a===!0,n.hasMoreHistory||ue(),!0}catch{return!1}finally{n.loadingOlderEvents=!1}}function dt(){for(let[e,t]of n.unconfirmedPermissions){let s=document.querySelector(`.permission[data-request-id="${e}"]`);if(!s||!s.querySelector("button")){n.unconfirmedPermissions.delete(e);continue}t.denied?re(t.sessionId,e).catch(()=>{}):oe(t.sessionId,e,t.optionId).catch(()=>{});let i=s.dataset.title?`\u26BF ${p(s.dataset.title)}`:"\u26BF";s.innerHTML=`<span style="opacity:0.5">${i} \u2014 ${p(t.optionName)}</span>`,n.unconfirmedPermissions.delete(e)}}function $e(e){let t=new Set;for(let s of e)s.type==="permission_response"&&t.add(JSON.parse(s.data).requestId);return{toolCalls:new Map,permissions:new Map,resolvedPermissions:t,currentBashEl:null}}function Pe(e,t,s,i,a){switch(e){case"user_message":{let r=j("user",t.text);if(t.images)for(let l of t.images){let d=document.createElement("img");d.className="user-image",d.src=l.path,r.appendChild(d)}break}case"assistant_message":{let l=(n.replayTarget||o.messages).lastElementChild;if(l&&l.classList.contains("msg")&&l.classList.contains("assistant")){let f=(l.getAttribute("data-raw")||"")+t.text;l.setAttribute("data-raw",f),l.innerHTML=ce(f),z(l);break}let d=j("assistant",t.text);d.setAttribute("data-raw",t.text),z(d);break}case"thinking":{let l=(n.replayTarget||o.messages).lastElementChild;if(l&&l.classList.contains("thinking")){let u=l.querySelector(".thinking-content");if(u){u.textContent+=`
8
- `+t.text;break}}let d=document.createElement("details");d.className="thinking",d.innerHTML=`<summary>\u283F thought</summary><div class="thinking-content">${p(t.text)}</div>`,E(d);break}case"tool_call":{let r=Ee[t.kind]||ke,l=document.createElement("div");l.className="tool-call",l.id=`tc-${t.id}`,l.dataset.kind=t.kind;let d=`<span class="icon">${r}</span> ${p(t.title)}`,u=t.rawInput;u&&u.command?d+=`<span class="tc-detail">$ ${p(u.command)}</span>`:u&&u.path&&(d+=`<span class="tc-detail">${p(u.path)}</span>`),l.innerHTML=d;let f=t.kind==="edit"?Te(u):null;if(f){let S=document.createElement("details");S.innerHTML=`<summary>diff</summary><div class="diff-view">${f}</div>`,l.appendChild(S)}let g=l.querySelector(".tc-detail");g&&l.addEventListener("click",S=>{S.target.closest("details")||g.classList.toggle("expanded")}),E(l),a&&a.toolCalls.set(t.id,l);break}case"tool_call_update":{let r=a?a.toolCalls.get(t.id):ot(`tc-${t.id}`);if(r){let l=t.status==="completed"?"\u2713":t.status==="failed"?"\u2717":"\u2026";r.className=`tool-call ${t.status}`;let d=r.querySelector(".icon");if(d&&(d.textContent=l),t.content&&t.content.length&&!r.querySelector("details")&&!r.querySelector(".tc-summary")){let u=t.content.map(f=>f.type==="terminal"?`[terminal ${f.terminalId}]`:f.content?.text?f.content.text:Array.isArray(f.content)?f.content.map(g=>g.text||"").join(""):"").filter(Boolean).join(`
9
- `);if(u)if(r.dataset.kind==="task_complete"){let f=document.createElement("div");f.className="tc-summary",f.textContent=u,r.appendChild(f)}else{let f=document.createElement("details");f.innerHTML=`<summary>output</summary><div class="tc-content">${p(u)}</div>`,r.appendChild(f)}}}(t.status==="completed"||t.status==="failed")&&n.pendingToolCallIds.delete(t.id);break}case"plan":{let r=document.createElement("div");r.className="plan",r.innerHTML='<div class="plan-title">\u2015 plan</div>'+(t.entries||[]).map(l=>`<div class="plan-entry">${Ce[l.status]||"?"} ${p(l.content)}</div>`).join(""),E(r);break}case"permission_request":{let r=document.createElement("div");r.className="permission",r.dataset.requestId=t.requestId,r.dataset.title=t.title||"",r.innerHTML=`<span class="title" style="opacity:0.5">\u26BF ${p(t.title)}</span> `,!(a?a.resolvedPermissions.has(t.requestId):s&&s.slice(i+1).some(d=>d.type==="permission_response"&&JSON.parse(d.data).requestId===t.requestId))&&t.options&&(r.querySelector(".title").style.opacity="1",t.options.forEach(d=>{let u=document.createElement("button"),f=(d.kind||"").includes("allow");u.className=f?"allow":"deny",u.textContent=d.name,u.onclick=()=>{(d.kind||"").includes("reject")||(d.kind||"").includes("deny")?re(n.sessionId,t.requestId).catch(()=>{}):oe(n.sessionId,t.requestId,d.optionId).catch(()=>{}),r.innerHTML=`<span style="opacity:0.5">\u26BF ${p(t.title)} \u2014 ${p(d.name)}</span>`},r.appendChild(u)})),E(r),a&&a.permissions.set(t.requestId,r);break}case"permission_response":{let r=a?a.permissions.get(t.requestId):qt(`.permission[data-request-id="${t.requestId}"]`);if(r){let l=r.dataset.title?`\u26BF ${r.dataset.title}`:"\u26BF",d=t.optionName||(t.denied?"denied":"allowed");r.innerHTML=`<span style="opacity:0.5">${p(l)} \u2014 ${p(d)}</span>`}break}case"bash_command":{let r=Z(t.command,!1);a?a.currentBashEl=r:r.id="bash-replay-pending";break}case"bash_result":{let r=a?a.currentBashEl:ot("bash-replay-pending");if(r){if(a||r.removeAttribute("id"),t.output){let l=r.querySelector(".bash-output");l&&(l.textContent=t.output,l.classList.add("has-content"))}X(r,t.code,t.signal),a&&(a.currentBashEl=null)}break}case"prompt_done":n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,h(!1);break}}function ut(){let e=n.replayQueue;n.replayQueue=[];for(let t of e)jt(t)||C(t)}function jt(e){switch(e.type){case"tool_call":return!!document.getElementById(`tc-${e.id}`);case"permission_request":return!!document.querySelector(`.permission[data-request-id="${e.requestId}"]`);case"thought_chunk":return!!n.currentThinkingEl;case"message_chunk":return!!n.currentAssistantEl;default:return!1}}function C(e){if(n.replayInProgress){console.log("[handleEvent-DEBUG] QUEUED (replayInProgress):",e.type),n.replayQueue.push(e);return}if(!(e.sessionId&&e.type!=="session_created"&&e.type!=="session_deleted"&&(!n.sessionId||e.sessionId!==n.sessionId)))switch(e.type){case"connected":if(e.cancelTimeout!=null&&(n.cancelTimeout=e.cancelTimeout),n.agentReloading){n.agentReloading=!1;let t=e.agent?.name??"",s=e.agent?.version??"",i=t&&s?`${t} ${s}`:"Agent";c(`${i} reloaded.`),h(!1)}break;case"session_created":if(!n.awaitingNewSession&&n.sessionId&&e.sessionId!==n.sessionId)break;if(n.awaitingNewSession=!1,n.sessionId=e.sessionId,n.sessionCwd=e.cwd||n.sessionCwd,n.sessionTitle=e.title||null,e.configOptions?.length&&ye(e.configOptions),Je(n.sessionId),n.clientId&&J(n.clientId,!document.hidden,n.sessionId).catch(()=>{}),le(n.sessionId,n.sessionTitle),A("connected","connected"),o.input.disabled=!1,o.sendBtn.disabled=!1,o.input.placeholder="Message or ?",h(!!e.busyKind),n.newTurnStarted=!1,e.busyKind==="bash"){let t=document.getElementById("bash-replay-pending");t&&(t.removeAttribute("id"),t.querySelector(".bash-cmd")?.classList.add("running"),n.currentBashEl=t)}else n.currentBashEl=null;o.messages.children.length===0&&c(`Session created: ${n.sessionTitle||e.sessionId.slice(0,8)+"\u2026"}`),R();break;case"user_message":{if(n.sentMessageForSession===e.sessionId){n.sentMessageForSession=null;break}if(k(),H(),n.newTurnStarted=!0,n.turnEnded=!1,e.sessionId===n.sessionId){let t=j("user",e.text);if(e.images)for(let s of e.images){let i=document.createElement("img");i.className="user-image",i.src=s.path,t.appendChild(i)}}break}case"message_chunk":if(n.turnEnded)break;F(),k(),n.currentAssistantEl||(n.currentAssistantEl=j("assistant",""),n.currentAssistantText=""),n.currentAssistantText+=e.text,n.currentAssistantEl.innerHTML=ce(n.currentAssistantText),I();break;case"thought_chunk":if(n.turnEnded)break;F(),n.currentThinkingEl||(n.currentThinkingEl=document.createElement("details"),n.currentThinkingEl.className="thinking",n.currentThinkingEl.innerHTML='<summary class="active">\u283F thinking...</summary><div class="thinking-content"></div>',n.currentThinkingText="",E(n.currentThinkingEl)),n.currentThinkingText+=e.text,n.currentThinkingEl.querySelector(".thinking-content").textContent=n.currentThinkingText,I();break;case"tool_call":{if(n.turnEnded)break;n.pendingToolCallIds.add(e.id),h(!0),F(),k(),H();let t=Ee[e.kind]||ke,s=document.createElement("div");s.className="tool-call",s.id=`tc-${e.id}`,s.dataset.kind=e.kind;let i=`<span class="icon">${t}</span> ${p(e.title)}`,a=e.rawInput;a&&a.command?i+=`<span class="tc-detail">$ ${p(a.command)}</span>`:a&&a.path&&(i+=`<span class="tc-detail">${p(a.path)}</span>`),s.innerHTML=i;let r=e.kind==="edit"?Te(a):null;if(r){let d=document.createElement("details");d.innerHTML=`<summary>diff</summary><div class="diff-view">${r}</div>`,s.appendChild(d)}let l=s.querySelector(".tc-detail");l&&s.addEventListener("click",d=>{d.target.closest("details")||l.classList.toggle("expanded")}),E(s);break}case"tool_call_update":{let t=document.getElementById(`tc-${e.id}`);if((e.status==="completed"||e.status==="failed")&&n.pendingToolCallIds.delete(e.id),t){let s=e.status==="completed"?"\u2713":e.status==="failed"?"\u2717":"\u2026";t.className=`tool-call ${e.status}`;let i=t.querySelector(".icon");if(i&&(i.textContent=s),e.content&&e.content.length&&!t.querySelector("details")&&!t.querySelector(".tc-summary")){let a=e.content.map(r=>r.type==="terminal"?`[terminal ${r.terminalId}]`:r.content?.text?r.content.text:Array.isArray(r.content)?r.content.map(l=>l.text||"").join(""):"").filter(Boolean).join(`
10
- `);if(a)if(t.dataset.kind==="task_complete"){let r=document.createElement("div");r.className="tc-summary",r.textContent=a,t.appendChild(r)}else{let r=document.createElement("details");r.innerHTML=`<summary>output</summary><div class="tc-content">${p(a)}</div>`,t.appendChild(r)}}}de(),I();break}case"plan":{k(),H();let t=document.createElement("div");t.className="plan",t.innerHTML='<div class="plan-title">\u2015 plan</div>'+e.entries.map(s=>`<div class="plan-entry">${Ce[s.status]||"?"} ${p(s.content)}</div>`).join(""),E(t);break}case"permission_request":{if(n.turnEnded||document.querySelector(`.permission[data-request-id="${e.requestId}"]`))break;n.pendingPermissionRequestIds.add(e.requestId),h(!0),k();let t=document.createElement("div");t.className="permission",t.dataset.requestId=e.requestId,t.dataset.title=e.title||"",t.innerHTML=`<span class="title">\u26BF ${p(e.title)}</span> `,e.options.forEach(s=>{let i=document.createElement("button"),a=(s.kind||"").includes("allow");i.className=a?"allow":"deny",i.textContent=s.name,i.onclick=()=>{let r=(s.kind||"").includes("reject")||(s.kind||"").includes("deny");r?re(n.sessionId,e.requestId).catch(()=>{}):oe(n.sessionId,e.requestId,s.optionId).catch(()=>{}),n.pendingPermissionRequestIds.delete(e.requestId),n.unconfirmedPermissions.set(e.requestId,{sessionId:n.sessionId,optionId:s.optionId,optionName:s.name,denied:r}),t.innerHTML=`<span style="opacity:0.5">\u26BF ${p(e.title)} \u2014 ${p(s.name)}</span>`,de()},t.appendChild(i)}),E(t);break}case"permission_response":{console.log("[PERM-DEBUG] permission_response received:",e.requestId,"sessionId:",e.sessionId,"state.sessionId:",n.sessionId),n.pendingPermissionRequestIds.delete(e.requestId),n.unconfirmedPermissions.delete(e.requestId);let t=document.querySelector(`.permission[data-request-id="${e.requestId}"]`);if(console.log("[PERM-DEBUG] permTarget found:",!!t,"sessionMatch:",e.sessionId===n.sessionId),e.sessionId===n.sessionId&&t){let s=t.dataset.title?`\u26BF ${t.dataset.title}`:"\u26BF",i=e.optionName||(e.denied?"denied":"allowed");t.innerHTML=`<span style="opacity:0.5">${p(s)} \u2014 ${p(i)}</span>`,console.log("[PERM-DEBUG] DOM updated successfully")}else console.log("[PERM-DEBUG] DOM NOT updated \u2014 check failed");de();break}case"bash_command":{if(n.sentBashForSession===e.sessionId){n.sentBashForSession=null;break}e.sessionId===n.sessionId&&(Z(e.command,!0),h(!0));break}case"bash_output":{if(e.sessionId!==n.sessionId)break;if(n.currentBashEl){let t=n.currentBashEl.querySelector(".bash-output");if(e.stream==="stderr"){let s=document.createElement("span");s.className="stderr",s.textContent=e.text,t.appendChild(s)}else t.appendChild(document.createTextNode(e.text));t.classList.add("has-content"),t.scrollTop=t.scrollHeight,I()}break}case"bash_done":{if(e.sessionId!==n.sessionId)break;X(n.currentBashEl,e.code,e.signal),e.error&&c(`err: ${e.error}`),h(!1);break}case"prompt_done":{if(Y(),e.stopReason==="cancelled"&&n.newTurnStarted){n.newTurnStarted=!1,k(),H();break}n.newTurnStarted=!1,e.stopReason==="cancelled"?Ot():At(),n.turnEnded=!0,n.pendingPromptDone=!0,de();break}case"session_deleted":e.sessionId===n.sessionId&&(c("warn: This session has been deleted."),o.input.disabled=!0,o.sendBtn.disabled=!0,o.input.placeholder="Session deleted");break;case"session_expired":{let t=n.sessionCwd;w(),c("warn: Previous session expired, created new one."),L({cwd:t||void 0});break}case"config_set":{We(e.configId,e.value);let t=x(e.configId),s=t?.name||e.configId,i=t?.options.find(a=>a.value===e.value)?.name||e.value;c(`ok: ${s}: ${i}`),e.configId==="mode"&&q(),R();break}case"config_option_update":e.configOptions?.length&&ye(e.configOptions);break;case"session_title_updated":e.sessionId===n.sessionId&&(n.sessionTitle=e.title,le(n.sessionId,n.sessionTitle));break;case"agent_reloading":n.agentReloading=!0,c("Agent reloading\u2026"),h(!0);break;case"agent_reloading_failed":c(`err: Agent reload failed: ${e.error}`),h(!1);break;case"error":n.awaitingNewSession=!1,n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,F(),k(),H(),c(`err: ${e.message}`),h(!1);break}}async function Ft(){try{let e=await navigator.serviceWorker?.ready;if(!e)return;let t=await fetch("/api/beta/push/vapid-key");if(!t.ok)return;let{publicKey:s}=await t.json(),a=(await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:Wt(s)})).toJSON();await fetch("/api/beta/push/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({endpoint:a.endpoint,keys:a.keys,clientId:n.clientId})})}catch(e){console.error("[push] subscribe failed:",e)}}async function Ut(){try{let e=await navigator.serviceWorker?.ready;if(!e)return;let t=await e.pushManager.getSubscription();if(!t)return;let s=t.endpoint;await t.unsubscribe(),await fetch("/api/beta/push/unsubscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({endpoint:s})})}catch(e){console.error("[push] unsubscribe failed:",e)}}function Wt(e){let t="=".repeat((4-e.length%4)%4),s=(e+t).replace(/-/g,"+").replace(/_/g,"/"),i=atob(s),a=new Uint8Array(i.length);for(let r=0;r<i.length;r++)a[r]=i.charCodeAt(r);return a}async function He(){try{let e=await navigator.serviceWorker?.ready;return e?await e.pushManager.getSubscription()!==null:!1}catch{return!1}}async function Ne(e){let t=e.split(/\s+/),s=t[0].toLowerCase(),i=t.slice(1).join(" ").trim();switch(s){case"/new":{let a=i||n.sessionCwd;return w(),c("Creating new session\u2026"),L({cwd:a||void 0}),!0}case"/pwd":return c(`\u{1F4C1} ${n.sessionCwd||"unknown"}`),!0;case"/rename":{if(!n.sessionId)return c("err: No active session"),!0;if(!i)return c(`Current: ${n.sessionTitle||"(untitled)"}`),c("Usage: /rename <new title>"),!0;try{await je(n.sessionId,i),c(`Renamed \u2192 ${i}`)}catch{c("err: Failed to rename session")}return!0}case"/sessions":return c("Removed. Use /switch to see all sessions."),!0;case"/exit":{if(!n.sessionId)return c("warn: No active session"),!0;let a=n.sessionId;try{let l=(await ie()).find(d=>d.id!==a);if(n.busy&&Q(),ge(a).catch(()=>{}),M=null,l){n.sessionSwitchGen++;let d=n.sessionSwitchGen;w(),n.sessionId=null;let[u]=await Promise.all([$(l.id),O(l.id)]);if(d!==n.sessionSwitchGen)return!0;C({type:"session_created",sessionId:u.id,cwd:u.cwd,title:u.title,configOptions:u.configOptions,busyKind:u.busyKind}),I(!0)}else{let d=n.sessionCwd;w(),n.sessionId=null,c("Creating new session\u2026"),L({cwd:d||void 0,inheritFromSessionId:null})}}catch{c("err: Failed to exit session")}return!0}case"/prune":{try{let l=(await(await fetch("/api/v1/sessions")).json()).filter(d=>d.id!==n.sessionId);if(l.length===0)return c("No other sessions to prune."),!0;await Promise.all(l.map(d=>ge(d.id).catch(()=>{}))),M=null,c(`Pruned ${l.length} session(s).`)}catch{c("err: Failed to prune sessions")}return!0}case"/switch":{if(!i)return c("Usage: /switch <title or id prefix>"),!0;try{let r=await(await fetch("/api/v1/sessions")).json(),l=i.toLowerCase(),d=r.find(g=>g.id.startsWith(i)||g.title&&g.title.toLowerCase().includes(l));if(!d)return c(`err: No session matching "${i}"`),!0;n.sessionSwitchGen++;let u=n.sessionSwitchGen;w(),n.sessionId=null;let[f]=await Promise.all([$(d.id),O(d.id)]);if(u!==n.sessionSwitchGen)return!0;C({type:"session_created",sessionId:f.id,cwd:f.cwd,title:f.title,configOptions:f.configOptions,busyKind:f.busyKind}),I(!0)}catch{w(),n.sessionId=null,c("err: Failed to switch session")}return!0}case"/cancel":return n.busy?(Q(),c("^C")):c("Nothing to cancel."),!0;case"/reload":return c("Reloading agent\u2026"),Ue().then(()=>{}).catch(a=>{c(`err: ${a instanceof Error?a.message:"Failed to reload agent"}`)}),!0;case"/help":case"?":{let a=[];n.serverVersion&&a.push(`WebAgent ${n.serverVersion}`),n.agentName&&n.agentVersion&&a.push(`${n.agentName} ${n.agentVersion}`),a.length&&c(a.join(" \xB7 ")),c("? \u2014 Show help"),c("/help \u2014 Show help (alias)"),c("!<command> \u2014 Run bash command");for(let r of pt){let l=r.args?`${r.cmd} ${r.args}`:r.cmd;c(`${l} \u2014 ${r.desc}`)}c("--- Shortcuts ---");for(let r of Kt)c(`${r.key} \u2014 ${r.desc}`);c("--- Tips ---");for(let r of Vt)c(r.text);return!0}case"/model":case"/mode":case"/think":{let r={"/model":"model","/mode":"mode","/think":"reasoning_effort"}[s],l=x(r);if(!i){let S=l?.options.find(se=>se.value===l.currentValue)?.name||l?.currentValue||"unknown";return c(`${l?.name||r}: ${S}`),c(`Type ${s} + space to pick from list`),!0}if(!l)return c(`err: ${s.slice(1)} is not available.`),!0;let d=i.trim(),u=S=>S.toLowerCase().replace(/[\s_]+/g,"-"),f=u(d),g=l.options.find(S=>u(S.value)===f||u(S.name)===f);if(!g){let S=l.options.filter(se=>u(se.value).includes(f)||u(se.name).includes(f));if(S.length===1)g=S[0];else if(S.length>1)return c(`err: Ambiguous "${i}". Type ${s} + space to see options.`),!0}return g?(l.currentValue=g.value,q(),R(),c(`${l.name} \u2192 ${g.name}`),await G(n.sessionId,r,g.value).catch(()=>{}),!0):(c(`err: Unknown "${i}". Type ${s} + space to see options.`),!0)}case"/notify":{if(typeof Notification>"u")return c("err: notifications not supported in this browser"),!0;let a=i.toLowerCase();if(a==="on"){if(Notification.permission==="denied")return c("notify: blocked \u2014 allow in browser site settings to enable"),!0;if(Notification.permission!=="granted"&&await Notification.requestPermission()!=="granted")return c("notify: blocked \u2014 allow in browser site settings to enable"),!0;let l=await He();return await Ft(),c(l?"notify: already enabled":"notify: enabled"),!0}if(a==="off")return await Ut(),c("notify: disabled"),!0;let r=Notification.permission;return r==="denied"?c("notify: blocked \u2014 allow in browser site settings to enable"):r==="granted"&&await He()?c("notify: enabled"):c("notify: off \u2014 use /notify on to enable"),!0}default:return!1}}var pt=[{cmd:"/cancel",args:"",desc:"Cancel current response"},{cmd:"/exit",args:"",desc:"Close current session"},{cmd:"/mode",args:"[name]",desc:"Pick or switch mode"},{cmd:"/model",args:"[name]",desc:"Pick or switch model"},{cmd:"/new",args:"[cwd]",desc:"New session"},{cmd:"/notify",args:"[on|off]",desc:"Toggle background notifications"},{cmd:"/prune",args:"",desc:"Delete all sessions except current"},{cmd:"/pwd",args:"",desc:"Show working directory"},{cmd:"/reload",args:"",desc:"Reload agent CLI"},{cmd:"/rename",args:"<new title>",desc:"Rename current session"},{cmd:"/switch",args:"<title|id>",desc:"Switch to session"},{cmd:"/think",args:"[level]",desc:"Pick or switch reasoning effort"}],Kt=[{key:"Enter",desc:"Send message"},{key:"Shift+Enter",desc:"New line"},{key:"^C",desc:"Cancel current response"},{key:"^M",desc:"Cycle mode (Agent \u2192 Plan \u2192 Autopilot)"},{key:"^U",desc:"Upload image"}],Vt=[{text:"Tap \u276F prompt to cycle mode"}],b=-1,m=[],v="commands",te=null,M=null,V=null,_e=!1;function qe(){let e=o.input.value;if(V!==null){if(e===V)return;V=null}let t=e.match(/^\/new /);if(t){let l=e.slice(t[0].length).toLowerCase();Jt(l);return}let s=e.match(/^\/switch /);if(s){let l=e.slice(s[0].length).toLowerCase();Gt(l,"switch");return}let i=e.match(/^\/(model|mode|think) /);if(i){let d={model:"model",mode:"mode",think:"reasoning_effort"}[i[1]],u=e.slice(i[0].length).toLowerCase();Qt(d,u);return}let a=e.match(/^\/notify /);if(a){let l=e.slice(a[0].length).toLowerCase();zt(l);return}if(!e.startsWith("/")||e.includes(" ")){y();return}v="commands";let r=e.toLowerCase();if(m=pt.filter(l=>l.cmd.startsWith(r)),m.length===0){y();return}b=0,U(),o.slashMenu.classList.add("active")}async function Gt(e,t="switch"){if(!M)try{M=await(await fetch("/api/v1/sessions")).json(),setTimeout(()=>{M=null},5e3)}catch{return}if(v=t,m=M.filter(i=>e?i.title&&i.title.toLowerCase().includes(e)||i.id.startsWith(e):!0),m.length===0){y();return}b=0,U(),o.slashMenu.classList.add("active")}async function Jt(e){if(!M)try{M=await(await fetch("/api/v1/sessions")).json(),setTimeout(()=>{M=null},5e3)}catch{return}v="new";let t=new Map;for(let i of M){let a=t.get(i.cwd);(!a||(i.last_active_at||i.created_at)>a.time)&&t.set(i.cwd,{cwd:i.cwd,time:i.last_active_at||i.created_at})}let s=[...t.values()].sort((i,a)=>a.time.localeCompare(i.time));if(e&&(s=s.filter(i=>i.cwd.toLowerCase().includes(e))),m=s,m.length===0){y();return}b=0,U(),o.slashMenu.classList.add("active")}function Qt(e,t){let s=x(e);if(!s){y();return}if(v="config",te=e,m=s.options.filter(i=>t?i.value.toLowerCase().includes(t)||i.name.toLowerCase().includes(t):!0),m.length===0){y();return}b=0,U(),o.slashMenu.classList.add("active")}var Yt=[{value:"on",name:"on",desc:"Enable background notifications"},{value:"off",name:"off",desc:"Disable background notifications"}];async function zt(e){if(v="notify",m=Yt.filter(i=>e?i.value.includes(e)||i.name.includes(e):!0),m.length===0){y();return}_e=await He();let t=_e?"on":"off",s=m.findIndex(i=>i.value===t);b=s>=0?s:0,U(),o.slashMenu.classList.add("active")}function U(){if(v==="new"){let t=(n.sessionCwd||"").toLowerCase();o.slashMenu.innerHTML=m.map((s,i)=>{let a=s.cwd.toLowerCase()===t;return`<div class="slash-item${i===b?" selected":""}" data-idx="${i}"><span class="slash-cmd"${a?' style="color:var(--green)"':""}>${p((a?"* ":" ")+s.cwd)}</span></div>`}).join("")}else if(v==="config"){let t=ae(te)?.toLowerCase()||"";o.slashMenu.innerHTML=m.map((s,i)=>{let a=s.value.toLowerCase()===t;return`<div class="slash-item${i===b?" selected":""}" data-idx="${i}"><span class="slash-cmd"${a?' style="color:var(--green)"':""}>${p((a?"* ":" ")+s.name)}</span></div>`}).join("")}else if(v==="notify"){let t=_e?"on":"off";o.slashMenu.innerHTML=m.map((s,i)=>{let a=s.value===t;return`<div class="slash-item${i===b?" selected":""}" data-idx="${i}"><span class="slash-cmd"${a?' style="color:var(--green)"':""}>${p((a?"* ":" ")+s.name)}</span><span class="slash-desc">${p(s.desc)}</span></div>`}).join("")}else v==="switch"?o.slashMenu.innerHTML=m.map((t,s)=>{let i=t.id===n.sessionId,a=i?"* ":" ",r=t.title||t.id.slice(0,8)+"\u2026",l=et(t.last_active_at||t.created_at);return`<div class="slash-item${s===b?" selected":""}" data-idx="${s}"><span class="slash-cmd"${i?' style="color:var(--green)"':""}>${p(a+r)}</span><span class="slash-desc">${p(t.cwd)} (${p(l)})</span></div>`}).join(""):o.slashMenu.innerHTML=m.map((t,s)=>{let i=t.args?`${t.cmd} ${t.args}`:t.cmd;return`<div class="slash-item${s===b?" selected":""}" data-idx="${s}"><span class="slash-cmd">${p(i)}</span><span class="slash-desc">${p(t.desc)}</span></div>`}).join("");let e=o.slashMenu.querySelector(".selected");e&&e.scrollIntoView({block:"nearest"})}function y(){o.slashMenu.classList.remove("active"),b=-1,m=[],v="commands",V=o.input.value}function Zt(e){if(!(e<0||e>=m.length)){if(v==="commands"){let t=m[e];o.input.value=t.cmd+(t.args?" ":""),y(),o.input.focus(),["/new","/switch","/model","/mode","/think","/notify"].includes(t.cmd)&&(V=null,qe())}else if(v==="config"){let t=m[e],s={model:"/model",mode:"/mode",reasoning_effort:"/think"}[te]||`/${te}`;o.input.value=`${s} ${t.name}`,y(),o.input.focus()}else if(v==="notify"){let t=m[e];o.input.value=`/notify ${t.value}`,y(),o.input.focus()}else if(v==="new"){let t=m[e];o.input.value=`/new ${t.cwd}`,y(),o.input.focus()}else if(v==="switch"){let t=m[e];o.input.value=`/switch ${t.title||t.id}`,y(),o.input.focus()}}}async function Xt(e){if(!(e<0||e>=m.length))if(v==="new"){let t=m[e];o.input.value="",y(),w(),c("Creating new session\u2026"),L({cwd:t.cwd})}else if(v==="config"){let t=m[e],s=te,i=x(s);o.input.value="",y(),i&&(i.currentValue=t.value),q(),R(),c(`${i?.name||s} \u2192 ${t.name}`),await G(n.sessionId,s,t.value).catch(()=>{})}else if(v==="switch"){let t=m[e];o.input.value="",y(),w(),n.sessionId=null,c("Switching\u2026"),Promise.all([$(t.id),O(t.id)]).then(([s,i])=>{C({type:"session_created",sessionId:s.id,cwd:s.cwd,title:s.title,configOptions:s.configOptions,busyKind:s.busyKind}),i&&I(!0)}).catch(()=>{w(),n.sessionId=null,c("err: Failed to switch session")})}else if(v==="notify"){let t=m[e];o.input.value=`/notify ${t.value}`,y(),Ne(o.input.value),o.input.value=""}else{let t=m[e];o.input.value=t.cmd+(t.args?" ":""),y(),o.input.focus(),["/new","/switch","/model","/mode","/think","/notify"].includes(t.cmd)&&(V=null,qe())}}function ft(e){return o.slashMenu.classList.contains("active")?e.key==="ArrowDown"?(b=(b+1)%m.length,U(),!0):e.key==="ArrowUp"?(b=(b-1+m.length)%m.length,U(),!0):e.key==="Tab"?(Zt(b),!0):!1:!1}o.slashMenu.addEventListener("mousedown",e=>{e.preventDefault();let t=e.target.closest(".slash-item");t&&Xt(Number(t.dataset.idx))});o.input.addEventListener("input",()=>{qe(),o.inputArea.classList.toggle("bash-mode",o.input.value.startsWith("!"))});function mt(e){return new Promise(t=>{let s=new FileReader;s.onload=()=>{let i=s.result.split(",")[1];t({data:i,mimeType:e.type,previewUrl:s.result})},s.readAsDataURL(e)})}function gt(e){n.pendingImages.push(e),pe(),o.input.focus()}function pe(){if(o.attachPreview.innerHTML="",n.pendingImages.length===0){o.attachPreview.classList.remove("active");return}o.attachPreview.classList.add("active"),n.pendingImages.forEach((e,t)=>{let s=document.createElement("span");s.className="attach-thumb",s.innerHTML=`<img src="${e.previewUrl}"><button class="remove">\xD7</button>`,s.querySelector(".remove").addEventListener("click",()=>{n.pendingImages.splice(t,1),pe()}),o.attachPreview.appendChild(s)})}o.attachBtn.onclick=()=>o.fileInput.click();o.fileInput.onchange=async()=>{for(let e of o.fileInput.files)e.type.startsWith("image/")&&gt(await mt(e));o.fileInput.value=""};o.input.addEventListener("paste",async e=>{for(let t of e.clipboardData.items)t.type.startsWith("image/")&&(e.preventDefault(),gt(await mt(t.getAsFile())))});var _=document.createElement("div");_.id="lightbox-overlay";var ne=document.createElement("img");_.appendChild(ne);document.body.appendChild(_);var fe=1,en=.5,tn=5;function nn(){return _.classList.contains("active")}function sn(e){ne.src=e,fe=1,ne.style.transform="",_.classList.add("active")}function ht(){_.classList.remove("active"),ne.src=""}document.getElementById("messages").addEventListener("click",e=>{let t=e.target;t.tagName==="IMG"&&t.classList.contains("user-image")&&sn(t.src)});_.addEventListener("click",e=>{e.target===_&&ht()});document.addEventListener("keydown",e=>{e.key==="Escape"&&nn()&&ht()});_.addEventListener("wheel",e=>{e.preventDefault();let t=e.deltaY>0?-.15:.15;fe=Math.min(tn,Math.max(en,fe+t)),ne.style.transform=`scale(${fe})`},{passive:!1});function Be(){return n.clientId!==null}n._onCancelTimeout=()=>c("warn: Agent not responding to cancel");function yt(){let e=o.input.value.trim();if(!e&&n.pendingImages.length===0)return;if((e.startsWith("/")||e==="?"||e.startsWith("? "))&&n.pendingImages.length===0){o.input.value="",o.input.style.height="auto",St(),Ne(e);return}if(e.startsWith("!")&&n.pendingImages.length===0){let i=e.slice(1).trim();if(!i)return;if(!n.sessionId){c("warn: Session not ready yet, please wait\u2026");return}if(!Be()){c("warn: Not connected, please retry");return}o.input.value="",o.input.style.height="auto",o.inputArea.classList.remove("bash-mode"),Z(i,!0),n.sentBashForSession=n.sessionId,Fe(n.sessionId,i).catch(()=>{}),h(!0);return}if(n.busy)return;if(o.input.value="",o.input.style.height="auto",o.inputArea.classList.remove("bash-mode"),!n.sessionId){c("warn: Session not ready yet, please wait\u2026");return}if(!Be()){c("warn: Not connected, please retry");return}let t=j("user",e||"(image)");for(let i of n.pendingImages){let a=document.createElement("img");a.className="user-image",a.src=i.previewUrl,t.appendChild(a)}let s=n.pendingImages.slice();n.pendingImages.length=0,pe(),s.length>0?Promise.all(s.map(i=>fetch(`/api/v1/sessions/${n.sessionId}/images`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({data:i.data,mimeType:i.mimeType})}).then(a=>a.json()).then(a=>({data:i.data,mimeType:i.mimeType,path:a.url})))).then(i=>{if(!Be()){t.remove(),c("warn: Not connected, please retry"),h(!1);return}he(n.sessionId,e||"What is in this image?",i.map(a=>({data:a.data,mimeType:a.mimeType,path:a.path}))).catch(()=>{})}):he(n.sessionId,e).catch(()=>{}),n.turnEnded=!1,n.sentMessageForSession=n.sessionId,h(!0),Xe()}function wt(){Q()&&c("^C")}function vt(){let e=o.input.value.trim();return e.startsWith("/")||e.startsWith("!")||e==="?"||e.startsWith("? ")}function St(){n.busy&&(vt()?(o.sendBtn.textContent="\u21B5",o.sendBtn.title="Send (Enter)",o.sendBtn.classList.remove("cancel")):(o.sendBtn.textContent="^C",o.sendBtn.title="Cancel (Ctrl+C)",o.sendBtn.classList.add("cancel")))}o.sendBtn.onclick=()=>{n.busy&&!vt()?wt():yt()};o.input.addEventListener("keydown",e=>{if(ft(e)){e.preventDefault();return}if(e.key==="Enter"&&!e.shiftKey){e.preventDefault(),y(),yt();return}if(e.key==="u"&&(e.ctrlKey||e.metaKey)&&!e.shiftKey){e.preventDefault(),o.fileInput.click();return}});document.addEventListener("keydown",e=>{if(e.key==="c"&&(e.ctrlKey||e.metaKey)&&!e.shiftKey&&n.busy&&!(window.getSelection()?.toString()||o.input.selectionStart!==o.input.selectionEnd)){e.preventDefault(),wt();return}e.key==="Escape"&&o.slashMenu.classList.contains("active")&&(e.preventDefault(),y(),o.input.focus())});function It(){let e=x("mode");if(!e||!e.options.length)return;let t=e.options.findIndex(i=>i.value===e.currentValue),s=e.options[(t+1)%e.options.length];e.currentValue=s.value,G(n.sessionId,"mode",s.value).catch(()=>{}),c(`Mode \u2192 ${s.name}`),q()}document.addEventListener("keydown",e=>{e.key==="m"&&(e.ctrlKey||e.metaKey)&&!e.shiftKey&&(e.preventDefault(),It())});o.prompt.addEventListener("click",It);o.input.addEventListener("input",St);o.input.addEventListener("input",()=>{o.input.style.height="auto",o.input.style.height=Math.min(o.input.scrollHeight,200)+"px"});async function on(e){try{let t=await navigator.serviceWorker?.ready;if(!t)return;let s=await t.pushManager.getSubscription();if(!s)return;await fetch("/api/beta/push/register-client",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({clientId:e,endpoint:s.endpoint})})}catch{}}function Ae(){A("connecting","connecting");let e=new EventSource("/api/v1/events/stream");n.eventSource=e,e.onmessage=async t=>{let s=JSON.parse(t.data);s.type==="connected"&&s.clientId&&(n.clientId=s.clientId,J(s.clientId,!document.hidden,n.sessionId??void 0).catch(()=>{}),on(s.clientId),!s.agent)||C(s)},e.onerror=()=>{e.close(),an(),setTimeout(Ae,3e3)},rn()}async function rn(){A("connecting","session loading");let e=n.sessionSwitchGen,t=Ge();if(t&&t===n.sessionId&&n.lastEventSeq>0){if(await Oe(t,!0,e),e!==n.sessionSwitchGen)return;dt(),I(!1);return}if(t){if(w(),await Oe(t,!1,e),e!==n.sessionSwitchGen)return;I(!0);return}try{let s=await ie();if(e!==n.sessionSwitchGen)return;if(s.length>0){if(w(),await Oe(s[0].id,!1,e),e!==n.sessionSwitchGen)return;I(!0);return}}catch{}e===n.sessionSwitchGen&&L()}async function Oe(e,t,s){if(t){try{let i=await $(e);if(s!==n.sessionSwitchGen)return;C({type:"session_created",sessionId:i.id,cwd:i.cwd,title:i.title,configOptions:i.configOptions,busyKind:i.busyKind})}catch{if(s!==n.sessionSwitchGen)return;let i=n.sessionCwd;w(),c("warn: Previous session expired, created new one."),L({cwd:i||void 0});return}if(s!==n.sessionSwitchGen)return;await Me(e)}else{n.sessionId=null;let i=O(e),a;try{let[r,l]=await Promise.all([$(e),i]);if(s!==n.sessionSwitchGen)return;a=r,l||c("warn: Failed to load history.")}catch{if(s!==n.sessionSwitchGen)return;let r=n.sessionCwd;w(),c("warn: Previous session expired, created new one."),L({cwd:r||void 0});return}C({type:"session_created",sessionId:a.id,cwd:a.cwd,title:a.title,configOptions:a.configOptions,busyKind:a.busyKind})}}function an(){A("disconnected","disconnected"),n.eventSource=null,n.clientId=null,k(),H(),n.currentBashEl&&X(n.currentBashEl,null,"disconnected"),n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,n.turnEnded=!1,Y(),h(!1)}document.addEventListener("visibilitychange",()=>{n.clientId&&J(n.clientId,!document.hidden,n.sessionId??void 0).catch(()=>{}),!document.hidden&&n.sessionId&&n.lastEventSeq>0&&!n.replayInProgress&&Me(n.sessionId).then(()=>I(!1))});o.messages.addEventListener("click",Ye);it(ze);Ae();fetch("/api/v1/version").then(e=>e.json()).then(e=>{typeof e.server=="string"&&(n.serverVersion=e.server);let t=e.agent;t&&(n.agentName=t.name??null,n.agentVersion=t.version??null)}).catch(()=>{});"serviceWorker"in navigator&&(navigator.serviceWorker.register("/sw.js"),navigator.serviceWorker.addEventListener("message",e=>{if(e.data?.type==="navigate"&&e.data.sessionId){let t=e.data.sessionId;if(n.sessionId===t)return;n.sessionSwitchGen++;let s=n.sessionSwitchGen;history.replaceState(null,"",`#${t}`),w(),n.sessionId=null,c("Switching\u2026"),Promise.all([$(t),O(t)]).then(([i,a])=>{s===n.sessionSwitchGen&&(C({type:"session_created",sessionId:i.id,cwd:i.cwd,title:i.title,configOptions:i.configOptions,busyKind:i.busyKind}),a&&I(!0))}).catch(()=>{w(),n.sessionId=null,c("err: Failed to switch session")})}}));