@lelouchhe/webagent 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/config.toml +12 -3
- package/dist/fonts/temml/Temml.woff2 +0 -0
- package/dist/index.html +4 -4
- package/dist/js/app.3OEVQXHK.js +4 -0
- package/dist/js/chunk.IJM5DBCO.js +173 -0
- package/dist/js/viewer.FCSSTUVY.js +1 -0
- package/dist/login.html +1 -1
- package/dist/share-viewer.html +4 -3
- package/dist/{styles.012p32dz.css → styles.00xfh3e6.css} +408 -3
- package/lib/auth-middleware.js +3 -0
- package/lib/bridge-event-config.js +29 -0
- package/lib/client-registry.js +104 -15
- package/lib/config.js +12 -5
- package/lib/image-dimensions.js +64 -0
- package/lib/model-picker.js +17 -0
- package/lib/preflight.js +41 -22
- package/lib/push-service.js +21 -69
- package/lib/routes.js +124 -12
- package/lib/server.js +19 -23
- package/lib/startup-checks.js +1 -0
- package/lib/store.js +15 -3
- package/lib/title-service.js +3 -19
- package/package.json +2 -1
- package/dist/js/app.GSAIYHML.js +0 -4
- package/dist/js/chunk.CGWFHJI2.js +0 -76
- package/dist/js/viewer.6DT53STL.js +0 -1
package/lib/server.js
CHANGED
|
@@ -22,6 +22,7 @@ import { join as pathJoin } from "node:path";
|
|
|
22
22
|
import { resolveSessionsAnchor } from "./sessions-anchor.js";
|
|
23
23
|
import { runStartupChecks } from "./startup-checks.js";
|
|
24
24
|
import { AttachmentDispatcher } from "./attachment-dispatch.js";
|
|
25
|
+
import { buildBridgeEventHandlerConfig as _buildBridgeEventHandlerConfig } from "./bridge-event-config.js";
|
|
25
26
|
import { createCounters as createAttachmentInterceptorCounters, } from "./attachment-interceptor.js";
|
|
26
27
|
// Prefix all console output with ISO-ish timestamps (YYYY-MM-DD HH:MM:SS)
|
|
27
28
|
for (const method of ["log", "error", "warn"]) {
|
|
@@ -75,13 +76,14 @@ setInterval(() => {
|
|
|
75
76
|
.info("counters", { ...attachmentInterceptorCounters });
|
|
76
77
|
}, ATTACHMENT_INTERCEPTOR_DUMP_MS).unref();
|
|
77
78
|
const sessions = new SessionManager(store, config.default_cwd, config.data_dir);
|
|
78
|
-
const titleService = new TitleService(store, sessions, config.default_cwd, config.title.
|
|
79
|
+
const titleService = new TitleService(store, sessions, config.default_cwd, config.title.models);
|
|
80
|
+
const sseManager = new SseManager();
|
|
81
|
+
const clientRegistry = new ClientRegistry();
|
|
79
82
|
const pushService = new PushService(store, config.data_dir, config.push.vapid_subject, {
|
|
80
83
|
globalVisibilitySuppression: config.push.global_visibility_suppression,
|
|
84
|
+
clientRegistry,
|
|
81
85
|
});
|
|
82
86
|
console.log(`[push] VAPID public key ready`);
|
|
83
|
-
const sseManager = new SseManager();
|
|
84
|
-
const clientRegistry = new ClientRegistry();
|
|
85
87
|
sseManager.onRemove((clientId) => {
|
|
86
88
|
pushService.removeClient(clientId);
|
|
87
89
|
clientRegistry.remove(clientId);
|
|
@@ -128,26 +130,20 @@ const server = createServer((req, res) => {
|
|
|
128
130
|
async function initBridge(agentCmd) {
|
|
129
131
|
const b = new AgentBridge(agentCmd);
|
|
130
132
|
b.setAttachmentDispatcher(attachmentDispatcher);
|
|
133
|
+
const eventHandlerConfig = _buildBridgeEventHandlerConfig({
|
|
134
|
+
cancelTimeout: config.limits.cancel_timeout,
|
|
135
|
+
recentPathsLimit: config.limits.recent_paths,
|
|
136
|
+
attachmentInterceptorCounters,
|
|
137
|
+
shouldLogSchemaDrift: () => {
|
|
138
|
+
const now = Date.now();
|
|
139
|
+
if (now - lastSchemaDriftAt < SCHEMA_DRIFT_THROTTLE_MS)
|
|
140
|
+
return false;
|
|
141
|
+
lastSchemaDriftAt = now;
|
|
142
|
+
return true;
|
|
143
|
+
},
|
|
144
|
+
});
|
|
131
145
|
b.on("event", (event) => {
|
|
132
|
-
handleAgentEvent(event, sessions, store, b,
|
|
133
|
-
cancelTimeout: config.limits.cancel_timeout,
|
|
134
|
-
recentPathsLimit: config.limits.recent_paths,
|
|
135
|
-
attachmentInterceptor: {
|
|
136
|
-
counters: attachmentInterceptorCounters,
|
|
137
|
-
logger: log.scope("attachment-interceptor"),
|
|
138
|
-
onSchemaDrift: (ctx) => {
|
|
139
|
-
const now = Date.now();
|
|
140
|
-
if (now - lastSchemaDriftAt < SCHEMA_DRIFT_THROTTLE_MS)
|
|
141
|
-
return;
|
|
142
|
-
lastSchemaDriftAt = now;
|
|
143
|
-
log
|
|
144
|
-
.scope("attachment-interceptor")
|
|
145
|
-
.error("schema drift detected — rawInput has no known path key", {
|
|
146
|
-
ctx,
|
|
147
|
-
});
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
}, sseManager, pushService, clientRegistry);
|
|
146
|
+
handleAgentEvent(event, sessions, store, b, eventHandlerConfig, sseManager, pushService, clientRegistry);
|
|
151
147
|
});
|
|
152
148
|
await b.start();
|
|
153
149
|
bridge = b;
|
|
@@ -181,7 +177,7 @@ process.on("SIGHUP", () => {
|
|
|
181
177
|
});
|
|
182
178
|
});
|
|
183
179
|
// --- Start ---
|
|
184
|
-
server.listen(config.port,
|
|
180
|
+
server.listen(config.port, config.host, () => {
|
|
185
181
|
void (async () => {
|
|
186
182
|
// The auth gate already ran in runStartupChecks (above) — either in
|
|
187
183
|
// this process or in a parent that handed off via WEBAGENT_STARTUP_
|
package/lib/startup-checks.js
CHANGED
|
@@ -44,6 +44,7 @@ export async function runStartupChecks(config) {
|
|
|
44
44
|
data_dir: config.data_dir,
|
|
45
45
|
agent_cmd: config.agent_cmd,
|
|
46
46
|
port: config.port,
|
|
47
|
+
host: config.host,
|
|
47
48
|
});
|
|
48
49
|
// 2. Auth bootstrap. Same `[check] auth: ...` style; first-run mint
|
|
49
50
|
// or refuse-to-serve land here.
|
package/lib/store.js
CHANGED
|
@@ -214,10 +214,22 @@ export class Store {
|
|
|
214
214
|
size INTEGER NOT NULL,
|
|
215
215
|
realpath TEXT NOT NULL,
|
|
216
216
|
upload_seq INTEGER NOT NULL,
|
|
217
|
+
width INTEGER,
|
|
218
|
+
height INTEGER,
|
|
217
219
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%d %H:%M:%f', 'now'))
|
|
218
220
|
);
|
|
219
221
|
CREATE INDEX IF NOT EXISTS idx_attachments_session ON attachments(session_id);
|
|
220
222
|
`);
|
|
223
|
+
const attachmentCols = this.db
|
|
224
|
+
.prepare("PRAGMA table_info(attachments)")
|
|
225
|
+
.all();
|
|
226
|
+
const attachmentColNames = new Set(attachmentCols.map((c) => c.name));
|
|
227
|
+
if (!attachmentColNames.has("width")) {
|
|
228
|
+
this.db.exec("ALTER TABLE attachments ADD COLUMN width INTEGER");
|
|
229
|
+
}
|
|
230
|
+
if (!attachmentColNames.has("height")) {
|
|
231
|
+
this.db.exec("ALTER TABLE attachments ADD COLUMN height INTEGER");
|
|
232
|
+
}
|
|
221
233
|
// owner_prefs — key-value store for owner-scoped defaults (display_name,
|
|
222
234
|
// last /by selection, etc). Single-user model = single owner scope.
|
|
223
235
|
// Stored as plain key/value so we don't grow a new table per pref.
|
|
@@ -618,9 +630,9 @@ export class Store {
|
|
|
618
630
|
const uploadSeq = seqRow.s;
|
|
619
631
|
this.db
|
|
620
632
|
.prepare(`INSERT INTO attachments
|
|
621
|
-
(id, session_id, kind, name, mime, size, realpath, upload_seq)
|
|
622
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
623
|
-
.run(input.id, input.sessionId, input.kind, input.name, input.mime, input.size, input.realpath, uploadSeq);
|
|
633
|
+
(id, session_id, kind, name, mime, size, realpath, upload_seq, width, height)
|
|
634
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
635
|
+
.run(input.id, input.sessionId, input.kind, input.name, input.mime, input.size, input.realpath, uploadSeq, input.width ?? null, input.height ?? null);
|
|
624
636
|
return this.db
|
|
625
637
|
.prepare("SELECT * FROM attachments WHERE id = ?")
|
|
626
638
|
.get(input.id);
|
package/lib/title-service.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { log } from "./log.js";
|
|
2
|
+
import { pickModelByPatterns } from "./model-picker.js";
|
|
2
3
|
const tlog = log.scope("title");
|
|
3
4
|
export class TitleService {
|
|
4
5
|
titleSessionId = null;
|
|
@@ -12,10 +13,7 @@ export class TitleService {
|
|
|
12
13
|
this.store = store;
|
|
13
14
|
this.sessions = sessions;
|
|
14
15
|
this.defaultCwd = defaultCwd;
|
|
15
|
-
|
|
16
|
-
this.modelPatterns = modelPatterns
|
|
17
|
-
.map((p) => p.trim().toLowerCase())
|
|
18
|
-
.filter((p) => p.length > 0);
|
|
16
|
+
this.modelPatterns = modelPatterns;
|
|
19
17
|
}
|
|
20
18
|
/** Generate a title for the session (non-blocking, fire-and-forget). */
|
|
21
19
|
generate(bridge, userMessage, sessionId, onTitle) {
|
|
@@ -83,7 +81,7 @@ export class TitleService {
|
|
|
83
81
|
// the agent's reported availableModels (`configOptions[id=model].options`).
|
|
84
82
|
// Empty pattern list, no model option, or no match → skip the call and
|
|
85
83
|
// inherit the agent's default model (`currentModelId`).
|
|
86
|
-
const picked = this.
|
|
84
|
+
const picked = pickModelByPatterns(configOptions, this.modelPatterns);
|
|
87
85
|
if (picked) {
|
|
88
86
|
await bridge.setConfigOption(id, "model", picked).catch(() => []);
|
|
89
87
|
}
|
|
@@ -94,18 +92,4 @@ export class TitleService {
|
|
|
94
92
|
return null;
|
|
95
93
|
}
|
|
96
94
|
}
|
|
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
|
-
}
|
|
111
95
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lelouchhe/webagent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "A terminal-style web UI for ACP-compatible agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"marked": "^18.0.2",
|
|
71
71
|
"proper-lockfile": "^4.1.2",
|
|
72
72
|
"smol-toml": "^1.6.0",
|
|
73
|
+
"temml": "^0.13.2",
|
|
73
74
|
"web-push": "^3.6.7",
|
|
74
75
|
"zod": "^4.3.6"
|
|
75
76
|
},
|
package/dist/js/app.GSAIYHML.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{a as B}from"./chunk.D4ZYHJAM.js";import{a as wt,b as D,c as vt,d as bt,e as ye,f as St,g as _,h as F,i as kt,j as k,k as Ve}from"./chunk.CGWFHJI2.js";import"./chunk.AJZBJBMO.js";var we=null;function $n(e){location.replace(e)}function Mn(e){let t;try{if(e.startsWith("/"))t=e.split("?")[0];else{let s=new URL(e);if(s.origin!==location.origin)return!1;t=s.pathname}}catch{return!1}return t.startsWith("/api/")}function _n(e){return typeof e=="string"?e:e instanceof URL?e.toString():e.url}function It(e={}){let t=e.baseFetch??globalThis.fetch.bind(globalThis),s=e.onUnauthorized??$n;we??=globalThis.fetch;let i=async(r,a)=>{let c=_n(r),d=Mn(c),u=a;if(d){let h=localStorage.getItem(B);if(h){let f=a?.headers??(r instanceof Request?r.headers:void 0),m=new Headers(f);m.has("Authorization")||m.set("Authorization",`Bearer ${h}`),u={...a??{},headers:m}}}let p=await t(r,u);if(d&&p.status===401){try{localStorage.removeItem(B)}catch{}s("/login")}return p};return globalThis.fetch=i,Nn}function Nn(){we&&(globalThis.fetch=we,we=null)}var Rn=new Set(["plan","read-only"]),qn=new Set(["autopilot","bypassPermissions","full-access","yolo"]),Hn=new Set(["agent","default","build"]);function ve(e){if(!e)return"";let t=e.match(/[#/]([^#/]+)$/);return t?t[1]:e}function Tt(e){return Rn.has(ve(e))}function Et(e){return qn.has(ve(e))}function xt(e){let t=ve(e);return t?!Hn.has(t):!1}function Ct(e){return ve(e).replace(/([A-Z])/g," $1").trim()}var je=class extends Error{name="ApiError";status;constructor(t,s){super(s),this.status=t}};async function C(e,t){let s=await fetch(e,t);if(!s.ok){let r=`HTTP ${s.status}`;try{let a=await s.json();a.error&&(r=typeof a.error=="string"?a.error:JSON.stringify(a.error))}catch{}throw new je(s.status,r)}let i=await s.text();if(i)return JSON.parse(i)}function L(e,t,s){let i={"Content-Type":"application/json"};return s&&(i["X-Client-Op-Id"]=s),C(e,{method:"POST",headers:i,body:JSON.stringify(t)})}function be(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"op-"+Date.now().toString(36)+"-"+Math.random().toString(36).slice(2,10)}function Se(e){let t={};return e?.cwd&&(t.cwd=e.cwd),e?.inheritFromSessionId&&(t.inheritFromSessionId=e.inheritFromSessionId),L("/api/v1/sessions",t)}function ne(e){return C("/api/v1/sessions/"+e,{method:"DELETE"})}function ke(){return C("/api/v1/sessions")}function P(e){return C("/api/v1/sessions/"+e)}function Lt(e){return C("/api/v1/sessions/"+e+"/snapshot")}function We(e,t,s){let i={text:t};return s?.length&&(i.attachments=s),L("/api/v1/sessions/"+e+"/prompt",i,be())}function Pt(e){return L("/api/v1/sessions/"+e+"/cancel",{},be())}function At(e,t,s){return L("/api/v1/sessions/"+e+"/permissions/"+t,{optionId:s},be())}function $t(e,t){return L("/api/v1/sessions/"+e+"/permissions/"+t,{denied:!0},be())}function se(e,t,s){let i=t.replace(/_/g,"-");return C("/api/v1/sessions/"+e+"/"+i,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:s})})}function Ie(e,t){return C("/api/v1/sessions/"+e+"/title",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:t})})}function Mt(e,t){return L("/api/v1/sessions/"+e+"/bash",{command:t})}function ie(e,t,s){let i={visible:t};return s&&(i.sessionId=s),L("/api/beta/clients/"+e+"/visibility",i)}function Te(){return L("/api/v1/bridge/reload",{})}function _t(){return L("/api/v1/sse-ticket",{})}function de(){return C("/api/v1/messages")}function Nt(e){return L(`/api/v1/messages/${encodeURIComponent(e)}/consume`,{})}function Ee(e){return C(`/api/v1/messages/${encodeURIComponent(e)}`,{method:"DELETE"})}function xe(){return C("/api/v1/tokens")}function Ce(e){return L("/api/v1/tokens",{name:e})}function Le(e){return C("/api/v1/tokens/"+encodeURIComponent(e),{method:"DELETE"})}var T=e=>document.querySelector(e),l={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"),modePill:T("#mode-pill"),statusBar:T("#status-bar")};function N(e){l.input.value=e,l.input.dispatchEvent(new window.Event("input",{bubbles:!0}))}var n={eventSource:null,clientId:null,sessionId:null,sessionSwitchGen:0,sessionCwd:null,sessionTitle:null,awaitingNewSession:!1,configOptions:[],sessionMode:null,sessionModel:null,currentAssistantEl:null,currentAssistantText:"",currentThinkingEl:null,currentThinkingText:"",busy:!1,pendingAttachments:[],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,lastStateSeq:0,oldestLoadedSeq:0,hasMoreHistory:!1,loadingOlderEvents:!1,replayInProgress:!1,replayTarget:null,replayQueue:[],agentReloading:!1,recentPathsLimit:10,previewToken:null},On={disconnected:"is-disconnected",connecting:"is-connecting",connected:"is-connected"};function Z(e,t=e){l.status.textContent="",l.status.className=`status-dot ${On[e]}`,l.status.dataset.state=e,l.status.setAttribute("aria-label",t),l.status.setAttribute("title",t)}function A(e){return n.configOptions.find(t=>t.id===e)}function Pe(e){return A(e)?.currentValue??null}function Rt(e,t){let s=A(e);s&&(s.currentValue=t)}function Ke(e){n.configOptions=e,e.length>0&&qt(),I(),R()}function Bn(e){n.configOptions.length>0||(n.sessionMode=e.session.mode??null,n.sessionModel=e.session.model??null)}function qt(){n.sessionMode=null,n.sessionModel=null}function Ht(e){return e==="mode"?n.sessionMode:n.sessionModel}function I(){if(l.inputArea.classList.remove("plan-mode","autopilot-mode","preview-mode"),n.previewToken){l.inputArea.classList.add("preview-mode"),l.input.placeholder="publish or cancel",l.modePill.textContent="preview",re();return}l.input.placeholder="Message or ?";let e=Pe("mode")||Ht("mode")||"";Tt(e)?l.inputArea.classList.add("plan-mode"):Et(e)&&l.inputArea.classList.add("autopilot-mode"),l.modePill.textContent=xt(e)?Ct(e):"",re()}function R(){let e=Pe("model")||Ht("model"),t=n.sessionCwd??"";if(l.statusBar.textContent="",t){e&&l.statusBar.appendChild(document.createTextNode(e+" \xB7 "));let s=document.createElement("span");s.className="status-cwd",s.textContent=t,l.statusBar.appendChild(s)}else e&&(l.statusBar.textContent=e)}function Dn(e){n.lastStateSeq=e.seq;let t=e.runtime.busy;y(t!=null),t==null&&oe(),Bn(e),I(),R()}function Ot(e){if(e.seq!==n.lastStateSeq+1)return!1;n.lastStateSeq=e.seq;let t=e.patch.runtime;if(t&&"busy"in t){let s=t.busy??null;y(s!=null),s==null&&oe()}return!0}async function q(e){let t=n.sessionSwitchGen;try{let s=await Lt(e);return n.sessionSwitchGen!==t?null:(Dn(s),s)}catch{return null}}function y(e){n.busy=e,e?l.prompt.classList.add("busy"):l.prompt.classList.remove("busy"),re()}function U({cwd:e,inheritFromSessionId:t=n.sessionId}={}){n.awaitingNewSession=!0,Se({cwd:e,inheritFromSessionId:t}).catch(()=>{})}var Bt=[];function Dt(e){Bt.push(e)}var Ft=()=>{};function Ut(e){Ft=e}function re(){Ft()}function v(){for(let e of Bt)e();l.messages.innerHTML="",n.currentAssistantEl=null,n.currentAssistantText="",n.currentThinkingEl=null,n.currentThinkingText="",n.pendingAttachments.length=0,n.followMessages=!0,n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,n.turnEnded=!1,n.newTurnStarted=!1,n._cancelTimerId!=null&&clearTimeout(n._cancelTimerId),n._cancelTimerId=null,n.lastEventSeq=0,n.lastStateSeq=0,n.oldestLoadedSeq=0,n.hasMoreHistory=!1,n.loadingOlderEvents=!1,n.replayInProgress=!1,n.replayQueue=[],n.previewToken=null,l.attachPreview.innerHTML="",l.attachPreview.classList.remove("active"),l.input.disabled=!1,l.sendBtn.disabled=!1,I(),y(!1),n.sessionTitle=null,n.sessionCwd=null,n.configOptions=[],qt(),Ae(null,null),l.statusBar.textContent=""}function H(){return!n.busy||!n.sessionId?!1:(Pt(n.sessionId).catch(()=>{}),!0)}function oe(){n._cancelTimerId!=null&&(clearTimeout(n._cancelTimerId),n._cancelTimerId=null)}function Vt(){return location.hash.slice(1)||null}function Ge(e){history.replaceState(null,"",`#${e}`)}function Ae(e,t){l.sessionInfo.textContent=t??(e?e.slice(0,8)+"\u2026":""),document.title=t??">_"}Z("disconnected");function $e(e,t){let s=document.createElement("div");return s.className=`msg ${e}`,s.innerHTML=e==="user"?_(t).replace(/\n/g,"<br>"):F(t),S(s),s}function o(e){let t=document.createElement("div");return t.className="system-msg",t.textContent=e,S(t),t}function V(){let e=n.currentAssistantEl;n.currentAssistantEl=null,n.currentAssistantText="",e&&typeof e.querySelector=="function"&&(e.removeAttribute("data-primed"),D(e))}function $(){if(n.currentThinkingEl){let e=n.currentThinkingEl.querySelector("summary");e.textContent="\u283F thought",e.classList.remove("active"),e.style.animation="none",n.currentThinkingEl.removeAttribute("data-primed"),n.currentThinkingEl=null,n.currentThinkingText=""}}var X=null,Fn=80;function ze(e){return e.scrollHeight-e.scrollTop-e.clientHeight<Fn}function Un(){n.followMessages=ze(l.messages)}l.messages.addEventListener("scroll",Un);function Vn(){return n.followMessages||ze(l.messages)}function S(e,t=!1){if(n.replayTarget)return n.replayTarget.appendChild(e),e;let s=t||Vn();return l.messages.appendChild(e),b(s),e}function jt(){j(),X=document.createElement("div"),X.id="waiting",X.innerHTML='<span class="cursor">\u258C</span>',S(X,!0)}function j(){X&&(X.remove(),X=null)}var Je=!1;function b(e){let t=l.messages;if(e||n.followMessages){typeof requestAnimationFrame=="function"?Je||(Je=!0,requestAnimationFrame(()=>{Je=!1,t.scrollTop=t.scrollHeight})):t.scrollTop=t.scrollHeight,n.followMessages=!0;return}n.followMessages=ze(t)}function E(e){if(e==null||e==="")return"";let t;if(typeof e=="number")t=new Date(e);else{let i=String(e);t=new Date(i.endsWith("Z")||/[+-]\d{2}:?\d{2}$/.test(i)?i:i+"Z")}if(isNaN(t.getTime()))return"";let s=i=>String(i).padStart(2,"0");return`${t.getFullYear()}-${s(t.getMonth()+1)}-${s(t.getDate())} ${s(t.getHours())}:${s(t.getMinutes())}`}function Wt(e,t=!1){let s=document.createElement("div");return s.className="bash-block",s.innerHTML=`<span class="bash-cmd${t?" running":""}">${_(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")}),S(s),t&&(n.currentBashEl=s),s}function Me(e,t,s){if(!e)return;let i=e.querySelector(".bash-cmd");i.classList.remove("running");let r="";if(s?r=`[signal: ${s}]`:t!==0&&t!=null&&(r=`[exit: ${t}]`),r){let a=document.createElement("span");a.className=`bash-exit ${t===0?"ok":"fail"}`,a.textContent=r,i.after(a)}e===n.currentBashEl&&(n.currentBashEl=null)}function Qe(e,t){let s=e.replace(/^\s+/,"");if(s==="")return{node:t,pathPrefix:"",tailQuery:""};let i=/\s$/.test(s),r=s.split(/\s+/).filter(m=>m.length>0),a=i?r:r.slice(0,-1),c=i?"":r[r.length-1]??"",d=t,u="",p=0;for(let m of a){let w=d.children?.find(g=>g.name===m);if(!w)break;u=u?`${u} ${w.name}`:w.name,d=w,p++}let h=a.slice(p);c&&h.push(c);let f=h.join(" ");return{node:d,pathPrefix:u,tailQuery:f}}function Kt(e,t,s){let i=[],r=t.trim().toLowerCase(),a=[];if(e.children)for(let m of e.children){if(r&&!m.name.toLowerCase().includes(r))continue;let g=(m.children?.length??0)>0||m.fetch||m.freeform;a.push({spec:{primary:m.name,secondary:m.desc},prefix:g?"\u203A":"",kind:"subcommand",node:m})}i.push(...a);let c=[],d="none",u="";if(e.fetch&&e.toSpec){if(s==="loading")d="loading";else if(s&&typeof s=="object"&&!Array.isArray(s)&&"error"in s)d="error",u=typeof s.error=="string"?s.error:"";else if(Array.isArray(s))if(s.length===0)d="empty";else{let w=(r?s.filter(g=>e.matches?e.matches(g,r):e.toSpec(g).primary.toLowerCase().includes(r)):s).map(g=>e.toSpec(g));w.length===0?d="no-match":(d="ok",c=w)}}let p=null;if(e.freeform){let m=t.trim();if(!(m.length>0&&c.some(g=>g.primary.toLowerCase()===m.toLowerCase()))){let g=e.freeform(t);g&&(p={spec:g,prefix:"\u21B5",kind:"freeform"},i.push(p))}}if((a.length>0||p!==null)&&d!=="none"&&i.push({spec:{primary:""},prefix:"",kind:"separator"}),d==="loading")i.push({spec:{primary:"(loading...)"},prefix:"",kind:"placeholder"});else if(d==="error")i.push({spec:{primary:`(${u||"error"})`},prefix:"",kind:"placeholder"});else if(d==="empty")i.push({spec:{primary:"(none)"},prefix:"",kind:"placeholder"});else if(d==="no-match")i.push({spec:{primary:"(no match)"},prefix:"",kind:"placeholder"});else if(d==="ok")for(let m of c)i.push({spec:m,prefix:m.current?"*":"",kind:"data"});return i}function Gt(e,t,s){let i=document.createElement("div");i.className="slash-item"+(t?" selected":"");let r=e.path!==void 0,a=document.createElement("div");a.className="slash-row-l1";let c=document.createElement("span");c.className="slash-prefix",c.textContent=s,a.appendChild(c);let d=document.createElement("span");if(d.className="slash-primary"+(e.current?" slash-current":""),d.textContent=e.primary,a.appendChild(d),e.secondary!==void 0){let u=document.createElement("span");u.className="slash-secondary",u.textContent=e.secondary,a.appendChild(u)}if(i.appendChild(a),r){let u=document.createElement("div");u.className="slash-row-l2";let p=document.createElement("span");if(p.className="slash-path",p.textContent=e.path,u.appendChild(p),e.pathSecondary!==void 0){let h=document.createElement("span");h.className="slash-path-secondary",h.textContent=e.pathSecondary,u.appendChild(h)}i.appendChild(u)}return i}var Ze={debug:0,info:1,warn:2,error:3,off:99},_e=new Set(["off","debug","info","warn","error"]),Jt=4096;function zt(e){try{let t=new WeakSet,s=JSON.stringify(e,(i,r)=>{if(r instanceof Error)return{name:r.name,message:r.message,stack:r.stack};if(typeof r=="function")return`[Function ${r.name??"anonymous"}]`;if(typeof r=="bigint")return r.toString()+"n";if(typeof r=="symbol")return r.toString();if(typeof r=="object"&&r!==null){if(t.has(r))return"[Circular]";t.add(r)}return r});return typeof s!="string"?String(e):s.length>Jt?s.slice(0,Jt)+`\u2026(truncated, ${s.length} bytes)`:s}catch{try{return String(e)}catch{return"[unserializable]"}}}function Ye(e){return e<10?"0"+e:String(e)}function jn(e){return e<10?"00"+e:e<100?"0"+e:String(e)}function Qt(e){let t=new Date(e);return`${Ye(t.getHours())}:${Ye(t.getMinutes())}:${Ye(t.getSeconds())}.${jn(t.getMilliseconds())}`}var et="off",Xe=null;function z(e){_e.has(e)&&(et=e)}function Ne(){return et}function Re(e){let t=Zt();if(t){z(t);return}e&&_e.has(e)?z(e):z("off")}function Yt(e){Xe=e}function Wn(e){try{let s=new URL(e).searchParams.get("debug");return s===null||!_e.has(s)?null:s}catch{return null}}function Zt(){return typeof location>"u"?null:Wn(location.href)}function Xt(e){let t=(s,i,r)=>{if(Ze[s]<Ze[et])return;let a=e?`[${e}] `:"";try{let c=console[s];r!==void 0?c(a+i,r):c(a+i)}catch{}if(Xe)try{let c=Qt(Date.now()),d=e?` [${e}]`:"",u=r!==void 0?" "+zt(r):"";Xe(`${c}${d} ${s.toUpperCase()} ${i}${u}`)}catch{}};return{debug:(s,i)=>{t("debug",s,i)},info:(s,i)=>{t("info",s,i)},warn:(s,i)=>{t("warn",s,i)},error:(s,i)=>{t("error",s,i)},scope:s=>Xt(e?`${e}.${s}`:s)}}var M=Xt();{let e=Zt();e&&z(e)}if(typeof window<"u"){let e=M.scope("window");window.addEventListener("error",t=>{e.error(t.message||"uncaught error",{filename:t.filename,lineno:t.lineno,colno:t.colno,stack:t.error instanceof Error?t.error.stack:void 0})}),window.addEventListener("unhandledrejection",t=>{let s=t.reason;e.error("unhandled rejection",{reason:s instanceof Error?{name:s.name,message:s.message,stack:s.stack}:s})})}async function W(e,t){n.sessionSwitchGen++;let s=n.sessionSwitchGen;try{let i=await ke();if(s!==n.sessionSwitchGen)return;let r=i.find(a=>a.id!==e);if(r){v(),n.sessionId=null,Ge(r.id);let[a,c]=await Promise.all([P(r.id),K(r.id)]);if(s!==n.sessionSwitchGen)return;x({type:"session_created",sessionId:a.id,cwd:a.cwd,title:a.title,configOptions:a.configOptions}),c&&b(!0);return}}catch{}s===n.sessionSwitchGen&&(v(),n.sessionId=null,U({cwd:t??void 0}))}function nt(e){return n.replayTarget?.querySelector(`[id="${e}"]`)??document.getElementById(e)}function Kn(e){return n.replayTarget?.querySelector(e)??document.querySelector(e)}function en(e){(typeof navigator<"u"?navigator.serviceWorker:void 0)?.controller?.postMessage({type:"close-notification",tag:e})}var tn="webagent_notify_tip_shown",nn="webagent_notify_tip_denied_shown";function Gn(){if(typeof Notification>"u"||n.replayInProgress)return;let e=Notification.permission;if(e!=="granted"){if(e==="denied"){if(localStorage.getItem(nn))return;localStorage.setItem(nn,"1"),o("tip: notifications are blocked \u2014 allow in browser site settings to enable");return}localStorage.getItem(tn)||(localStorage.setItem(tn,"1"),o("tip: use /notify to enable background notifications"))}}function qe(){St(n.pendingPromptDone,n.pendingToolCallIds.size,n.pendingPermissionRequestIds.size)&&(j(),$(),V(),y(!1),n.pendingPromptDone=!1,Gn())}function Jn(){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?.querySelector("button"))continue;let i=t.querySelector(".title")?.textContent??"\u26BF";t.innerHTML=`<span class="dim">${_(i)} \u2014 cancelled</span>`}n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear()}function zn(){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()}var sn=200;async function K(e){n.replayInProgress=!0,n.replayQueue=[];try{let t=await fetch(`/api/v1/sessions/${e}/events?limit=${sn}`);if(!t.ok)return!1;let s=await t.json(),{events:i,streaming:r,hasMore:a}=ye(s),c=document.createDocumentFragment(),d=rt(i);n.replayTarget=c;for(let u=0;u<i.length;u++){let p=JSON.parse(i[u].data);ot(i[u].type,p,i,u,d)}return n.replayTarget=null,l.messages.style.display="none",l.messages.appendChild(c),l.messages.style.display="",i.length&&(n.lastEventSeq=i[i.length-1].seq,n.oldestLoadedSeq=i[0].seq),n.hasMoreHistory=a===!0,n.hasMoreHistory&&Yn(),rn(),st(i,r),!0}catch{return!1}finally{n.replayTarget=null,n.replayInProgress=!1,ln()}}function st(e,t){if(t.thinking){let s;if(e.length){for(let i=e.length-1;i>=0;i--)if(e[i].type==="thinking"){let r=l.messages.querySelectorAll(".thinking");s=r[r.length-1];break}}else{let i=l.messages.querySelectorAll(".thinking");s=i[i.length-1]}if(s){n.currentThinkingEl=s,n.currentThinkingText=s.getAttribute("data-raw")??"",s.setAttribute("data-primed","");let i=s.querySelector("summary");i&&(i.textContent="\u283F thinking...",i.classList.add("active"))}}if(t.assistant){let s;if(e.length){for(let i=e.length-1;i>=0;i--)if(e[i].type==="assistant_message"){let r=l.messages.querySelectorAll(".msg.assistant");s=r[r.length-1];break}}else{let i=l.messages.querySelectorAll(".msg.assistant");s=i[i.length-1]}s&&(n.currentAssistantEl=s,n.currentAssistantText=s.getAttribute("data-raw")??"",s.setAttribute("data-primed",""))}}function rn(){let e=l.messages.querySelector("[data-sync-boundary]");e&&e.removeAttribute("data-sync-boundary");let t=l.messages.lastElementChild;t&&t.setAttribute("data-sync-boundary","")}var tt=new Map;function it(e){let t=tt.get(e);if(t)return t;let s=Qn(e);return tt.set(e,s),s.finally(()=>{tt.delete(e)}).catch(()=>{}),s}async function Qn(e){n.replayInProgress=!0,n.replayQueue=[];try{let t=`/api/v1/sessions/${e}/events?after=${n.lastEventSeq}`,s=await fetch(t);if(!s.ok||n.sessionId&&e!==n.sessionId)return!1;let i=await s.json(),{events:r,streaming:a}=ye(i);for(let f of l.messages.querySelectorAll("[data-primed]")){let m=f.getAttribute("data-raw")??"";if(f.removeAttribute("data-primed"),f.classList.contains("msg")&&f.classList.contains("assistant"))f.innerHTML=F(m),D(f);else if(f.classList.contains("thinking")){let w=f.querySelector(".thinking-content");w&&(w.textContent=m);let g=f.querySelector("summary");g&&(g.textContent="\u283F thought",g.classList.remove("active"),g.style.animation="none")}}let c=l.messages.querySelector("[data-sync-boundary]");if(c)for(;c.nextElementSibling;)c.nextElementSibling.remove();if(n.currentAssistantEl=null,n.currentAssistantText="",n.currentThinkingEl=null,n.currentThinkingText="",n.currentBashEl=null,r.length===0)return st(r,a),!0;let d=document.createDocumentFragment(),u=rt(r);n.replayTarget=d;for(let f=0;f<r.length;f++){let m=JSON.parse(r[f].data);ot(r[f].type,m,r,f,u)}n.replayTarget=null;let p=l.messages.lastElementChild,h=d.firstElementChild;if(p&&h){if(p.classList.contains("msg")&&p.classList.contains("assistant")&&h.classList.contains("msg")&&h.classList.contains("assistant")){let f=p.getAttribute("data-raw")??"",m=h.getAttribute("data-raw")??"",w=f+m;p.setAttribute("data-raw",w),p.innerHTML=F(w),D(p),h.remove()}else if(p.classList.contains("thinking")&&h.classList.contains("thinking")){let f=p.getAttribute("data-raw")??"",m=h.getAttribute("data-raw")??"",w=f+`
|
|
2
|
-
`+m;p.setAttribute("data-raw",w);let g=p.querySelector(".thinking-content");g&&(g.textContent=w),h.remove()}}return l.messages.appendChild(d),n.lastEventSeq=r[r.length-1].seq,rn(),st(r,a),!0}catch{return!1}finally{n.replayTarget=null,n.replayInProgress=!1,ln()}}var ue=null;function Yn(){He();let e=document.createElement("div");e.id="history-sentinel",e.className="history-sentinel",e.textContent="\u2191 loading\u2026",l.messages.prepend(e),typeof IntersectionObserver=="function"&&(ue=new IntersectionObserver(t=>{t[0].isIntersecting&&!n.loadingOlderEvents&&n.hasMoreHistory&&n.sessionId&&Zn(n.sessionId)},{root:l.messages,rootMargin:"200px 0px 0px 0px"}),ue.observe(e))}function He(){ue&&(ue.disconnect(),ue=null),document.getElementById("history-sentinel")?.remove()}Dt(He);async function Zn(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=${sn}&before=${n.oldestLoadedSeq}`);if(!t.ok||e!==n.sessionId)return!1;let s=await t.json(),{events:i,hasMore:r}=ye(s);if(i.length===0)return n.hasMoreHistory=!1,He(),!0;let a=document.createDocumentFragment(),c=rt(i);n.replayTarget=a;for(let h=0;h<i.length;h++){let f=JSON.parse(i[h].data);ot(i[h].type,f,i,h,c)}n.replayTarget=null;let d=l.messages,u=d.scrollHeight,p=document.getElementById("history-sentinel");return p?p.after(a):d.prepend(a),d.scrollTop+=d.scrollHeight-u,n.oldestLoadedSeq=i[0].seq,n.hasMoreHistory=r===!0,n.hasMoreHistory||He(),!0}catch{return!1}finally{n.loadingOlderEvents=!1}}function rt(e){let t=new Set;for(let s of e)if(s.type==="permission_response"){let i=JSON.parse(s.data);t.add(i.requestId)}return{toolCalls:new Map,permissions:new Map,resolvedPermissions:t,currentBashEl:null}}function on(e){let t=document.createElement("details");t.className="message",t.open=!0,t.setAttribute("data-message-id",e.message_id),t.setAttribute("data-raw",e.body);let s=e.from_label??e.from_ref,i=e.title?` \xB7 ${_(e.title)}`:"";t.innerHTML=`<summary>\u2709\uFE0E ${_(s)}${i}</summary><div class="message-content">${F(e.body)}</div>`,S(t);let r=t.querySelector(".message-content");r&&D(r)}function ot(e,t,s,i,r){let a=t;if(kt(e)){es(e,a,s,i,r);return}switch(e){case"prompt_done":n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,y(!1);break;case"message":on(a);break}}function ee(){return{findToolCallEl:e=>document.getElementById(`tc-${e}`),findPermissionEl:e=>document.querySelector(`.permission[data-request-id="${e}"]`),findBashEl:()=>n.currentBashEl,enhanceMarkdown:D}}function Xn(e,t,s){return{findToolCallEl:i=>e?e.toolCalls.get(i)??null:nt(`tc-${i}`),findPermissionEl:i=>e?e.permissions.get(i)??null:Kn(`.permission[data-request-id="${i}"]`),findBashEl:()=>e?e.currentBashEl:nt("bash-replay-pending"),isPermissionResolved:i=>e?e.resolvedPermissions.has(i):t.slice(s+1).some(r=>{let a=JSON.parse(r.data);return r.type==="permission_response"&&a.requestId===i}),enhanceMarkdown:D}}function an(e,t,s,i){e.querySelectorAll("button").forEach(a=>{let c=a.dataset.optionId??"",d=a.dataset.optionKind??"",u=a.textContent||"";a.onclick=()=>{bt(d).apiAction==="deny"?$t(n.sessionId,t).catch(()=>{}):At(n.sessionId,t,c).catch(()=>{}),e.innerHTML=`<span class="dim">\u26BF ${_(s)} \u2014 ${_(u)}</span>`,i?.()}})}function es(e,t,s,i,r){let a=Xn(r,s,i);switch(e){case"assistant_message":{let d=(n.replayTarget??l.messages).lastElementChild,u=t.text??"";if(d?.classList.contains("msg")&&d.classList.contains("assistant")){let f=(d.getAttribute("data-raw")??"")+u;d.setAttribute("data-raw",f),d.innerHTML=F(f),D(d);break}let p=k(e,t,a);p&&S(p);break}case"thinking":{let d=(n.replayTarget??l.messages).lastElementChild,u=t.text??"";if(d?.classList.contains("thinking")){let h=d.querySelector(".thinking-content");if(h){let m=(d.getAttribute("data-raw")??"")+`
|
|
3
|
-
`+u;d.setAttribute("data-raw",m),h.textContent=m;break}}let p=k(e,t,a);p&&S(p);break}case"tool_call":{let c=k(e,t,a);c&&(S(c),r&&r.toolCalls.set(t.id,c));break}case"tool_call_update":{k(e,t,a),(t.status==="completed"||t.status==="failed")&&n.pendingToolCallIds.delete(t.id);break}case"permission_request":{let c=t.requestId,d=t.title??"",u=k(e,t,a);u&&((a.isPermissionResolved?.(c)??!1)||an(u,c,d),S(u),r&&r.permissions.set(c,u));break}case"bash_command":{let c=k(e,t,a);c&&(r?r.currentBashEl=c:c.id="bash-replay-pending",S(c));break}case"bash_result":{let c=r?r.currentBashEl:nt("bash-replay-pending");c&&!r&&c.removeAttribute("id"),k(e,t,a),r&&(r.currentBashEl=null);break}case"user_message":case"plan":case"permission_response":{let c=k(e,t,a);c&&S(c);break}}}function ln(){let e=n.replayQueue;n.replayQueue=[];for(let t of e)ts(t)||x(t)}function ts(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 x(e){if(n.replayInProgress){n.replayQueue.push(e);return}let t="sessionId"in e?e.sessionId:void 0;if(!(t&&e.type!=="session_created"&&e.type!=="session_deleted"&&(!n.sessionId||t!==n.sessionId)))switch(e.type){case"connected":if(e.cancelTimeout!=null&&(n.cancelTimeout=e.cancelTimeout),e.recentPathsLimit!=null&&(n.recentPathsLimit=e.recentPathsLimit),Re(e.debugLevel),n.agentReloading){n.agentReloading=!1;let s=e.agent.name,i=e.agent.version,r=s&&i?`${s} ${i}`:"Agent";o(`${r} reloaded.`),y(!1)}break;case"state_patch":{!Ot({seq:e.seq,patch:e.patch})&&n.sessionId===e.sessionId&&q(n.sessionId);break}case"session_created":if(!n.awaitingNewSession&&n.sessionId&&e.sessionId!==n.sessionId)break;n.awaitingNewSession=!1,n.sessionId=e.sessionId,n.sessionCwd=e.cwd??n.sessionCwd,n.sessionTitle=e.title??null,e.configOptions&&e.configOptions.length&&Ke(e.configOptions),I(),R(),Ge(n.sessionId),n.clientId&&ie(n.clientId,!document.hidden,n.sessionId).catch(()=>{}),Ae(n.sessionId,n.sessionTitle),Z("connected","connected"),l.input.disabled=!1,l.sendBtn.disabled=!1,n.newTurnStarted=!1;{let s=document.getElementById("bash-replay-pending");s?(s.removeAttribute("id"),s.querySelector(".bash-cmd")?.classList.add("running"),n.currentBashEl=s):n.currentBashEl=null}l.messages.children.length===0&&o(`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($(),V(),n.newTurnStarted=!0,n.turnEnded=!1,e.sessionId===n.sessionId){let s=k("user_message",e,ee());s&&S(s)}break}case"message_chunk":if(n.turnEnded)break;j(),$(),n.currentAssistantEl||(n.currentAssistantEl=$e("assistant",""),n.currentAssistantText=""),n.currentAssistantText+=e.text,n.currentAssistantEl.innerHTML=F(n.currentAssistantText),b();break;case"thought_chunk":if(n.turnEnded)break;j(),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="",S(n.currentThinkingEl)),n.currentThinkingText+=e.text,n.currentThinkingEl.querySelector(".thinking-content").textContent=n.currentThinkingText,b();break;case"tool_call":{if(n.turnEnded)break;n.pendingToolCallIds.add(e.id),y(!0),j(),$(),V();let s=k("tool_call",e,ee());s&&S(s);break}case"tool_call_update":{(e.status==="completed"||e.status==="failed")&&n.pendingToolCallIds.delete(e.id),k("tool_call_update",e,ee()),qe(),b();break}case"plan":{$(),V();let s=k("plan",e,ee());s&&S(s);break}case"permission_request":{if(n.turnEnded||document.querySelector(`.permission[data-request-id="${e.requestId}"]`))break;n.pendingPermissionRequestIds.add(e.requestId),y(!0),$();let s=k("permission_request",e,ee());if(s){let i=e.requestId;an(s,i,e.title,()=>{n.pendingPermissionRequestIds.delete(i),qe()}),S(s)}break}case"permission_response":{n.pendingPermissionRequestIds.delete(e.requestId),e.sessionId===n.sessionId&&k("permission_response",e,ee()),qe();break}case"bash_command":{if(n.sentBashForSession===e.sessionId){n.sentBashForSession=null;break}if(e.sessionId===n.sessionId){let s=k("bash_command",e,ee());s&&(s.querySelector(".bash-cmd").classList.add("running"),S(s),n.currentBashEl=s),y(!0)}break}case"bash_output":{if(e.sessionId!==n.sessionId)break;if(n.currentBashEl){let s=n.currentBashEl.querySelector(".bash-output");if(!s)break;if(e.stream==="stderr"){let i=document.createElement("span");i.className="stderr",i.textContent=e.text,s.appendChild(i)}else s.appendChild(document.createTextNode(e.text));s.classList.add("has-content"),s.scrollTop=s.scrollHeight,b()}break}case"bash_done":{if(e.sessionId!==n.sessionId)break;Me(n.currentBashEl,e.code,e.signal),e.error&&o(`err: ${e.error}`),y(!1);break}case"prompt_done":{if(oe(),e.stopReason==="cancelled"&&n.newTurnStarted){n.newTurnStarted=!1,$(),V();break}n.newTurnStarted=!1,e.stopReason==="cancelled"?Jn():zn(),n.turnEnded=!0,n.pendingPromptDone=!0,qe();break}case"session_deleted":e.sessionId===n.sessionId&&W(e.sessionId,n.sessionCwd??void 0);break;case"session_expired":{W(n.sessionId,n.sessionCwd??void 0);break}case"config_set":{Rt(e.configId,e.value);let s=A(e.configId),i=s?.name??e.configId,r=s?.options.find(a=>a.value===e.value)?.name??e.value;o(`ok: ${i}: ${r}`),e.configId==="mode"&&I(),R();break}case"config_option_update":e.configOptions.length&&Ke(e.configOptions);break;case"session_title_updated":e.sessionId===n.sessionId&&(n.sessionTitle=e.title,Ae(n.sessionId,n.sessionTitle));break;case"agent_reloading":n.agentReloading=!0,o("Agent reloading\u2026"),y(!0);break;case"agent_reloading_failed":o(`err: Agent reload failed: ${e.error}`),y(!1);break;case"error":n.awaitingNewSession=!1,n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,j(),$(),V(),o(`err: ${e.message}`),y(!1);break;case"message_created":o(`inbox: new message ${e.messageId} \u2014 /inbox to view`);break;case"message_consumed":o(`inbox: ${e.messageId} consumed \u2192 session ${e.sessionId}`),en(`msg-${e.messageId}`);break;case"message_acked":o(`inbox: ${e.messageId} dismissed`),en(`msg-${e.messageId}`);break;case"message":e.sessionId===n.sessionId&&(on(e),b());break;default:break}}async function pe({cwd:e,showCwd:t=!1}={}){if(!n.sessionId){o("warn: No active session");return}let s=n.sessionId,i=e??n.sessionCwd??void 0;n.busy&&H(),v(),o(t&&i?`Clearing session and starting at ${i}\u2026`:"Clearing session\u2026"),n.awaitingNewSession=!0;try{await Se({cwd:i,inheritFromSessionId:s})}catch{n.awaitingNewSession=!1,o("err: Failed to clear session");return}ne(s).catch(()=>{})}var fe=M.scope("share");function ns(e,t){return e.startsWith("http")?e:e?`${location.origin}${e}`:`${location.origin}/s/${t}`}async function Oe(){if(!n.sessionId){o("share: no active session");return}let e=n.sessionId;try{let t=await fetch(`/api/v1/sessions/${encodeURIComponent(e)}/share`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({}),credentials:"same-origin"});if(!t.ok){let r=await t.text();t.status===409?o("share: session busy (agent streaming) \u2014 retry after the response completes"):t.status===403?o("share: forbidden (not owner)"):t.status===400&&r.includes("sanitize")?o(`share: \u2717 sanitize blocked this session \u2014 ${r}`):o(`share: create failed ${t.status} \u2014 ${r.slice(0,200)}`);return}let s=await t.json();n.previewToken=s.token,I();let i=s.reused?"reused":"ready";o(`share: preview ${i} \u2014 ^P publish \xB7 ^C cancel`)}catch(t){fe.error("preview create error",{err:t}),o(`share: network error \u2014 ${String(t)}`)}}async function at(){if(!n.sessionId){o("share: no active session");return}if(!n.previewToken){o("share: no preview to publish \u2014 run /share first");return}let e=n.sessionId,t=n.previewToken;try{let s=await fetch(`/api/v1/sessions/${encodeURIComponent(e)}/share/publish`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:t}),credentials:"same-origin"});if(!s.ok){let d=await s.text();s.status===403?o("share: forbidden (not owner)"):s.status===404?o("share: token not found (already revoked?)"):o(`share: publish failed ${s.status} \u2014 ${d.slice(0,200)}`);return}let i=await s.json(),r=ns(i.public_url,i.token);n.previewToken=null,I();let a=o(`share: published
|
|
4
|
-
`),c=document.createElement("a");c.href=r,c.textContent=r,c.target="_blank",c.rel="noopener noreferrer",a.appendChild(c)}catch(s){fe.error("publish error",{err:s}),o(`share: network error \u2014 ${String(s)}`)}}function lt(){if(!n.previewToken){o("share: no preview active");return}n.previewToken=null,I(),o("share: preview canceled")}async function ct(){let e=await fetch("/api/v1/shares",{credentials:"same-origin"});if(!e.ok)throw new Error(`list failed ${e.status}`);return(await e.json()).shares.filter(s=>s.shared_at!=null)}async function Be(e){if(!e){o("share: usage \u2014 /share revoke <token>");return}try{let s=(await ss()).find(a=>a.token===e);if(!s){o("share: token not found (already revoked?)");return}let i=await fetch(`/api/v1/sessions/${encodeURIComponent(s.session_id)}/share`,{method:"DELETE",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:e}),credentials:"same-origin"});if(!i.ok){i.status===403?o("share: forbidden (not owner)"):i.status===404?o(`share: ${e} already revoked`):o(`share: revoke failed ${i.status}`);return}let r=await i.json();o(r.revoked?`share: revoked ${e}`:`share: ${e} already revoked`)}catch(t){fe.error("revoke error",{err:t}),o(`share: network error \u2014 ${String(t)}`)}}async function ss(){let e=await fetch("/api/v1/shares",{credentials:"same-origin"});if(!e.ok)throw new Error(`list failed ${e.status}`);return(await e.json()).shares.filter(s=>s.shared_at!=null)}function me(e){let t=`${location.origin}/s/${e}`;window.open(t,"_blank","noopener"),o(`share: opened ${t}`)}async function cn(){try{let e=await fetch("/api/v1/share/by",{credentials:"same-origin"});return e.ok?(await e.json()).value:null}catch{return null}}async function he(e){try{let t=await fetch("/api/v1/share/by",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:e}),credentials:"same-origin"});if(!t.ok){let i=await t.text();o(`share: set author failed ${t.status} \u2014 ${i.slice(0,200)}`);return}let s=await t.json();if(n.previewToken&&n.sessionId)try{await fetch(`/api/v1/sessions/${encodeURIComponent(n.sessionId)}/share`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:n.previewToken,display_name:s.value??""}),credentials:"same-origin"})}catch(i){fe.warn("by: preview patch failed",{err:i})}o(s.value?`share: author set to '${s.value}'`:"share: author cleared (publish anonymously)")}catch(t){fe.error("by error",{err:t}),o(`share: network error \u2014 ${String(t)}`)}}var ae=!1;async function mn(){try{ae=await(await navigator.serviceWorker.ready).pushManager.getSubscription()!==null}catch{ae=!1}}function is(e){let t="=".repeat((4-e.length%4)%4),s=(e+t).replace(/-/g,"+").replace(/_/g,"/"),i=atob(s),r=new Uint8Array(i.length);for(let a=0;a<i.length;a++)r[a]=i.charCodeAt(a);return r}async function rs(){try{let e=await navigator.serviceWorker.ready,t=await fetch("/api/beta/push/vapid-key");if(!t.ok)return;let{publicKey:s}=await t.json(),r=(await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:is(s)})).toJSON();await fetch("/api/beta/push/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({endpoint:r.endpoint,keys:r.keys,clientId:n.clientId})})}catch(e){M.scope("push").error("subscribe failed",{err:e})}}async function os(){try{let t=await(await navigator.serviceWorker.ready).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){M.scope("push").error("unsubscribe failed",{err:e})}}async function as(){return(await fetch("/api/v1/sessions")).json()}async function dn(){let e=n.recentPathsLimit,t=e>0?`/api/v1/recent-paths?limit=${e}`:"/api/v1/recent-paths";return(await(await fetch(t)).json()).map(r=>({cwd:r.cwd,time:r.last_used_at}))}var un=async()=>{let{messages:e}=await de();return e},pn=async()=>xe();async function hn(e){n.sessionSwitchGen++;let t=n.sessionSwitchGen;v(),n.sessionId=null;try{let[s,i]=await Promise.all([P(e),K(e),q(e)]);if(t!==n.sessionSwitchGen)return;x({type:"session_created",sessionId:s.id,cwd:s.cwd,title:s.title,configOptions:s.configOptions}),i&&b(!0)}catch{v(),n.sessionId=null,o("err: Failed to switch session")}}async function ls(e,t,s){let i=A(e);i&&(i.currentValue=t),I(),R(),o(`${i?.name??e} \u2192 ${s}`),n.sessionId&&await se(n.sessionId,e,t).catch(()=>{})}async function ut(e){try{let t=await Nt(e.id);t.alreadyConsumed?o(`inbox: already consumed \u2192 switching to ${t.sessionId}`):o(`inbox: opened as ${t.sessionId}`),await hn(t.sessionId)}catch(t){o(`err: consume failed (${t.message})`)}}async function cs(e){try{await Ee(e.id),o(`inbox: ack ${e.id}`)}catch(t){o(`err: ack failed (${t.message})`)}}async function ds(e){if(!/^[A-Za-z0-9_-]{1,64}$/.test(e)){o("err: token name must match [A-Za-z0-9_-]{1,64}");return}try{let t=await Ce(e);o(`token: created ${t.name} (${t.scope})`),o(`${t.token}`),o("\u2014 save this now; it will never be shown again")}catch(t){let s=t;s.status===409?o(`err: token "${e}" already exists`):s.status===403?o("err: admin scope required to manage tokens"):o(`err: create failed (${s.message})`)}}async function us(e){try{await Le(e),o(`token: revoked ${e}`)}catch(t){let s=t;s.status===404?o(`err: no token named "${e}"`):s.status===403?o("err: admin scope required to manage tokens"):s.status===400&&/using|yourself|cannot/i.test(s.message)?o("err: can't revoke the token you're signed in with \u2014 use another admin token"):o(`err: revoke failed (${s.message})`)}}async function ps(){if(typeof Notification>"u"){o("err: notifications not supported in this browser");return}if(Notification.permission==="denied"){o("notify: blocked \u2014 allow in browser site settings to enable");return}if(Notification.permission!=="granted"&&await Notification.requestPermission()!=="granted"){o("notify: blocked \u2014 allow in browser site settings to enable");return}await mn();let e=ae;await rs(),ae=!0,o(e?"notify: already enabled":"notify: enabled")}async function fs(){await os(),ae=!1,o("notify: disabled")}function fn(e,t){let s=e.shared_at?E(e.shared_at):"\u2014";return{primary:e.token,secondary:e.session_title??void 0,path:`/s/${e.token}`,pathSecondary:s,onSelect:()=>{t==="open"?me(e.token):Be(e.token)}}}var Q={name:"<root>",children:[{name:"/cancel",desc:"Cancel current response",onSelect:()=>{n.busy?(H(),o("^C")):o("Nothing to cancel.")}},{name:"/clear",desc:"Clear current session",fetch:dn,toSpec:e=>{let t=e,s=t.cwd.toLowerCase()===(n.sessionCwd??"").toLowerCase();return{primary:t.cwd,current:s,onSelect:()=>{pe({cwd:t.cwd,showCwd:!0})}}},freeform:e=>{let t=e.trim();return t?{primary:`clear and start at '${t}'`,onSelect:()=>{pe({cwd:t,showCwd:!0})}}:null}},{name:"/log",desc:"Set log level",fetch:()=>[{value:"off",name:"off",desc:"Disable logging"},{value:"error",name:"error",desc:"Show errors only"},{value:"warn",name:"warn",desc:"Show warnings and errors"},{value:"info",name:"info",desc:"Show info, warnings, errors"},{value:"debug",name:"debug",desc:"Show all messages"}],toSpec:e=>{let t=e;return{primary:t.name,secondary:t.desc,current:t.value===Ne(),onSelect:()=>{z(t.value),o(`log: ${t.value}`),t.value!=="off"&&M.info("log enabled",{level:t.value})}}}},{name:"/exit",desc:"End current session",onSelect:async()=>{if(!n.sessionId){o("warn: No active session");return}let e=n.sessionId;try{n.busy&&H(),ne(e).catch(()=>{}),await W(e,n.sessionCwd??void 0)}catch{o("err: Failed to exit session")}}},{name:"/help",desc:"Show help",onSelect:()=>{ms()}},{name:"/inbox",desc:"Manage inbox",fetch:un,toSpec:e=>{let t=e,s=t.from_label??t.from_ref,i=E(t.created_at);return{primary:t.title,secondary:i,path:t.cwd??"",pathSecondary:s,onSelect:()=>ut(t)}},children:[{name:"dismiss",desc:"Dismiss only",fetch:un,toSpec:e=>{let t=e,s=t.from_label??t.from_ref,i=E(t.created_at);return{primary:t.title,secondary:i,path:t.cwd??"",pathSecondary:s,onSelect:()=>cs(t)}}}]},{name:"/logout",desc:"Log out",onSelect:()=>{try{localStorage.removeItem(B)}catch{}try{n.eventSource?.close()}catch{}o("Logged out."),location.replace("/login")}},dt("/mode","Switch mode","mode"),dt("/model","Switch model","model"),{name:"/new",desc:"Create new session",fetch:dn,toSpec:e=>{let t=e,s=t.cwd.toLowerCase()===(n.sessionCwd??"").toLowerCase();return{primary:t.cwd,current:s,onSelect:()=>{v(),o("Creating new session\u2026"),U({cwd:t.cwd})}}},freeform:e=>{let t=e.trim();return t?{primary:`create session at '${t}'`,onSelect:()=>{v(),o("Creating new session\u2026"),U({cwd:t})}}:null}},{name:"/notify",desc:"Toggle notifications",fetch:async()=>(await mn(),[{value:"on",name:"on",desc:"Enable notifications"},{value:"off",name:"off",desc:"Disable notifications"}]),toSpec:e=>{let t=e,s=t.value==="on"===ae;return{primary:t.name,secondary:t.desc,current:s,onSelect:t.value==="on"?ps:fs}}},{name:"/reload",desc:"Reload agent",onSelect:()=>{o("Reloading agent\u2026"),Te().catch(e=>{o(`err: ${e instanceof Error?e.message:"Failed to reload agent"}`)})}},{name:"/rename",desc:"Rename session",freeform:e=>{let t=e.trim();return t?{primary:`rename to '${t}'`,onSelect:async()=>{if(!n.sessionId){o("err: No active session");return}try{await Ie(n.sessionId,t),o(`Renamed \u2192 ${t}`)}catch{o("err: Failed to rename session")}}}:null}},{name:"/share",desc:"Share a read-only snapshot",fetch:ct,toSpec:e=>fn(e,"open"),freeform:e=>{let t=e.trim();return t?{primary:`open share '${t}'`,onSelect:()=>{me(t)}}:{primary:"create new share preview",onSelect:()=>Oe()}},children:[{name:"by",desc:"Set author",fetch:async()=>[{value:await cn()}],toSpec:e=>{let t=e.value;return{primary:t??"(none)",secondary:t?"current \u2014 Enter to clear":"currently anonymous",onSelect:()=>he(null)}},freeform:e=>{let t=e.trim();return t?{primary:`set author to '${t}'`,onSelect:()=>he(t)}:null}},{name:"revoke",desc:"Revoke a share",fetch:ct,toSpec:e=>fn(e,"revoke")}]},{name:"/switch",desc:"Switch session",fetch:as,matches:(e,t)=>{let s=e;return(s.title??"").toLowerCase().includes(t)||s.id.startsWith(t)},toSpec:e=>{let t=e,s=t.title??t.id,i=E(t.last_active_at);return{primary:s,secondary:i,path:t.cwd,current:t.id===n.sessionId,onSelect:()=>hn(t.id)}}},dt("/think","Set thinking effort","reasoning_effort"),{name:"/token",desc:"Manage API tokens",fetch:pn,toSpec:e=>{let t=e,s=t.lastUsedAt?E(t.lastUsedAt):"never";return{primary:t.name,secondary:`last used ${s}`,path:`${t.scope} \xB7 created ${E(t.createdAt)}`,current:t.isSelf,onSelect:t.isSelf?void 0:()=>{o(`\u2014 use /token revoke ${t.name} to revoke`)}}},freeform:e=>{let t=e.trim();return/^[A-Za-z0-9_-]{1,64}$/.test(t)?{primary:`create token '${t}'`,onSelect:()=>ds(t)}:null},children:[{name:"revoke",desc:"Revoke token",fetch:async()=>(await pn()).filter(e=>!e.isSelf),toSpec:e=>{let t=e,s=t.lastUsedAt?E(t.lastUsedAt):"never";return{primary:t.name,secondary:`last used ${s}`,path:`${t.scope} \xB7 created ${E(t.createdAt)}`,onSelect:()=>us(t.name)}}}]}]};function dt(e,t,s){return{name:e,desc:t,fetch:()=>{let i=A(s);return i?i.options:[]},toSpec:i=>{let r=i,a=Pe(s);return{primary:r.name,current:r.value===a,onSelect:()=>ls(s,r.value,r.name)}}}}function ms(){let e=[];n.serverVersion&&e.push(`WebAgent ${n.serverVersion}`),n.agentName&&n.agentVersion&&e.push(`${n.agentName} ${n.agentVersion}`),e.length&&o(e.join(" \xB7 ")),o("\u203A has next step * current value"),o("Tab completes \xB7 Enter sends raw text"),o("? \u2014 Show help"),o("!<command> \u2014 Run bash command");for(let t of Q.children)o(`${t.name} \u2014 ${t.desc??""}`);o("--- Shortcuts ---");for(let t of hs)o(`${t.key} \u2014 ${t.desc}`);o("--- Tips ---");for(let t of gs)o(t.text)}var hs=[{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:"Attach file (any type)"}],gs=[{text:"Tap \u276F prompt to cycle mode"}];async function ys(){try{let e=await navigator.serviceWorker.ready,t=await fetch("/api/beta/push/vapid-key");if(!t.ok)return;let{publicKey:s}=await t.json(),r=(await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:vs(s)})).toJSON();await fetch("/api/beta/push/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({endpoint:r.endpoint,keys:r.keys,clientId:n.clientId})})}catch(e){M.scope("push").error("subscribe failed",{err:e})}}async function ws(){try{let t=await(await navigator.serviceWorker.ready).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){M.scope("push").error("unsubscribe failed",{err:e})}}function vs(e){let t="=".repeat((4-e.length%4)%4),s=(e+t).replace(/-/g,"+").replace(/_/g,"/"),i=atob(s),r=new Uint8Array(i.length);for(let a=0;a<i.length;a++)r[a]=i.charCodeAt(a);return r}async function gn(){try{return await(await navigator.serviceWorker.ready).pushManager.getSubscription()!==null}catch{return!1}}async function pt(e){let t=e.split(/\s+/),s=t[0].toLowerCase(),i=t.slice(1).join(" ").trim();switch(s){case"/share":{let r=i.split(/\s+/),a=(r[0]??"").toLowerCase();if(a==="")return await Oe(),!0;if(a==="revoke"){let d=r[1]??"";return await Be(d),!0}if(a==="by"){let d=i.replace(/^by(\s+|$)/i,"").trim();return await he(d===""?null:d),!0}let c=r[0]??"";return/^[A-Za-z0-9_-]{24}$/.test(c)?(me(c),!0):(o(`share: unknown subcommand '${a}' \u2014 try /share, /share by <name>, /share <token>, or /share revoke <token>`),!0)}case"/new":{let r=i||n.sessionCwd||void 0,a=n.sessionId;return v(),n.sessionId=null,o("Creating new session\u2026"),U({cwd:r,inheritFromSessionId:a}),!0}case"/rename":{if(!n.sessionId)return o("err: No active session"),!0;if(!i)return o(`Current: ${n.sessionTitle??"(untitled)"}`),o("Usage: /rename <new title>"),!0;try{await Ie(n.sessionId,i),o(`Renamed \u2192 ${i}`)}catch{o("err: Failed to rename session")}return!0}case"/sessions":return o("Removed. Use /switch to see all sessions."),!0;case"/clear":return await pe({cwd:i||void 0,showCwd:!!i}),!0;case"/exit":{if(!n.sessionId)return o("warn: No active session"),!0;let r=n.sessionId;try{n.busy&&H(),ne(r).catch(()=>{}),await W(r,n.sessionCwd??void 0)}catch{o("err: Failed to exit session")}return!0}case"/logout":{try{localStorage.removeItem(B)}catch{}try{n.eventSource?.close()}catch{}return o("Logged out."),location.replace("/login"),!0}case"/token":{let r=i.trim().split(/\s+/).filter(Boolean),a=r[0];if(!a){try{let d=await xe();if(d.length===0)return o("token: (none)"),!0;for(let u of d){let p=u.lastUsedAt?E(u.lastUsedAt):"never";o(`${u.name} (${u.scope}) \u2014 created ${E(u.createdAt)}, last used ${p}`)}o("\u2014 /token <name> to create \xB7 /token revoke <name> to revoke")}catch(d){let u=d;u.status===403?o("err: admin scope required to manage tokens"):o(`err: token list failed (${u.message})`)}return!0}if(a==="revoke"){let d=r[1];if(!d)return o("err: usage /token revoke <name>"),!0;try{await Le(d),o(`token: revoked ${d}`)}catch(u){let p=u;p.status===404?o(`err: no token named "${d}"`):p.status===403?o("err: admin scope required to manage tokens"):p.status===400&&/using|yourself|cannot/i.test(p.message)?o("err: can't revoke the token you're signed in with \u2014 use another admin token"):o(`err: revoke failed (${p.message})`)}return!0}let c=a;if(!/^[A-Za-z0-9_-]{1,64}$/.test(c))return o("err: token name must match [A-Za-z0-9_-]{1,64}"),!0;try{let d=await Ce(c);o(`token: created ${d.name} (${d.scope})`),o(`${d.token}`),o("\u2014 save this now; it will never be shown again")}catch(d){let u=d;u.status===409?o(`err: token "${c}" already exists`):u.status===403?o("err: admin scope required to manage tokens"):o(`err: create failed (${u.message})`)}return!0}case"/switch":{if(!i)return o("Usage: /switch <title or id prefix>"),!0;try{let a=await(await fetch("/api/v1/sessions")).json(),c=i.toLowerCase(),d=a.find(h=>h.id.startsWith(i)||h.title?.toLowerCase().includes(c));if(!d)return o(`err: No session matching "${i}"`),!0;n.sessionSwitchGen++;let u=n.sessionSwitchGen;v(),n.sessionId=null;let[p]=await Promise.all([P(d.id),K(d.id),q(d.id)]);if(u!==n.sessionSwitchGen)return!0;x({type:"session_created",sessionId:p.id,cwd:p.cwd,title:p.title,configOptions:p.configOptions}),b(!0)}catch{v(),n.sessionId=null,o("err: Failed to switch session")}return!0}case"/cancel":return n.busy?(H(),o("^C")):o("Nothing to cancel."),!0;case"/reload":return o("Reloading agent\u2026"),Te().then(()=>{}).catch(r=>{o(`err: ${r instanceof Error?r.message:"Failed to reload agent"}`)}),!0;case"/help":case"?":{let r=[];n.serverVersion&&r.push(`WebAgent ${n.serverVersion}`),n.agentName&&n.agentVersion&&r.push(`${n.agentName} ${n.agentVersion}`),r.length&&o(r.join(" \xB7 ")),o("\u203A has next step * current value"),o("Tab completes \xB7 Enter sends raw text"),o("? \u2014 Show help"),o("!<command> \u2014 Run bash command");for(let a of Q.children)o(`${a.name} \u2014 ${a.desc??""}`);return o("--- Shortcuts ---"),o("Enter \u2014 Send message"),o("Shift+Enter \u2014 New line"),o("^C \u2014 Cancel current response"),o("^M \u2014 Cycle mode (Agent \u2192 Plan \u2192 Autopilot)"),o("^U \u2014 Attach file (any type)"),o("--- Tips ---"),o("Tap \u276F prompt to cycle mode"),!0}case"/model":case"/mode":case"/think":{let a={"/model":"model","/mode":"mode","/think":"reasoning_effort"}[s],c=A(a);if(!i){let f=c?.options.find(m=>m.value===c.currentValue)?.name??c?.currentValue??"unknown";return o(`${c?.name??a}: ${f}`),o(`Type ${s} + space to pick from list`),!0}if(!c)return o(`err: ${s.slice(1)} is not available.`),!0;let d=i.trim(),u=f=>f.toLowerCase().replace(/[\s_]+/g,"-"),p=u(d),h=c.options.find(f=>u(f.value)===p||u(f.name)===p);if(!h){let f=c.options.filter(m=>u(m.value).includes(p)||u(m.name).includes(p));if(f.length===1)h=f[0];else if(f.length>1)return o(`err: Ambiguous "${i}". Type ${s} + space to see options.`),!0}return h?(c.currentValue=h.value,I(),R(),o(`${c.name} \u2192 ${h.name}`),await se(n.sessionId,a,h.value).catch(()=>{}),!0):(o(`err: Unknown "${i}". Type ${s} + space to see options.`),!0)}case"/inbox":{let r=i.trim().split(/\s+/).filter(Boolean),a=r[0];if(!a){try{let{messages:f}=await de();if(f.length===0)return o("inbox: empty"),!0;for(let m of f){let w=m.from_label??m.from_ref,g=E(m.created_at);o(`${m.id} \xB7 ${m.title} \xB7 ${w} \xB7 ${g}`)}o("\u2014 /inbox <id> to open \xB7 /inbox dismiss <id> to dismiss only")}catch(f){o(`err: inbox list failed (${f.message})`)}return!0}let c=a==="dismiss",d=c?r[1]:a;if(!d)return o("err: usage /inbox <id> | /inbox dismiss <id>"),!0;let u;try{({messages:u}=await de())}catch(f){return o(`err: inbox list failed (${f.message})`),!0}let p=d.toLowerCase(),h=u.find(f=>f.id===d)??u.find(f=>f.id.toLowerCase().startsWith(p))??u.find(f=>f.title.toLowerCase().includes(p));if(!h)return o(`err: no inbox message matching "${d}"`),!0;if(c){try{await Ee(h.id),o(`inbox: dismissed ${h.id}`)}catch(f){o(`err: dismiss failed (${f.message})`)}return!0}try{await ut(h)}catch(f){o(`err: consume failed (${f.message})`)}return!0}case"/notify":{if(typeof Notification>"u")return o("err: notifications not supported in this browser"),!0;let r=i.toLowerCase();if(r==="on"){if(Notification.permission==="denied")return o("notify: blocked \u2014 allow in browser site settings to enable"),!0;if(Notification.permission!=="granted"&&await Notification.requestPermission()!=="granted")return o("notify: blocked \u2014 allow in browser site settings to enable"),!0;let c=await gn();return await ys(),o(c?"notify: already enabled":"notify: enabled"),!0}if(r==="off")return await ws(),o("notify: disabled"),!0;let a=Notification.permission;return a==="denied"?o("notify: blocked \u2014 allow in browser site settings to enable"):a==="granted"&&await gn()?o("notify: enabled"):o("notify: off \u2014 use /notify on to enable"),!0}case"/log":{let r=i.toLowerCase().trim();return r===""?(o(`log: ${Ne()} (use /log <off|debug|info|warn|error>)`),!0):["off","debug","info","warn","error"].includes(r)?(z(r),o(`log: ${r}`),r!=="off"&&M.info("log enabled",{level:r}),!0):(o(`err: invalid level '${r}' (use off|debug|info|warn|error)`),!0)}default:return!1}}var ge=null,De=Q,te,G=[],Y=-1,le=null;function ht(){let e=l.input.value;if(le!==null){if(e===le)return;le=null}if(!e.startsWith("/")){O();return}let{node:t,pathPrefix:s,tailQuery:i}=Qe(e,Q);if(s!==ge)if(ge=s,De=t,t.fetch&&t.toSpec){let r=t.fetch();if(r instanceof Promise){te="loading";let a=s;r.then(c=>{ge===a&&(te=c,ft(yn(),s))},c=>{if(ge!==a)return;te={error:c instanceof Error&&c.message?c.message:"fetch failed"},ft(yn(),s)})}else te=r}else te=void 0;else De=t;ft(i,s)}function yn(){let{tailQuery:e}=Qe(l.input.value,Q);return e}function ft(e,t){let s=Kt(De,e,te);if(G=s,s.length===0){O();return}let i=s.findIndex(r=>r.kind!=="separator"&&r.kind!=="placeholder");Y=i>=0?i:0,mt(t),l.slashMenu.classList.add("active")}function mt(e){l.slashMenu.innerHTML="",l.slashMenu.classList.toggle("slash-menu-root",e===""),G.forEach((s,i)=>{if(s.kind==="separator"){let c=document.createElement("div");c.className="slash-separator",c.dataset.idx=String(i),l.slashMenu.appendChild(c);return}let r=i===Y,a=Gt(s.spec,r,s.prefix);s.prefix==="\u203A"&&a.classList.add("slash-arrow"),s.kind==="placeholder"&&a.classList.add("slash-placeholder"),a.dataset.idx=String(i),l.slashMenu.appendChild(a)}),l.slashMenu.dataset.pathPrefix=e;let t=l.slashMenu.querySelector(".slash-item.selected");t&&t.scrollIntoView({block:"nearest"})}function O(){l.slashMenu.classList.remove("active"),l.slashMenu.innerHTML="",Y=-1,G=[],ge=null,De=Q,te=void 0,le=l.input.value}function bs(){let e=G[Y];if(!e)return;let t=l.slashMenu.dataset.pathPrefix??"";if(!(e.kind==="separator"||e.kind==="placeholder")){if(e.kind==="subcommand"&&e.node){let i=(e.node.children??[]).length>0||!!e.node.fetch||!!e.node.freeform;N(`${t}${t?" ":""}${e.node.name}${i?" ":""}`),i?(le=null,ht()):O()}else e.kind==="data"?(N(`${t}${t?" ":""}${e.spec.primary}`),O()):e.kind==="freeform"&&O();l.input.focus()}}async function Ss(e){let t=G[e];if(!t)return;let s=l.slashMenu.dataset.pathPrefix??"";if(!(t.kind==="separator"||t.kind==="placeholder")){if(t.kind==="subcommand"&&t.node){if((t.node.children??[]).length>0||!!t.node.fetch||!!t.node.freeform){N(`${s}${s?" ":""}${t.node.name} `),le=null,ht(),l.input.focus();return}O(),N(""),t.node.onSelect&&await t.node.onSelect();return}O(),N(""),t.spec.onSelect?await t.spec.onSelect():o("Read-only entry \u2014 no action.")}}function vn(e){return!l.slashMenu.classList.contains("active")||G.length===0?!1:e.key==="ArrowDown"?(Y=wn(Y,1),mt(l.slashMenu.dataset.pathPrefix??""),!0):e.key==="ArrowUp"?(Y=wn(Y,-1),mt(l.slashMenu.dataset.pathPrefix??""),!0):e.key==="Tab"?(bs(),!0):!1}function wn(e,t){let s=G.length;for(let i=1;i<=s;i++){let r=(e+t*i+s)%s;if(G[r].kind!=="separator"&&G[r].kind!=="placeholder")return r}return e}l.slashMenu.addEventListener("mousedown",e=>{e.preventDefault();let s=e.target?.closest(".slash-item");s?.dataset.idx!==void 0&&Ss(Number(s.dataset.idx))});l.input.addEventListener("input",()=>{ht(),l.inputArea.classList.toggle("bash-mode",l.input.value.startsWith("!"))});function ks(e){return new Promise(t=>{let s=new FileReader;s.onload=()=>{t(s.result)},s.readAsDataURL(e)})}async function bn(e){let t=e.type.startsWith("image/"),s={kind:t?"image":"file",file:e,mimeType:e.type||"application/octet-stream",name:e.name||(t?"image":"file")};return t&&(s.previewUrl=await ks(e)),s}function Sn(e){n.pendingAttachments.push(e),Fe(),l.input.focus()}function Fe(){if(l.attachPreview.innerHTML="",n.pendingAttachments.length===0){l.attachPreview.classList.remove("active");return}l.attachPreview.classList.add("active"),n.pendingAttachments.forEach((e,t)=>{let s=document.createElement("span");if(s.className="attach-thumb",e.kind==="image"&&e.previewUrl){let r=document.createElement("img");r.src=e.previewUrl,s.appendChild(r)}else{s.classList.add("attach-file");let r=document.createElement("span");r.className="attach-file-name",r.textContent=e.name,s.appendChild(r)}let i=document.createElement("button");i.className="remove",i.textContent="\xD7",i.addEventListener("click",()=>{n.pendingAttachments.splice(t,1),Fe()}),s.appendChild(i),l.attachPreview.appendChild(s)})}l.fileInput.onchange=async()=>{for(let e of l.fileInput.files)Sn(await bn(e));l.fileInput.value=""};l.input.addEventListener("paste",e=>{let t=e.clipboardData?.items;if(t)for(let s of t)s.type.startsWith("image/")&&(e.preventDefault(),(async()=>{let i=s.getAsFile();i&&Sn(await bn(i))})())});var ce={send:()=>{},cancel:()=>{},attach:()=>{},publish:()=>{},cancelPreview:()=>{}};function Tn(e){ce=e}function kn(){let e=l.input.value.trim();return e.startsWith("/")||e.startsWith("!")||e==="?"||e.startsWith("? ")}function Is(){if(n.previewToken)return[{label:"^P",title:"Publish preview (Ctrl+P)",className:"publish",onClick:()=>{ce.publish()}},{label:"^C",title:"Cancel preview (Ctrl+C)",className:"cancel",onClick:()=>{ce.cancelPreview()}}];let e={label:"^U",title:"Attach file (Ctrl+U)",onClick:()=>{ce.attach()}},t=()=>{n.busy&&!kn()?ce.cancel():ce.send()};return n.busy&&!kn()?[e,{label:"^C",title:"Cancel (Ctrl+C)",className:"cancel",onClick:t}]:[e,{label:"\u21B5",title:"Send (Enter)",onClick:t}]}var Ts=["cancel","publish"];function In(e,t){e.textContent=t.label,e.title=t.title,e.disabled=t.disabled??!1,e.onclick=t.onClick;for(let s of Ts)e.classList.remove(s);t.className&&e.classList.add(t.className)}function Es(){let[e,t]=Is();In(l.attachBtn,e),In(l.sendBtn,t),l.input.disabled=!!n.previewToken}Ut(Es);function gt(){return n.clientId!==null}n._onCancelTimeout=()=>o("warn: Agent not responding to cancel");function Cn(){let e=l.input.value.trim();if(!e&&n.pendingAttachments.length===0)return;if((e.startsWith("/")||e==="?"||e.startsWith("? "))&&n.pendingAttachments.length===0){N(""),l.input.style.height="auto",pt(e);return}if(e.startsWith("!")&&n.pendingAttachments.length===0){let r=e.slice(1).trim();if(!r)return;if(!n.sessionId){o("warn: Session not ready yet, please wait\u2026");return}if(!gt()){o("warn: Not connected, please retry");return}N(""),l.input.style.height="auto",l.inputArea.classList.remove("bash-mode"),Wt(r,!0),n.sentBashForSession=n.sessionId,Mt(n.sessionId,r).catch(()=>{}),y(!0);return}if(n.busy)return;if(N(""),l.input.style.height="auto",l.inputArea.classList.remove("bash-mode"),!n.sessionId){o("warn: Session not ready yet, please wait\u2026");return}if(!gt()){o("warn: Not connected, please retry");return}let t=$e("user",e||"(attachment)"),s=[];for(let r of n.pendingAttachments)if(r.kind==="image"&&r.previewUrl){let a=document.createElement("img");a.className="user-image",a.src=r.previewUrl,a.alt=r.name,t.appendChild(a),s.push(null)}else{let a=document.createElement("div");a.className="user-attachment",a.textContent=`[${r.kind}: ${r.name}]`,t.appendChild(a),s.push(a)}let i=n.pendingAttachments.slice();n.pendingAttachments.length=0,Fe(),i.length>0?Promise.all(i.map(r=>{let a=new FormData;return a.append("file",r.file,r.name),fetch(`/api/v1/sessions/${n.sessionId}/attachments`,{method:"POST",body:a}).then(c=>c.json()).then(c=>({kind:c.kind,attachmentId:c.attachmentId,displayName:c.displayName,mimeType:c.mimeType,url:c.url}))})).then(r=>{if(!gt()){t.remove(),o("warn: Not connected, please retry"),y(!1);return}for(let c=0;c<r.length;c++){let d=s[c];if(!d)continue;let u=r[c];if(u.kind==="image"||!u.url)continue;let p=document.createElement("a");p.className="user-file",p.href=u.url,p.target="_blank",p.rel="noopener",p.download=u.displayName,p.textContent=u.displayName,d.replaceWith(p)}let a=r.map(({kind:c,attachmentId:d,displayName:u,mimeType:p})=>({kind:c,attachmentId:d,displayName:u,mimeType:p}));We(n.sessionId,e||"What is in this attachment?",a).catch(En)}):We(n.sessionId,e).catch(En),n.turnEnded=!1,n.sentMessageForSession=n.sessionId,y(!0),jt()}function En(e){y(!1),j();let t=e instanceof Error?e.message:String(e);o(`err: ${t}`)}function Ln(){H()&&o("^C")}Tn({send:Cn,cancel:Ln,attach:()=>{l.fileInput.click()},publish:()=>{at()},cancelPreview:lt});re();l.input.addEventListener("keydown",e=>{if(vn(e)){e.preventDefault();return}if(e.key==="Enter"&&!e.shiftKey){e.preventDefault(),O(),Cn();return}if(e.key==="u"&&(e.ctrlKey||e.metaKey)&&!e.shiftKey){e.preventDefault(),l.fileInput.click();return}});document.addEventListener("keydown",e=>{if(e.key==="c"&&(e.ctrlKey||e.metaKey)&&!e.shiftKey&&!(!!window.getSelection()?.toString()||l.input.selectionStart!==l.input.selectionEnd)){if(n.previewToken){e.preventDefault(),lt();return}if(n.busy){e.preventDefault(),Ln();return}}e.key==="Escape"&&l.slashMenu.classList.contains("active")&&(e.preventDefault(),O(),l.input.focus())});var xn=0;function Pn(){let e=A("mode");if(!e?.options.length){Date.now()-xn>3e3&&(o("Mode switcher temporarily unavailable. Try `/new` to start a fresh session."),xn=Date.now());return}let t=e.options.findIndex(i=>i.value===e.currentValue),s=e.options[(t+1)%e.options.length];e.currentValue=s.value,se(n.sessionId,"mode",s.value).catch(()=>{}),o(`Mode \u2192 ${s.name}`),I()}document.addEventListener("keydown",e=>{e.key==="m"&&(e.ctrlKey||e.metaKey)&&!e.shiftKey&&(e.preventDefault(),Pn())});document.addEventListener("keydown",e=>{n.previewToken&&(!(e.ctrlKey||e.metaKey)||e.shiftKey||e.key==="p"&&(e.preventDefault(),at()))});l.prompt.addEventListener("click",Pn);l.input.addEventListener("input",re);l.input.addEventListener("input",()=>{l.input.style.height="auto",l.input.style.height=Math.min(l.input.scrollHeight,200)+"px"});async function xs(e){try{let s=await(await navigator.serviceWorker.ready).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 Ue(){Z("connecting","connecting"),Cs()}async function Cs(){let e;try{e=(await _t()).ticket}catch{setTimeout(Ue,3e3);return}let t=new EventSource(`/api/v1/events/stream?ticket=${encodeURIComponent(e)}`);n.eventSource=t,t.onmessage=s=>{let i=JSON.parse(s.data);i.type==="connected"&&i.clientId&&(n.clientId=i.clientId,Re(i.debugLevel),ie(i.clientId,!document.hidden,n.sessionId??void 0),xs(i.clientId),!i.agent)||x(i)},t.onerror=()=>{t.close(),Ps(),setTimeout(Ue,3e3)},t.addEventListener("heartbeat",()=>{n.clientId&&(document.hidden||ie(n.clientId,!0,n.sessionId??void 0))}),Ls()}async function Ls(){Z("connecting","session loading");let e=n.sessionSwitchGen,t=Vt();if(t&&t===n.sessionId&&n.lastEventSeq>0){if(await yt(t,!0,e),e!==n.sessionSwitchGen)return;b(!1);return}if(t){if(v(),await yt(t,!1,e),e!==n.sessionSwitchGen)return;b(!0);return}try{let s=await ke();if(e!==n.sessionSwitchGen)return;if(s.length>0){if(v(),await yt(s[0].id,!1,e),e!==n.sessionSwitchGen)return;b(!0);return}}catch{}e===n.sessionSwitchGen&&U()}async function yt(e,t,s){if(t){try{let i=await P(e);if(s!==n.sessionSwitchGen)return;x({type:"session_created",sessionId:i.id,cwd:i.cwd,title:i.title,configOptions:i.configOptions})}catch{if(s!==n.sessionSwitchGen)return;await W(e,n.sessionCwd??void 0);return}if(s!==n.sessionSwitchGen)return;await Promise.all([q(e),it(e)])}else{n.sessionId=null;let i=K(e),r=q(e),a;try{let[c,d]=await Promise.all([P(e),i]);if(await r,s!==n.sessionSwitchGen)return;a=c,d||o("warn: Failed to load history.")}catch{if(s!==n.sessionSwitchGen)return;await W(e,n.sessionCwd??void 0);return}x({type:"session_created",sessionId:a.id,cwd:a.cwd,title:a.title,configOptions:a.configOptions})}}function Ps(){Z("disconnected","disconnected"),n.eventSource=null,n.clientId=null,$(),V(),n.currentBashEl&&Me(n.currentBashEl,null,"disconnected"),n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,n.turnEnded=!1,oe(),y(!1)}function An(e,t){let s=`/api/beta/clients/${encodeURIComponent(e)}/visibility`,i=JSON.stringify({visible:!1,sessionId:t});try{fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},body:i,keepalive:!0}).catch(()=>{})}catch{}}document.addEventListener("visibilitychange",()=>{if(n.clientId&&(document.hidden?An(n.clientId,n.sessionId??null):ie(n.clientId,!0,n.sessionId??void 0)),!document.hidden&&n.sessionId&&n.lastEventSeq>0&&!n.replayInProgress){let e=n.sessionId;Promise.all([q(e),it(e)]).then(()=>{b(!1)})}});window.addEventListener("pagehide",()=>{n.clientId&&An(n.clientId,n.sessionId??null)});if(!localStorage.getItem(B))throw location.replace("/login"),new Error("redirecting to /login");It();Yt(o);l.messages.addEventListener("click",wt);Ve(vt);Ue();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=typeof t.name=="string"?t.name:null,n.agentVersion=typeof t.version=="string"?t.version:null)}).catch(()=>{});"serviceWorker"in navigator&&(navigator.serviceWorker.register("/sw.js"),navigator.serviceWorker.addEventListener("message",e=>{let t=e.data;if(t?.type==="navigate"&&t.sessionId){let s=t.sessionId;if(n.sessionId===s)return;n.sessionSwitchGen++;let i=n.sessionSwitchGen;history.replaceState(null,"",`#${s}`),v(),n.sessionId=null,o("Switching\u2026"),Promise.all([P(s),K(s)]).then(([r,a])=>{i===n.sessionSwitchGen&&(x({type:"session_created",sessionId:r.id,cwd:r.cwd,title:r.title,configOptions:r.configOptions}),a&&b(!0))}).catch(()=>{v(),n.sessionId=null,o("err: Failed to switch session")})}}));
|