@jyork0828/pi-pilot 0.0.2 → 0.0.5
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/dist/index.js +641 -165
- package/dist/index.js.map +1 -1
- package/package.json +14 -11
- package/public/assets/c-BIGW1oBm.js +1 -0
- package/public/assets/cpp-DIPi6g--.js +1 -0
- package/public/assets/csharp-DSvCPggb.js +1 -0
- package/public/assets/css-CLj8gQPS.js +1 -0
- package/public/assets/dart-bE4Kk8sk.js +1 -0
- package/public/assets/diff-D97Zzqfu.js +1 -0
- package/public/assets/dockerfile-BcOcwvcX.js +1 -0
- package/public/assets/elixir-CkH2-t6x.js +1 -0
- package/public/assets/github-dark-DHJKELXO.js +1 -0
- package/public/assets/github-light-DAi9KRSo.js +1 -0
- package/public/assets/go-C27-OAKa.js +1 -0
- package/public/assets/graphql-ChdNCCLP.js +1 -0
- package/public/assets/haskell-Df6bDoY_.js +1 -0
- package/public/assets/html-pp8916En.js +1 -0
- package/public/assets/index-CBa7EReb.js +411 -0
- package/public/assets/index-DeSNeuE1.css +1 -0
- package/public/assets/ini-BEwlwnbL.js +1 -0
- package/public/assets/java-CylS5w8V.js +1 -0
- package/public/assets/javascript-wDzz0qaB.js +1 -0
- package/public/assets/json-Cp-IABpG.js +1 -0
- package/public/assets/jsonc-Des-eS-w.js +1 -0
- package/public/assets/jsx-g9-lgVsj.js +1 -0
- package/public/assets/kotlin-BdnUsdx6.js +1 -0
- package/public/assets/less-B1dDrJ26.js +1 -0
- package/public/assets/lua-BaeVxFsk.js +1 -0
- package/public/assets/make-CHLpvVh8.js +1 -0
- package/public/assets/markdown-Cvjx9yec.js +1 -0
- package/public/assets/mdx-Cmh6b_Ma.js +1 -0
- package/public/assets/objective-c-DXmwc3jG.js +1 -0
- package/public/assets/php-Csjmro_R.js +1 -0
- package/public/assets/python-B6aJPvgy.js +1 -0
- package/public/assets/r-Dspwwk_N.js +1 -0
- package/public/assets/ruby-CV7NnX5q.js +1 -0
- package/public/assets/rust-B1yitclQ.js +1 -0
- package/public/assets/scala-C151Ov-r.js +1 -0
- package/public/assets/scss-D5BDwBP9.js +1 -0
- package/public/assets/shellscript-Yzrsuije.js +1 -0
- package/public/assets/sql-CRqJ_cUM.js +1 -0
- package/public/assets/svelte-DR4MIrkg.js +1 -0
- package/public/assets/swift-D82vCrfD.js +1 -0
- package/public/assets/toml-vGWfd6FD.js +1 -0
- package/public/assets/tsx-COt5Ahok.js +1 -0
- package/public/assets/typescript-BPQ3VLAy.js +1 -0
- package/public/assets/vue-DMJtu8ND.js +1 -0
- package/public/assets/xml-sdJ4AIDG.js +1 -0
- package/public/assets/yaml-Buea-lGh.js +1 -0
- package/public/favicon.svg +15 -0
- package/public/index.html +12 -3
- package/public/assets/index-Cpw6tfat.css +0 -1
- package/public/assets/index-Dw_Ppxym.js +0 -223
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/api/workspaces.ts","../src/storage/resource-writer.ts","../src/storage/workspace-registry.ts","../src/storage/workspace-stats.ts","../src/workspace-manager.ts","../src/extensions/plan/schema.ts","../src/extensions/plan/factory.ts","../src/extensions/index.ts","../src/ws/extension-ui.ts","../src/api/config.ts","../src/api/files.ts","../src/api/resources.ts","../src/api/fs.ts","../src/api/model-configs.ts","../src/ws/hub.ts","../src/ws/bridge.ts"],"sourcesContent":["/**\n * pi-pilot server entry.\n *\n * Boots one process that serves both HTTP (Hono) and WebSocket (`ws`) on\n * the same port. Localhost only.\n */\n\nimport type { Server as HttpServer } from \"node:http\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, extname, join, resolve, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { serve } from \"@hono/node-server\";\nimport type { Context } from \"hono\";\nimport { Hono } from \"hono\";\nimport { cors } from \"hono/cors\";\nimport { config } from \"./config.ts\";\nimport { workspacesRoute } from \"./api/workspaces.ts\";\nimport { fsRoute } from \"./api/fs.ts\";\nimport { modelConfigsRoute } from \"./api/model-configs.ts\";\nimport { attachWsHub } from \"./ws/hub.ts\";\nimport { workspaceManager } from \"./workspace-manager.ts\";\n\nconst app = new Hono();\nconst distDir = dirname(fileURLToPath(import.meta.url));\nconst webRoot = resolve(process.env.PI_PILOT_WEB_ROOT ?? join(distDir, \"..\", \"public\"));\nconst webIndexPath = join(webRoot, \"index.html\");\n\nconst mimeTypes: Record<string, string> = {\n\t\".css\": \"text/css; charset=utf-8\",\n\t\".html\": \"text/html; charset=utf-8\",\n\t\".ico\": \"image/x-icon\",\n\t\".js\": \"text/javascript; charset=utf-8\",\n\t\".json\": \"application/json; charset=utf-8\",\n\t\".map\": \"application/json; charset=utf-8\",\n\t\".png\": \"image/png\",\n\t\".svg\": \"image/svg+xml\",\n\t\".txt\": \"text/plain; charset=utf-8\",\n\t\".webp\": \"image/webp\",\n\t\".woff\": \"font/woff\",\n\t\".woff2\": \"font/woff2\",\n};\n\nfunction isApiOrWsPath(pathname: string): boolean {\n\treturn pathname === \"/api\" || pathname.startsWith(\"/api/\") || pathname === \"/ws\";\n}\n\nfunction safeResolveWebPath(pathname: string): string | undefined {\n\tlet decoded: string;\n\ttry {\n\t\tdecoded = decodeURIComponent(pathname);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tconst relativePath = decoded === \"/\" ? \"index.html\" : decoded.replace(/^\\/+/, \"\");\n\tconst candidate = resolve(webRoot, relativePath);\n\tif (candidate !== webRoot && !candidate.startsWith(`${webRoot}${sep}`)) {\n\t\treturn undefined;\n\t}\n\treturn candidate;\n}\n\nasync function readWebFile(path: string): Promise<Buffer | undefined> {\n\ttry {\n\t\treturn await readFile(path);\n\t} catch (err) {\n\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\tif (code === \"ENOENT\" || code === \"EISDIR\") return undefined;\n\t\tthrow err;\n\t}\n}\n\nasync function serveWeb(c: Context): Promise<Response> {\n\tconst pathname = new URL(c.req.url).pathname;\n\tif (isApiOrWsPath(pathname)) return c.notFound();\n\n\tconst assetPath = safeResolveWebPath(pathname);\n\tif (!assetPath) return c.text(\"invalid asset path\", 400);\n\n\tconst asset = await readWebFile(assetPath);\n\tconst body = asset ?? (await readFile(webIndexPath));\n\tconst filePath = asset ? assetPath : webIndexPath;\n\tconst headers: Record<string, string> = {\n\t\t\"Content-Type\": mimeTypes[extname(filePath)] ?? \"application/octet-stream\",\n\t\t\"Cache-Control\": asset && pathname.startsWith(\"/assets/\")\n\t\t\t? \"public, max-age=31536000, immutable\"\n\t\t\t: \"no-cache\",\n\t};\n\n\treturn new Response(body as unknown as BodyInit, { headers });\n}\n\napp.use(\n\t\"/api/*\",\n\tcors({\n\t\torigin: config.corsOrigin,\n\t\tallowMethods: [\"GET\", \"POST\", \"PUT\", \"DELETE\"],\n\t}),\n);\n\napp.get(\"/api/health\", (c) => c.json({ ok: true }));\napp.route(\"/api/workspaces\", workspacesRoute);\napp.route(\"/api/fs\", fsRoute);\napp.route(\"/api/model-configs\", modelConfigsRoute);\n\nif (existsSync(webIndexPath)) {\n\tapp.get(\"*\", serveWeb);\n} else {\n\tapp.get(\"/\", (c) =>\n\t\tc.text(\n\t\t\t\"pi-pilot server is running, but the web UI assets were not found. \" +\n\t\t\t\t\"Run `pnpm build` from the repository root, or set PI_PILOT_WEB_ROOT to a built web dist directory.\",\n\t\t\t500,\n\t\t),\n\t);\n}\n\nconst server = serve(\n\t{\n\t\tfetch: app.fetch,\n\t\thostname: config.host,\n\t\tport: config.port,\n\t},\n\t(info) => {\n\t\tconsole.log(`[pi-pilot] http://${info.address}:${info.port}`);\n\t},\n);\n\nattachWsHub(server as unknown as HttpServer);\n\nasync function shutdown(reason: string): Promise<void> {\n\tconsole.log(`[pi-pilot] shutting down (${reason})`);\n\ttry {\n\t\tawait workspaceManager.disposeAll();\n\t} catch (e) {\n\t\tconsole.error(\"[pi-pilot] disposeAll error:\", e);\n\t}\n\tserver.close(() => process.exit(0));\n\t// Hard-kill if close hangs.\n\tsetTimeout(() => process.exit(1), 3000).unref();\n}\n\nprocess.on(\"SIGINT\", () => void shutdown(\"SIGINT\"));\nprocess.on(\"SIGTERM\", () => void shutdown(\"SIGTERM\"));\n","/**\n * Process-wide config, sourced from env vars.\n *\n * We do not read any file at startup beyond what pi's SDK does internally.\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const config = {\n\t/** HTTP + WS port. */\n\tport: Number(process.env.PI_PILOT_PORT ?? 5174),\n\n\t/** Bind address. Hard-coded localhost; do NOT make this configurable\n\t * without first reviewing the security implications of exposing bash. */\n\thost: \"127.0.0.1\",\n\n\t/** Where pi-pilot stores its own (non-pi) state. */\n\tdataDir: process.env.PI_PILOT_DATA_DIR ?? join(homedir(), \".pi\", \"webui\"),\n\n\t/** Dev origin allowed by CORS for /api. The Vite dev server. */\n\tcorsOrigin: process.env.PI_PILOT_CORS_ORIGIN ?? \"http://localhost:5173\",\n} as const;\n","/**\n * /api/workspaces routes.\n */\n\nimport { stat } from \"node:fs/promises\";\nimport { basename, isAbsolute, resolve } from \"node:path\";\nimport { Hono } from \"hono\";\nimport type {\n\tAddWorkspaceRequest,\n\tAddWorkspaceResponse,\n\tHistoryResponse,\n\tListForkPointsResponse,\n\tListSessionsResponse,\n\tListWorkspacesResponse,\n\tOkResponse,\n} from \"@pi-pilot/shared\";\nimport { HttpError } from \"../storage/resource-writer.ts\";\nimport {\n\taddWorkspace,\n\tgetWorkspace,\n\tlistWorkspaces,\n\tremoveWorkspace,\n} from \"../storage/workspace-registry.ts\";\nimport { enrichWorkspace } from \"../storage/workspace-stats.ts\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport { mountConfigRoutes } from \"./config.ts\";\nimport { mountFilesRoute } from \"./files.ts\";\nimport { mountResourcesRoute } from \"./resources.ts\";\n\nexport const workspacesRoute = new Hono();\n\nworkspacesRoute.get(\"/\", async (c) => {\n\tconst raw = await listWorkspaces();\n\tconst workspaces = await Promise.all(raw.map(enrichWorkspace));\n\tconst body: ListWorkspacesResponse = { workspaces };\n\treturn c.json(body);\n});\n\nworkspacesRoute.get(\"/:id/sessions\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tconst sessions = await workspaceManager.listSessions(id);\n\t\tconst body: ListSessionsResponse = { sessions };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] list sessions for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.delete(\"/:id/sessions\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\tconst sessionPath = c.req.query(\"path\");\n\tif (!sessionPath) {\n\t\treturn c.json({ ok: false, error: \"path query is required\" }, 400);\n\t}\n\n\ttry {\n\t\tawait workspaceManager.deleteSession(id, sessionPath);\n\t\tconst body: OkResponse = { ok: true };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tif (err instanceof HttpError) {\n\t\t\treturn c.json(\n\t\t\t\t{ ok: false, error: err.message },\n\t\t\t\terr.status as 400 | 404 | 409 | 500,\n\t\t\t);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] delete session for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.get(\"/:id/fork-points\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tconst runtime = await workspaceManager.getOrCreate(id);\n\t\tconst raw = runtime.session.getUserMessagesForForking();\n\t\tconst points = raw.map((p) => ({\n\t\t\tentryId: p.entryId,\n\t\t\ttext: p.text.length > 120 ? p.text.slice(0, 120) + \"…\" : p.text,\n\t\t}));\n\t\tconst body: ListForkPointsResponse = { points };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] fork-points for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.get(\"/:id/history\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tawait workspaceManager.getOrCreate(id);\n\t\tconst sessionPath = c.req.query(\"sessionPath\");\n\t\tconst body: HistoryResponse = workspaceManager.getSessionHistory(id, sessionPath);\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] history for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.post(\"/\", async (c) => {\n\tconst body = (await c.req.json()) as AddWorkspaceRequest;\n\tif (!body?.path || typeof body.path !== \"string\") {\n\t\treturn c.json({ ok: false, error: \"path is required\" }, 400);\n\t}\n\tif (!isAbsolute(body.path)) {\n\t\treturn c.json({ ok: false, error: \"path must be absolute\" }, 400);\n\t}\n\tconst resolved = resolve(body.path);\n\ttry {\n\t\tconst st = await stat(resolved);\n\t\tif (!st.isDirectory()) {\n\t\t\treturn c.json({ ok: false, error: \"path is not a directory\" }, 400);\n\t\t}\n\t} catch {\n\t\treturn c.json({ ok: false, error: \"path does not exist\" }, 400);\n\t}\n\n\tconst stored = await addWorkspace({\n\t\tpath: resolved,\n\t\tname: body.name?.trim() || basename(resolved) || resolved,\n\t});\n\tconst ws = await enrichWorkspace(stored);\n\tconst res: AddWorkspaceResponse = { workspace: ws };\n\treturn c.json(res);\n});\n\nworkspacesRoute.delete(\"/:id\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t// Dispose the runtime if it was open. Best-effort.\n\tawait workspaceManager.dispose(id);\n\tawait removeWorkspace(id);\n\n\tconst body: OkResponse = { ok: true };\n\treturn c.json(body);\n});\n\nmountConfigRoutes(workspacesRoute);\nmountResourcesRoute(workspacesRoute);\nmountFilesRoute(workspacesRoute);\n","/**\n * Filesystem CRUD for skills and prompt templates.\n *\n * pi's ResourceLoader is read-only from our perspective (`reload()` re-reads\n * from disk but doesn't write). So pi-pilot writes the files itself and then\n * asks the loader to re-scan.\n *\n * Layout (matches pi's discovery rules — see docs/skills.md, docs/prompt-templates.md):\n *\n * skills:\n * user → <agentDir>/skills/<name>/SKILL.md\n * project → <workspaceCwd>/.pi/skills/<name>/SKILL.md\n *\n * prompts:\n * user → <agentDir>/prompts/<name>.md\n * project → <workspaceCwd>/.pi/prompts/<name>.md\n *\n * Safety\n * - `name` is restricted to lowercase a-z / 0-9 / hyphens (skills) and\n * additionally underscores (prompts) so it cannot escape its parent dir.\n * - `assertUnder(path, allowedRoots)` double-checks the resolved file path\n * is actually inside one of the allowed roots before any write/unlink.\n * - We never delete a directory that contains anything other than known\n * skill content via this module \\u2014 see `deleteSkill` for the precise rule.\n *\n * Frontmatter handling\n * - Known fields (name, description, disable-model-invocation /\n * argument-hint) are parsed into typed slots and exposed to the\n * editor as discrete form inputs.\n * - Unknown keys (`allowed-tools`, `metadata`, `license`, \\u2026) are\n * captured as an opaque list of raw `key: value` lines and round-\n * tripped on save. The editor never displays or rewrites them; the\n * write path concatenates them back below the known fields in the\n * order they appeared in the source. This means custom YAML survives\n * edits, but multi-line scalars / anchors / nested maps still won't\n * because the parser is line-oriented (acceptable: pi's documented\n * skill/prompt fields are all single-line scalars).\n */\n\nimport {\n\tmkdir,\n\treadFile,\n\trm,\n\tstat,\n\tunlink,\n\twriteFile,\n} from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, resolve, sep } from \"node:path\";\nimport type { ResourceScope } from \"@pi-pilot/shared\";\n\n// ---------- path helpers ----------\n\nconst SKILL_NAME_RE = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;\nconst PROMPT_NAME_RE = /^[a-z0-9_](?:[a-z0-9_-]{0,62}[a-z0-9_])?$/;\n\nfunction ensureSkillName(name: string): void {\n\tif (!SKILL_NAME_RE.test(name)) {\n\t\tthrow new HttpError(400, \"skill name must be lowercase a-z/0-9/hyphens, 1-64 chars, no leading/trailing/double hyphens\");\n\t}\n}\n\nfunction ensurePromptName(name: string): void {\n\tif (!PROMPT_NAME_RE.test(name)) {\n\t\tthrow new HttpError(400, \"prompt name must be lowercase a-z/0-9/hyphens/underscores, 1-64 chars\");\n\t}\n}\n\n/**\n * Throw if `target` does not resolve to a descendant of any path in `roots`.\n * Prevents `../../etc/passwd` style escapes even though the name regex above\n * already forbids slashes \\u2014 belt + suspenders.\n */\nfunction assertUnder(target: string, roots: string[]): void {\n\tconst t = resolve(target);\n\tconst ok = roots.some((root) => {\n\t\tconst r = resolve(root);\n\t\treturn t === r || t.startsWith(r + sep);\n\t});\n\tif (!ok) {\n\t\tthrow new HttpError(500, `refusing to touch path outside managed roots: ${target}`);\n\t}\n}\n\nfunction skillDirFor(scope: ResourceScope, name: string, roots: ResourceRoots): string {\n\tensureSkillName(name);\n\tconst base = scope === \"user\" ? roots.userSkills : roots.projectSkills;\n\treturn join(base, name);\n}\n\nfunction promptFileFor(scope: ResourceScope, name: string, roots: ResourceRoots): string {\n\tensurePromptName(name);\n\tconst base = scope === \"user\" ? roots.userPrompts : roots.projectPrompts;\n\treturn join(base, `${name}.md`);\n}\n\nexport interface ResourceRoots {\n\t/** ~/.pi/agent/skills */\n\tuserSkills: string;\n\t/** <cwd>/.pi/skills */\n\tprojectSkills: string;\n\t/** ~/.pi/agent/prompts */\n\tuserPrompts: string;\n\t/** <cwd>/.pi/prompts */\n\tprojectPrompts: string;\n}\n\nexport function resolveResourceRoots(opts: {\n\tagentDir: string;\n\tworkspaceCwd: string;\n}): ResourceRoots {\n\treturn {\n\t\tuserSkills: join(opts.agentDir, \"skills\"),\n\t\tprojectSkills: join(opts.workspaceCwd, \".pi\", \"skills\"),\n\t\tuserPrompts: join(opts.agentDir, \"prompts\"),\n\t\tprojectPrompts: join(opts.workspaceCwd, \".pi\", \"prompts\"),\n\t};\n}\n\n/** Map an absolute file path to its scope, by checking which root it sits in. */\nexport function scopeFor(absPath: string, roots: ResourceRoots): ResourceScope | undefined {\n\tconst p = resolve(absPath);\n\tfor (const [root, scope] of [\n\t\t[roots.userSkills, \"user\"],\n\t\t[roots.projectSkills, \"project\"],\n\t\t[roots.userPrompts, \"user\"],\n\t\t[roots.projectPrompts, \"project\"],\n\t] as const) {\n\t\tconst r = resolve(root);\n\t\tif (p === r || p.startsWith(r + sep)) return scope;\n\t}\n\treturn undefined;\n}\n\n// ---------- frontmatter ----------\n\n/** A frontmatter line we recognized. `raw` is the original `key: value`\n * text, used to round-trip unknown keys verbatim. */\ninterface FrontmatterLine {\n\tkey: string;\n\tvalue: string | boolean;\n\traw: string;\n}\n\ninterface ParsedFile {\n\tfrontmatter: Record<string, string | boolean>;\n\t/** Every frontmatter line in source order, with the original raw text. */\n\tlines: FrontmatterLine[];\n\tbody: string;\n}\n\n/**\n * Tiny YAML-frontmatter reader. Recognises `key: value`, `key: \"value\"`,\n * and `key: true|false`. Quoted strings honour `\\\"` and `\\\\` escapes;\n * everything else is treated as a raw scalar (trimmed). This is enough for\n * pi's documented skill/prompt frontmatter fields. Files with multi-line\n * scalars, anchors, etc. won't round-trip cleanly through pi-pilot.\n */\nfunction parseFile(content: string): ParsedFile {\n\tconst rawLines = content.split(/\\r?\\n/);\n\tif (rawLines[0] !== \"---\") {\n\t\treturn { frontmatter: {}, lines: [], body: content };\n\t}\n\tconst fm: Record<string, string | boolean> = {};\n\tconst lines: FrontmatterLine[] = [];\n\tlet i = 1;\n\twhile (i < rawLines.length && rawLines[i] !== \"---\") {\n\t\tconst line = rawLines[i] ?? \"\";\n\t\tconst m = line.match(/^([A-Za-z][A-Za-z0-9_-]*)\\s*:\\s*(.*?)\\s*$/);\n\t\tif (m) {\n\t\t\tconst key = m[1]!;\n\t\t\tconst rawVal = m[2] ?? \"\";\n\t\t\tconst value = parseScalar(rawVal);\n\t\t\tfm[key] = value;\n\t\t\tlines.push({ key, value, raw: line });\n\t\t}\n\t\ti++;\n\t}\n\tif (i >= rawLines.length) {\n\t\t// No closing `---` \\u2014 treat the whole thing as body, no frontmatter.\n\t\treturn { frontmatter: {}, lines: [], body: content };\n\t}\n\tconst body = rawLines.slice(i + 1).join(\"\\n\");\n\treturn { frontmatter: fm, lines, body };\n}\n\n/**\n * Filter `lines` (every frontmatter entry as it appeared on disk) down\n * to the entries whose keys are *not* in `knownKeys`. Used to preserve\n * arbitrary frontmatter (`allowed-tools`, `metadata`, `license`, \\u2026) on\n * write.\n *\n * Returns the raw source text of each kept line (without the trailing\n * newline). The build path emits these verbatim so values keep their\n * original quoting style.\n */\nfunction extraLines(lines: FrontmatterLine[], knownKeys: readonly string[]): string[] {\n\tconst known = new Set(knownKeys);\n\treturn lines.filter((l) => !known.has(l.key)).map((l) => l.raw);\n}\n\nfunction parseScalar(raw: string): string | boolean {\n\tif (raw === \"true\") return true;\n\tif (raw === \"false\") return false;\n\tif (raw.startsWith('\"') && raw.endsWith('\"') && raw.length >= 2) {\n\t\treturn raw.slice(1, -1).replace(/\\\\([\"\\\\])/g, \"$1\");\n\t}\n\tif (raw.startsWith(\"'\") && raw.endsWith(\"'\") && raw.length >= 2) {\n\t\treturn raw.slice(1, -1).replace(/''/g, \"'\");\n\t}\n\treturn raw;\n}\n\n/**\n * Emit a frontmatter+body file. Field order is stable so re-saves produce\n * deterministic diffs:\n *\n * 1. Known fields in the caller-supplied order (skipping undefined / \"\").\n * 2. Extra raw lines (unknown frontmatter keys captured on read) appended\n * verbatim. They keep their original ordering relative to each other.\n *\n * Boolean/string known values only; arrays and nested maps round-trip via\n * the raw `extras` channel only.\n */\nfunction buildFile(\n\tfields: Array<[string, string | boolean | undefined]>,\n\tbody: string,\n\textras: string[] = [],\n): string {\n\tconst out: string[] = [\"---\"];\n\tfor (const [key, value] of fields) {\n\t\tif (value === undefined || value === \"\") continue;\n\t\tif (typeof value === \"boolean\") {\n\t\t\tout.push(`${key}: ${value ? \"true\" : \"false\"}`);\n\t\t} else {\n\t\t\tout.push(`${key}: ${formatString(value)}`);\n\t\t}\n\t}\n\tfor (const raw of extras) out.push(raw);\n\tout.push(\"---\");\n\tconst trimmedBody = body.replace(/\\s+$/, \"\");\n\tconst text = `${out.join(\"\\n\")}\\n${trimmedBody}\\n`;\n\treturn text;\n}\n\n/** Frontmatter keys we render as form inputs. Everything else is opaque\n * passthrough. Keep these arrays here so read and write agree. */\nconst SKILL_KNOWN_KEYS = [\"name\", \"description\", \"disable-model-invocation\"] as const;\nconst PROMPT_KNOWN_KEYS = [\"description\", \"argument-hint\"] as const;\n\nfunction formatString(value: string): string {\n\t// Always double-quote so embedded `:`, `#`, leading/trailing whitespace,\n\t// and the like don't break YAML parsing. Escape `\\` and `\"`.\n\tconst escaped = value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n\treturn `\"${escaped}\"`;\n}\n\n// ---------- skills ----------\n\nexport interface CreateSkillOpts {\n\troots: ResourceRoots;\n\tscope: ResourceScope;\n\tname: string;\n\tdescription: string;\n\tbody: string;\n\tdisableModelInvocation?: boolean;\n}\n\nexport async function createSkill(opts: CreateSkillOpts): Promise<string> {\n\tconst dir = skillDirFor(opts.scope, opts.name, opts.roots);\n\tassertUnder(dir, [opts.roots.userSkills, opts.roots.projectSkills]);\n\tif (await exists(dir)) {\n\t\tthrow new HttpError(409, `skill already exists at ${dir}`);\n\t}\n\tconst file = join(dir, \"SKILL.md\");\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"name\", opts.name],\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"disable-model-invocation\", opts.disableModelInvocation],\n\t\t],\n\t\topts.body,\n\t);\n\tawait mkdir(dir, { recursive: true });\n\tawait writeFile(file, text, \"utf8\");\n\treturn file;\n}\n\nexport interface UpdateSkillOpts {\n\troots: ResourceRoots;\n\tfilePath: string;\n\tname: string;\n\tdescription: string;\n\tbody: string;\n\tdisableModelInvocation?: boolean;\n}\n\nexport async function updateSkill(opts: UpdateSkillOpts): Promise<string> {\n\tif (!isAbsolute(opts.filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(opts.filePath, [opts.roots.userSkills, opts.roots.projectSkills]);\n\tif (!(await exists(opts.filePath))) {\n\t\tthrow new HttpError(404, `skill file not found: ${opts.filePath}`);\n\t}\n\t// Round-trip arbitrary frontmatter (`allowed-tools`, `metadata`, \\u2026)\n\t// from the existing file. Re-read each save so a side edit on disk\n\t// between open and save isn't clobbered.\n\tconst existing = await readFile(opts.filePath, \"utf8\");\n\tconst extras = extraLines(parseFile(existing).lines, SKILL_KNOWN_KEYS);\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"name\", opts.name],\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"disable-model-invocation\", opts.disableModelInvocation],\n\t\t],\n\t\topts.body,\n\t\textras,\n\t);\n\tawait writeFile(opts.filePath, text, \"utf8\");\n\treturn opts.filePath;\n}\n\nexport async function deleteSkill(filePath: string, roots: ResourceRoots): Promise<void> {\n\tif (!isAbsolute(filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(filePath, [roots.userSkills, roots.projectSkills]);\n\tif (!(await exists(filePath))) {\n\t\tthrow new HttpError(404, `skill file not found: ${filePath}`);\n\t}\n\t// A skill can be either a bare .md file (root-level under skills/) or a\n\t// SKILL.md inside its own directory. Match pi's discovery: if the parent\n\t// directory is itself the skills root, delete just the .md file;\n\t// otherwise delete the parent directory (skill + scripts + assets).\n\tconst dir = dirname(filePath);\n\tconst parentIsRoot =\n\t\tresolve(dir) === resolve(roots.userSkills) ||\n\t\tresolve(dir) === resolve(roots.projectSkills);\n\tif (parentIsRoot) {\n\t\tawait unlink(filePath);\n\t\treturn;\n\t}\n\tassertUnder(dir, [roots.userSkills, roots.projectSkills]);\n\tawait rm(dir, { recursive: true, force: true });\n}\n\n// ---------- prompts ----------\n\nexport interface CreatePromptOpts {\n\troots: ResourceRoots;\n\tscope: ResourceScope;\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tbody: string;\n}\n\nexport async function createPrompt(opts: CreatePromptOpts): Promise<string> {\n\tconst file = promptFileFor(opts.scope, opts.name, opts.roots);\n\tassertUnder(file, [opts.roots.userPrompts, opts.roots.projectPrompts]);\n\tif (await exists(file)) {\n\t\tthrow new HttpError(409, `prompt already exists at ${file}`);\n\t}\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"argument-hint\", opts.argumentHint],\n\t\t],\n\t\topts.body,\n\t);\n\tawait mkdir(dirname(file), { recursive: true });\n\tawait writeFile(file, text, \"utf8\");\n\treturn file;\n}\n\nexport interface UpdatePromptOpts {\n\troots: ResourceRoots;\n\tfilePath: string;\n\t/** Stem of the new filename. If different from the existing file's stem,\n\t * the file is renamed. */\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tbody: string;\n}\n\nexport async function updatePrompt(opts: UpdatePromptOpts): Promise<string> {\n\tif (!isAbsolute(opts.filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(opts.filePath, [opts.roots.userPrompts, opts.roots.projectPrompts]);\n\tif (!(await exists(opts.filePath))) {\n\t\tthrow new HttpError(404, `prompt file not found: ${opts.filePath}`);\n\t}\n\tensurePromptName(opts.name);\n\tconst dir = dirname(opts.filePath);\n\tconst newPath = join(dir, `${opts.name}.md`);\n\tassertUnder(newPath, [opts.roots.userPrompts, opts.roots.projectPrompts]);\n\t// Preserve arbitrary frontmatter from the existing file (re-read so a\n\t// concurrent on-disk edit doesn't get silently dropped).\n\tconst existing = await readFile(opts.filePath, \"utf8\");\n\tconst extras = extraLines(parseFile(existing).lines, PROMPT_KNOWN_KEYS);\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"argument-hint\", opts.argumentHint],\n\t\t],\n\t\topts.body,\n\t\textras,\n\t);\n\tif (resolve(newPath) !== resolve(opts.filePath)) {\n\t\tif (await exists(newPath)) {\n\t\t\tthrow new HttpError(409, `prompt already exists at ${newPath}`);\n\t\t}\n\t\t// Rename is NOT a single syscall here — we have to write the new\n\t\t// content under a different name and then drop the old file. If the\n\t\t// unlink fails (perms, file lock, cross-fs handle, etc.) we must roll\n\t\t// back the new file; otherwise `loader.reload()` would surface an\n\t\t// orphaned `/oldname` prompt the user never asked to keep.\n\t\tawait writeFile(newPath, text, \"utf8\");\n\t\ttry {\n\t\t\tawait unlink(opts.filePath);\n\t\t} catch (err) {\n\t\t\tawait unlink(newPath).catch(() => undefined);\n\t\t\tthrow err;\n\t\t}\n\t\treturn newPath;\n\t}\n\tawait writeFile(opts.filePath, text, \"utf8\");\n\treturn opts.filePath;\n}\n\nexport async function deletePrompt(filePath: string, roots: ResourceRoots): Promise<void> {\n\tif (!isAbsolute(filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(filePath, [roots.userPrompts, roots.projectPrompts]);\n\tif (!(await exists(filePath))) {\n\t\tthrow new HttpError(404, `prompt file not found: ${filePath}`);\n\t}\n\tawait unlink(filePath);\n}\n\n// ---------- reads (for editor pre-fill) ----------\n\nexport interface ReadResourceFileResult {\n\tbody: string;\n\tname: string;\n\tdescription: string;\n\tdisableModelInvocation?: boolean;\n\targumentHint?: string;\n}\n\nexport async function readSkillFile(filePath: string, roots: ResourceRoots): Promise<ReadResourceFileResult> {\n\tassertUnder(filePath, [roots.userSkills, roots.projectSkills]);\n\tconst text = await readFile(filePath, \"utf8\");\n\tconst { frontmatter, body } = parseFile(text);\n\treturn {\n\t\tbody,\n\t\tname: stringOr(frontmatter.name, \"\"),\n\t\tdescription: stringOr(frontmatter.description, \"\"),\n\t\tdisableModelInvocation: booleanOr(frontmatter[\"disable-model-invocation\"], undefined),\n\t};\n}\n\nexport async function readPromptFile(filePath: string, roots: ResourceRoots): Promise<ReadResourceFileResult> {\n\tassertUnder(filePath, [roots.userPrompts, roots.projectPrompts]);\n\tconst text = await readFile(filePath, \"utf8\");\n\tconst { frontmatter, body } = parseFile(text);\n\t// Prompt's \"name\" is the filename stem, not a frontmatter field.\n\tconst stem = basename(filePath).replace(/\\.md$/, \"\");\n\treturn {\n\t\tbody,\n\t\tname: stem,\n\t\tdescription: stringOr(frontmatter.description, \"\"),\n\t\targumentHint: stringOr(frontmatter[\"argument-hint\"], undefined),\n\t};\n}\n\nfunction basename(p: string): string {\n\tconst parts = p.split(sep);\n\treturn parts.at(-1) || p;\n}\n\nfunction stringOr<T>(value: unknown, fallback: T): string | T {\n\treturn typeof value === \"string\" ? value : fallback;\n}\n\nfunction booleanOr<T>(value: unknown, fallback: T): boolean | T {\n\treturn typeof value === \"boolean\" ? value : fallback;\n}\n\n// ---------- misc ----------\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Tagged error so the route layer can map to specific HTTP statuses without\n * losing the message. Anything else thrown becomes a 500.\n */\nexport class HttpError extends Error {\n\tconstructor(public readonly status: number, message: string) {\n\t\tsuper(message);\n\t}\n}\n","/**\n * Persistence for the list of workspaces the user has registered.\n *\n * pi itself doesn't need this — it's tied to a single cwd at a time. We need\n * it because the WebUI shows a multi-workspace picker.\n *\n * File format (~/.pi/webui/workspaces.json):\n * { \"workspaces\": [{ \"id\", \"name\", \"path\", \"addedAt\" }] }\n *\n * Storage holds only the *persistent* fields. Derived telemetry\n * (gitBranch, fileCount) is added by the API layer via `enrichWorkspace`\n * — see `workspace-stats.ts`. Keeping them out of storage avoids stale\n * branch names on disk when the user switches branches outside the app.\n */\n\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { config } from \"../config.ts\";\n\n/** Persistent shape on disk. API responses augment this with derived fields. */\nexport interface StoredWorkspace {\n\tid: string;\n\tname: string;\n\tpath: string;\n\t/** ISO-8601 */\n\taddedAt: string;\n}\n\nconst REGISTRY_PATH = join(config.dataDir, \"workspaces.json\");\n\ninterface RegistryFile {\n\tworkspaces: StoredWorkspace[];\n}\n\nlet cache: RegistryFile | undefined;\n\nasync function load(): Promise<RegistryFile> {\n\tif (cache) return cache;\n\ttry {\n\t\tconst raw = await readFile(REGISTRY_PATH, \"utf8\");\n\t\tcache = JSON.parse(raw) as RegistryFile;\n\t\tif (!Array.isArray(cache.workspaces)) cache = { workspaces: [] };\n\t} catch (err) {\n\t\tif ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\tcache = { workspaces: [] };\n\t\t} else {\n\t\t\tthrow err;\n\t\t}\n\t}\n\treturn cache;\n}\n\nasync function save(): Promise<void> {\n\tif (!cache) return;\n\tawait mkdir(dirname(REGISTRY_PATH), { recursive: true });\n\tawait writeFile(REGISTRY_PATH, JSON.stringify(cache, null, 2), \"utf8\");\n}\n\nexport async function listWorkspaces(): Promise<StoredWorkspace[]> {\n\tconst r = await load();\n\treturn [...r.workspaces];\n}\n\nexport async function getWorkspace(id: string): Promise<StoredWorkspace | undefined> {\n\tconst r = await load();\n\treturn r.workspaces.find((w) => w.id === id);\n}\n\nexport async function getWorkspaceByPath(path: string): Promise<StoredWorkspace | undefined> {\n\tconst r = await load();\n\treturn r.workspaces.find((w) => w.path === path);\n}\n\nexport async function addWorkspace(input: { path: string; name: string }): Promise<StoredWorkspace> {\n\tconst r = await load();\n\tconst existing = r.workspaces.find((w) => w.path === input.path);\n\tif (existing) return existing;\n\tconst ws: StoredWorkspace = {\n\t\tid: randomUUID(),\n\t\tname: input.name,\n\t\tpath: input.path,\n\t\taddedAt: new Date().toISOString(),\n\t};\n\tr.workspaces.push(ws);\n\tawait save();\n\treturn ws;\n}\n\nexport async function removeWorkspace(id: string): Promise<boolean> {\n\tconst r = await load();\n\tconst before = r.workspaces.length;\n\tr.workspaces = r.workspaces.filter((w) => w.id !== id);\n\tif (r.workspaces.length === before) return false;\n\tawait save();\n\treturn true;\n}\n","/**\n * Derive instrument-panel telemetry from a workspace directory.\n *\n * Two probes today:\n * - current git branch (`git rev-parse --abbrev-ref HEAD`)\n * - tracked-file count (`git ls-files --cached --others --exclude-standard`)\n *\n * Both probes fail silently — non-git directories, missing `git` binary, or\n * broken repos all collapse to `null`. The UI fades the slot when both are\n * null, so a failure looks identical to \"not implemented yet\" from the\n * operator's perspective.\n *\n * In-memory 30-second TTL cache keyed by absolute path so the list endpoint\n * doesn't fork two child processes per workspace on every refresh. The TTL is\n * a deliberate trade: branch changes made outside the app will surface\n * within 30 seconds, which is the same staleness operators tolerate from\n * sidebar listings elsewhere.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport type { Workspace } from \"@pi-pilot/shared\";\nimport type { StoredWorkspace } from \"./workspace-registry.ts\";\n\nconst exec = promisify(execFile);\n\ninterface StatsCacheEntry {\n\tgitBranch: string | null;\n\tfileCount: number | null;\n\texpiresAt: number;\n}\n\nconst CACHE_TTL_MS = 30_000;\nconst cache = new Map<string, StatsCacheEntry>();\n\n/**\n * In-flight probes per path. Prevents the herd: if `enrichWorkspace` is called\n * twice for the same path before the first probe completes, both await the\n * same promise rather than each spawning two child processes.\n */\nconst inflight = new Map<string, Promise<StatsCacheEntry>>();\n\n/** Wrap a `StoredWorkspace` with derived stats. Never throws — failed probes\n * surface as nulls. */\nexport async function enrichWorkspace(ws: StoredWorkspace): Promise<Workspace> {\n\tconst stats = await getStats(ws.path);\n\treturn {\n\t\tid: ws.id,\n\t\tname: ws.name,\n\t\tpath: ws.path,\n\t\taddedAt: ws.addedAt,\n\t\tgitBranch: stats.gitBranch,\n\t\tfileCount: stats.fileCount,\n\t};\n}\n\nasync function getStats(path: string): Promise<StatsCacheEntry> {\n\tconst now = Date.now();\n\tconst cached = cache.get(path);\n\tif (cached && cached.expiresAt > now) return cached;\n\n\tconst pending = inflight.get(path);\n\tif (pending) return pending;\n\n\tconst probe = probeStats(path)\n\t\t.then((stats) => {\n\t\t\tconst entry: StatsCacheEntry = {\n\t\t\t\t...stats,\n\t\t\t\texpiresAt: Date.now() + CACHE_TTL_MS,\n\t\t\t};\n\t\t\tcache.set(path, entry);\n\t\t\treturn entry;\n\t\t})\n\t\t.finally(() => {\n\t\t\tinflight.delete(path);\n\t\t});\n\n\tinflight.set(path, probe);\n\treturn probe;\n}\n\nasync function probeStats(\n\tpath: string,\n): Promise<{ gitBranch: string | null; fileCount: number | null }> {\n\t// Run both probes in parallel; either may fail independently.\n\tconst [branchResult, filesResult] = await Promise.allSettled([\n\t\trunGit(path, [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]),\n\t\trunGit(path, [\n\t\t\t\"ls-files\",\n\t\t\t\"--cached\",\n\t\t\t\"--others\",\n\t\t\t\"--exclude-standard\",\n\t\t]),\n\t]);\n\n\tlet gitBranch: string | null = null;\n\tif (branchResult.status === \"fulfilled\") {\n\t\tconst out = branchResult.value.trim();\n\t\t// `HEAD` means detached HEAD; show the shortened commit instead, but\n\t\t// keep it terse so it still fits the BRANCH slot.\n\t\tif (out && out !== \"HEAD\") {\n\t\t\tgitBranch = out;\n\t\t} else if (out === \"HEAD\") {\n\t\t\t// Try to grab the short SHA for the detached case.\n\t\t\ttry {\n\t\t\t\tconst sha = (await runGit(path, [\"rev-parse\", \"--short\", \"HEAD\"])).trim();\n\t\t\t\tgitBranch = sha ? `@${sha}` : null;\n\t\t\t} catch {\n\t\t\t\tgitBranch = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tlet fileCount: number | null = null;\n\tif (filesResult.status === \"fulfilled\") {\n\t\tconst out = filesResult.value;\n\t\tif (out) {\n\t\t\t// `git ls-files` prints one path per line. Count non-empty lines so\n\t\t\t// a single trailing newline doesn't add a phantom file.\n\t\t\tlet n = 0;\n\t\t\tfor (let i = 0; i < out.length; i++) {\n\t\t\t\tif (out.charCodeAt(i) === 0x0a) n++;\n\t\t\t}\n\t\t\t// Add 1 when the last line doesn't end in a newline (rare).\n\t\t\tif (out.length > 0 && out.charCodeAt(out.length - 1) !== 0x0a) n++;\n\t\t\tfileCount = n;\n\t\t} else {\n\t\t\tfileCount = 0;\n\t\t}\n\t}\n\n\treturn { gitBranch, fileCount };\n}\n\n/**\n * Run `git` with the given args in `cwd`. Resolves with stdout on exit code 0.\n * 2-second timeout — slow probes are treated as failures rather than blocking\n * the list endpoint. Output capped at 5MB so a pathologically huge repo can't\n * OOM the server.\n */\nasync function runGit(cwd: string, args: string[]): Promise<string> {\n\tconst { stdout } = await exec(\"git\", args, {\n\t\tcwd,\n\t\ttimeout: 2000,\n\t\tmaxBuffer: 5 * 1024 * 1024,\n\t\t// Don't inherit GIT_DIR / GIT_WORK_TREE from the server process —\n\t\t// could leak the server's own repo into a workspace probe.\n\t\tenv: { ...process.env, GIT_DIR: undefined, GIT_WORK_TREE: undefined },\n\t});\n\treturn stdout;\n}\n","/**\n * WorkspaceManager — lazy lifecycle of one AgentSessionRuntime per workspace.\n *\n * Invariants:\n * - At most one runtime per workspaceId at any time.\n * - The runtime's cwd matches the workspace's path.\n * - Callers must call `dispose(id)` (or `disposeAll()` on shutdown) — we\n * don't currently idle-evict.\n *\n * NOT thread-safe across concurrent `getOrCreate` calls for the same id;\n * Node is single-threaded so this is fine, but be aware if you ever add\n * worker_threads.\n */\n\nimport { unlink } from \"node:fs/promises\";\nimport { isAbsolute, resolve } from \"node:path\";\nimport type { WebSocket } from \"ws\";\nimport {\n\tAgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionRuntime,\n\tcreateAgentSessionServices,\n\tgetAgentDir,\n\tSessionManager,\n\ttype ExtensionError,\n\ttype SessionInfo,\n} from \"@earendil-works/pi-coding-agent\";\nimport { HttpError } from \"./storage/resource-writer.ts\";\nimport { getWorkspace } from \"./storage/workspace-registry.ts\";\nimport { builtinExtensionFactories } from \"./extensions/index.ts\";\nimport type {\n\tSessionSummary,\n\tHistoryItem,\n\tHistoryResponse,\n\tWsExtensionError,\n\tWsServerMessage,\n} from \"@pi-pilot/shared\";\nimport { ExtensionUIBridge } from \"./ws/extension-ui.ts\";\n\n/**\n * Two-tier extension stance:\n *\n * 1. Third-party / disk pi extensions are OFF by default. The pi\n * extension system is TUI-native (renderers, ANSI themes, modal\n * dialogs, keyboard overlays) and cannot be bridged to the Web.\n * `EXTENSIONS_ENABLED` (env: `PI_PILOT_ENABLE_EXTENSIONS=1`) is the\n * escape hatch — even then no `ctx.ui.*` is bridged, so extensions\n * that only register tools / slash commands work and anything\n * UI-driven appears broken. Exported so `/api/resources` can show\n * this state in the panel.\n *\n * 2. pi-pilot's own first-party builtin extensions ARE loaded\n * unconditionally via `extensionFactories` (see `extensions/`).\n * That channel is independent of disk scanning, so they ship with\n * pi-pilot regardless of the env flag. These are not \"plugins\" in\n * the third-party sense — they're features of pi-pilot that happen\n * to use the extension API to get richer registration hooks\n * (`pi.registerTool`, `pi.registerCommand`, lifecycle events).\n */\nexport const EXTENSIONS_ENABLED = process.env.PI_PILOT_ENABLE_EXTENSIONS === \"1\";\n\n/**\n * Build a fresh runtime bound to `cwd`. Used both for initial creation and\n * for in-place session replacement (newSession / switchSession / fork).\n */\nconst createRuntime: CreateAgentSessionRuntimeFactory = async ({\n\tcwd,\n\tsessionManager,\n\tsessionStartEvent,\n}) => {\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tresourceLoaderOptions: {\n\t\t\tnoExtensions: !EXTENSIONS_ENABLED,\n\t\t\textensionFactories: builtinExtensionFactories,\n\t\t},\n\t});\n\tconst sessionResult = await createAgentSessionFromServices({\n\t\tservices,\n\t\tsessionManager,\n\t\tsessionStartEvent,\n\t});\n\treturn {\n\t\t...sessionResult,\n\t\tservices,\n\t\tdiagnostics: services.diagnostics,\n\t};\n};\n\ninterface WorkspaceState {\n\truntime: AgentSessionRuntime;\n\tbridge: ExtensionUIBridge;\n}\n\nclass WorkspaceManager {\n\tprivate readonly states = new Map<string, WorkspaceState>();\n\t/**\n\t * Subscribers live independently of `states` so the hub can register a\n\t * WebSocket *before* `getOrCreate` triggers a runtime build (which may\n\t * fire `session_start` synchronously, and any UI request from a\n\t * session_start handler would otherwise broadcast to an empty set).\n\t */\n\tprivate readonly subscribers = new Map<string, Set<WebSocket>>();\n\t/** Per-workspace lock to serialize concurrent creations. */\n\tprivate readonly pending = new Map<string, Promise<WorkspaceState>>();\n\tprivate readonly rebindListeners = new Map<string, Set<() => void>>();\n\n\tprivate getOrCreateSubscriberSet(workspaceId: string): Set<WebSocket> {\n\t\tlet set = this.subscribers.get(workspaceId);\n\t\tif (!set) {\n\t\t\tset = new Set();\n\t\t\tthis.subscribers.set(workspaceId, set);\n\t\t}\n\t\treturn set;\n\t}\n\n\tasync getOrCreate(workspaceId: string): Promise<AgentSessionRuntime> {\n\t\tconst existing = this.states.get(workspaceId);\n\t\tif (existing) return existing.runtime;\n\n\t\tconst inflight = this.pending.get(workspaceId);\n\t\tif (inflight) return (await inflight).runtime;\n\n\t\tconst p = this.build(workspaceId);\n\t\tthis.pending.set(workspaceId, p);\n\t\ttry {\n\t\t\tconst state = await p;\n\t\t\tthis.states.set(workspaceId, state);\n\t\t\treturn state.runtime;\n\t\t} finally {\n\t\t\tthis.pending.delete(workspaceId);\n\t\t}\n\t}\n\n\tprivate async build(workspaceId: string): Promise<WorkspaceState> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new Error(`Workspace not found: ${workspaceId}`);\n\n\t\t// SessionManager.continueRecent → opens the most recently used session\n\t\t// in this cwd, or starts a new one if none exists. Matches what `pi`\n\t\t// itself does on launch.\n\t\tconst sessionManager = SessionManager.continueRecent(ws.path);\n\n\t\tconst runtime = await createAgentSessionRuntime(createRuntime, {\n\t\t\tcwd: ws.path,\n\t\t\tagentDir: getAgentDir(),\n\t\t\tsessionManager,\n\t\t});\n\n\t\t// Subscriber set is owned by the manager so it can pre-exist any\n\t\t// runtime build (extensions may fire `onError` from `session_start`\n\t\t// before any client has subscribed). The bridge itself is stateless\n\t\t// — see `ws/extension-ui.ts` for why every method is a no-op.\n\t\tconst subscribers = this.getOrCreateSubscriberSet(workspaceId);\n\t\tconst bridge = new ExtensionUIBridge();\n\n\t\t// `onError` fires whenever an extension event handler throws (or pi\n\t\t// rejects a registration). We forward to every subscriber so the user\n\t\t// sees the failure as a toast instead of having it disappear into\n\t\t// server logs. Re-bound on every session replacement below.\n\t\tconst onError = (err: ExtensionError) => {\n\t\t\tconst msg: WsExtensionError = {\n\t\t\t\ttype: \"extension_error\",\n\t\t\t\tworkspaceId,\n\t\t\t\textensionPath: err.extensionPath,\n\t\t\t\tevent: err.event,\n\t\t\t\tmessage: err.error,\n\t\t\t};\n\t\t\tbroadcastTo(subscribers, msg);\n\t\t\t// Mirror to the server log so the stack survives even if every\n\t\t\t// subscriber is gone (no tab open, reconnect window, …).\n\t\t\tconsole.error(\n\t\t\t\t`[ext-error] ${workspaceId} ${err.extensionPath}@${err.event}: ${err.error}` +\n\t\t\t\t\t(err.stack ? `\\n${err.stack}` : \"\"),\n\t\t\t);\n\t\t};\n\n\t\t// Bind initially, and re-bind on every session replacement so the\n\t\t// new `AgentSession` instance also sees our UI context.\n\t\tawait runtime.session.bindExtensions({ uiContext: bridge, onError });\n\t\truntime.setRebindSession(async () => {\n\t\t\tawait runtime.session.bindExtensions({ uiContext: bridge, onError });\n\t\t\tthis.notifySessionReplaced(workspaceId);\n\t\t});\n\n\t\treturn { runtime, bridge };\n\t}\n\n\tget(workspaceId: string): AgentSessionRuntime | undefined {\n\t\treturn this.states.get(workspaceId)?.runtime;\n\t}\n\n\t/**\n\t * Register a WS connection as a subscriber for `workspaceId`. Safe to\n\t * call before `getOrCreate`; the set is lazily created so the bridge,\n\t * when later built, sees the same Set instance and any pre-existing\n\t * subscribers.\n\t */\n\taddSubscriber(workspaceId: string, ws: WebSocket): void {\n\t\tthis.getOrCreateSubscriberSet(workspaceId).add(ws);\n\t}\n\n\tremoveSubscriber(workspaceId: string, ws: WebSocket): void {\n\t\tconst set = this.subscribers.get(workspaceId);\n\t\tif (!set) return;\n\t\tset.delete(ws);\n\t\tif (set.size === 0) this.subscribers.delete(workspaceId);\n\t}\n\n\t/**\n\t * Fan a server-initiated message out to every WS subscribed to the\n\t * workspace. Used by API handlers that mutate runtime state and need\n\t * to refresh derived snapshots (e.g. `context_usage` after `setModel`,\n\t * which pi's event stream doesn't surface unless thinking-level also\n\t * clamps).\n\t */\n\tbroadcast(workspaceId: string, msg: WsServerMessage): void {\n\t\tconst set = this.subscribers.get(workspaceId);\n\t\tif (!set || set.size === 0) return;\n\t\tbroadcastTo(set, msg);\n\t}\n\n\tonSessionReplaced(workspaceId: string, listener: () => void): () => void {\n\t\tlet listeners = this.rebindListeners.get(workspaceId);\n\t\tif (!listeners) {\n\t\t\tlisteners = new Set();\n\t\t\tthis.rebindListeners.set(workspaceId, listeners);\n\t\t}\n\t\tlisteners.add(listener);\n\t\treturn () => {\n\t\t\tconst current = this.rebindListeners.get(workspaceId);\n\t\t\tif (!current) return;\n\t\t\tcurrent.delete(listener);\n\t\t\tif (current.size === 0) {\n\t\t\t\tthis.rebindListeners.delete(workspaceId);\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate notifySessionReplaced(workspaceId: string): void {\n\t\tconst listeners = this.rebindListeners.get(workspaceId);\n\t\tif (!listeners) return;\n\t\tfor (const listener of [...listeners]) {\n\t\t\ttry {\n\t\t\t\tlistener();\n\t\t\t} catch (e) {\n\t\t\t\tconsole.error(`[wm] rebind listener for ${workspaceId} failed:`, e);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync listSessions(workspaceId: string): Promise<SessionSummary[]> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new Error(`Workspace not found: ${workspaceId}`);\n\n\t\tconst sessions = await SessionManager.list(ws.path);\n\t\treturn sessions\n\t\t\t.slice()\n\t\t\t.sort((a, b) => b.modified.getTime() - a.modified.getTime())\n\t\t\t.map(toSessionSummary);\n\t}\n\n\tgetSessionHistory(workspaceId: string, sessionPath?: string): HistoryResponse {\n\t\tconst runtime = this.states.get(workspaceId)?.runtime;\n\t\tif (!runtime) return { items: [], isStreaming: false };\n\n\t\t// If the caller specifies a sessionPath, verify it matches the active\n\t\t// runtime. This prevents returning history from the wrong session when\n\t\t// the server restarted and picked a different \"recent\" session.\n\t\tif (sessionPath) {\n\t\t\tconst activeFile = runtime.session.sessionFile\n\t\t\t\t? resolve(runtime.session.sessionFile)\n\t\t\t\t: undefined;\n\t\t\tif (activeFile !== resolve(sessionPath)) {\n\t\t\t\treturn { items: [], isStreaming: false };\n\t\t\t}\n\t\t}\n\n\t\tconst isStreaming: boolean = runtime.session.isStreaming ?? false;\n\n\t\tconst branch = runtime.session.sessionManager.getBranch();\n\t\tconst items: HistoryItem[] = [];\n\t\t// toolCall args live in assistant messages; collect them so we can\n\t\t// attach them to the corresponding toolResult items.\n\t\tconst argsByCallId = new Map<string, string>();\n\n\t\tfor (const entry of branch) {\n\t\t\tif (entry.type !== \"message\") continue;\n\t\t\tconst msg = entry.message;\n\t\t\tconst role = (msg as { role: string }).role;\n\n\t\t\tif (role === \"user\") {\n\t\t\t\tconst text = extractUserText(msg as { content: unknown });\n\t\t\t\tif (text) items.push({ kind: \"user\", text });\n\t\t\t} else if (role === \"assistant\") {\n\t\t\t\tconst { text, thinking, toolCalls } = extractAssistantContent(\n\t\t\t\t\tmsg as { content: unknown[] },\n\t\t\t\t);\n\t\t\t\tfor (const tc of toolCalls) {\n\t\t\t\t\targsByCallId.set(tc.id, tc.args);\n\t\t\t\t}\n\t\t\t\tif (text || thinking) items.push({ kind: \"assistant\", text, thinking });\n\t\t\t} else if (role === \"toolResult\") {\n\t\t\t\tconst tr = msg as {\n\t\t\t\t\ttoolCallId: string;\n\t\t\t\t\ttoolName: string;\n\t\t\t\t\tcontent: unknown[];\n\t\t\t\t\tisError: boolean;\n\t\t\t\t};\n\t\t\t\titems.push({\n\t\t\t\t\tkind: \"tool\",\n\t\t\t\t\ttoolCallId: tr.toolCallId,\n\t\t\t\t\ttoolName: tr.toolName,\n\t\t\t\t\targs: argsByCallId.get(tr.toolCallId) ?? \"\",\n\t\t\t\t\ttext: extractContentText(tr.content),\n\t\t\t\t\tisError: tr.isError,\n\t\t\t\t});\n\t\t\t} else if (role === \"bashExecution\") {\n\t\t\t\tconst be = msg as {\n\t\t\t\t\tcommand: string;\n\t\t\t\t\toutput: string;\n\t\t\t\t\texitCode: number | undefined;\n\t\t\t\t};\n\t\t\t\titems.push({\n\t\t\t\t\tkind: \"bash\",\n\t\t\t\t\tcommand: be.command,\n\t\t\t\t\toutput: be.output,\n\t\t\t\t\texitCode: be.exitCode,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn { items, isStreaming };\n\t}\n\n\t/**\n\t * Delete a session JSONL file belonging to this workspace.\n\t *\n\t * Errors are tagged with HTTP semantics via HttpError so the route layer\n\t * can map them to the right status code:\n\t * - 400: sessionPath not absolute\n\t * - 404: workspace gone, or session not in this workspace's list\n\t * - 409: file is the currently-active session (caller must switch first)\n\t *\n\t * Idempotent on ENOENT: if the file is missing at unlink time (e.g. a\n\t * concurrent external delete between list and unlink), we treat it as\n\t * success — the goal state has been reached.\n\t */\n\tasync deleteSession(workspaceId: string, sessionPath: string): Promise<void> {\n\t\t// Defensive re-check even though the route already validates the\n\t\t// workspace — keeps the manager API safe for any caller (extensions,\n\t\t// tests). Mirrors listSessions/switchSession.\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new HttpError(404, `Workspace not found: ${workspaceId}`);\n\t\tif (!isAbsolute(sessionPath)) {\n\t\t\tthrow new HttpError(400, \"Session path must be absolute\");\n\t\t}\n\n\t\tconst sessions = await SessionManager.list(ws.path);\n\t\tconst resolved = resolve(sessionPath);\n\t\tconst target = sessions.find((session) => resolve(session.path) === resolved);\n\t\tif (!target) {\n\t\t\tthrow new HttpError(404, `Session not found: ${sessionPath}`);\n\t\t}\n\n\t\tconst runtime = this.states.get(workspaceId)?.runtime;\n\t\tconst activePath = runtime?.session.sessionFile\n\t\t\t? resolve(runtime.session.sessionFile)\n\t\t\t: undefined;\n\t\tif (activePath === resolved) {\n\t\t\tthrow new HttpError(\n\t\t\t\t409,\n\t\t\t\t\"Cannot delete the currently active session — switch to another session first\",\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tawait unlink(resolved);\n\t\t} catch (err) {\n\t\t\tif ((err as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[wm] deleteSession: ${resolved} was already gone at unlink time`,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync switchSession(workspaceId: string, sessionPath: string): Promise<boolean> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new Error(`Workspace not found: ${workspaceId}`);\n\t\tif (!isAbsolute(sessionPath)) {\n\t\t\tthrow new Error(\"Session path must be absolute\");\n\t\t}\n\n\t\tconst sessions = await SessionManager.list(ws.path);\n\t\tconst resolved = resolve(sessionPath);\n\t\tconst target = sessions.find((session) => resolve(session.path) === resolved);\n\t\tif (!target) {\n\t\t\tthrow new Error(`Session not found: ${sessionPath}`);\n\t\t}\n\n\t\tconst runtime = await this.getOrCreate(workspaceId);\n\t\tconst currentPath = runtime.session.sessionFile ? resolve(runtime.session.sessionFile) : undefined;\n\t\tif (currentPath === resolved) return false;\n\t\tif (runtime.session.isStreaming) {\n\t\t\tthrow new Error(\"Cannot switch sessions while the agent is streaming\");\n\t\t}\n\n\t\tconst result = await runtime.switchSession(resolved, { cwdOverride: ws.path });\n\t\treturn !result.cancelled;\n\t}\n\n\tasync dispose(workspaceId: string): Promise<void> {\n\t\tconst state = this.states.get(workspaceId);\n\t\tif (!state) return;\n\t\tthis.states.delete(workspaceId);\n\t\tthis.rebindListeners.delete(workspaceId);\n\t\tthis.subscribers.delete(workspaceId);\n\t\ttry {\n\t\t\tstate.bridge.dispose();\n\t\t} catch (e) {\n\t\t\tconsole.error(`[wm] dispose bridge ${workspaceId} failed:`, e);\n\t\t}\n\t\ttry {\n\t\t\tstate.runtime.session.dispose();\n\t\t} catch (e) {\n\t\t\tconsole.error(`[wm] dispose ${workspaceId} failed:`, e);\n\t\t}\n\t}\n\n\tasync disposeAll(): Promise<void> {\n\t\tconst ids = [...this.states.keys()];\n\t\tawait Promise.all(ids.map((id) => this.dispose(id)));\n\t}\n}\n\nfunction toSessionSummary(info: SessionInfo): SessionSummary {\n\tconst preview = info.firstMessage.replace(/\\s+/g, \" \").trim();\n\treturn {\n\t\tpath: info.path,\n\t\tname: info.name,\n\t\tupdatedAt: info.modified.toISOString(),\n\t\tpreview: preview ? preview.slice(0, 160) : undefined,\n\t};\n}\n\n/** Extract text from a user message (string or content blocks). */\nfunction extractUserText(msg: { content: unknown }): string {\n\tif (typeof msg.content === \"string\") return msg.content;\n\treturn extractContentText(msg.content as unknown[]);\n}\n\n/** Extract text + thinking + toolCalls from an assistant message's content blocks. */\nfunction extractAssistantContent(msg: { content: unknown[] }): {\n\ttext: string;\n\tthinking: string;\n\ttoolCalls: { id: string; args: string }[];\n} {\n\tconst textParts: string[] = [];\n\tconst thinkingParts: string[] = [];\n\tconst toolCalls: { id: string; args: string }[] = [];\n\tfor (const block of msg.content ?? []) {\n\t\tif (!block || typeof block !== \"object\") continue;\n\t\tconst b = block as { type?: string; text?: string; thinking?: string; id?: string; arguments?: unknown };\n\t\tif (b.type === \"text\" && typeof b.text === \"string\") textParts.push(b.text);\n\t\telse if (b.type === \"thinking\" && typeof b.thinking === \"string\") thinkingParts.push(b.thinking);\n\t\telse if (b.type === \"toolCall\" && typeof b.id === \"string\") {\n\t\t\ttoolCalls.push({\n\t\t\t\tid: b.id,\n\t\t\t\targs: b.arguments != null ? JSON.stringify(b.arguments) : \"\",\n\t\t\t});\n\t\t}\n\t}\n\treturn { text: textParts.join(\"\"), thinking: thinkingParts.join(\"\"), toolCalls };\n}\n\n/** Extract text from a content block array (TextContent items). */\nfunction extractContentText(content: unknown[]): string {\n\tif (!Array.isArray(content)) return \"\";\n\tconst parts: string[] = [];\n\tfor (const block of content) {\n\t\tif (block && typeof block === \"object\" && (block as { type?: string }).type === \"text\") {\n\t\t\tconst text = (block as { text?: unknown }).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\treturn parts.join(\"\");\n}\n\nexport const workspaceManager = new WorkspaceManager();\n\n/**\n * Best-effort fan-out of a server-initiated message to every connection\n * subscribed to a workspace. Mirrors `ExtensionUIBridge.broadcast` but\n * lives at the manager level so non-UI events (extension errors today;\n * extension_error variants later) can reuse it without needing a bridge\n * reference.\n */\nfunction broadcastTo(subscribers: Set<WebSocket>, msg: WsServerMessage): void {\n\tconst wire = JSON.stringify(msg);\n\tfor (const ws of subscribers) {\n\t\tif (ws.readyState !== ws.OPEN) continue;\n\t\ttry {\n\t\t\tws.send(wire);\n\t\t} catch {\n\t\t\t// Bad sockets drop on the next close event.\n\t\t}\n\t}\n}\n","/**\n * TypeBox schemas for the update_plan tool.\n *\n * Kept separate from factory.ts so future plan-related code (web shape\n * mirrors via shared types, future tooling) can import the parameter\n * shape without pulling pi runtime types into the dependency graph.\n */\n\nimport { Type, type Static } from \"typebox\";\n\nexport const planItemStatusSchema = Type.Union([\n\tType.Literal(\"pending\"),\n\tType.Literal(\"in_progress\"),\n\tType.Literal(\"completed\"),\n]);\n\nexport const planItemSchema = Type.Object({\n\tid: Type.String({ description: \"Short stable identifier for the item (kebab-case, e.g. \\\"wire-factory\\\").\" }),\n\ttitle: Type.String({ description: \"One-line description of the step. Specific and verifiable.\" }),\n\tstatus: planItemStatusSchema,\n\tnote: Type.Optional(Type.String({ description: \"Optional short context — blocker, decision, or follow-up.\" })),\n});\n\nexport const updatePlanParamsSchema = Type.Object({\n\titems: Type.Array(planItemSchema, {\n\t\tdescription: \"The full ordered plan. Always send the complete list; previous tool calls are not merged.\",\n\t}),\n});\n\nexport type PlanItem = Static<typeof planItemSchema>;\nexport type UpdatePlanParams = Static<typeof updatePlanParamsSchema>;\n","/**\n * pi-pilot's first-party plan extension.\n *\n * Registers:\n * - `update_plan` tool: agent emits the full plan (id/title/status[/note])\n * as one tool call to publish or refresh it. State machine: pending →\n * in_progress → completed. The full list is always sent — there is no\n * merging by id across calls; the most recent successful tool call IS\n * the current plan.\n * - `/plan` slash command: user-facing entry point. Expands into a user\n * message asking the agent to draft a plan and call update_plan\n * before starting work. The literal `/plan` text never reaches the\n * LLM — pi consumes the slash invocation and the handler injects the\n * rewritten message via `pi.sendUserMessage`.\n *\n * Why it's a builtin extension and not `customTools`: registering through\n * the extension API gives us the slash command (customTools can't) and\n * groups the tool + command + prompt guidelines in one place. The factory\n * runs inside the SDK's `extensionFactories` channel, which is independent\n * of the `noExtensions` disk-scan flag — so this loads even when the user\n * has third-party pi extensions turned off (the default for pi-pilot).\n *\n * Plan state is implicitly persisted in the session JSONL as tool call\n * history, so fork / new_session / replay get correct semantics for free;\n * we deliberately don't track plan state on our side.\n */\n\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport { updatePlanParamsSchema } from \"./schema.ts\";\n\nexport const planExtensionFactory: ExtensionFactory = (pi) => {\n\tpi.registerTool({\n\t\tname: \"update_plan\",\n\t\tlabel: \"Plan\",\n\t\tdescription:\n\t\t\t\"Publish or refresh the working plan for the current task. Use for any task that takes 3+ discrete steps. Always send the full ordered list; previous calls are replaced, not merged.\",\n\t\tparameters: updatePlanParamsSchema,\n\t\tpromptSnippet:\n\t\t\t\"update_plan: maintain a live checklist for multi-step tasks; update statuses as you progress.\",\n\t\tpromptGuidelines: [\n\t\t\t\"For tasks with 3+ discrete steps, call update_plan once with the full list before starting work.\",\n\t\t\t\"After completing each step, call update_plan again with refreshed statuses, then continue immediately — do not pause the turn just because the plan was updated.\",\n\t\t\t\"Plan items should be specific and verifiable (e.g. \\\"Add typebox dependency to packages/server\\\"), not vague (\\\"Set up infrastructure\\\").\",\n\t\t\t\"Exactly one item should be in_progress at a time. Mark completed only when the work is actually done.\",\n\t\t],\n\t\texecute: async (_toolCallId, params) => ({\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\ttext: `Plan updated (${params.items.length} item${params.items.length === 1 ? \"\" : \"s\"}).`,\n\t\t\t\t},\n\t\t\t],\n\t\t\tdetails: params,\n\t\t}),\n\t});\n\n\tpi.registerCommand(\"plan\", {\n\t\tdescription: \"Ask the agent to draft a plan for the current task.\",\n\t\thandler: async (args) => {\n\t\t\tconst task = args.trim();\n\t\t\tconst message = task\n\t\t\t\t? `Draft a plan for: ${task}. Use the update_plan tool to publish it before starting any work.`\n\t\t\t\t: \"Draft a plan for the current task. Use the update_plan tool to publish it before starting any work.\";\n\t\t\tpi.sendUserMessage(message);\n\t\t},\n\t});\n};\n","/**\n * Registry of pi-pilot's first-party builtin extensions.\n *\n * These factories are passed to pi via `DefaultResourceLoaderOptions.\n * extensionFactories`, which is an independent channel from the disk\n * extension scanner (see `resource-loader.js`). The `noExtensions` flag\n * only controls disk scanning; factories registered here always load.\n *\n * Two-tier stance: third-party / disk pi extensions stay off by default\n * (rendered as \"not supported\" in the Resources panel), but pi-pilot\n * ships its own first-party features via this list. To add a new builtin\n * feature, drop a new directory under `extensions/<name>/` with a\n * `factory.ts` exporting an `ExtensionFactory`, then append it here.\n *\n * NOT a barrel file — this defines a value (the factories list), not a\n * re-export of unrelated symbols.\n */\n\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport { planExtensionFactory } from \"./plan/factory.ts\";\n\nexport const builtinExtensionFactories: ExtensionFactory[] = [planExtensionFactory];\n","/**\n * No-op implementation of pi's `ExtensionUIContext`.\n *\n * pi-pilot does not support pi extensions (see README / workspace-manager).\n * The extension system is TUI-native — custom renderers, ANSI themes,\n * keyboard overlays, modal dialogs — and cannot be bridged to the Web in\n * any meaningful way. Rather than render a half-working subset that lies\n * about what's supported, every method here is an intentional no-op or\n * returns a sensible default.\n *\n * Why keep the class at all: the pi SDK still calls\n * `bindExtensions({ uiContext })`, and the type contract requires a\n * full `ExtensionUIContext`. When `PI_PILOT_ENABLE_EXTENSIONS=1`, the\n * loader will load extensions and any UI calls land here and quietly\n * do nothing. An extension that registers tools / slash commands but\n * doesn't touch `ctx.ui` still works; everything UI-driven is dead.\n *\n * Methods covered:\n * - Dialogs: select, confirm, input, editor → resolve to default\n * - Fire-and-forget: notify, setStatus, setWidget, setTitle,\n * setEditorText, pasteToEditor → no-op\n * - TUI-only: onTerminalInput, setWorkingMessage,\n * setWorkingVisible, setWorkingIndicator,\n * setHiddenThinkingLabel, setFooter, setHeader,\n * custom, getEditorText, addAutocompleteProvider,\n * setEditorComponent, getEditorComponent, theme,\n * getAllThemes, getTheme, setTheme,\n * getToolsExpanded, setToolsExpanded\n */\n\nimport type { ExtensionUIContext } from \"@earendil-works/pi-coding-agent\";\n\nexport class ExtensionUIBridge implements ExtensionUIContext {\n\t/** Symmetric with the old bridge so workspace-manager's dispose path\n\t * can call it uniformly. There is no state to release. */\n\tdispose(): void {}\n\n\t// ============== dialog methods (resolve to default) ==============\n\n\tselect(): Promise<string | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\tconfirm(): Promise<boolean> {\n\t\treturn Promise.resolve(false);\n\t}\n\tinput(): Promise<string | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\teditor(): Promise<string | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\n\t// ============== fire-and-forget methods (no-op) ==============\n\n\tnotify(): void {}\n\tsetStatus(): void {}\n\tsetWidget(): void {}\n\tsetTitle(): void {}\n\tsetEditorText(): void {}\n\tpasteToEditor(): void {}\n\n\t// ============== TUI-only methods (no-op / defaults) ==============\n\n\tonTerminalInput(): () => void {\n\t\treturn () => {};\n\t}\n\tsetWorkingMessage(): void {}\n\tsetWorkingVisible(): void {}\n\tsetWorkingIndicator(): void {}\n\tsetHiddenThinkingLabel(): void {}\n\tsetFooter(): void {}\n\tsetHeader(): void {}\n\tasync custom<T>(): Promise<T> {\n\t\t// `custom()` is the primary TUI rendering API used by extensions to\n\t\t// draw arbitrary terminal dialogs/overlays. It cannot be bridged to\n\t\t// the Web. Match pi's documented RPC-mode behavior of returning\n\t\t// undefined; the SDK's callers handle that explicitly.\n\t\treturn undefined as unknown as T;\n\t}\n\tgetEditorText(): string {\n\t\treturn \"\";\n\t}\n\taddAutocompleteProvider(): void {}\n\tsetEditorComponent(): void {}\n\tgetEditorComponent(): undefined {\n\t\treturn undefined;\n\t}\n\tget theme(): never {\n\t\t// Extensions that read `ctx.ui.theme` directly see undefined. The\n\t\t// cast keeps the type structural.\n\t\treturn undefined as unknown as never;\n\t}\n\tgetAllThemes(): { name: string; path: string | undefined }[] {\n\t\treturn [];\n\t}\n\tgetTheme(): undefined {\n\t\treturn undefined;\n\t}\n\tsetTheme(): { success: boolean; error?: string } {\n\t\treturn { success: false, error: \"Theme switching not supported in pi-pilot\" };\n\t}\n\tgetToolsExpanded(): boolean {\n\t\treturn false;\n\t}\n\tsetToolsExpanded(): void {}\n}\n","/**\n * /api/workspaces/:id/config routes.\n *\n * Reads and mutates the runtime config (model, thinking level, tools) for a\n * workspace's active AgentSession. Mounted onto the workspaces Hono router\n * via `mountConfigRoutes`.\n */\n\nimport type { Hono, Context } from \"hono\";\nimport type { AgentSessionRuntime } from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tConfigResponse,\n\tModelInfo,\n\tPiContextUsage,\n\tSetModelRequest,\n\tSetThinkingLevelRequest,\n\tSetToolsRequest,\n\tThinkingLevel,\n\tToolInfo as ToolInfoDTO,\n} from \"@pi-pilot/shared\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport { getWorkspace } from \"../storage/workspace-registry.ts\";\n\n// ---------- helpers ----------\n\nfunction buildConfigResponse(workspaceId: string): ConfigResponse {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) throw new Error(\"runtime not initialized\");\n\n\tconst session = runtime.session;\n\tconst model = session.model;\n\n\tconst currentModel: ModelInfo | null = model\n\t\t? {\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodelId: model.id,\n\t\t\t\tname: model.name,\n\t\t\t\treasoning: model.reasoning,\n\t\t\t}\n\t\t: null;\n\n\tconst availableModels: ModelInfo[] = session.modelRegistry\n\t\t.getAvailable()\n\t\t.map((m) => ({\n\t\t\tprovider: m.provider,\n\t\t\tmodelId: m.id,\n\t\t\tname: m.name,\n\t\t\treasoning: m.reasoning,\n\t\t}));\n\n\tconst allTools: ToolInfoDTO[] = session.getAllTools().map((t) => ({\n\t\tname: t.name,\n\t\tdescription: t.description,\n\t}));\n\n\treturn {\n\t\tcurrentModel,\n\t\tthinkingLevel: session.thinkingLevel as ThinkingLevel,\n\t\tavailableThinkingLevels: session.getAvailableThinkingLevels() as ThinkingLevel[],\n\t\tactiveTools: session.getActiveToolNames(),\n\t\tavailableModels,\n\t\tallTools,\n\t};\n}\n\n/** Return 404 early if workspace does not exist in the registry. */\nasync function requireWorkspace(c: Context, id: string): Promise<boolean> {\n\tconst ws = await getWorkspace(id);\n\tif (!ws) {\n\t\tc.status(404);\n\t\tc.header(\"Content-Type\", \"application/json\");\n\t\t// We must return the body via the route handler, so we set a flag.\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n/** Return 409 if the agent is currently streaming. */\nfunction rejectIfStreaming(c: Context, workspaceId: string): boolean {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (runtime?.session.isStreaming) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/**\n * Push a fresh `context_usage` snapshot to every WS subscriber of the\n * workspace. Pi's event stream only surfaces context-usage shifts via\n * `agent_end` / `compaction_end` / `thinking_level_changed`, so a model\n * swap whose new contextWindow differs but doesn't clamp thinking level\n * would otherwise leave clients displaying the previous model's percent\n * until the next assistant turn.\n */\nfunction broadcastContextUsage(\n\tworkspaceId: string,\n\truntime: AgentSessionRuntime,\n): void {\n\tconst usage = runtime.session.getContextUsage();\n\tif (!usage) return;\n\tconst payload: PiContextUsage = {\n\t\tkind: \"context_usage\",\n\t\ttokens: usage.tokens,\n\t\tcontextWindow: usage.contextWindow,\n\t\tpercent: usage.percent,\n\t};\n\tworkspaceManager.broadcast(workspaceId, {\n\t\ttype: \"event\",\n\t\tworkspaceId,\n\t\tsessionPath: runtime.session.sessionFile ?? null,\n\t\tpayload,\n\t});\n}\n\n// ---------- mount ----------\n\nexport function mountConfigRoutes(app: Hono): void {\n\t// GET /api/workspaces/:id/config\n\tapp.get(\"/:id/config\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n\n\t// PUT /api/workspaces/:id/config/model\n\tapp.put(\"/:id/config/model\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst body = (await c.req.json()) as SetModelRequest;\n\t\tif (!body?.provider || !body?.modelId) {\n\t\t\treturn c.json({ ok: false, error: \"provider and modelId are required\" }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\tconst runtime = await workspaceManager.getOrCreate(id);\n\t\t\tif (rejectIfStreaming(c, id)) {\n\t\t\t\treturn c.json({ ok: false, error: \"cannot change model while the agent is streaming\" }, 409);\n\t\t\t}\n\n\t\t\tconst model = runtime.session.modelRegistry.getAvailable().find(\n\t\t\t\t(m) => m.provider === body.provider && m.id === body.modelId,\n\t\t\t);\n\t\t\tif (!model) {\n\t\t\t\treturn c.json({ ok: false, error: `model not found or no auth: ${body.provider}/${body.modelId}` }, 404);\n\t\t\t}\n\n\t\t\tawait runtime.session.setModel(model);\n\t\t\t// New model may have a different contextWindow. Pi's setModel only\n\t\t\t// emits an event when thinking-level gets clamped — when the new\n\t\t\t// model accepts the current level unchanged, no event fires and\n\t\t\t// the WS CTX indicator would stay pinned to the previous model's\n\t\t\t// percent until the next agent_end. Push a snapshot ourselves.\n\t\t\tbroadcastContextUsage(id, runtime);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n\n\t// PUT /api/workspaces/:id/config/thinking-level\n\tapp.put(\"/:id/config/thinking-level\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst body = (await c.req.json()) as SetThinkingLevelRequest;\n\t\tconst validLevels: ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"];\n\t\tif (!body?.level || !validLevels.includes(body.level)) {\n\t\t\treturn c.json({ ok: false, error: `level must be one of: ${validLevels.join(\", \")}` }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tif (rejectIfStreaming(c, id)) {\n\t\t\t\treturn c.json({ ok: false, error: \"cannot change thinking level while the agent is streaming\" }, 409);\n\t\t\t}\n\n\t\t\tconst runtime = workspaceManager.get(id);\n\t\t\tif (!runtime) {\n\t\t\t\treturn c.json({ ok: false, error: \"runtime not initialized\" }, 500);\n\t\t\t}\n\t\t\truntime.session.setThinkingLevel(body.level);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n\n\t// PUT /api/workspaces/:id/config/tools\n\tapp.put(\"/:id/config/tools\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst body = (await c.req.json()) as SetToolsRequest;\n\t\tif (!body?.tools || !Array.isArray(body.tools)) {\n\t\t\treturn c.json({ ok: false, error: \"tools must be an array of tool names\" }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tif (rejectIfStreaming(c, id)) {\n\t\t\t\treturn c.json({ ok: false, error: \"cannot change tools while the agent is streaming\" }, 409);\n\t\t\t}\n\n\t\t\tconst runtime = workspaceManager.get(id);\n\t\t\tif (!runtime) {\n\t\t\t\treturn c.json({ ok: false, error: \"runtime not initialized\" }, 500);\n\t\t\t}\n\t\t\truntime.session.setActiveToolsByName(body.tools);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n}\n","/**\n * /api/workspaces/:id/files/search — workspace-scoped file lookup for the\n * composer's `@` autocomplete.\n *\n * Source of truth is `git ls-files --cached --others --exclude-standard`,\n * which is fast and honors `.gitignore`. Non-git directories fall back to a\n * bounded recursive walk that skips obvious build/dep dirs. Filtering is\n * case-insensitive substring on the workspace-relative path; results are\n * ranked by where the match landed (basename beats path) and capped at\n * `limit` (default 30, max 100).\n *\n * The full file list per workspace is cached in-process with a 10-second\n * TTL so a user mashing keys in the composer doesn't fork a `git` child\n * per keystroke. Same in-flight dedup pattern as workspace-stats.ts.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { readdir } from \"node:fs/promises\";\nimport { join, relative, sep } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport type { Hono } from \"hono\";\nimport type { SearchFilesResponse, SearchFileEntry } from \"@pi-pilot/shared\";\nimport { getWorkspace } from \"../storage/workspace-registry.ts\";\n\nconst exec = promisify(execFile);\n\nconst LIST_TTL_MS = 10_000;\n/** Soft cap on the per-workspace cache. Without it, every workspace a user\n * ever opened would stay resident even after they stopped using it (the TTL\n * only re-checks on next read of the *same* key). FIFO eviction is good\n * enough — the access pattern is \"the workspace I'm composing in right\n * now\", which gets re-inserted on every miss, naturally promoting itself. */\nconst MAX_CACHED_WORKSPACES = 16;\n/** Hard ceiling on files we ever consider, across both code paths. Keeps\n * search latency bounded on pathological repos and protects against a\n * bug in the walk. */\nconst MAX_FILES_TRACKED = 20_000;\nconst DEFAULT_LIMIT = 30;\nconst MAX_LIMIT = 100;\nconst WALK_MAX_DEPTH = 8;\nconst WALK_IGNORES = new Set([\n\t\".git\",\n\t\"node_modules\",\n\t\"dist\",\n\t\"build\",\n\t\".next\",\n\t\".turbo\",\n\t\".cache\",\n\t\".vite\",\n\t\"out\",\n\t\"coverage\",\n\t\"target\",\n\t\"venv\",\n\t\".venv\",\n\t\"__pycache__\",\n\t\".DS_Store\",\n]);\n\ninterface ListCacheEntry {\n\tfiles: string[];\n\texpiresAt: number;\n}\n\nconst listCache = new Map<string, ListCacheEntry>();\nconst inflight = new Map<string, Promise<ListCacheEntry>>();\n\nasync function getFileList(workspacePath: string): Promise<string[]> {\n\tconst now = Date.now();\n\tconst cached = listCache.get(workspacePath);\n\tif (cached && cached.expiresAt > now) return cached.files;\n\n\tconst pending = inflight.get(workspacePath);\n\tif (pending) return (await pending).files;\n\n\tconst probe = probeFileList(workspacePath)\n\t\t.then((files) => {\n\t\t\tconst entry: ListCacheEntry = {\n\t\t\t\tfiles,\n\t\t\t\texpiresAt: Date.now() + LIST_TTL_MS,\n\t\t\t};\n\t\t\t// Re-insert (delete + set) so this key moves to the back of the\n\t\t\t// Map's insertion order — the FIFO evictor below treats the head\n\t\t\t// as the oldest, so this keeps the \"actively used\" workspace warm.\n\t\t\tlistCache.delete(workspacePath);\n\t\t\tlistCache.set(workspacePath, entry);\n\t\t\twhile (listCache.size > MAX_CACHED_WORKSPACES) {\n\t\t\t\tconst oldest = listCache.keys().next().value;\n\t\t\t\tif (!oldest) break;\n\t\t\t\tlistCache.delete(oldest);\n\t\t\t}\n\t\t\treturn entry;\n\t\t})\n\t\t.finally(() => inflight.delete(workspacePath));\n\tinflight.set(workspacePath, probe);\n\treturn (await probe).files;\n}\n\nasync function probeFileList(workspacePath: string): Promise<string[]> {\n\t// Try git first — fastest, respects gitignore, single subprocess.\n\ttry {\n\t\tconst { stdout } = await exec(\n\t\t\t\"git\",\n\t\t\t[\"ls-files\", \"--cached\", \"--others\", \"--exclude-standard\"],\n\t\t\t{\n\t\t\t\tcwd: workspacePath,\n\t\t\t\ttimeout: 3000,\n\t\t\t\tmaxBuffer: 16 * 1024 * 1024,\n\t\t\t\tenv: { ...process.env, GIT_DIR: undefined, GIT_WORK_TREE: undefined },\n\t\t\t},\n\t\t);\n\t\tconst lines = stdout.split(\"\\n\");\n\t\tconst out: string[] = [];\n\t\tfor (const line of lines) {\n\t\t\tif (!line) continue;\n\t\t\tout.push(line);\n\t\t\tif (out.length >= MAX_FILES_TRACKED) break;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\t// Fall through to a bounded walk.\n\t}\n\n\tconst out: string[] = [];\n\tawait walkDir(workspacePath, workspacePath, 0, out);\n\treturn out;\n}\n\nasync function walkDir(\n\troot: string,\n\tdir: string,\n\tdepth: number,\n\tout: string[],\n): Promise<void> {\n\tif (out.length >= MAX_FILES_TRACKED) return;\n\tif (depth > WALK_MAX_DEPTH) return;\n\tlet dirents;\n\ttry {\n\t\tdirents = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const d of dirents) {\n\t\tif (out.length >= MAX_FILES_TRACKED) return;\n\t\t// Surface dotfiles like .env / .eslintrc / .gitignore — only skip the\n\t\t// dotfile *directories* listed in WALK_IGNORES (.git, .next, .cache,\n\t\t// .turbo, .vite, .venv) plus the .DS_Store noise file.\n\t\tif (WALK_IGNORES.has(d.name)) continue;\n\t\tconst abs = join(dir, d.name);\n\t\tif (d.isDirectory()) {\n\t\t\tawait walkDir(root, abs, depth + 1, out);\n\t\t} else if (d.isFile()) {\n\t\t\tout.push(relative(root, abs).split(sep).join(\"/\"));\n\t\t}\n\t}\n}\n\n/** Score a relPath against a lowercase query. Higher = better. Returns\n * null if the path doesn't match at all. */\nfunction scoreMatch(relPath: string, q: string): number | null {\n\tconst lower = relPath.toLowerCase();\n\tconst slash = lower.lastIndexOf(\"/\");\n\tconst base = slash >= 0 ? lower.slice(slash + 1) : lower;\n\n\t// Tier 1: basename prefix match — best signal.\n\tif (base.startsWith(q)) return 1000 - relPath.length;\n\t// Tier 2: basename substring.\n\tconst baseIdx = base.indexOf(q);\n\tif (baseIdx >= 0) return 800 - baseIdx - relPath.length * 0.01;\n\t// Tier 3: anywhere in the path.\n\tconst pathIdx = lower.indexOf(q);\n\tif (pathIdx >= 0) return 500 - pathIdx - relPath.length * 0.01;\n\treturn null;\n}\n\nasync function ensureWorkspaceExists(id: string): Promise<string | null> {\n\tconst ws = await getWorkspace(id);\n\treturn ws ? ws.path : null;\n}\n\nexport function mountFilesRoute(app: Hono): void {\n\tapp.get(\"/:id/files/search\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst workspacePath = await ensureWorkspaceExists(id);\n\t\tif (!workspacePath) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst qRaw = (c.req.query(\"q\") ?? \"\").trim();\n\t\tconst limitRaw = c.req.query(\"limit\");\n\t\tlet limit = limitRaw ? Number(limitRaw) : DEFAULT_LIMIT;\n\t\tif (!Number.isFinite(limit) || limit <= 0) limit = DEFAULT_LIMIT;\n\t\tif (limit > MAX_LIMIT) limit = MAX_LIMIT;\n\n\t\ttry {\n\t\t\tconst all = await getFileList(workspacePath);\n\t\t\tlet entries: SearchFileEntry[];\n\t\t\tlet truncated = false;\n\t\t\tif (!qRaw) {\n\t\t\t\t// Empty query: just return the head of the list so the popover\n\t\t\t\t// has something to show before the user types.\n\t\t\t\tconst slice = all.slice(0, limit);\n\t\t\t\tentries = slice.map((relPath) => ({\n\t\t\t\t\tpath: join(workspacePath, relPath),\n\t\t\t\t\trelPath,\n\t\t\t\t}));\n\t\t\t\ttruncated = all.length > limit;\n\t\t\t} else {\n\t\t\t\tconst q = qRaw.toLowerCase();\n\t\t\t\tconst scored: { relPath: string; score: number }[] = [];\n\t\t\t\tlet matchCount = 0;\n\t\t\t\tfor (const relPath of all) {\n\t\t\t\t\tconst score = scoreMatch(relPath, q);\n\t\t\t\t\tif (score === null) continue;\n\t\t\t\t\tmatchCount++;\n\t\t\t\t\tscored.push({ relPath, score });\n\t\t\t\t}\n\t\t\t\tscored.sort((a, b) => b.score - a.score);\n\t\t\t\tconst top = scored.slice(0, limit);\n\t\t\t\tentries = top.map((e) => ({\n\t\t\t\t\tpath: join(workspacePath, e.relPath),\n\t\t\t\t\trelPath: e.relPath,\n\t\t\t\t}));\n\t\t\t\ttruncated = matchCount > limit;\n\t\t\t}\n\t\t\tconst body: SearchFilesResponse = { workspacePath, entries, truncated };\n\t\t\treturn c.json(body);\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tconsole.error(`[api/files] search for ${id} failed:`, err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n}\n","/**\n * /api/workspaces/:id/resources route family.\n *\n * GET → snapshot of skills / prompts / extensions discovered\n * by the workspace's pi `ResourceLoader` (read-only\n * inventory \\u2014 no filesystem touched here).\n * POST .../reload → ask the loader to re-scan; useful after creating\n * files outside pi-pilot or after editing.\n * GET .../skill?path → pre-fill data for the skill editor (parsed\n * frontmatter + body).\n * POST .../skills → create a new skill (directory + SKILL.md) under the\n * chosen scope, then reload + return fresh inventory.\n * PUT .../skills → update an existing skill in place.\n * DELETE .../skills → remove a skill (and its parent directory if it\n * wasn't a bare .md at the skills root).\n * GET .../prompt?path → pre-fill data for the prompt editor.\n * POST .../prompts → create a new prompt template.\n * PUT .../prompts → update; renames the file if `name` changed.\n * DELETE .../prompts → unlink the file.\n *\n * All mutation endpoints respond with the post-reload `ResourcesResponse`\n * so the client doesn't need a follow-up GET.\n */\n\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { Context, Hono } from \"hono\";\nimport { getAgentDir } from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tCreatePromptRequest,\n\tCreateSkillRequest,\n\tExtensionInfo,\n\tExtensionLoadError,\n\tPromptInfo,\n\tResourceFileResponse,\n\tResourceScope,\n\tResourceSource,\n\tResourcesResponse,\n\tSkillInfo,\n\tUpdatePromptRequest,\n\tUpdateSkillRequest,\n} from \"@pi-pilot/shared\";\nimport { getWorkspace } from \"../storage/workspace-registry.ts\";\nimport {\n\tcreatePrompt,\n\tcreateSkill,\n\tdeletePrompt,\n\tdeleteSkill,\n\tHttpError,\n\treadPromptFile,\n\treadSkillFile,\n\tresolveResourceRoots,\n\ttype ResourceRoots,\n\tscopeFor,\n\tupdatePrompt,\n\tupdateSkill,\n} from \"../storage/resource-writer.ts\";\nimport { EXTENSIONS_ENABLED, workspaceManager } from \"../workspace-manager.ts\";\n\ninterface PiSourceInfo {\n\tscope: \"user\" | \"project\" | \"temporary\";\n\tsource: string;\n\tpath: string;\n}\n\nfunction toResourceSource(info: PiSourceInfo): ResourceSource {\n\treturn {\n\t\tscope: info.scope,\n\t\tlabel: info.source,\n\t\tpath: info.path,\n\t};\n}\n\n/**\n * Scan the two extension discovery directories to detect extension files\n * that exist on disk but were not loaded (because extensions are disabled).\n * This lets the UI tell the user \"you have extensions installed but they\n * are disabled\" rather than leaving them to wonder why nothing works.\n *\n * Only meaningful when `EXTENSIONS_ENABLED === false`; callers should skip\n * the scan when the operator opted in via `PI_PILOT_ENABLE_EXTENSIONS=1`.\n */\nasync function scanExtensionDirs(workspaceCwd: string): Promise<string[]> {\n\tconst dirs = [join(getAgentDir(), \"extensions\"), join(workspaceCwd, \".pi\", \"extensions\")];\n\tconst found: string[] = [];\n\tfor (const dir of dirs) {\n\t\ttry {\n\t\t\tconst entries = await readdir(dir, { withFileTypes: true });\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (entry.isFile() && (entry.name.endsWith(\".ts\") || entry.name.endsWith(\".js\"))) {\n\t\t\t\t\tfound.push(join(dir, entry.name));\n\t\t\t\t} else if (entry.isDirectory()) {\n\t\t\t\t\t// Could be a package-style extension; list the directory.\n\t\t\t\t\tfound.push(join(dir, entry.name));\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Directory doesn't exist or isn't readable — that's fine.\n\t\t}\n\t}\n\treturn found;\n}\n\n/** Build a ResourcesResponse from the current loader state.\n *\n * `roots` is supplied so each skill/prompt's `managed` flag can be set:\n * resources whose filePath is under one of pi-pilot's writeable roots get\n * Edit/Delete affordances; resources that came from packages, settings\n * extras, ~/.agents/ paths, or CLI flags are read-only.\n *\n * `workspaceCwd` is the workspace's root directory, used to scan for\n * disabled extensions when extensions are off.\n */\nasync function snapshot(\n\tworkspaceId: string,\n\troots: ResourceRoots,\n\tworkspaceCwd: string,\n): Promise<ResourcesResponse> {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) throw new HttpError(500, \"runtime not initialized\");\n\tconst loader = runtime.services.resourceLoader;\n\n\tconst { skills } = loader.getSkills();\n\tconst { prompts } = loader.getPrompts();\n\tconst extResult = loader.getExtensions();\n\n\t// Pi resolves command-name collisions across extensions by appending\n\t// `:N` to later registrations (two extensions registering `plan` become\n\t// invocation names `plan` and `plan:1`). We surface those resolved\n\t// names so the composer's autocomplete picks them up and a user-typed\n\t// `/plan:1` actually invokes the right handler. The runner is the only\n\t// public source of truth for resolved names — the raw per-extension\n\t// `Map<string, RegisteredCommand>.keys()` doesn't know about\n\t// cross-extension conflicts.\n\t//\n\t// `extensionRunner` becomes available after `bindExtensions()` (called\n\t// in workspace-manager.build); the `?? []` fallback is defensive for\n\t// the brief window after runtime creation but before bind.\n\tconst resolvedExtCommandsByPath = new Map<string, string[]>();\n\tconst resolvedCommands = runtime.session.extensionRunner?.getRegisteredCommands() ?? [];\n\tfor (const cmd of resolvedCommands) {\n\t\tconst key = cmd.sourceInfo.path;\n\t\tlet list = resolvedExtCommandsByPath.get(key);\n\t\tif (!list) {\n\t\t\tlist = [];\n\t\t\tresolvedExtCommandsByPath.set(key, list);\n\t\t}\n\t\tlist.push(cmd.invocationName);\n\t}\n\n\tconst skillsOut: SkillInfo[] = skills.map((s) => ({\n\t\tname: s.name,\n\t\tdescription: s.description,\n\t\tfilePath: s.filePath,\n\t\tdisableModelInvocation: s.disableModelInvocation,\n\t\tsource: toResourceSource(s.sourceInfo),\n\t\tmanaged: scopeFor(s.filePath, roots) !== undefined,\n\t}));\n\n\tconst promptsOut: PromptInfo[] = prompts.map((p) => ({\n\t\tname: p.name,\n\t\tdescription: p.description,\n\t\targumentHint: p.argumentHint,\n\t\tfilePath: p.filePath,\n\t\tcontent: p.content,\n\t\tsource: toResourceSource(p.sourceInfo),\n\t\tmanaged: scopeFor(p.filePath, roots) !== undefined,\n\t}));\n\n\tconst extensionsOut: ExtensionInfo[] = extResult.extensions.map((e) => {\n\t\t// Match on `sourceInfo.path` because that's what ResolvedCommand's\n\t\t// sourceInfo carries; `e.path` may differ for package-style\n\t\t// extensions. Fall back to the raw command keys if the runner\n\t\t// hasn't populated yet (defensive — shouldn't happen post-bind).\n\t\tconst resolved = resolvedExtCommandsByPath.get(e.sourceInfo.path);\n\t\treturn {\n\t\t\tpath: e.path,\n\t\t\tresolvedPath: e.resolvedPath,\n\t\t\tsource: toResourceSource(e.sourceInfo),\n\t\t\ttools: [...e.tools.keys()],\n\t\t\tcommands: resolved ?? [...e.commands.keys()],\n\t\t\tflags: [...e.flags.keys()],\n\t\t\tshortcuts: [...e.shortcuts.keys()],\n\t\t};\n\t});\n\n\tconst extensionErrors: ExtensionLoadError[] = extResult.errors.map((err) => ({\n\t\tpath: err.path,\n\t\terror: err.error,\n\t}));\n\n\t// Only surface \"you have extensions on disk that we skipped\" when we\n\t// actually skipped them. When the operator opted in via the env flag,\n\t// any extensions found on disk are represented by `extensions` /\n\t// `extensionErrors` instead, and surfacing them a second time here\n\t// would double-count.\n\tconst disabledExtensions = EXTENSIONS_ENABLED ? [] : await scanExtensionDirs(workspaceCwd);\n\n\treturn {\n\t\tskills: skillsOut,\n\t\tprompts: promptsOut,\n\t\textensionsEnabled: EXTENSIONS_ENABLED,\n\t\textensions: extensionsOut,\n\t\textensionErrors,\n\t\tdisabledExtensions,\n\t};\n}\n\nasync function rootsFor(\n\tworkspaceId: string,\n): Promise<{ roots: ResourceRoots; workspaceCwd: string }> {\n\tconst ws = await getWorkspace(workspaceId);\n\tif (!ws) throw new HttpError(404, \"workspace not found\");\n\tconst roots = resolveResourceRoots({ agentDir: getAgentDir(), workspaceCwd: ws.path });\n\treturn { roots, workspaceCwd: ws.path };\n}\n\n/** Map a thrown error into a Hono JSON response. */\nfunction respondError(c: Context, err: unknown) {\n\tif (err instanceof HttpError) {\n\t\treturn c.json({ ok: false, error: err.message }, err.status as 400 | 404 | 409 | 500);\n\t}\n\tconst message = err instanceof Error ? err.message : String(err);\n\tconsole.error(`[api/resources] unexpected error:`, err);\n\treturn c.json({ ok: false, error: message }, 500);\n}\n\n/** Reload the loader so subsequent reads see the change we just made. */\nasync function reload(workspaceId: string): Promise<void> {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) throw new HttpError(500, \"runtime not initialized\");\n\tawait runtime.services.resourceLoader.reload();\n}\n\nexport function mountResourcesRoute(app: Hono): void {\n\t// ---------- GET inventory ----------\n\tapp.get(\"/:id/resources\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- POST reload ----------\n\tapp.post(\"/:id/resources/reload\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tawait reload(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- GET skill body ----------\n\tapp.get(\"/:id/resources/skill\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tconst { roots } = await rootsFor(id);\n\t\t\tconst scope = scopeFor(filePath, roots);\n\t\t\tif (scope === undefined) {\n\t\t\t\treturn c.json({ ok: false, error: \"skill file is not under a managed root\" }, 400);\n\t\t\t}\n\t\t\tconst data = await readSkillFile(filePath, roots);\n\t\t\tconst body: ResourceFileResponse = {\n\t\t\t\tfilePath,\n\t\t\t\tscope,\n\t\t\t\tname: data.name,\n\t\t\t\tdescription: data.description,\n\t\t\t\tdisableModelInvocation: data.disableModelInvocation,\n\t\t\t\tbody: data.body,\n\t\t\t};\n\t\t\treturn c.json(body);\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- POST skills (create) ----------\n\tapp.post(\"/:id/resources/skills\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as CreateSkillRequest | null;\n\t\tif (!body || !isScope(body.scope) || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"scope, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait createSkill({\n\t\t\t\troots,\n\t\t\t\tscope: body.scope,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\tbody: body.body,\n\t\t\t\tdisableModelInvocation: body.disableModelInvocation,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- PUT skills (update) ----------\n\tapp.put(\"/:id/resources/skills\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as UpdateSkillRequest | null;\n\t\tif (!body || typeof body.filePath !== \"string\" || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"filePath, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait updateSkill({\n\t\t\t\troots,\n\t\t\t\tfilePath: body.filePath,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\tbody: body.body,\n\t\t\t\tdisableModelInvocation: body.disableModelInvocation,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- DELETE skills ----------\n\tapp.delete(\"/:id/resources/skills\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait deleteSkill(filePath, roots);\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- GET prompt body ----------\n\tapp.get(\"/:id/resources/prompt\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tconst { roots } = await rootsFor(id);\n\t\t\tconst scope = scopeFor(filePath, roots);\n\t\t\tif (scope === undefined) {\n\t\t\t\treturn c.json({ ok: false, error: \"prompt file is not under a managed root\" }, 400);\n\t\t\t}\n\t\t\tconst data = await readPromptFile(filePath, roots);\n\t\t\tconst body: ResourceFileResponse = {\n\t\t\t\tfilePath,\n\t\t\t\tscope,\n\t\t\t\tname: data.name,\n\t\t\t\tdescription: data.description,\n\t\t\t\targumentHint: data.argumentHint,\n\t\t\t\tbody: data.body,\n\t\t\t};\n\t\t\treturn c.json(body);\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- POST prompts (create) ----------\n\tapp.post(\"/:id/resources/prompts\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as CreatePromptRequest | null;\n\t\tif (!body || !isScope(body.scope) || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"scope, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait createPrompt({\n\t\t\t\troots,\n\t\t\t\tscope: body.scope,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\targumentHint: body.argumentHint,\n\t\t\t\tbody: body.body,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- PUT prompts (update) ----------\n\tapp.put(\"/:id/resources/prompts\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as UpdatePromptRequest | null;\n\t\tif (!body || typeof body.filePath !== \"string\" || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"filePath, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait updatePrompt({\n\t\t\t\troots,\n\t\t\t\tfilePath: body.filePath,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\targumentHint: body.argumentHint,\n\t\t\t\tbody: body.body,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- DELETE prompts ----------\n\tapp.delete(\"/:id/resources/prompts\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait deletePrompt(filePath, roots);\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n}\n\nfunction isScope(value: unknown): value is ResourceScope {\n\treturn value === \"user\" || value === \"project\";\n}\n","/**\n * /api/fs/browse — directory listing for the web's <FsBrowser>.\n *\n * Only directories are returned (we never list files). Hidden entries are\n * excluded by default; pass ?showHidden=1 to include them.\n *\n * The `path` query parameter must be absolute. If omitted, defaults to the\n * user's home directory.\n */\n\nimport { readdir } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\nimport { Hono } from \"hono\";\nimport type { BrowseFsResponse, FsEntry } from \"@pi-pilot/shared\";\n\nexport const fsRoute = new Hono();\n\nfsRoute.get(\"/browse\", async (c) => {\n\tconst rawPath = c.req.query(\"path\");\n\tconst showHidden = c.req.query(\"showHidden\") === \"1\";\n\n\tconst target = rawPath && isAbsolute(rawPath) ? resolve(rawPath) : homedir();\n\tlet dirents;\n\ttry {\n\t\tdirents = await readdir(target, { withFileTypes: true });\n\t} catch (err) {\n\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\tconst msg = code === \"EACCES\" ? \"permission denied\" : code === \"ENOENT\" ? \"not found\" : \"read failed\";\n\t\treturn c.json({ ok: false, error: msg, path: target }, 400);\n\t}\n\n\tconst entries: FsEntry[] = dirents\n\t\t.filter((d) => d.isDirectory())\n\t\t.filter((d) => showHidden || !d.name.startsWith(\".\"))\n\t\t.map((d) => ({\n\t\t\tname: d.name,\n\t\t\tpath: join(target, d.name),\n\t\t\ttype: \"dir\" as const,\n\t\t}))\n\t\t.sort((a, b) => a.name.localeCompare(b.name));\n\n\tconst parent = (() => {\n\t\tconst p = dirname(target);\n\t\treturn p === target ? null : p;\n\t})();\n\n\tconst body: BrowseFsResponse = { path: target, parent, entries };\n\treturn c.json(body);\n});\n","/**\n * /api/model-configs routes.\n *\n * Reads and writes ~/.pi/agent/models.json. After any mutation the active\n * workspace runtimes have their modelRegistry refreshed so changes take\n * effect immediately without restarting the server.\n */\n\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { Hono } from \"hono\";\nimport {\n\tgetAgentDir,\n} from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tModelsConfig,\n\tModelsConfigResponse,\n\tSetModelsConfigRequest,\n\tUpsertProviderRequest,\n\tAddModelToProviderRequest,\n\tUpdateModelRequest,\n} from \"@pi-pilot/shared\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\n\nexport const modelConfigsRoute = new Hono();\n\n// ---------- helpers ----------\n\n/**\n * Serialize all read-modify-write operations on models.json. Although\n * Node.js is single-threaded, the async gaps between readFile and\n * writeFile allow interleaving of concurrent requests — without a lock\n * a later write could silently overwrite an earlier mutation.\n */\nlet writeLock = Promise.resolve();\nfunction withWriteLock<T>(fn: () => Promise<T>): Promise<T> {\n\tconst next = writeLock.then(fn, fn);\n\twriteLock = next.then(() => {}, () => {});\n\treturn next;\n}\n\nfunction modelsPath(): string {\n\treturn join(getAgentDir(), \"models.json\");\n}\n\nasync function readModelsJson(): Promise<ModelsConfig> {\n\ttry {\n\t\tconst raw = await readFile(modelsPath(), \"utf-8\");\n\t\treturn JSON.parse(raw) as ModelsConfig;\n\t} catch (err) {\n\t\tif ((err as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n\t\t\treturn { providers: {} };\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nasync function writeModelsJson(config: ModelsConfig): Promise<void> {\n\tconst p = modelsPath();\n\tawait mkdir(dirname(p), { recursive: true });\n\tawait writeFile(p, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\n/** Lightweight error for validation failures inside withWriteLock. */\nclass ValidationError extends Error {\n\tconstructor(message: string, public readonly status: number) {\n\t\tsuper(message);\n\t}\n}\n\nfunction refreshRegistry(workspaceId?: string): void {\n\tif (!workspaceId) return;\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (runtime) {\n\t\ttry {\n\t\t\truntime.session.modelRegistry.refresh();\n\t\t} catch (e) {\n\t\t\tconsole.error(`[model-configs] refresh registry for ${workspaceId} failed:`, e);\n\t\t}\n\t}\n}\n\n// ---------- routes ----------\n\n/** GET /api/model-configs */\nmodelConfigsRoute.get(\"/\", async (c) => {\n\ttry {\n\t\tconst config = await readModelsJson();\n\t\tconst body: ModelsConfigResponse = { config };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** PUT /api/model-configs — replace entire models.json */\nmodelConfigsRoute.put(\"/\", async (c) => {\n\tconst body = (await c.req.json()) as SetModelsConfigRequest;\n\tif (!body?.config?.providers) {\n\t\treturn c.json({ ok: false, error: \"config.providers is required\" }, 400);\n\t}\n\ttry {\n\t\tawait withWriteLock(async () => {\n\t\t\tawait writeModelsJson(body.config);\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config: body.config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** POST /api/model-configs/providers — add or update a provider */\nmodelConfigsRoute.post(\"/providers\", async (c) => {\n\tconst body = (await c.req.json()) as UpsertProviderRequest;\n\tif (!body?.name || !body?.provider) {\n\t\treturn c.json({ ok: false, error: \"name and provider are required\" }, 400);\n\t}\n\tif (!body.provider.baseUrl || !body.provider.api || !body.provider.apiKey) {\n\t\treturn c.json({ ok: false, error: \"provider must have baseUrl, api, and apiKey\" }, 400);\n\t}\n\tif (!Array.isArray(body.provider.models)) {\n\t\treturn c.json({ ok: false, error: \"provider.models must be an array\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tcfg.providers[body.name] = body.provider;\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** DELETE /api/model-configs/providers?name=... — delete a provider */\nmodelConfigsRoute.delete(\"/providers\", async (c) => {\n\tconst name = c.req.query(\"name\");\n\tif (!name) {\n\t\treturn c.json({ ok: false, error: \"name query param is required\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[name]) {\n\t\t\t\tthrow new ValidationError(`provider \"${name}\" not found`, 404);\n\t\t\t}\n\t\t\tdelete cfg.providers[name];\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** POST /api/model-configs/providers/:provider/models — add a model */\nmodelConfigsRoute.post(\"/providers/:provider/models\", async (c) => {\n\tconst provider = c.req.param(\"provider\");\n\tconst body = (await c.req.json()) as AddModelToProviderRequest;\n\tif (!body?.model?.id || !body?.model?.name) {\n\t\treturn c.json({ ok: false, error: \"model.id and model.name are required\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[provider]) {\n\t\t\t\tthrow new ValidationError(`provider \"${provider}\" not found`, 404);\n\t\t\t}\n\t\t\tconst existing = cfg.providers[provider].models.find(m => m.id === body.model.id);\n\t\t\tif (existing) {\n\t\t\t\tthrow new ValidationError(`model \"${body.model.id}\" already exists in provider \"${provider}\"`, 409);\n\t\t\t}\n\t\t\tcfg.providers[provider].models.push(body.model);\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404 | 409);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** PUT /api/model-configs/providers/:provider/models/:modelId — update a model */\nmodelConfigsRoute.put(\"/providers/:provider/models/:modelId\", async (c) => {\n\tconst provider = c.req.param(\"provider\");\n\tconst modelId = c.req.param(\"modelId\");\n\tconst body = (await c.req.json()) as UpdateModelRequest;\n\tif (!body?.model) {\n\t\treturn c.json({ ok: false, error: \"model is required\" }, 400);\n\t}\n\tif (body.model.id !== modelId) {\n\t\treturn c.json({ ok: false, error: \"model.id does not match URL parameter\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[provider]) {\n\t\t\t\tthrow new ValidationError(`provider \"${provider}\" not found`, 404);\n\t\t\t}\n\t\t\tconst idx = cfg.providers[provider].models.findIndex(m => m.id === modelId);\n\t\t\tif (idx === -1) {\n\t\t\t\tthrow new ValidationError(`model \"${modelId}\" not found in provider \"${provider}\"`, 404);\n\t\t\t}\n\t\t\tcfg.providers[provider].models[idx] = body.model;\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** DELETE /api/model-configs/providers/:provider/models/:modelId — delete a model */\nmodelConfigsRoute.delete(\"/providers/:provider/models/:modelId\", async (c) => {\n\tconst provider = c.req.param(\"provider\");\n\tconst modelId = c.req.param(\"modelId\");\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[provider]) {\n\t\t\t\tthrow new ValidationError(`provider \"${provider}\" not found`, 404);\n\t\t\t}\n\t\t\tconst idx = cfg.providers[provider].models.findIndex(m => m.id === modelId);\n\t\t\tif (idx === -1) {\n\t\t\t\tthrow new ValidationError(`model \"${modelId}\" not found in provider \"${provider}\"`, 404);\n\t\t\t}\n\t\t\tcfg.providers[provider].models.splice(idx, 1);\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n","/**\n * WebSocket hub.\n *\n * One connection ↔ at most one (workspace, session) subscription. The\n * client switches subscription with another `subscribe` message; we just\n * tear down the old listener and attach a new one.\n *\n * On disconnect, all per-connection state is released. The runtime in the\n * WorkspaceManager is NOT torn down (other connections or future ones\n * may reuse it).\n *\n * Subscribed connections are registered with the workspace manager so it\n * can fan server-initiated messages (extension load errors, future\n * workspace-level events) out to them. pi extensions themselves are not\n * supported — `ctx.ui.*` calls are no-ops, so no UI request/response\n * traffic flows over the WS.\n */\n\nimport { WebSocketServer, type WebSocket } from \"ws\";\nimport type { Server } from \"node:http\";\nimport type { AgentSessionRuntime } from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tPiAssistantTiming,\n\tPiContextUsage,\n\tWsClientMessage,\n\tWsServerMessage,\n} from \"@pi-pilot/shared\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport { translatePiEvent } from \"./bridge.ts\";\n\ninterface ConnectionState {\n\tworkspaceId?: string;\n\tunsubscribeSession?: () => void;\n\tunsubscribeRebind?: () => void;\n}\n\n/**\n * Per-workspace session replacement lock. Serializes new_session, fork,\n * and switchSession calls so concurrent clicks or messages don't race\n * on the same runtime.\n */\nconst replacementLocks = new Map<string, Promise<void>>();\n\nfunction withReplacementLock(workspaceId: string, fn: () => Promise<void>): Promise<void> {\n\tconst prev = replacementLocks.get(workspaceId) ?? Promise.resolve();\n\tconst next = prev.then(fn, fn);\n\treplacementLocks.set(workspaceId, next);\n\t// Clean up reference when chain settles to avoid unbounded growth.\n\t// Use .then(cleanup, cleanup) so cleanup runs on rejection too and no\n\t// unhandled-rejection leaks from the derived promise.\n\tconst cleanup = () => {\n\t\tif (replacementLocks.get(workspaceId) === next) {\n\t\t\treplacementLocks.delete(workspaceId);\n\t\t}\n\t};\n\tnext.then(cleanup, cleanup);\n\treturn next;\n}\n\nexport function attachWsHub(httpServer: Server): WebSocketServer {\n\tconst wss = new WebSocketServer({ server: httpServer, path: \"/ws\" });\n\n\twss.on(\"connection\", (ws) => {\n\t\tconst state: ConnectionState = {};\n\n\t\tws.on(\"message\", async (raw) => {\n\t\t\tlet msg: WsClientMessage;\n\t\t\ttry {\n\t\t\t\tmsg = JSON.parse(raw.toString()) as WsClientMessage;\n\t\t\t} catch {\n\t\t\t\tsend(ws, { type: \"error\", message: \"invalid JSON\" });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait handle(ws, state, msg);\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\tsend(ws, { type: \"error\", message, command: msg.type });\n\t\t\t}\n\t\t});\n\n\t\tws.on(\"close\", () => {\n\t\t\tdetach(state, ws);\n\t\t});\n\t});\n\n\treturn wss;\n}\n\nasync function handle(ws: WebSocket, state: ConnectionState, msg: WsClientMessage): Promise<void> {\n\tswitch (msg.type) {\n\t\tcase \"subscribe\": {\n\t\t\tconst hadCurrentSubscription =\n\t\t\t\tstate.workspaceId === msg.workspaceId && !!state.unsubscribeSession;\n\n\t\t\t// Register this connection as a subscriber BEFORE building the\n\t\t\t// runtime. Extensions that fire `ctx.ui.notify(...)` from a\n\t\t\t// `session_start` handler emit it synchronously during\n\t\t\t// `getOrCreate`'s bindExtensions call — if no subscriber were\n\t\t\t// registered yet, the bridge would broadcast to an empty set and\n\t\t\t// the message would be lost.\n\t\t\tensureRebindListener(ws, state, msg.workspaceId);\n\t\t\tawait workspaceManager.getOrCreate(msg.workspaceId);\n\n\t\t\tlet switched = false;\n\t\t\tlet switchError: string | undefined;\n\t\t\tif (msg.sessionPath) {\n\t\t\t\tawait withReplacementLock(msg.workspaceId, async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tswitched = await workspaceManager.switchSession(msg.workspaceId, msg.sessionPath!);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tswitchError = err instanceof Error ? err.message : String(err);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (!switched && !hadCurrentSubscription) {\n\t\t\t\tbindCurrentSession(ws, state, msg.workspaceId);\n\t\t\t}\n\t\t\tif (switchError) {\n\t\t\t\tsend(ws, { type: \"error\", message: switchError, command: \"subscribe\" });\n\t\t\t}\n\t\t\tsend(ws, { type: \"ack\", command: \"subscribe\" });\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"prompt\": {\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"prompt\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Reject prompts while a session replacement is in progress.\n\t\t\tif (replacementLocks.has(wsId)) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"session switching in progress\", command: \"prompt\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\tif (!runtime) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"runtime gone\", command: \"prompt\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Fire-and-forget; success is observable as `event` messages\n\t\t\t// streaming back through the subscription, so no `ack` is sent.\n\t\t\tvoid runtime.session\n\t\t\t\t.prompt(msg.message, {\n\t\t\t\t\tstreamingBehavior: msg.streamingBehavior,\n\t\t\t\t})\n\t\t\t\t.catch((err: unknown) => {\n\t\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\t\tsend(ws, { type: \"error\", message, command: \"prompt\" });\n\t\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"abort\": {\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"abort\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (replacementLocks.has(wsId)) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"session switching in progress\", command: \"abort\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\tif (!runtime) return;\n\t\t\t// Success is observable as an `agent_end` event; no ack needed.\n\t\t\tawait runtime.session.abort();\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"new_session\": {\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"new_session\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait withReplacementLock(msg.workspaceId, async () => {\n\t\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\t\tif (!runtime) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"runtime gone\", command: \"new_session\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (runtime.session.isStreaming) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"cannot create session while streaming\", command: \"new_session\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst result = await runtime.newSession();\n\t\t\t\tif (result.cancelled) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"new session cancelled\", command: \"new_session\" });\n\t\t\t\t}\n\t\t\t\t// Success → rebind fires automatically → client gets `subscribed`.\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"fork\": {\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"fork\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait withReplacementLock(msg.workspaceId, async () => {\n\t\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\t\tif (!runtime) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"runtime gone\", command: \"fork\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (runtime.session.isStreaming) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"cannot fork while streaming\", command: \"fork\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst result = await runtime.fork(msg.entryId);\n\t\t\t\tif (result.cancelled) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"fork cancelled\", command: \"fork\" });\n\t\t\t\t}\n\t\t\t\t// Success → rebind fires automatically → client gets `subscribed`.\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tdefault: {\n\t\t\t// Exhaustiveness check\n\t\t\tconst _: never = msg;\n\t\t\tvoid _;\n\t\t\tsend(ws, { type: \"error\", message: \"unknown command\" });\n\t\t}\n\t}\n}\n\nfunction ensureRebindListener(ws: WebSocket, state: ConnectionState, workspaceId: string): void {\n\tif (state.workspaceId === workspaceId && state.unsubscribeRebind) return;\n\n\tdetach(state, ws);\n\tstate.workspaceId = workspaceId;\n\tworkspaceManager.addSubscriber(workspaceId, ws);\n\tstate.unsubscribeRebind = workspaceManager.onSessionReplaced(workspaceId, () => {\n\t\tif (state.workspaceId !== workspaceId) return;\n\t\tbindCurrentSession(ws, state, workspaceId);\n\t});\n}\n\nfunction bindCurrentSession(ws: WebSocket, state: ConnectionState, workspaceId: string): void {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) {\n\t\tsend(ws, { type: \"error\", message: \"runtime gone\", command: \"subscribe\" });\n\t\treturn;\n\t}\n\n\tstate.unsubscribeSession?.();\n\tconst session = runtime.session;\n\tconst sessionPath = session.sessionFile ?? null;\n\n\t// Per-binding timing state. Lives in the subscribe closure so it\n\t// auto-resets on session replacement (each new bindCurrentSession\n\t// allocates a fresh closure). Tracks the wall-clock anchors needed to\n\t// derive `PiAssistantTiming` once the assistant turn completes.\n\tlet assistantStartAt: number | undefined;\n\tlet assistantFirstTokenAt: number | undefined;\n\n\tstate.unsubscribeSession = session.subscribe((ev) => {\n\t\tconst payload = translatePiEvent(ev);\n\t\tif (!payload) return;\n\n\t\t// Capture timing anchors *before* forwarding the event so the\n\t\t// firstToken stamp is taken at the moment the delta arrives at\n\t\t// the server, not at whatever later moment the listener runs.\n\t\tif (payload.kind === \"message_start\" && payload.role === \"assistant\") {\n\t\t\tassistantStartAt = performance.now();\n\t\t\tassistantFirstTokenAt = undefined;\n\t\t} else if (\n\t\t\tpayload.kind === \"message_update\" &&\n\t\t\tpayload.delta.kind === \"text\" &&\n\t\t\tassistantStartAt !== undefined &&\n\t\t\tassistantFirstTokenAt === undefined\n\t\t) {\n\t\t\tassistantFirstTokenAt = performance.now();\n\t\t}\n\n\t\tsend(ws, {\n\t\t\ttype: \"event\",\n\t\t\tworkspaceId,\n\t\t\tsessionPath,\n\t\t\tpayload,\n\t\t});\n\n\t\t// Emit timing telemetry once the assistant message closes. Skipped\n\t\t// if `message_start` wasn't observed (defensive — should not happen\n\t\t// in a healthy event stream, but a hot-reconnect mid-turn could).\n\t\tif (\n\t\t\tpayload.kind === \"message_end\" &&\n\t\t\tpayload.role === \"assistant\" &&\n\t\t\tassistantStartAt !== undefined\n\t\t) {\n\t\t\tconst now = performance.now();\n\t\t\tconst timing: PiAssistantTiming = {\n\t\t\t\tkind: \"assistant_timing\",\n\t\t\t\tfirstTokenMs:\n\t\t\t\t\tassistantFirstTokenAt !== undefined\n\t\t\t\t\t\t? Math.round(assistantFirstTokenAt - assistantStartAt)\n\t\t\t\t\t\t: null,\n\t\t\t\ttotalMs: Math.round(now - assistantStartAt),\n\t\t\t};\n\t\t\tsend(ws, {\n\t\t\t\ttype: \"event\",\n\t\t\t\tworkspaceId,\n\t\t\t\tsessionPath,\n\t\t\t\tpayload: timing,\n\t\t\t});\n\t\t\tassistantStartAt = undefined;\n\t\t\tassistantFirstTokenAt = undefined;\n\t\t}\n\n\t\t// Context usage shifts on turn completion (new assistant usage),\n\t\t// on compaction end (token count just reset), and on model swap\n\t\t// (context window may have changed). Sample after the event so the\n\t\t// client never sees a stale window. `getContextUsage()` returns\n\t\t// undefined when there's no model or no contextWindow — in that\n\t\t// case we don't bother sending; the existing snapshot stays.\n\t\tif (\n\t\t\tpayload.kind === \"agent_end\" ||\n\t\t\tpayload.kind === \"compaction_end\" ||\n\t\t\tpayload.kind === \"session_info_changed\" ||\n\t\t\tpayload.kind === \"thinking_level_changed\"\n\t\t) {\n\t\t\tsendContextUsage(ws, runtime, workspaceId, sessionPath);\n\t\t}\n\t});\n\n\tsend(ws, {\n\t\ttype: \"subscribed\",\n\t\tworkspaceId,\n\t\tsessionPath,\n\t\tsessionId: session.sessionId,\n\t});\n\t// Prime the client with a usage snapshot so the indicator isn't blank\n\t// until the next agent_end.\n\tsendContextUsage(ws, runtime, workspaceId, sessionPath);\n}\n\nfunction sendContextUsage(\n\tws: WebSocket,\n\truntime: AgentSessionRuntime,\n\tworkspaceId: string,\n\tsessionPath: string | null,\n): void {\n\tconst usage = runtime.session.getContextUsage();\n\tif (!usage) return;\n\tconst payload: PiContextUsage = {\n\t\tkind: \"context_usage\",\n\t\ttokens: usage.tokens,\n\t\tcontextWindow: usage.contextWindow,\n\t\tpercent: usage.percent,\n\t};\n\tsend(ws, {\n\t\ttype: \"event\",\n\t\tworkspaceId,\n\t\tsessionPath,\n\t\tpayload,\n\t});\n}\n\nfunction detach(state: ConnectionState, ws?: WebSocket): void {\n\tstate.unsubscribeSession?.();\n\tstate.unsubscribeSession = undefined;\n\tstate.unsubscribeRebind?.();\n\tstate.unsubscribeRebind = undefined;\n\tif (state.workspaceId && ws) {\n\t\tworkspaceManager.removeSubscriber(state.workspaceId, ws);\n\t}\n\tstate.workspaceId = undefined;\n}\n\nfunction send(ws: WebSocket, msg: WsServerMessage): void {\n\tif (ws.readyState !== ws.OPEN) return;\n\tws.send(JSON.stringify(msg));\n}\n","/**\n * Translate a pi AgentSessionEvent into our narrow wire payload.\n *\n * Anything not in the switch is dropped (returns undefined). Adding support\n * for a new event type means: add a case here, and a matching variant in\n * `@pi-pilot/shared/ws-protocol.ts`.\n */\n\nimport type { AgentSessionEvent } from \"@earendil-works/pi-coding-agent\";\nimport type { PiEventPayload } from \"@pi-pilot/shared\";\n\nexport function translatePiEvent(ev: AgentSessionEvent): PiEventPayload | undefined {\n\tswitch (ev.type) {\n\t\tcase \"agent_start\":\n\t\t\treturn { kind: \"agent_start\" };\n\n\t\tcase \"agent_end\":\n\t\t\treturn { kind: \"agent_end\", willRetry: ev.willRetry };\n\n\t\tcase \"turn_start\":\n\t\t\treturn { kind: \"turn_start\" };\n\n\t\tcase \"turn_end\":\n\t\t\treturn { kind: \"turn_end\" };\n\n\t\tcase \"message_start\": {\n\t\t\tconst role = roleOf(ev.message);\n\t\t\t// Carry user text on the wire so the client can reconcile its\n\t\t\t// optimistic bubble (or insert one when no optimistic push\n\t\t\t// happened — extension-command path). Other roles stream via\n\t\t\t// message_update deltas; a start-time snapshot would be stale.\n\t\t\tconst text = role === \"user\" ? extractUserText(ev.message) : undefined;\n\t\t\treturn { kind: \"message_start\", role, text };\n\t\t}\n\n\t\tcase \"message_end\":\n\t\t\treturn { kind: \"message_end\", role: roleOf(ev.message) };\n\n\t\tcase \"message_update\": {\n\t\t\tconst ame = ev.assistantMessageEvent;\n\t\t\tif (ame.type === \"text_delta\") {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"message_update\",\n\t\t\t\t\tdelta: { kind: \"text\", contentIndex: ame.contentIndex, text: ame.delta },\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (ame.type === \"thinking_delta\") {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"message_update\",\n\t\t\t\t\tdelta: { kind: \"thinking\", contentIndex: ame.contentIndex, text: ame.delta },\n\t\t\t\t};\n\t\t\t}\n\t\t\t// Other deltas (start/end, toolcall) are not surfaced in MVP.\n\t\t\treturn { kind: \"message_update\", delta: { kind: \"other\" } };\n\t\t}\n\n\t\tcase \"tool_execution_start\":\n\t\t\treturn {\n\t\t\t\tkind: \"tool_execution_start\",\n\t\t\t\ttoolCallId: ev.toolCallId,\n\t\t\t\ttoolName: ev.toolName,\n\t\t\t\targs: ev.args,\n\t\t\t};\n\n\t\tcase \"tool_execution_update\":\n\t\t\treturn {\n\t\t\t\tkind: \"tool_execution_update\",\n\t\t\t\ttoolCallId: ev.toolCallId,\n\t\t\t\ttoolName: ev.toolName,\n\t\t\t\tpartialText: extractText(ev.partialResult),\n\t\t\t};\n\n\t\tcase \"tool_execution_end\":\n\t\t\treturn {\n\t\t\t\tkind: \"tool_execution_end\",\n\t\t\t\ttoolCallId: ev.toolCallId,\n\t\t\t\ttoolName: ev.toolName,\n\t\t\t\tisError: ev.isError,\n\t\t\t\ttext: extractText(ev.result),\n\t\t\t};\n\n\t\tcase \"queue_update\":\n\t\t\treturn {\n\t\t\t\tkind: \"queue_update\",\n\t\t\t\tsteering: [...ev.steering],\n\t\t\t\tfollowUp: [...ev.followUp],\n\t\t\t};\n\n\t\tcase \"auto_retry_start\":\n\t\t\treturn {\n\t\t\t\tkind: \"auto_retry_start\",\n\t\t\t\tattempt: ev.attempt,\n\t\t\t\tmaxAttempts: ev.maxAttempts,\n\t\t\t\tdelayMs: ev.delayMs,\n\t\t\t\terrorMessage: ev.errorMessage,\n\t\t\t};\n\n\t\tcase \"auto_retry_end\":\n\t\t\treturn {\n\t\t\t\tkind: \"auto_retry_end\",\n\t\t\t\tsuccess: ev.success,\n\t\t\t\tattempt: ev.attempt,\n\t\t\t\tfinalError: ev.finalError,\n\t\t\t};\n\n\t\tcase \"compaction_start\":\n\t\t\treturn { kind: \"compaction_start\", reason: ev.reason };\n\n\t\tcase \"compaction_end\":\n\t\t\treturn {\n\t\t\t\tkind: \"compaction_end\",\n\t\t\t\treason: ev.reason,\n\t\t\t\taborted: ev.aborted,\n\t\t\t\twillRetry: ev.willRetry,\n\t\t\t\terrorMessage: ev.errorMessage,\n\t\t\t};\n\n\t\tcase \"session_info_changed\":\n\t\t\treturn { kind: \"session_info_changed\", name: ev.name };\n\n\t\tcase \"thinking_level_changed\":\n\t\t\treturn { kind: \"thinking_level_changed\", level: ev.level };\n\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nfunction roleOf(message: unknown): \"user\" | \"assistant\" | \"toolResult\" | \"bashExecution\" {\n\tconst role = (message as { role?: string } | undefined)?.role;\n\tif (role === \"user\" || role === \"assistant\" || role === \"toolResult\" || role === \"bashExecution\") {\n\t\treturn role;\n\t}\n\treturn \"assistant\";\n}\n\n/**\n * Best-effort extraction of user-message text. Pi's user-role\n * `AgentMessage` has either `content: string` (single text body) or\n * `content: ContentBlock[]` (typed blocks; we keep only `TextContent`).\n * Returns `undefined` when neither shape yields text — the wire field\n * is optional so an undefined here is fine.\n */\nfunction extractUserText(message: unknown): string | undefined {\n\tif (!message || typeof message !== \"object\") return undefined;\n\tconst content = (message as { content?: unknown }).content;\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return undefined;\n\tconst parts: string[] = [];\n\tfor (const block of content) {\n\t\tif (block && typeof block === \"object\" && (block as { type?: string }).type === \"text\") {\n\t\t\tconst text = (block as { text?: unknown }).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\treturn parts.length === 0 ? undefined : parts.join(\"\");\n}\n\n/** Best-effort extraction of human-readable text from a tool result-shaped object. */\nfunction extractText(result: unknown): string | undefined {\n\tif (!result || typeof result !== \"object\") return undefined;\n\tconst content = (result as { content?: unknown }).content;\n\tif (!Array.isArray(content)) return undefined;\n\tconst parts: string[] = [];\n\tfor (const c of content) {\n\t\tif (c && typeof c === \"object\" && (c as { type?: string }).type === \"text\") {\n\t\t\tconst text = (c as { text?: unknown }).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\treturn parts.length === 0 ? undefined : parts.join(\"\");\n}\n"],"mappings":";;;AAQA,SAAS,kBAAkB;AAC3B,SAAS,YAAAA,iBAAgB;AACzB,SAAS,WAAAC,UAAS,SAAS,QAAAC,OAAM,WAAAC,UAAS,OAAAC,YAAW;AACrD,SAAS,qBAAqB;AAC9B,SAAS,aAAa;AAEtB,SAAS,QAAAC,aAAY;AACrB,SAAS,YAAY;;;ACTrB,SAAS,eAAe;AACxB,SAAS,YAAY;AAEd,IAAM,SAAS;AAAA;AAAA,EAErB,MAAM,OAAO,QAAQ,IAAI,iBAAiB,IAAI;AAAA;AAAA;AAAA,EAI9C,MAAM;AAAA;AAAA,EAGN,SAAS,QAAQ,IAAI,qBAAqB,KAAK,QAAQ,GAAG,OAAO,OAAO;AAAA;AAAA,EAGxE,YAAY,QAAQ,IAAI,wBAAwB;AACjD;;;AClBA,SAAS,QAAAC,aAAY;AACrB,SAAS,YAAAC,WAAU,cAAAC,aAAY,WAAAC,gBAAe;AAC9C,SAAS,YAAY;;;ACiCrB;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,SAAS,YAAY,QAAAC,OAAM,SAAS,WAAW;AAKxD,IAAM,gBAAgB;AACtB,IAAM,iBAAiB;AAEvB,SAAS,gBAAgB,MAAoB;AAC5C,MAAI,CAAC,cAAc,KAAK,IAAI,GAAG;AAC9B,UAAM,IAAI,UAAU,KAAK,8FAA8F;AAAA,EACxH;AACD;AAEA,SAAS,iBAAiB,MAAoB;AAC7C,MAAI,CAAC,eAAe,KAAK,IAAI,GAAG;AAC/B,UAAM,IAAI,UAAU,KAAK,uEAAuE;AAAA,EACjG;AACD;AAOA,SAAS,YAAY,QAAgB,OAAuB;AAC3D,QAAM,IAAI,QAAQ,MAAM;AACxB,QAAM,KAAK,MAAM,KAAK,CAAC,SAAS;AAC/B,UAAM,IAAI,QAAQ,IAAI;AACtB,WAAO,MAAM,KAAK,EAAE,WAAW,IAAI,GAAG;AAAA,EACvC,CAAC;AACD,MAAI,CAAC,IAAI;AACR,UAAM,IAAI,UAAU,KAAK,iDAAiD,MAAM,EAAE;AAAA,EACnF;AACD;AAEA,SAAS,YAAY,OAAsB,MAAc,OAA8B;AACtF,kBAAgB,IAAI;AACpB,QAAM,OAAO,UAAU,SAAS,MAAM,aAAa,MAAM;AACzD,SAAOA,MAAK,MAAM,IAAI;AACvB;AAEA,SAAS,cAAc,OAAsB,MAAc,OAA8B;AACxF,mBAAiB,IAAI;AACrB,QAAM,OAAO,UAAU,SAAS,MAAM,cAAc,MAAM;AAC1D,SAAOA,MAAK,MAAM,GAAG,IAAI,KAAK;AAC/B;AAaO,SAAS,qBAAqB,MAGnB;AACjB,SAAO;AAAA,IACN,YAAYA,MAAK,KAAK,UAAU,QAAQ;AAAA,IACxC,eAAeA,MAAK,KAAK,cAAc,OAAO,QAAQ;AAAA,IACtD,aAAaA,MAAK,KAAK,UAAU,SAAS;AAAA,IAC1C,gBAAgBA,MAAK,KAAK,cAAc,OAAO,SAAS;AAAA,EACzD;AACD;AAGO,SAAS,SAAS,SAAiB,OAAiD;AAC1F,QAAM,IAAI,QAAQ,OAAO;AACzB,aAAW,CAAC,MAAM,KAAK,KAAK;AAAA,IAC3B,CAAC,MAAM,YAAY,MAAM;AAAA,IACzB,CAAC,MAAM,eAAe,SAAS;AAAA,IAC/B,CAAC,MAAM,aAAa,MAAM;AAAA,IAC1B,CAAC,MAAM,gBAAgB,SAAS;AAAA,EACjC,GAAY;AACX,UAAM,IAAI,QAAQ,IAAI;AACtB,QAAI,MAAM,KAAK,EAAE,WAAW,IAAI,GAAG,EAAG,QAAO;AAAA,EAC9C;AACA,SAAO;AACR;AA0BA,SAAS,UAAU,SAA6B;AAC/C,QAAM,WAAW,QAAQ,MAAM,OAAO;AACtC,MAAI,SAAS,CAAC,MAAM,OAAO;AAC1B,WAAO,EAAE,aAAa,CAAC,GAAG,OAAO,CAAC,GAAG,MAAM,QAAQ;AAAA,EACpD;AACA,QAAM,KAAuC,CAAC;AAC9C,QAAM,QAA2B,CAAC;AAClC,MAAI,IAAI;AACR,SAAO,IAAI,SAAS,UAAU,SAAS,CAAC,MAAM,OAAO;AACpD,UAAM,OAAO,SAAS,CAAC,KAAK;AAC5B,UAAM,IAAI,KAAK,MAAM,2CAA2C;AAChE,QAAI,GAAG;AACN,YAAM,MAAM,EAAE,CAAC;AACf,YAAM,SAAS,EAAE,CAAC,KAAK;AACvB,YAAM,QAAQ,YAAY,MAAM;AAChC,SAAG,GAAG,IAAI;AACV,YAAM,KAAK,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC;AAAA,IACrC;AACA;AAAA,EACD;AACA,MAAI,KAAK,SAAS,QAAQ;AAEzB,WAAO,EAAE,aAAa,CAAC,GAAG,OAAO,CAAC,GAAG,MAAM,QAAQ;AAAA,EACpD;AACA,QAAM,OAAO,SAAS,MAAM,IAAI,CAAC,EAAE,KAAK,IAAI;AAC5C,SAAO,EAAE,aAAa,IAAI,OAAO,KAAK;AACvC;AAYA,SAAS,WAAW,OAA0B,WAAwC;AACrF,QAAM,QAAQ,IAAI,IAAI,SAAS;AAC/B,SAAO,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG;AAC/D;AAEA,SAAS,YAAY,KAA+B;AACnD,MAAI,QAAQ,OAAQ,QAAO;AAC3B,MAAI,QAAQ,QAAS,QAAO;AAC5B,MAAI,IAAI,WAAW,GAAG,KAAK,IAAI,SAAS,GAAG,KAAK,IAAI,UAAU,GAAG;AAChE,WAAO,IAAI,MAAM,GAAG,EAAE,EAAE,QAAQ,cAAc,IAAI;AAAA,EACnD;AACA,MAAI,IAAI,WAAW,GAAG,KAAK,IAAI,SAAS,GAAG,KAAK,IAAI,UAAU,GAAG;AAChE,WAAO,IAAI,MAAM,GAAG,EAAE,EAAE,QAAQ,OAAO,GAAG;AAAA,EAC3C;AACA,SAAO;AACR;AAaA,SAAS,UACR,QACA,MACA,SAAmB,CAAC,GACX;AACT,QAAM,MAAgB,CAAC,KAAK;AAC5B,aAAW,CAAC,KAAK,KAAK,KAAK,QAAQ;AAClC,QAAI,UAAU,UAAa,UAAU,GAAI;AACzC,QAAI,OAAO,UAAU,WAAW;AAC/B,UAAI,KAAK,GAAG,GAAG,KAAK,QAAQ,SAAS,OAAO,EAAE;AAAA,IAC/C,OAAO;AACN,UAAI,KAAK,GAAG,GAAG,KAAK,aAAa,KAAK,CAAC,EAAE;AAAA,IAC1C;AAAA,EACD;AACA,aAAW,OAAO,OAAQ,KAAI,KAAK,GAAG;AACtC,MAAI,KAAK,KAAK;AACd,QAAM,cAAc,KAAK,QAAQ,QAAQ,EAAE;AAC3C,QAAM,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC;AAAA,EAAK,WAAW;AAAA;AAC9C,SAAO;AACR;AAIA,IAAM,mBAAmB,CAAC,QAAQ,eAAe,0BAA0B;AAC3E,IAAM,oBAAoB,CAAC,eAAe,eAAe;AAEzD,SAAS,aAAa,OAAuB;AAG5C,QAAM,UAAU,MAAM,QAAQ,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAK;AAChE,SAAO,IAAI,OAAO;AACnB;AAaA,eAAsB,YAAY,MAAwC;AACzE,QAAM,MAAM,YAAY,KAAK,OAAO,KAAK,MAAM,KAAK,KAAK;AACzD,cAAY,KAAK,CAAC,KAAK,MAAM,YAAY,KAAK,MAAM,aAAa,CAAC;AAClE,MAAI,MAAM,OAAO,GAAG,GAAG;AACtB,UAAM,IAAI,UAAU,KAAK,2BAA2B,GAAG,EAAE;AAAA,EAC1D;AACA,QAAM,OAAOA,MAAK,KAAK,UAAU;AACjC,QAAM,OAAO;AAAA,IACZ;AAAA,MACC,CAAC,QAAQ,KAAK,IAAI;AAAA,MAClB,CAAC,eAAe,KAAK,WAAW;AAAA,MAChC,CAAC,4BAA4B,KAAK,sBAAsB;AAAA,IACzD;AAAA,IACA,KAAK;AAAA,EACN;AACA,QAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,QAAM,UAAU,MAAM,MAAM,MAAM;AAClC,SAAO;AACR;AAWA,eAAsB,YAAY,MAAwC;AACzE,MAAI,CAAC,WAAW,KAAK,QAAQ,GAAG;AAC/B,UAAM,IAAI,UAAU,KAAK,2BAA2B;AAAA,EACrD;AACA,cAAY,KAAK,UAAU,CAAC,KAAK,MAAM,YAAY,KAAK,MAAM,aAAa,CAAC;AAC5E,MAAI,CAAE,MAAM,OAAO,KAAK,QAAQ,GAAI;AACnC,UAAM,IAAI,UAAU,KAAK,yBAAyB,KAAK,QAAQ,EAAE;AAAA,EAClE;AAIA,QAAM,WAAW,MAAM,SAAS,KAAK,UAAU,MAAM;AACrD,QAAM,SAAS,WAAW,UAAU,QAAQ,EAAE,OAAO,gBAAgB;AACrE,QAAM,OAAO;AAAA,IACZ;AAAA,MACC,CAAC,QAAQ,KAAK,IAAI;AAAA,MAClB,CAAC,eAAe,KAAK,WAAW;AAAA,MAChC,CAAC,4BAA4B,KAAK,sBAAsB;AAAA,IACzD;AAAA,IACA,KAAK;AAAA,IACL;AAAA,EACD;AACA,QAAM,UAAU,KAAK,UAAU,MAAM,MAAM;AAC3C,SAAO,KAAK;AACb;AAEA,eAAsB,YAAY,UAAkB,OAAqC;AACxF,MAAI,CAAC,WAAW,QAAQ,GAAG;AAC1B,UAAM,IAAI,UAAU,KAAK,2BAA2B;AAAA,EACrD;AACA,cAAY,UAAU,CAAC,MAAM,YAAY,MAAM,aAAa,CAAC;AAC7D,MAAI,CAAE,MAAM,OAAO,QAAQ,GAAI;AAC9B,UAAM,IAAI,UAAU,KAAK,yBAAyB,QAAQ,EAAE;AAAA,EAC7D;AAKA,QAAM,MAAM,QAAQ,QAAQ;AAC5B,QAAM,eACL,QAAQ,GAAG,MAAM,QAAQ,MAAM,UAAU,KACzC,QAAQ,GAAG,MAAM,QAAQ,MAAM,aAAa;AAC7C,MAAI,cAAc;AACjB,UAAM,OAAO,QAAQ;AACrB;AAAA,EACD;AACA,cAAY,KAAK,CAAC,MAAM,YAAY,MAAM,aAAa,CAAC;AACxD,QAAM,GAAG,KAAK,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAC/C;AAaA,eAAsB,aAAa,MAAyC;AAC3E,QAAM,OAAO,cAAc,KAAK,OAAO,KAAK,MAAM,KAAK,KAAK;AAC5D,cAAY,MAAM,CAAC,KAAK,MAAM,aAAa,KAAK,MAAM,cAAc,CAAC;AACrE,MAAI,MAAM,OAAO,IAAI,GAAG;AACvB,UAAM,IAAI,UAAU,KAAK,4BAA4B,IAAI,EAAE;AAAA,EAC5D;AACA,QAAM,OAAO;AAAA,IACZ;AAAA,MACC,CAAC,eAAe,KAAK,WAAW;AAAA,MAChC,CAAC,iBAAiB,KAAK,YAAY;AAAA,IACpC;AAAA,IACA,KAAK;AAAA,EACN;AACA,QAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAM,UAAU,MAAM,MAAM,MAAM;AAClC,SAAO;AACR;AAaA,eAAsB,aAAa,MAAyC;AAC3E,MAAI,CAAC,WAAW,KAAK,QAAQ,GAAG;AAC/B,UAAM,IAAI,UAAU,KAAK,2BAA2B;AAAA,EACrD;AACA,cAAY,KAAK,UAAU,CAAC,KAAK,MAAM,aAAa,KAAK,MAAM,cAAc,CAAC;AAC9E,MAAI,CAAE,MAAM,OAAO,KAAK,QAAQ,GAAI;AACnC,UAAM,IAAI,UAAU,KAAK,0BAA0B,KAAK,QAAQ,EAAE;AAAA,EACnE;AACA,mBAAiB,KAAK,IAAI;AAC1B,QAAM,MAAM,QAAQ,KAAK,QAAQ;AACjC,QAAM,UAAUA,MAAK,KAAK,GAAG,KAAK,IAAI,KAAK;AAC3C,cAAY,SAAS,CAAC,KAAK,MAAM,aAAa,KAAK,MAAM,cAAc,CAAC;AAGxE,QAAM,WAAW,MAAM,SAAS,KAAK,UAAU,MAAM;AACrD,QAAM,SAAS,WAAW,UAAU,QAAQ,EAAE,OAAO,iBAAiB;AACtE,QAAM,OAAO;AAAA,IACZ;AAAA,MACC,CAAC,eAAe,KAAK,WAAW;AAAA,MAChC,CAAC,iBAAiB,KAAK,YAAY;AAAA,IACpC;AAAA,IACA,KAAK;AAAA,IACL;AAAA,EACD;AACA,MAAI,QAAQ,OAAO,MAAM,QAAQ,KAAK,QAAQ,GAAG;AAChD,QAAI,MAAM,OAAO,OAAO,GAAG;AAC1B,YAAM,IAAI,UAAU,KAAK,4BAA4B,OAAO,EAAE;AAAA,IAC/D;AAMA,UAAM,UAAU,SAAS,MAAM,MAAM;AACrC,QAAI;AACH,YAAM,OAAO,KAAK,QAAQ;AAAA,IAC3B,SAAS,KAAK;AACb,YAAM,OAAO,OAAO,EAAE,MAAM,MAAM,MAAS;AAC3C,YAAM;AAAA,IACP;AACA,WAAO;AAAA,EACR;AACA,QAAM,UAAU,KAAK,UAAU,MAAM,MAAM;AAC3C,SAAO,KAAK;AACb;AAEA,eAAsB,aAAa,UAAkB,OAAqC;AACzF,MAAI,CAAC,WAAW,QAAQ,GAAG;AAC1B,UAAM,IAAI,UAAU,KAAK,2BAA2B;AAAA,EACrD;AACA,cAAY,UAAU,CAAC,MAAM,aAAa,MAAM,cAAc,CAAC;AAC/D,MAAI,CAAE,MAAM,OAAO,QAAQ,GAAI;AAC9B,UAAM,IAAI,UAAU,KAAK,0BAA0B,QAAQ,EAAE;AAAA,EAC9D;AACA,QAAM,OAAO,QAAQ;AACtB;AAYA,eAAsB,cAAc,UAAkB,OAAuD;AAC5G,cAAY,UAAU,CAAC,MAAM,YAAY,MAAM,aAAa,CAAC;AAC7D,QAAM,OAAO,MAAM,SAAS,UAAU,MAAM;AAC5C,QAAM,EAAE,aAAa,KAAK,IAAI,UAAU,IAAI;AAC5C,SAAO;AAAA,IACN;AAAA,IACA,MAAM,SAAS,YAAY,MAAM,EAAE;AAAA,IACnC,aAAa,SAAS,YAAY,aAAa,EAAE;AAAA,IACjD,wBAAwB,UAAU,YAAY,0BAA0B,GAAG,MAAS;AAAA,EACrF;AACD;AAEA,eAAsB,eAAe,UAAkB,OAAuD;AAC7G,cAAY,UAAU,CAAC,MAAM,aAAa,MAAM,cAAc,CAAC;AAC/D,QAAM,OAAO,MAAM,SAAS,UAAU,MAAM;AAC5C,QAAM,EAAE,aAAa,KAAK,IAAI,UAAU,IAAI;AAE5C,QAAM,OAAO,SAAS,QAAQ,EAAE,QAAQ,SAAS,EAAE;AACnD,SAAO;AAAA,IACN;AAAA,IACA,MAAM;AAAA,IACN,aAAa,SAAS,YAAY,aAAa,EAAE;AAAA,IACjD,cAAc,SAAS,YAAY,eAAe,GAAG,MAAS;AAAA,EAC/D;AACD;AAEA,SAAS,SAAS,GAAmB;AACpC,QAAM,QAAQ,EAAE,MAAM,GAAG;AACzB,SAAO,MAAM,GAAG,EAAE,KAAK;AACxB;AAEA,SAAS,SAAY,OAAgB,UAAyB;AAC7D,SAAO,OAAO,UAAU,WAAW,QAAQ;AAC5C;AAEA,SAAS,UAAa,OAAgB,UAA0B;AAC/D,SAAO,OAAO,UAAU,YAAY,QAAQ;AAC7C;AAIA,eAAe,OAAO,GAA6B;AAClD,MAAI;AACH,UAAM,KAAK,CAAC;AACZ,WAAO;AAAA,EACR,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAMO,IAAM,YAAN,cAAwB,MAAM;AAAA,EACpC,YAA4B,QAAgB,SAAiB;AAC5D,UAAM,OAAO;AADc;AAAA,EAE5B;AAAA,EAF4B;AAG7B;;;AChfA,SAAS,SAAAC,QAAO,YAAAC,WAAU,aAAAC,kBAAiB;AAC3C,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAC9B,SAAS,kBAAkB;AAY3B,IAAM,gBAAgBC,MAAK,OAAO,SAAS,iBAAiB;AAM5D,IAAI;AAEJ,eAAe,OAA8B;AAC5C,MAAI,MAAO,QAAO;AAClB,MAAI;AACH,UAAM,MAAM,MAAMC,UAAS,eAAe,MAAM;AAChD,YAAQ,KAAK,MAAM,GAAG;AACtB,QAAI,CAAC,MAAM,QAAQ,MAAM,UAAU,EAAG,SAAQ,EAAE,YAAY,CAAC,EAAE;AAAA,EAChE,SAAS,KAAK;AACb,QAAK,IAA8B,SAAS,UAAU;AACrD,cAAQ,EAAE,YAAY,CAAC,EAAE;AAAA,IAC1B,OAAO;AACN,YAAM;AAAA,IACP;AAAA,EACD;AACA,SAAO;AACR;AAEA,eAAe,OAAsB;AACpC,MAAI,CAAC,MAAO;AACZ,QAAMC,OAAMC,SAAQ,aAAa,GAAG,EAAE,WAAW,KAAK,CAAC;AACvD,QAAMC,WAAU,eAAe,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,MAAM;AACtE;AAEA,eAAsB,iBAA6C;AAClE,QAAM,IAAI,MAAM,KAAK;AACrB,SAAO,CAAC,GAAG,EAAE,UAAU;AACxB;AAEA,eAAsB,aAAa,IAAkD;AACpF,QAAM,IAAI,MAAM,KAAK;AACrB,SAAO,EAAE,WAAW,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AAC5C;AAOA,eAAsB,aAAa,OAAiE;AACnG,QAAM,IAAI,MAAM,KAAK;AACrB,QAAM,WAAW,EAAE,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM,IAAI;AAC/D,MAAI,SAAU,QAAO;AACrB,QAAM,KAAsB;AAAA,IAC3B,IAAI,WAAW;AAAA,IACf,MAAM,MAAM;AAAA,IACZ,MAAM,MAAM;AAAA,IACZ,UAAS,oBAAI,KAAK,GAAE,YAAY;AAAA,EACjC;AACA,IAAE,WAAW,KAAK,EAAE;AACpB,QAAM,KAAK;AACX,SAAO;AACR;AAEA,eAAsB,gBAAgB,IAA8B;AACnE,QAAM,IAAI,MAAM,KAAK;AACrB,QAAM,SAAS,EAAE,WAAW;AAC5B,IAAE,aAAa,EAAE,WAAW,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AACrD,MAAI,EAAE,WAAW,WAAW,OAAQ,QAAO;AAC3C,QAAM,KAAK;AACX,SAAO;AACR;;;AC7EA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAI1B,IAAM,OAAO,UAAU,QAAQ;AAQ/B,IAAM,eAAe;AACrB,IAAMC,SAAQ,oBAAI,IAA6B;AAO/C,IAAM,WAAW,oBAAI,IAAsC;AAI3D,eAAsB,gBAAgB,IAAyC;AAC9E,QAAM,QAAQ,MAAM,SAAS,GAAG,IAAI;AACpC,SAAO;AAAA,IACN,IAAI,GAAG;AAAA,IACP,MAAM,GAAG;AAAA,IACT,MAAM,GAAG;AAAA,IACT,SAAS,GAAG;AAAA,IACZ,WAAW,MAAM;AAAA,IACjB,WAAW,MAAM;AAAA,EAClB;AACD;AAEA,eAAe,SAAS,MAAwC;AAC/D,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,SAASA,OAAM,IAAI,IAAI;AAC7B,MAAI,UAAU,OAAO,YAAY,IAAK,QAAO;AAE7C,QAAM,UAAU,SAAS,IAAI,IAAI;AACjC,MAAI,QAAS,QAAO;AAEpB,QAAM,QAAQ,WAAW,IAAI,EAC3B,KAAK,CAAC,UAAU;AAChB,UAAM,QAAyB;AAAA,MAC9B,GAAG;AAAA,MACH,WAAW,KAAK,IAAI,IAAI;AAAA,IACzB;AACA,IAAAA,OAAM,IAAI,MAAM,KAAK;AACrB,WAAO;AAAA,EACR,CAAC,EACA,QAAQ,MAAM;AACd,aAAS,OAAO,IAAI;AAAA,EACrB,CAAC;AAEF,WAAS,IAAI,MAAM,KAAK;AACxB,SAAO;AACR;AAEA,eAAe,WACd,MACkE;AAElE,QAAM,CAAC,cAAc,WAAW,IAAI,MAAM,QAAQ,WAAW;AAAA,IAC5D,OAAO,MAAM,CAAC,aAAa,gBAAgB,MAAM,CAAC;AAAA,IAClD,OAAO,MAAM;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD,CAAC;AAAA,EACF,CAAC;AAED,MAAI,YAA2B;AAC/B,MAAI,aAAa,WAAW,aAAa;AACxC,UAAM,MAAM,aAAa,MAAM,KAAK;AAGpC,QAAI,OAAO,QAAQ,QAAQ;AAC1B,kBAAY;AAAA,IACb,WAAW,QAAQ,QAAQ;AAE1B,UAAI;AACH,cAAM,OAAO,MAAM,OAAO,MAAM,CAAC,aAAa,WAAW,MAAM,CAAC,GAAG,KAAK;AACxE,oBAAY,MAAM,IAAI,GAAG,KAAK;AAAA,MAC/B,QAAQ;AACP,oBAAY;AAAA,MACb;AAAA,IACD;AAAA,EACD;AAEA,MAAI,YAA2B;AAC/B,MAAI,YAAY,WAAW,aAAa;AACvC,UAAM,MAAM,YAAY;AACxB,QAAI,KAAK;AAGR,UAAI,IAAI;AACR,eAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACpC,YAAI,IAAI,WAAW,CAAC,MAAM,GAAM;AAAA,MACjC;AAEA,UAAI,IAAI,SAAS,KAAK,IAAI,WAAW,IAAI,SAAS,CAAC,MAAM,GAAM;AAC/D,kBAAY;AAAA,IACb,OAAO;AACN,kBAAY;AAAA,IACb;AAAA,EACD;AAEA,SAAO,EAAE,WAAW,UAAU;AAC/B;AAQA,eAAe,OAAO,KAAa,MAAiC;AACnE,QAAM,EAAE,OAAO,IAAI,MAAM,KAAK,OAAO,MAAM;AAAA,IAC1C;AAAA,IACA,SAAS;AAAA,IACT,WAAW,IAAI,OAAO;AAAA;AAAA;AAAA,IAGtB,KAAK,EAAE,GAAG,QAAQ,KAAK,SAAS,QAAW,eAAe,OAAU;AAAA,EACrE,CAAC;AACD,SAAO;AACR;;;ACxIA,SAAS,UAAAC,eAAc;AACvB,SAAS,cAAAC,aAAY,WAAAC,gBAAe;AAEpC;AAAA,EAGC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGM;;;ACnBP,SAAS,YAAyB;AAE3B,IAAM,uBAAuB,KAAK,MAAM;AAAA,EAC9C,KAAK,QAAQ,SAAS;AAAA,EACtB,KAAK,QAAQ,aAAa;AAAA,EAC1B,KAAK,QAAQ,WAAW;AACzB,CAAC;AAEM,IAAM,iBAAiB,KAAK,OAAO;AAAA,EACzC,IAAI,KAAK,OAAO,EAAE,aAAa,0EAA4E,CAAC;AAAA,EAC5G,OAAO,KAAK,OAAO,EAAE,aAAa,6DAA6D,CAAC;AAAA,EAChG,QAAQ;AAAA,EACR,MAAM,KAAK,SAAS,KAAK,OAAO,EAAE,aAAa,iEAA4D,CAAC,CAAC;AAC9G,CAAC;AAEM,IAAM,yBAAyB,KAAK,OAAO;AAAA,EACjD,OAAO,KAAK,MAAM,gBAAgB;AAAA,IACjC,aAAa;AAAA,EACd,CAAC;AACF,CAAC;;;ACGM,IAAM,uBAAyC,CAAC,OAAO;AAC7D,KAAG,aAAa;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACC;AAAA,IACD,YAAY;AAAA,IACZ,eACC;AAAA,IACD,kBAAkB;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,IACA,SAAS,OAAO,aAAa,YAAY;AAAA,MACxC,SAAS;AAAA,QACR;AAAA,UACC,MAAM;AAAA,UACN,MAAM,iBAAiB,OAAO,MAAM,MAAM,QAAQ,OAAO,MAAM,WAAW,IAAI,KAAK,GAAG;AAAA,QACvF;AAAA,MACD;AAAA,MACA,SAAS;AAAA,IACV;AAAA,EACD,CAAC;AAED,KAAG,gBAAgB,QAAQ;AAAA,IAC1B,aAAa;AAAA,IACb,SAAS,OAAO,SAAS;AACxB,YAAM,OAAO,KAAK,KAAK;AACvB,YAAM,UAAU,OACb,qBAAqB,IAAI,uEACzB;AACH,SAAG,gBAAgB,OAAO;AAAA,IAC3B;AAAA,EACD,CAAC;AACF;;;AC7CO,IAAM,4BAAgD,CAAC,oBAAoB;;;ACW3E,IAAM,oBAAN,MAAsD;AAAA;AAAA;AAAA,EAG5D,UAAgB;AAAA,EAAC;AAAA;AAAA,EAIjB,SAAsC;AACrC,WAAO,QAAQ,QAAQ,MAAS;AAAA,EACjC;AAAA,EACA,UAA4B;AAC3B,WAAO,QAAQ,QAAQ,KAAK;AAAA,EAC7B;AAAA,EACA,QAAqC;AACpC,WAAO,QAAQ,QAAQ,MAAS;AAAA,EACjC;AAAA,EACA,SAAsC;AACrC,WAAO,QAAQ,QAAQ,MAAS;AAAA,EACjC;AAAA;AAAA,EAIA,SAAe;AAAA,EAAC;AAAA,EAChB,YAAkB;AAAA,EAAC;AAAA,EACnB,YAAkB;AAAA,EAAC;AAAA,EACnB,WAAiB;AAAA,EAAC;AAAA,EAClB,gBAAsB;AAAA,EAAC;AAAA,EACvB,gBAAsB;AAAA,EAAC;AAAA;AAAA,EAIvB,kBAA8B;AAC7B,WAAO,MAAM;AAAA,IAAC;AAAA,EACf;AAAA,EACA,oBAA0B;AAAA,EAAC;AAAA,EAC3B,oBAA0B;AAAA,EAAC;AAAA,EAC3B,sBAA4B;AAAA,EAAC;AAAA,EAC7B,yBAA+B;AAAA,EAAC;AAAA,EAChC,YAAkB;AAAA,EAAC;AAAA,EACnB,YAAkB;AAAA,EAAC;AAAA,EACnB,MAAM,SAAwB;AAK7B,WAAO;AAAA,EACR;AAAA,EACA,gBAAwB;AACvB,WAAO;AAAA,EACR;AAAA,EACA,0BAAgC;AAAA,EAAC;AAAA,EACjC,qBAA2B;AAAA,EAAC;AAAA,EAC5B,qBAAgC;AAC/B,WAAO;AAAA,EACR;AAAA,EACA,IAAI,QAAe;AAGlB,WAAO;AAAA,EACR;AAAA,EACA,eAA6D;AAC5D,WAAO,CAAC;AAAA,EACT;AAAA,EACA,WAAsB;AACrB,WAAO;AAAA,EACR;AAAA,EACA,WAAiD;AAChD,WAAO,EAAE,SAAS,OAAO,OAAO,4CAA4C;AAAA,EAC7E;AAAA,EACA,mBAA4B;AAC3B,WAAO;AAAA,EACR;AAAA,EACA,mBAAyB;AAAA,EAAC;AAC3B;;;AJ7CO,IAAM,qBAAqB,QAAQ,IAAI,+BAA+B;AAM7E,IAAM,gBAAkD,OAAO;AAAA,EAC9D;AAAA,EACA;AAAA,EACA;AACD,MAAM;AACL,QAAM,WAAW,MAAM,2BAA2B;AAAA,IACjD;AAAA,IACA,uBAAuB;AAAA,MACtB,cAAc,CAAC;AAAA,MACf,oBAAoB;AAAA,IACrB;AAAA,EACD,CAAC;AACD,QAAM,gBAAgB,MAAM,+BAA+B;AAAA,IAC1D;AAAA,IACA;AAAA,IACA;AAAA,EACD,CAAC;AACD,SAAO;AAAA,IACN,GAAG;AAAA,IACH;AAAA,IACA,aAAa,SAAS;AAAA,EACvB;AACD;AAOA,IAAM,mBAAN,MAAuB;AAAA,EACL,SAAS,oBAAI,IAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOzC,cAAc,oBAAI,IAA4B;AAAA;AAAA,EAE9C,UAAU,oBAAI,IAAqC;AAAA,EACnD,kBAAkB,oBAAI,IAA6B;AAAA,EAE5D,yBAAyB,aAAqC;AACrE,QAAI,MAAM,KAAK,YAAY,IAAI,WAAW;AAC1C,QAAI,CAAC,KAAK;AACT,YAAM,oBAAI,IAAI;AACd,WAAK,YAAY,IAAI,aAAa,GAAG;AAAA,IACtC;AACA,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,YAAY,aAAmD;AACpE,UAAM,WAAW,KAAK,OAAO,IAAI,WAAW;AAC5C,QAAI,SAAU,QAAO,SAAS;AAE9B,UAAMC,YAAW,KAAK,QAAQ,IAAI,WAAW;AAC7C,QAAIA,UAAU,SAAQ,MAAMA,WAAU;AAEtC,UAAM,IAAI,KAAK,MAAM,WAAW;AAChC,SAAK,QAAQ,IAAI,aAAa,CAAC;AAC/B,QAAI;AACH,YAAM,QAAQ,MAAM;AACpB,WAAK,OAAO,IAAI,aAAa,KAAK;AAClC,aAAO,MAAM;AAAA,IACd,UAAE;AACD,WAAK,QAAQ,OAAO,WAAW;AAAA,IAChC;AAAA,EACD;AAAA,EAEA,MAAc,MAAM,aAA8C;AACjE,UAAM,KAAK,MAAM,aAAa,WAAW;AACzC,QAAI,CAAC,GAAI,OAAM,IAAI,MAAM,wBAAwB,WAAW,EAAE;AAK9D,UAAM,iBAAiB,eAAe,eAAe,GAAG,IAAI;AAE5D,UAAM,UAAU,MAAM,0BAA0B,eAAe;AAAA,MAC9D,KAAK,GAAG;AAAA,MACR,UAAU,YAAY;AAAA,MACtB;AAAA,IACD,CAAC;AAMD,UAAM,cAAc,KAAK,yBAAyB,WAAW;AAC7D,UAAM,SAAS,IAAI,kBAAkB;AAMrC,UAAM,UAAU,CAAC,QAAwB;AACxC,YAAM,MAAwB;AAAA,QAC7B,MAAM;AAAA,QACN;AAAA,QACA,eAAe,IAAI;AAAA,QACnB,OAAO,IAAI;AAAA,QACX,SAAS,IAAI;AAAA,MACd;AACA,kBAAY,aAAa,GAAG;AAG5B,cAAQ;AAAA,QACP,eAAe,WAAW,IAAI,IAAI,aAAa,IAAI,IAAI,KAAK,KAAK,IAAI,KAAK,MACxE,IAAI,QAAQ;AAAA,EAAK,IAAI,KAAK,KAAK;AAAA,MAClC;AAAA,IACD;AAIA,UAAM,QAAQ,QAAQ,eAAe,EAAE,WAAW,QAAQ,QAAQ,CAAC;AACnE,YAAQ,iBAAiB,YAAY;AACpC,YAAM,QAAQ,QAAQ,eAAe,EAAE,WAAW,QAAQ,QAAQ,CAAC;AACnE,WAAK,sBAAsB,WAAW;AAAA,IACvC,CAAC;AAED,WAAO,EAAE,SAAS,OAAO;AAAA,EAC1B;AAAA,EAEA,IAAI,aAAsD;AACzD,WAAO,KAAK,OAAO,IAAI,WAAW,GAAG;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,cAAc,aAAqB,IAAqB;AACvD,SAAK,yBAAyB,WAAW,EAAE,IAAI,EAAE;AAAA,EAClD;AAAA,EAEA,iBAAiB,aAAqB,IAAqB;AAC1D,UAAM,MAAM,KAAK,YAAY,IAAI,WAAW;AAC5C,QAAI,CAAC,IAAK;AACV,QAAI,OAAO,EAAE;AACb,QAAI,IAAI,SAAS,EAAG,MAAK,YAAY,OAAO,WAAW;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,UAAU,aAAqB,KAA4B;AAC1D,UAAM,MAAM,KAAK,YAAY,IAAI,WAAW;AAC5C,QAAI,CAAC,OAAO,IAAI,SAAS,EAAG;AAC5B,gBAAY,KAAK,GAAG;AAAA,EACrB;AAAA,EAEA,kBAAkB,aAAqB,UAAkC;AACxE,QAAI,YAAY,KAAK,gBAAgB,IAAI,WAAW;AACpD,QAAI,CAAC,WAAW;AACf,kBAAY,oBAAI,IAAI;AACpB,WAAK,gBAAgB,IAAI,aAAa,SAAS;AAAA,IAChD;AACA,cAAU,IAAI,QAAQ;AACtB,WAAO,MAAM;AACZ,YAAM,UAAU,KAAK,gBAAgB,IAAI,WAAW;AACpD,UAAI,CAAC,QAAS;AACd,cAAQ,OAAO,QAAQ;AACvB,UAAI,QAAQ,SAAS,GAAG;AACvB,aAAK,gBAAgB,OAAO,WAAW;AAAA,MACxC;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,sBAAsB,aAA2B;AACxD,UAAM,YAAY,KAAK,gBAAgB,IAAI,WAAW;AACtD,QAAI,CAAC,UAAW;AAChB,eAAW,YAAY,CAAC,GAAG,SAAS,GAAG;AACtC,UAAI;AACH,iBAAS;AAAA,MACV,SAAS,GAAG;AACX,gBAAQ,MAAM,4BAA4B,WAAW,YAAY,CAAC;AAAA,MACnE;AAAA,IACD;AAAA,EACD;AAAA,EAEA,MAAM,aAAa,aAAgD;AAClE,UAAM,KAAK,MAAM,aAAa,WAAW;AACzC,QAAI,CAAC,GAAI,OAAM,IAAI,MAAM,wBAAwB,WAAW,EAAE;AAE9D,UAAM,WAAW,MAAM,eAAe,KAAK,GAAG,IAAI;AAClD,WAAO,SACL,MAAM,EACN,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,QAAQ,IAAI,EAAE,SAAS,QAAQ,CAAC,EAC1D,IAAI,gBAAgB;AAAA,EACvB;AAAA,EAEA,kBAAkB,aAAqB,aAAuC;AAC7E,UAAM,UAAU,KAAK,OAAO,IAAI,WAAW,GAAG;AAC9C,QAAI,CAAC,QAAS,QAAO,EAAE,OAAO,CAAC,GAAG,aAAa,MAAM;AAKrD,QAAI,aAAa;AAChB,YAAM,aAAa,QAAQ,QAAQ,cAChCC,SAAQ,QAAQ,QAAQ,WAAW,IACnC;AACH,UAAI,eAAeA,SAAQ,WAAW,GAAG;AACxC,eAAO,EAAE,OAAO,CAAC,GAAG,aAAa,MAAM;AAAA,MACxC;AAAA,IACD;AAEA,UAAM,cAAuB,QAAQ,QAAQ,eAAe;AAE5D,UAAM,SAAS,QAAQ,QAAQ,eAAe,UAAU;AACxD,UAAM,QAAuB,CAAC;AAG9B,UAAM,eAAe,oBAAI,IAAoB;AAE7C,eAAW,SAAS,QAAQ;AAC3B,UAAI,MAAM,SAAS,UAAW;AAC9B,YAAM,MAAM,MAAM;AAClB,YAAM,OAAQ,IAAyB;AAEvC,UAAI,SAAS,QAAQ;AACpB,cAAM,OAAO,gBAAgB,GAA2B;AACxD,YAAI,KAAM,OAAM,KAAK,EAAE,MAAM,QAAQ,KAAK,CAAC;AAAA,MAC5C,WAAW,SAAS,aAAa;AAChC,cAAM,EAAE,MAAM,UAAU,UAAU,IAAI;AAAA,UACrC;AAAA,QACD;AACA,mBAAW,MAAM,WAAW;AAC3B,uBAAa,IAAI,GAAG,IAAI,GAAG,IAAI;AAAA,QAChC;AACA,YAAI,QAAQ,SAAU,OAAM,KAAK,EAAE,MAAM,aAAa,MAAM,SAAS,CAAC;AAAA,MACvE,WAAW,SAAS,cAAc;AACjC,cAAM,KAAK;AAMX,cAAM,KAAK;AAAA,UACV,MAAM;AAAA,UACN,YAAY,GAAG;AAAA,UACf,UAAU,GAAG;AAAA,UACb,MAAM,aAAa,IAAI,GAAG,UAAU,KAAK;AAAA,UACzC,MAAM,mBAAmB,GAAG,OAAO;AAAA,UACnC,SAAS,GAAG;AAAA,QACb,CAAC;AAAA,MACF,WAAW,SAAS,iBAAiB;AACpC,cAAM,KAAK;AAKX,cAAM,KAAK;AAAA,UACV,MAAM;AAAA,UACN,SAAS,GAAG;AAAA,UACZ,QAAQ,GAAG;AAAA,UACX,UAAU,GAAG;AAAA,QACd,CAAC;AAAA,MACF;AAAA,IACD;AAEA,WAAO,EAAE,OAAO,YAAY;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,cAAc,aAAqB,aAAoC;AAI5E,UAAM,KAAK,MAAM,aAAa,WAAW;AACzC,QAAI,CAAC,GAAI,OAAM,IAAI,UAAU,KAAK,wBAAwB,WAAW,EAAE;AACvE,QAAI,CAACC,YAAW,WAAW,GAAG;AAC7B,YAAM,IAAI,UAAU,KAAK,+BAA+B;AAAA,IACzD;AAEA,UAAM,WAAW,MAAM,eAAe,KAAK,GAAG,IAAI;AAClD,UAAM,WAAWD,SAAQ,WAAW;AACpC,UAAM,SAAS,SAAS,KAAK,CAAC,YAAYA,SAAQ,QAAQ,IAAI,MAAM,QAAQ;AAC5E,QAAI,CAAC,QAAQ;AACZ,YAAM,IAAI,UAAU,KAAK,sBAAsB,WAAW,EAAE;AAAA,IAC7D;AAEA,UAAM,UAAU,KAAK,OAAO,IAAI,WAAW,GAAG;AAC9C,UAAM,aAAa,SAAS,QAAQ,cACjCA,SAAQ,QAAQ,QAAQ,WAAW,IACnC;AACH,QAAI,eAAe,UAAU;AAC5B,YAAM,IAAI;AAAA,QACT;AAAA,QACA;AAAA,MACD;AAAA,IACD;AAEA,QAAI;AACH,YAAME,QAAO,QAAQ;AAAA,IACtB,SAAS,KAAK;AACb,UAAK,KAA+B,SAAS,UAAU;AACtD,gBAAQ;AAAA,UACP,uBAAuB,QAAQ;AAAA,QAChC;AACA;AAAA,MACD;AACA,YAAM;AAAA,IACP;AAAA,EACD;AAAA,EAEA,MAAM,cAAc,aAAqB,aAAuC;AAC/E,UAAM,KAAK,MAAM,aAAa,WAAW;AACzC,QAAI,CAAC,GAAI,OAAM,IAAI,MAAM,wBAAwB,WAAW,EAAE;AAC9D,QAAI,CAACD,YAAW,WAAW,GAAG;AAC7B,YAAM,IAAI,MAAM,+BAA+B;AAAA,IAChD;AAEA,UAAM,WAAW,MAAM,eAAe,KAAK,GAAG,IAAI;AAClD,UAAM,WAAWD,SAAQ,WAAW;AACpC,UAAM,SAAS,SAAS,KAAK,CAAC,YAAYA,SAAQ,QAAQ,IAAI,MAAM,QAAQ;AAC5E,QAAI,CAAC,QAAQ;AACZ,YAAM,IAAI,MAAM,sBAAsB,WAAW,EAAE;AAAA,IACpD;AAEA,UAAM,UAAU,MAAM,KAAK,YAAY,WAAW;AAClD,UAAM,cAAc,QAAQ,QAAQ,cAAcA,SAAQ,QAAQ,QAAQ,WAAW,IAAI;AACzF,QAAI,gBAAgB,SAAU,QAAO;AACrC,QAAI,QAAQ,QAAQ,aAAa;AAChC,YAAM,IAAI,MAAM,qDAAqD;AAAA,IACtE;AAEA,UAAM,SAAS,MAAM,QAAQ,cAAc,UAAU,EAAE,aAAa,GAAG,KAAK,CAAC;AAC7E,WAAO,CAAC,OAAO;AAAA,EAChB;AAAA,EAEA,MAAM,QAAQ,aAAoC;AACjD,UAAM,QAAQ,KAAK,OAAO,IAAI,WAAW;AACzC,QAAI,CAAC,MAAO;AACZ,SAAK,OAAO,OAAO,WAAW;AAC9B,SAAK,gBAAgB,OAAO,WAAW;AACvC,SAAK,YAAY,OAAO,WAAW;AACnC,QAAI;AACH,YAAM,OAAO,QAAQ;AAAA,IACtB,SAAS,GAAG;AACX,cAAQ,MAAM,uBAAuB,WAAW,YAAY,CAAC;AAAA,IAC9D;AACA,QAAI;AACH,YAAM,QAAQ,QAAQ,QAAQ;AAAA,IAC/B,SAAS,GAAG;AACX,cAAQ,MAAM,gBAAgB,WAAW,YAAY,CAAC;AAAA,IACvD;AAAA,EACD;AAAA,EAEA,MAAM,aAA4B;AACjC,UAAM,MAAM,CAAC,GAAG,KAAK,OAAO,KAAK,CAAC;AAClC,UAAM,QAAQ,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC,CAAC;AAAA,EACpD;AACD;AAEA,SAAS,iBAAiB,MAAmC;AAC5D,QAAM,UAAU,KAAK,aAAa,QAAQ,QAAQ,GAAG,EAAE,KAAK;AAC5D,SAAO;AAAA,IACN,MAAM,KAAK;AAAA,IACX,MAAM,KAAK;AAAA,IACX,WAAW,KAAK,SAAS,YAAY;AAAA,IACrC,SAAS,UAAU,QAAQ,MAAM,GAAG,GAAG,IAAI;AAAA,EAC5C;AACD;AAGA,SAAS,gBAAgB,KAAmC;AAC3D,MAAI,OAAO,IAAI,YAAY,SAAU,QAAO,IAAI;AAChD,SAAO,mBAAmB,IAAI,OAAoB;AACnD;AAGA,SAAS,wBAAwB,KAI/B;AACD,QAAM,YAAsB,CAAC;AAC7B,QAAM,gBAA0B,CAAC;AACjC,QAAM,YAA4C,CAAC;AACnD,aAAW,SAAS,IAAI,WAAW,CAAC,GAAG;AACtC,QAAI,CAAC,SAAS,OAAO,UAAU,SAAU;AACzC,UAAM,IAAI;AACV,QAAI,EAAE,SAAS,UAAU,OAAO,EAAE,SAAS,SAAU,WAAU,KAAK,EAAE,IAAI;AAAA,aACjE,EAAE,SAAS,cAAc,OAAO,EAAE,aAAa,SAAU,eAAc,KAAK,EAAE,QAAQ;AAAA,aACtF,EAAE,SAAS,cAAc,OAAO,EAAE,OAAO,UAAU;AAC3D,gBAAU,KAAK;AAAA,QACd,IAAI,EAAE;AAAA,QACN,MAAM,EAAE,aAAa,OAAO,KAAK,UAAU,EAAE,SAAS,IAAI;AAAA,MAC3D,CAAC;AAAA,IACF;AAAA,EACD;AACA,SAAO,EAAE,MAAM,UAAU,KAAK,EAAE,GAAG,UAAU,cAAc,KAAK,EAAE,GAAG,UAAU;AAChF;AAGA,SAAS,mBAAmB,SAA4B;AACvD,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,SAAS;AAC5B,QAAI,SAAS,OAAO,UAAU,YAAa,MAA4B,SAAS,QAAQ;AACvF,YAAM,OAAQ,MAA6B;AAC3C,UAAI,OAAO,SAAS,SAAU,OAAM,KAAK,IAAI;AAAA,IAC9C;AAAA,EACD;AACA,SAAO,MAAM,KAAK,EAAE;AACrB;AAEO,IAAM,mBAAmB,IAAI,iBAAiB;AASrD,SAAS,YAAY,aAA6B,KAA4B;AAC7E,QAAM,OAAO,KAAK,UAAU,GAAG;AAC/B,aAAW,MAAM,aAAa;AAC7B,QAAI,GAAG,eAAe,GAAG,KAAM;AAC/B,QAAI;AACH,SAAG,KAAK,IAAI;AAAA,IACb,QAAQ;AAAA,IAER;AAAA,EACD;AACD;;;AKteA,SAAS,oBAAoB,aAAqC;AACjE,QAAM,UAAU,iBAAiB,IAAI,WAAW;AAChD,MAAI,CAAC,QAAS,OAAM,IAAI,MAAM,yBAAyB;AAEvD,QAAM,UAAU,QAAQ;AACxB,QAAM,QAAQ,QAAQ;AAEtB,QAAM,eAAiC,QACpC;AAAA,IACA,UAAU,MAAM;AAAA,IAChB,SAAS,MAAM;AAAA,IACf,MAAM,MAAM;AAAA,IACZ,WAAW,MAAM;AAAA,EAClB,IACC;AAEH,QAAM,kBAA+B,QAAQ,cAC3C,aAAa,EACb,IAAI,CAAC,OAAO;AAAA,IACZ,UAAU,EAAE;AAAA,IACZ,SAAS,EAAE;AAAA,IACX,MAAM,EAAE;AAAA,IACR,WAAW,EAAE;AAAA,EACd,EAAE;AAEH,QAAM,WAA0B,QAAQ,YAAY,EAAE,IAAI,CAAC,OAAO;AAAA,IACjE,MAAM,EAAE;AAAA,IACR,aAAa,EAAE;AAAA,EAChB,EAAE;AAEF,SAAO;AAAA,IACN;AAAA,IACA,eAAe,QAAQ;AAAA,IACvB,yBAAyB,QAAQ,2BAA2B;AAAA,IAC5D,aAAa,QAAQ,mBAAmB;AAAA,IACxC;AAAA,IACA;AAAA,EACD;AACD;AAGA,eAAe,iBAAiB,GAAY,IAA8B;AACzE,QAAM,KAAK,MAAM,aAAa,EAAE;AAChC,MAAI,CAAC,IAAI;AACR,MAAE,OAAO,GAAG;AACZ,MAAE,OAAO,gBAAgB,kBAAkB;AAE3C,WAAO;AAAA,EACR;AACA,SAAO;AACR;AAGA,SAAS,kBAAkB,GAAY,aAA8B;AACpE,QAAM,UAAU,iBAAiB,IAAI,WAAW;AAChD,MAAI,SAAS,QAAQ,aAAa;AACjC,WAAO;AAAA,EACR;AACA,SAAO;AACR;AAUA,SAAS,sBACR,aACA,SACO;AACP,QAAM,QAAQ,QAAQ,QAAQ,gBAAgB;AAC9C,MAAI,CAAC,MAAO;AACZ,QAAM,UAA0B;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,MAAM;AAAA,IACd,eAAe,MAAM;AAAA,IACrB,SAAS,MAAM;AAAA,EAChB;AACA,mBAAiB,UAAU,aAAa;AAAA,IACvC,MAAM;AAAA,IACN;AAAA,IACA,aAAa,QAAQ,QAAQ,eAAe;AAAA,IAC5C;AAAA,EACD,CAAC;AACF;AAIO,SAAS,kBAAkBG,MAAiB;AAElD,EAAAA,KAAI,IAAI,eAAe,OAAO,MAAM;AACnC,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAMC,UAAS,MAAM,iBAAiB,GAAG,EAAE;AAC3C,QAAI,CAACA,QAAQ,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAEjE,QAAI;AACH,YAAM,iBAAiB,YAAY,EAAE;AACrC,aAAO,EAAE,KAAK,oBAAoB,EAAE,CAAC;AAAA,IACtC,SAAS,KAAK;AACb,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,IACjD;AAAA,EACD,CAAC;AAGD,EAAAD,KAAI,IAAI,qBAAqB,OAAO,MAAM;AACzC,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAMC,UAAS,MAAM,iBAAiB,GAAG,EAAE;AAC3C,QAAI,CAACA,QAAQ,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAEjE,UAAM,OAAQ,MAAM,EAAE,IAAI,KAAK;AAC/B,QAAI,CAAC,MAAM,YAAY,CAAC,MAAM,SAAS;AACtC,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,oCAAoC,GAAG,GAAG;AAAA,IAC7E;AAEA,QAAI;AACH,YAAM,UAAU,MAAM,iBAAiB,YAAY,EAAE;AACrD,UAAI,kBAAkB,GAAG,EAAE,GAAG;AAC7B,eAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,mDAAmD,GAAG,GAAG;AAAA,MAC5F;AAEA,YAAM,QAAQ,QAAQ,QAAQ,cAAc,aAAa,EAAE;AAAA,QAC1D,CAAC,MAAM,EAAE,aAAa,KAAK,YAAY,EAAE,OAAO,KAAK;AAAA,MACtD;AACA,UAAI,CAAC,OAAO;AACX,eAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,+BAA+B,KAAK,QAAQ,IAAI,KAAK,OAAO,GAAG,GAAG,GAAG;AAAA,MACxG;AAEA,YAAM,QAAQ,QAAQ,SAAS,KAAK;AAMpC,4BAAsB,IAAI,OAAO;AACjC,aAAO,EAAE,KAAK,oBAAoB,EAAE,CAAC;AAAA,IACtC,SAAS,KAAK;AACb,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,IACjD;AAAA,EACD,CAAC;AAGD,EAAAD,KAAI,IAAI,8BAA8B,OAAO,MAAM;AAClD,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAMC,UAAS,MAAM,iBAAiB,GAAG,EAAE;AAC3C,QAAI,CAACA,QAAQ,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAEjE,UAAM,OAAQ,MAAM,EAAE,IAAI,KAAK;AAC/B,UAAM,cAA+B,CAAC,OAAO,WAAW,OAAO,UAAU,QAAQ,OAAO;AACxF,QAAI,CAAC,MAAM,SAAS,CAAC,YAAY,SAAS,KAAK,KAAK,GAAG;AACtD,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,yBAAyB,YAAY,KAAK,IAAI,CAAC,GAAG,GAAG,GAAG;AAAA,IAC3F;AAEA,QAAI;AACH,YAAM,iBAAiB,YAAY,EAAE;AACrC,UAAI,kBAAkB,GAAG,EAAE,GAAG;AAC7B,eAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,4DAA4D,GAAG,GAAG;AAAA,MACrG;AAEA,YAAM,UAAU,iBAAiB,IAAI,EAAE;AACvC,UAAI,CAAC,SAAS;AACb,eAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,0BAA0B,GAAG,GAAG;AAAA,MACnE;AACA,cAAQ,QAAQ,iBAAiB,KAAK,KAAK;AAC3C,aAAO,EAAE,KAAK,oBAAoB,EAAE,CAAC;AAAA,IACtC,SAAS,KAAK;AACb,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,IACjD;AAAA,EACD,CAAC;AAGD,EAAAD,KAAI,IAAI,qBAAqB,OAAO,MAAM;AACzC,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAMC,UAAS,MAAM,iBAAiB,GAAG,EAAE;AAC3C,QAAI,CAACA,QAAQ,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAEjE,UAAM,OAAQ,MAAM,EAAE,IAAI,KAAK;AAC/B,QAAI,CAAC,MAAM,SAAS,CAAC,MAAM,QAAQ,KAAK,KAAK,GAAG;AAC/C,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,uCAAuC,GAAG,GAAG;AAAA,IAChF;AAEA,QAAI;AACH,YAAM,iBAAiB,YAAY,EAAE;AACrC,UAAI,kBAAkB,GAAG,EAAE,GAAG;AAC7B,eAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,mDAAmD,GAAG,GAAG;AAAA,MAC5F;AAEA,YAAM,UAAU,iBAAiB,IAAI,EAAE;AACvC,UAAI,CAAC,SAAS;AACb,eAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,0BAA0B,GAAG,GAAG;AAAA,MACnE;AACA,cAAQ,QAAQ,qBAAqB,KAAK,KAAK;AAC/C,aAAO,EAAE,KAAK,oBAAoB,EAAE,CAAC;AAAA,IACtC,SAAS,KAAK;AACb,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,IACjD;AAAA,EACD,CAAC;AACF;;;ACpNA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,eAAe;AACxB,SAAS,QAAAC,OAAM,UAAU,OAAAC,YAAW;AACpC,SAAS,aAAAC,kBAAiB;AAK1B,IAAMC,QAAOC,WAAUC,SAAQ;AAE/B,IAAM,cAAc;AAMpB,IAAM,wBAAwB;AAI9B,IAAM,oBAAoB;AAC1B,IAAM,gBAAgB;AACtB,IAAM,YAAY;AAClB,IAAM,iBAAiB;AACvB,IAAM,eAAe,oBAAI,IAAI;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAOD,IAAM,YAAY,oBAAI,IAA4B;AAClD,IAAMC,YAAW,oBAAI,IAAqC;AAE1D,eAAe,YAAY,eAA0C;AACpE,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,SAAS,UAAU,IAAI,aAAa;AAC1C,MAAI,UAAU,OAAO,YAAY,IAAK,QAAO,OAAO;AAEpD,QAAM,UAAUA,UAAS,IAAI,aAAa;AAC1C,MAAI,QAAS,SAAQ,MAAM,SAAS;AAEpC,QAAM,QAAQ,cAAc,aAAa,EACvC,KAAK,CAAC,UAAU;AAChB,UAAM,QAAwB;AAAA,MAC7B;AAAA,MACA,WAAW,KAAK,IAAI,IAAI;AAAA,IACzB;AAIA,cAAU,OAAO,aAAa;AAC9B,cAAU,IAAI,eAAe,KAAK;AAClC,WAAO,UAAU,OAAO,uBAAuB;AAC9C,YAAM,SAAS,UAAU,KAAK,EAAE,KAAK,EAAE;AACvC,UAAI,CAAC,OAAQ;AACb,gBAAU,OAAO,MAAM;AAAA,IACxB;AACA,WAAO;AAAA,EACR,CAAC,EACA,QAAQ,MAAMA,UAAS,OAAO,aAAa,CAAC;AAC9C,EAAAA,UAAS,IAAI,eAAe,KAAK;AACjC,UAAQ,MAAM,OAAO;AACtB;AAEA,eAAe,cAAc,eAA0C;AAEtE,MAAI;AACH,UAAM,EAAE,OAAO,IAAI,MAAMH;AAAA,MACxB;AAAA,MACA,CAAC,YAAY,YAAY,YAAY,oBAAoB;AAAA,MACzD;AAAA,QACC,KAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAW,KAAK,OAAO;AAAA,QACvB,KAAK,EAAE,GAAG,QAAQ,KAAK,SAAS,QAAW,eAAe,OAAU;AAAA,MACrE;AAAA,IACD;AACA,UAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,UAAMI,OAAgB,CAAC;AACvB,eAAW,QAAQ,OAAO;AACzB,UAAI,CAAC,KAAM;AACX,MAAAA,KAAI,KAAK,IAAI;AACb,UAAIA,KAAI,UAAU,kBAAmB;AAAA,IACtC;AACA,WAAOA;AAAA,EACR,QAAQ;AAAA,EAER;AAEA,QAAM,MAAgB,CAAC;AACvB,QAAM,QAAQ,eAAe,eAAe,GAAG,GAAG;AAClD,SAAO;AACR;AAEA,eAAe,QACd,MACA,KACA,OACA,KACgB;AAChB,MAAI,IAAI,UAAU,kBAAmB;AACrC,MAAI,QAAQ,eAAgB;AAC5B,MAAI;AACJ,MAAI;AACH,cAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,EACrD,QAAQ;AACP;AAAA,EACD;AACA,aAAW,KAAK,SAAS;AACxB,QAAI,IAAI,UAAU,kBAAmB;AAIrC,QAAI,aAAa,IAAI,EAAE,IAAI,EAAG;AAC9B,UAAM,MAAMC,MAAK,KAAK,EAAE,IAAI;AAC5B,QAAI,EAAE,YAAY,GAAG;AACpB,YAAM,QAAQ,MAAM,KAAK,QAAQ,GAAG,GAAG;AAAA,IACxC,WAAW,EAAE,OAAO,GAAG;AACtB,UAAI,KAAK,SAAS,MAAM,GAAG,EAAE,MAAMC,IAAG,EAAE,KAAK,GAAG,CAAC;AAAA,IAClD;AAAA,EACD;AACD;AAIA,SAAS,WAAW,SAAiB,GAA0B;AAC9D,QAAM,QAAQ,QAAQ,YAAY;AAClC,QAAM,QAAQ,MAAM,YAAY,GAAG;AACnC,QAAM,OAAO,SAAS,IAAI,MAAM,MAAM,QAAQ,CAAC,IAAI;AAGnD,MAAI,KAAK,WAAW,CAAC,EAAG,QAAO,MAAO,QAAQ;AAE9C,QAAM,UAAU,KAAK,QAAQ,CAAC;AAC9B,MAAI,WAAW,EAAG,QAAO,MAAM,UAAU,QAAQ,SAAS;AAE1D,QAAM,UAAU,MAAM,QAAQ,CAAC;AAC/B,MAAI,WAAW,EAAG,QAAO,MAAM,UAAU,QAAQ,SAAS;AAC1D,SAAO;AACR;AAEA,eAAe,sBAAsB,IAAoC;AACxE,QAAM,KAAK,MAAM,aAAa,EAAE;AAChC,SAAO,KAAK,GAAG,OAAO;AACvB;AAEO,SAAS,gBAAgBC,MAAiB;AAChD,EAAAA,KAAI,IAAI,qBAAqB,OAAO,MAAM;AACzC,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAM,gBAAgB,MAAM,sBAAsB,EAAE;AACpD,QAAI,CAAC,cAAe,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAExE,UAAM,QAAQ,EAAE,IAAI,MAAM,GAAG,KAAK,IAAI,KAAK;AAC3C,UAAM,WAAW,EAAE,IAAI,MAAM,OAAO;AACpC,QAAI,QAAQ,WAAW,OAAO,QAAQ,IAAI;AAC1C,QAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,EAAG,SAAQ;AACnD,QAAI,QAAQ,UAAW,SAAQ;AAE/B,QAAI;AACH,YAAM,MAAM,MAAM,YAAY,aAAa;AAC3C,UAAI;AACJ,UAAI,YAAY;AAChB,UAAI,CAAC,MAAM;AAGV,cAAM,QAAQ,IAAI,MAAM,GAAG,KAAK;AAChC,kBAAU,MAAM,IAAI,CAAC,aAAa;AAAA,UACjC,MAAMF,MAAK,eAAe,OAAO;AAAA,UACjC;AAAA,QACD,EAAE;AACF,oBAAY,IAAI,SAAS;AAAA,MAC1B,OAAO;AACN,cAAM,IAAI,KAAK,YAAY;AAC3B,cAAM,SAA+C,CAAC;AACtD,YAAI,aAAa;AACjB,mBAAW,WAAW,KAAK;AAC1B,gBAAM,QAAQ,WAAW,SAAS,CAAC;AACnC,cAAI,UAAU,KAAM;AACpB;AACA,iBAAO,KAAK,EAAE,SAAS,MAAM,CAAC;AAAA,QAC/B;AACA,eAAO,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACvC,cAAM,MAAM,OAAO,MAAM,GAAG,KAAK;AACjC,kBAAU,IAAI,IAAI,CAAC,OAAO;AAAA,UACzB,MAAMA,MAAK,eAAe,EAAE,OAAO;AAAA,UACnC,SAAS,EAAE;AAAA,QACZ,EAAE;AACF,oBAAY,aAAa;AAAA,MAC1B;AACA,YAAM,OAA4B,EAAE,eAAe,SAAS,UAAU;AACtE,aAAO,EAAE,KAAK,IAAI;AAAA,IACnB,SAAS,KAAK;AACb,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,cAAQ,MAAM,0BAA0B,EAAE,YAAY,GAAG;AACzD,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,IACjD;AAAA,EACD,CAAC;AACF;;;AC9MA,SAAS,WAAAG,gBAAe;AACxB,SAAS,QAAAC,aAAY;AAErB,SAAS,eAAAC,oBAAmB;AAsC5B,SAAS,iBAAiB,MAAoC;AAC7D,SAAO;AAAA,IACN,OAAO,KAAK;AAAA,IACZ,OAAO,KAAK;AAAA,IACZ,MAAM,KAAK;AAAA,EACZ;AACD;AAWA,eAAe,kBAAkB,cAAyC;AACzE,QAAM,OAAO,CAACC,MAAKC,aAAY,GAAG,YAAY,GAAGD,MAAK,cAAc,OAAO,YAAY,CAAC;AACxF,QAAM,QAAkB,CAAC;AACzB,aAAW,OAAO,MAAM;AACvB,QAAI;AACH,YAAM,UAAU,MAAME,SAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC1D,iBAAW,SAAS,SAAS;AAC5B,YAAI,MAAM,OAAO,MAAM,MAAM,KAAK,SAAS,KAAK,KAAK,MAAM,KAAK,SAAS,KAAK,IAAI;AACjF,gBAAM,KAAKF,MAAK,KAAK,MAAM,IAAI,CAAC;AAAA,QACjC,WAAW,MAAM,YAAY,GAAG;AAE/B,gBAAM,KAAKA,MAAK,KAAK,MAAM,IAAI,CAAC;AAAA,QACjC;AAAA,MACD;AAAA,IACD,QAAQ;AAAA,IAER;AAAA,EACD;AACA,SAAO;AACR;AAYA,eAAe,SACd,aACA,OACA,cAC6B;AAC7B,QAAM,UAAU,iBAAiB,IAAI,WAAW;AAChD,MAAI,CAAC,QAAS,OAAM,IAAI,UAAU,KAAK,yBAAyB;AAChE,QAAM,SAAS,QAAQ,SAAS;AAEhC,QAAM,EAAE,OAAO,IAAI,OAAO,UAAU;AACpC,QAAM,EAAE,QAAQ,IAAI,OAAO,WAAW;AACtC,QAAM,YAAY,OAAO,cAAc;AAcvC,QAAM,4BAA4B,oBAAI,IAAsB;AAC5D,QAAM,mBAAmB,QAAQ,QAAQ,iBAAiB,sBAAsB,KAAK,CAAC;AACtF,aAAW,OAAO,kBAAkB;AACnC,UAAM,MAAM,IAAI,WAAW;AAC3B,QAAI,OAAO,0BAA0B,IAAI,GAAG;AAC5C,QAAI,CAAC,MAAM;AACV,aAAO,CAAC;AACR,gCAA0B,IAAI,KAAK,IAAI;AAAA,IACxC;AACA,SAAK,KAAK,IAAI,cAAc;AAAA,EAC7B;AAEA,QAAM,YAAyB,OAAO,IAAI,CAAC,OAAO;AAAA,IACjD,MAAM,EAAE;AAAA,IACR,aAAa,EAAE;AAAA,IACf,UAAU,EAAE;AAAA,IACZ,wBAAwB,EAAE;AAAA,IAC1B,QAAQ,iBAAiB,EAAE,UAAU;AAAA,IACrC,SAAS,SAAS,EAAE,UAAU,KAAK,MAAM;AAAA,EAC1C,EAAE;AAEF,QAAM,aAA2B,QAAQ,IAAI,CAAC,OAAO;AAAA,IACpD,MAAM,EAAE;AAAA,IACR,aAAa,EAAE;AAAA,IACf,cAAc,EAAE;AAAA,IAChB,UAAU,EAAE;AAAA,IACZ,SAAS,EAAE;AAAA,IACX,QAAQ,iBAAiB,EAAE,UAAU;AAAA,IACrC,SAAS,SAAS,EAAE,UAAU,KAAK,MAAM;AAAA,EAC1C,EAAE;AAEF,QAAM,gBAAiC,UAAU,WAAW,IAAI,CAAC,MAAM;AAKtE,UAAM,WAAW,0BAA0B,IAAI,EAAE,WAAW,IAAI;AAChE,WAAO;AAAA,MACN,MAAM,EAAE;AAAA,MACR,cAAc,EAAE;AAAA,MAChB,QAAQ,iBAAiB,EAAE,UAAU;AAAA,MACrC,OAAO,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC;AAAA,MACzB,UAAU,YAAY,CAAC,GAAG,EAAE,SAAS,KAAK,CAAC;AAAA,MAC3C,OAAO,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC;AAAA,MACzB,WAAW,CAAC,GAAG,EAAE,UAAU,KAAK,CAAC;AAAA,IAClC;AAAA,EACD,CAAC;AAED,QAAM,kBAAwC,UAAU,OAAO,IAAI,CAAC,SAAS;AAAA,IAC5E,MAAM,IAAI;AAAA,IACV,OAAO,IAAI;AAAA,EACZ,EAAE;AAOF,QAAM,qBAAqB,qBAAqB,CAAC,IAAI,MAAM,kBAAkB,YAAY;AAEzF,SAAO;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,mBAAmB;AAAA,IACnB,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,EACD;AACD;AAEA,eAAe,SACd,aAC0D;AAC1D,QAAM,KAAK,MAAM,aAAa,WAAW;AACzC,MAAI,CAAC,GAAI,OAAM,IAAI,UAAU,KAAK,qBAAqB;AACvD,QAAM,QAAQ,qBAAqB,EAAE,UAAUC,aAAY,GAAG,cAAc,GAAG,KAAK,CAAC;AACrF,SAAO,EAAE,OAAO,cAAc,GAAG,KAAK;AACvC;AAGA,SAAS,aAAa,GAAY,KAAc;AAC/C,MAAI,eAAe,WAAW;AAC7B,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,IAAI,QAAQ,GAAG,IAAI,MAA+B;AAAA,EACrF;AACA,QAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAQ,MAAM,qCAAqC,GAAG;AACtD,SAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AACjD;AAGA,eAAe,OAAO,aAAoC;AACzD,QAAM,UAAU,iBAAiB,IAAI,WAAW;AAChD,MAAI,CAAC,QAAS,OAAM,IAAI,UAAU,KAAK,yBAAyB;AAChE,QAAM,QAAQ,SAAS,eAAe,OAAO;AAC9C;AAEO,SAAS,oBAAoBE,MAAiB;AAEpD,EAAAA,KAAI,IAAI,kBAAkB,OAAO,MAAM;AACtC,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAM,KAAK,MAAM,aAAa,EAAE;AAChC,QAAI,CAAC,GAAI,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAC7D,QAAI;AACH,YAAM,iBAAiB,YAAY,EAAE;AACrC,YAAM,EAAE,OAAO,aAAa,IAAI,MAAM,SAAS,EAAE;AACjD,aAAO,EAAE,KAAK,MAAM,SAAS,IAAI,OAAO,YAAY,CAAC;AAAA,IACtD,SAAS,KAAK;AACb,aAAO,aAAa,GAAG,GAAG;AAAA,IAC3B;AAAA,EACD,CAAC;AAGD,EAAAA,KAAI,KAAK,yBAAyB,OAAO,MAAM;AAC9C,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAM,KAAK,MAAM,aAAa,EAAE;AAChC,QAAI,CAAC,GAAI,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAC7D,QAAI;AACH,YAAM,iBAAiB,YAAY,EAAE;AACrC,YAAM,OAAO,EAAE;AACf,YAAM,EAAE,OAAO,aAAa,IAAI,MAAM,SAAS,EAAE;AACjD,aAAO,EAAE,KAAK,MAAM,SAAS,IAAI,OAAO,YAAY,CAAC;AAAA,IACtD,SAAS,KAAK;AACb,aAAO,aAAa,GAAG,GAAG;AAAA,IAC3B;AAAA,EACD,CAAC;AAGD,EAAAA,KAAI,IAAI,wBAAwB,OAAO,MAAM;AAC5C,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAM,KAAK,MAAM,aAAa,EAAE;AAChC,QAAI,CAAC,GAAI,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAC7D,UAAM,WAAW,EAAE,IAAI,MAAM,MAAM;AACnC,QAAI,CAAC,SAAU,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,yBAAyB,GAAG,GAAG;AAChF,QAAI;AACH,YAAM,EAAE,MAAM,IAAI,MAAM,SAAS,EAAE;AACnC,YAAM,QAAQ,SAAS,UAAU,KAAK;AACtC,UAAI,UAAU,QAAW;AACxB,eAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,yCAAyC,GAAG,GAAG;AAAA,MAClF;AACA,YAAM,OAAO,MAAM,cAAc,UAAU,KAAK;AAChD,YAAM,OAA6B;AAAA,QAClC;AAAA,QACA;AAAA,QACA,MAAM,KAAK;AAAA,QACX,aAAa,KAAK;AAAA,QAClB,wBAAwB,KAAK;AAAA,QAC7B,MAAM,KAAK;AAAA,MACZ;AACA,aAAO,EAAE,KAAK,IAAI;AAAA,IACnB,SAAS,KAAK;AACb,aAAO,aAAa,GAAG,GAAG;AAAA,IAC3B;AAAA,EACD,CAAC;AAGD,EAAAA,KAAI,KAAK,yBAAyB,OAAO,MAAM;AAC9C,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAM,KAAK,MAAM,aAAa,EAAE;AAChC,QAAI,CAAC,GAAI,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAC7D,UAAM,OAAQ,MAAM,EAAE,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AACjD,QAAI,CAAC,QAAQ,CAAC,QAAQ,KAAK,KAAK,KAAK,OAAO,KAAK,SAAS,YAAY,OAAO,KAAK,gBAAgB,YAAY,OAAO,KAAK,SAAS,UAAU;AAC5I,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,8CAA8C,GAAG,GAAG;AAAA,IACvF;AACA,QAAI;AACH,YAAM,iBAAiB,YAAY,EAAE;AACrC,YAAM,EAAE,OAAO,aAAa,IAAI,MAAM,SAAS,EAAE;AACjD,YAAM,YAAY;AAAA,QACjB;AAAA,QACA,OAAO,KAAK;AAAA,QACZ,MAAM,KAAK;AAAA,QACX,aAAa,KAAK;AAAA,QAClB,MAAM,KAAK;AAAA,QACX,wBAAwB,KAAK;AAAA,MAC9B,CAAC;AACD,YAAM,OAAO,EAAE;AACf,aAAO,EAAE,KAAK,MAAM,SAAS,IAAI,OAAO,YAAY,CAAC;AAAA,IACtD,SAAS,KAAK;AACb,aAAO,aAAa,GAAG,GAAG;AAAA,IAC3B;AAAA,EACD,CAAC;AAGD,EAAAA,KAAI,IAAI,yBAAyB,OAAO,MAAM;AAC7C,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAM,KAAK,MAAM,aAAa,EAAE;AAChC,QAAI,CAAC,GAAI,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAC7D,UAAM,OAAQ,MAAM,EAAE,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AACjD,QAAI,CAAC,QAAQ,OAAO,KAAK,aAAa,YAAY,OAAO,KAAK,SAAS,YAAY,OAAO,KAAK,gBAAgB,YAAY,OAAO,KAAK,SAAS,UAAU;AACzJ,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,iDAAiD,GAAG,GAAG;AAAA,IAC1F;AACA,QAAI;AACH,YAAM,iBAAiB,YAAY,EAAE;AACrC,YAAM,EAAE,OAAO,aAAa,IAAI,MAAM,SAAS,EAAE;AACjD,YAAM,YAAY;AAAA,QACjB;AAAA,QACA,UAAU,KAAK;AAAA,QACf,MAAM,KAAK;AAAA,QACX,aAAa,KAAK;AAAA,QAClB,MAAM,KAAK;AAAA,QACX,wBAAwB,KAAK;AAAA,MAC9B,CAAC;AACD,YAAM,OAAO,EAAE;AACf,aAAO,EAAE,KAAK,MAAM,SAAS,IAAI,OAAO,YAAY,CAAC;AAAA,IACtD,SAAS,KAAK;AACb,aAAO,aAAa,GAAG,GAAG;AAAA,IAC3B;AAAA,EACD,CAAC;AAGD,EAAAA,KAAI,OAAO,yBAAyB,OAAO,MAAM;AAChD,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAM,KAAK,MAAM,aAAa,EAAE;AAChC,QAAI,CAAC,GAAI,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAC7D,UAAM,WAAW,EAAE,IAAI,MAAM,MAAM;AACnC,QAAI,CAAC,SAAU,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,yBAAyB,GAAG,GAAG;AAChF,QAAI;AACH,YAAM,iBAAiB,YAAY,EAAE;AACrC,YAAM,EAAE,OAAO,aAAa,IAAI,MAAM,SAAS,EAAE;AACjD,YAAM,YAAY,UAAU,KAAK;AACjC,YAAM,OAAO,EAAE;AACf,aAAO,EAAE,KAAK,MAAM,SAAS,IAAI,OAAO,YAAY,CAAC;AAAA,IACtD,SAAS,KAAK;AACb,aAAO,aAAa,GAAG,GAAG;AAAA,IAC3B;AAAA,EACD,CAAC;AAGD,EAAAA,KAAI,IAAI,yBAAyB,OAAO,MAAM;AAC7C,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAM,KAAK,MAAM,aAAa,EAAE;AAChC,QAAI,CAAC,GAAI,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAC7D,UAAM,WAAW,EAAE,IAAI,MAAM,MAAM;AACnC,QAAI,CAAC,SAAU,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,yBAAyB,GAAG,GAAG;AAChF,QAAI;AACH,YAAM,EAAE,MAAM,IAAI,MAAM,SAAS,EAAE;AACnC,YAAM,QAAQ,SAAS,UAAU,KAAK;AACtC,UAAI,UAAU,QAAW;AACxB,eAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,0CAA0C,GAAG,GAAG;AAAA,MACnF;AACA,YAAM,OAAO,MAAM,eAAe,UAAU,KAAK;AACjD,YAAM,OAA6B;AAAA,QAClC;AAAA,QACA;AAAA,QACA,MAAM,KAAK;AAAA,QACX,aAAa,KAAK;AAAA,QAClB,cAAc,KAAK;AAAA,QACnB,MAAM,KAAK;AAAA,MACZ;AACA,aAAO,EAAE,KAAK,IAAI;AAAA,IACnB,SAAS,KAAK;AACb,aAAO,aAAa,GAAG,GAAG;AAAA,IAC3B;AAAA,EACD,CAAC;AAGD,EAAAA,KAAI,KAAK,0BAA0B,OAAO,MAAM;AAC/C,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAM,KAAK,MAAM,aAAa,EAAE;AAChC,QAAI,CAAC,GAAI,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAC7D,UAAM,OAAQ,MAAM,EAAE,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AACjD,QAAI,CAAC,QAAQ,CAAC,QAAQ,KAAK,KAAK,KAAK,OAAO,KAAK,SAAS,YAAY,OAAO,KAAK,gBAAgB,YAAY,OAAO,KAAK,SAAS,UAAU;AAC5I,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,8CAA8C,GAAG,GAAG;AAAA,IACvF;AACA,QAAI;AACH,YAAM,iBAAiB,YAAY,EAAE;AACrC,YAAM,EAAE,OAAO,aAAa,IAAI,MAAM,SAAS,EAAE;AACjD,YAAM,aAAa;AAAA,QAClB;AAAA,QACA,OAAO,KAAK;AAAA,QACZ,MAAM,KAAK;AAAA,QACX,aAAa,KAAK;AAAA,QAClB,cAAc,KAAK;AAAA,QACnB,MAAM,KAAK;AAAA,MACZ,CAAC;AACD,YAAM,OAAO,EAAE;AACf,aAAO,EAAE,KAAK,MAAM,SAAS,IAAI,OAAO,YAAY,CAAC;AAAA,IACtD,SAAS,KAAK;AACb,aAAO,aAAa,GAAG,GAAG;AAAA,IAC3B;AAAA,EACD,CAAC;AAGD,EAAAA,KAAI,IAAI,0BAA0B,OAAO,MAAM;AAC9C,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAM,KAAK,MAAM,aAAa,EAAE;AAChC,QAAI,CAAC,GAAI,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAC7D,UAAM,OAAQ,MAAM,EAAE,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AACjD,QAAI,CAAC,QAAQ,OAAO,KAAK,aAAa,YAAY,OAAO,KAAK,SAAS,YAAY,OAAO,KAAK,gBAAgB,YAAY,OAAO,KAAK,SAAS,UAAU;AACzJ,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,iDAAiD,GAAG,GAAG;AAAA,IAC1F;AACA,QAAI;AACH,YAAM,iBAAiB,YAAY,EAAE;AACrC,YAAM,EAAE,OAAO,aAAa,IAAI,MAAM,SAAS,EAAE;AACjD,YAAM,aAAa;AAAA,QAClB;AAAA,QACA,UAAU,KAAK;AAAA,QACf,MAAM,KAAK;AAAA,QACX,aAAa,KAAK;AAAA,QAClB,cAAc,KAAK;AAAA,QACnB,MAAM,KAAK;AAAA,MACZ,CAAC;AACD,YAAM,OAAO,EAAE;AACf,aAAO,EAAE,KAAK,MAAM,SAAS,IAAI,OAAO,YAAY,CAAC;AAAA,IACtD,SAAS,KAAK;AACb,aAAO,aAAa,GAAG,GAAG;AAAA,IAC3B;AAAA,EACD,CAAC;AAGD,EAAAA,KAAI,OAAO,0BAA0B,OAAO,MAAM;AACjD,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAM,KAAK,MAAM,aAAa,EAAE;AAChC,QAAI,CAAC,GAAI,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAC7D,UAAM,WAAW,EAAE,IAAI,MAAM,MAAM;AACnC,QAAI,CAAC,SAAU,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,yBAAyB,GAAG,GAAG;AAChF,QAAI;AACH,YAAM,iBAAiB,YAAY,EAAE;AACrC,YAAM,EAAE,OAAO,aAAa,IAAI,MAAM,SAAS,EAAE;AACjD,YAAM,aAAa,UAAU,KAAK;AAClC,YAAM,OAAO,EAAE;AACf,aAAO,EAAE,KAAK,MAAM,SAAS,IAAI,OAAO,YAAY,CAAC;AAAA,IACtD,SAAS,KAAK;AACb,aAAO,aAAa,GAAG,GAAG;AAAA,IAC3B;AAAA,EACD,CAAC;AACF;AAEA,SAAS,QAAQ,OAAwC;AACxD,SAAO,UAAU,UAAU,UAAU;AACtC;;;AXtbO,IAAM,kBAAkB,IAAI,KAAK;AAExC,gBAAgB,IAAI,KAAK,OAAO,MAAM;AACrC,QAAM,MAAM,MAAM,eAAe;AACjC,QAAM,aAAa,MAAM,QAAQ,IAAI,IAAI,IAAI,eAAe,CAAC;AAC7D,QAAM,OAA+B,EAAE,WAAW;AAClD,SAAO,EAAE,KAAK,IAAI;AACnB,CAAC;AAED,gBAAgB,IAAI,iBAAiB,OAAO,MAAM;AACjD,QAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,QAAM,UAAU,MAAM,aAAa,EAAE;AACrC,MAAI,CAAC,QAAS,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAElE,MAAI;AACH,UAAM,WAAW,MAAM,iBAAiB,aAAa,EAAE;AACvD,UAAM,OAA6B,EAAE,SAAS;AAC9C,WAAO,EAAE,KAAK,IAAI;AAAA,EACnB,SAAS,KAAK;AACb,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,MAAM,2BAA2B,EAAE,YAAY,GAAG;AAC1D,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,EACjD;AACD,CAAC;AAED,gBAAgB,OAAO,iBAAiB,OAAO,MAAM;AACpD,QAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,QAAM,UAAU,MAAM,aAAa,EAAE;AACrC,MAAI,CAAC,QAAS,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAElE,QAAM,cAAc,EAAE,IAAI,MAAM,MAAM;AACtC,MAAI,CAAC,aAAa;AACjB,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,yBAAyB,GAAG,GAAG;AAAA,EAClE;AAEA,MAAI;AACH,UAAM,iBAAiB,cAAc,IAAI,WAAW;AACpD,UAAM,OAAmB,EAAE,IAAI,KAAK;AACpC,WAAO,EAAE,KAAK,IAAI;AAAA,EACnB,SAAS,KAAK;AACb,QAAI,eAAe,WAAW;AAC7B,aAAO,EAAE;AAAA,QACR,EAAE,IAAI,OAAO,OAAO,IAAI,QAAQ;AAAA,QAChC,IAAI;AAAA,MACL;AAAA,IACD;AACA,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,MAAM,4BAA4B,EAAE,YAAY,GAAG;AAC3D,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,EACjD;AACD,CAAC;AAED,gBAAgB,IAAI,oBAAoB,OAAO,MAAM;AACpD,QAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,QAAM,UAAU,MAAM,aAAa,EAAE;AACrC,MAAI,CAAC,QAAS,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAElE,MAAI;AACH,UAAM,UAAU,MAAM,iBAAiB,YAAY,EAAE;AACrD,UAAM,MAAM,QAAQ,QAAQ,0BAA0B;AACtD,UAAM,SAAS,IAAI,IAAI,CAAC,OAAO;AAAA,MAC9B,SAAS,EAAE;AAAA,MACX,MAAM,EAAE,KAAK,SAAS,MAAM,EAAE,KAAK,MAAM,GAAG,GAAG,IAAI,WAAM,EAAE;AAAA,IAC5D,EAAE;AACF,UAAM,OAA+B,EAAE,OAAO;AAC9C,WAAO,EAAE,KAAK,IAAI;AAAA,EACnB,SAAS,KAAK;AACb,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,MAAM,yBAAyB,EAAE,YAAY,GAAG;AACxD,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,EACjD;AACD,CAAC;AAED,gBAAgB,IAAI,gBAAgB,OAAO,MAAM;AAChD,QAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,QAAM,UAAU,MAAM,aAAa,EAAE;AACrC,MAAI,CAAC,QAAS,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAElE,MAAI;AACH,UAAM,iBAAiB,YAAY,EAAE;AACrC,UAAM,cAAc,EAAE,IAAI,MAAM,aAAa;AAC7C,UAAM,OAAwB,iBAAiB,kBAAkB,IAAI,WAAW;AAChF,WAAO,EAAE,KAAK,IAAI;AAAA,EACnB,SAAS,KAAK;AACb,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,MAAM,qBAAqB,EAAE,YAAY,GAAG;AACpD,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,EACjD;AACD,CAAC;AAED,gBAAgB,KAAK,KAAK,OAAO,MAAM;AACtC,QAAM,OAAQ,MAAM,EAAE,IAAI,KAAK;AAC/B,MAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,SAAS,UAAU;AACjD,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,mBAAmB,GAAG,GAAG;AAAA,EAC5D;AACA,MAAI,CAACC,YAAW,KAAK,IAAI,GAAG;AAC3B,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,wBAAwB,GAAG,GAAG;AAAA,EACjE;AACA,QAAM,WAAWC,SAAQ,KAAK,IAAI;AAClC,MAAI;AACH,UAAM,KAAK,MAAMC,MAAK,QAAQ;AAC9B,QAAI,CAAC,GAAG,YAAY,GAAG;AACtB,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,0BAA0B,GAAG,GAAG;AAAA,IACnE;AAAA,EACD,QAAQ;AACP,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,sBAAsB,GAAG,GAAG;AAAA,EAC/D;AAEA,QAAM,SAAS,MAAM,aAAa;AAAA,IACjC,MAAM;AAAA,IACN,MAAM,KAAK,MAAM,KAAK,KAAKC,UAAS,QAAQ,KAAK;AAAA,EAClD,CAAC;AACD,QAAM,KAAK,MAAM,gBAAgB,MAAM;AACvC,QAAM,MAA4B,EAAE,WAAW,GAAG;AAClD,SAAO,EAAE,KAAK,GAAG;AAClB,CAAC;AAED,gBAAgB,OAAO,QAAQ,OAAO,MAAM;AAC3C,QAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,QAAM,UAAU,MAAM,aAAa,EAAE;AACrC,MAAI,CAAC,QAAS,QAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,GAAG,GAAG;AAGlE,QAAM,iBAAiB,QAAQ,EAAE;AACjC,QAAM,gBAAgB,EAAE;AAExB,QAAM,OAAmB,EAAE,IAAI,KAAK;AACpC,SAAO,EAAE,KAAK,IAAI;AACnB,CAAC;AAED,kBAAkB,eAAe;AACjC,oBAAoB,eAAe;AACnC,gBAAgB,eAAe;;;AYvJ/B,SAAS,WAAAC,gBAAe;AACxB,SAAS,WAAAC,gBAAe;AACxB,SAAS,WAAAC,UAAS,cAAAC,aAAY,QAAAC,OAAM,WAAAC,gBAAe;AACnD,SAAS,QAAAC,aAAY;AAGd,IAAM,UAAU,IAAIA,MAAK;AAEhC,QAAQ,IAAI,WAAW,OAAO,MAAM;AACnC,QAAM,UAAU,EAAE,IAAI,MAAM,MAAM;AAClC,QAAM,aAAa,EAAE,IAAI,MAAM,YAAY,MAAM;AAEjD,QAAM,SAAS,WAAWH,YAAW,OAAO,IAAIE,SAAQ,OAAO,IAAIJ,SAAQ;AAC3E,MAAI;AACJ,MAAI;AACH,cAAU,MAAMD,SAAQ,QAAQ,EAAE,eAAe,KAAK,CAAC;AAAA,EACxD,SAAS,KAAK;AACb,UAAM,OAAQ,IAA8B;AAC5C,UAAM,MAAM,SAAS,WAAW,sBAAsB,SAAS,WAAW,cAAc;AACxF,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,KAAK,MAAM,OAAO,GAAG,GAAG;AAAA,EAC3D;AAEA,QAAM,UAAqB,QACzB,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,EAC7B,OAAO,CAAC,MAAM,cAAc,CAAC,EAAE,KAAK,WAAW,GAAG,CAAC,EACnD,IAAI,CAAC,OAAO;AAAA,IACZ,MAAM,EAAE;AAAA,IACR,MAAMI,MAAK,QAAQ,EAAE,IAAI;AAAA,IACzB,MAAM;AAAA,EACP,EAAE,EACD,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAE7C,QAAM,UAAU,MAAM;AACrB,UAAM,IAAIF,SAAQ,MAAM;AACxB,WAAO,MAAM,SAAS,OAAO;AAAA,EAC9B,GAAG;AAEH,QAAM,OAAyB,EAAE,MAAM,QAAQ,QAAQ,QAAQ;AAC/D,SAAO,EAAE,KAAK,IAAI;AACnB,CAAC;;;ACzCD,SAAS,YAAAK,WAAU,aAAAC,YAAW,SAAAC,cAAa;AAC3C,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAC9B,SAAS,QAAAC,aAAY;AACrB;AAAA,EACC,eAAAC;AAAA,OACM;AAWA,IAAM,oBAAoB,IAAIC,MAAK;AAU1C,IAAI,YAAY,QAAQ,QAAQ;AAChC,SAAS,cAAiB,IAAkC;AAC3D,QAAM,OAAO,UAAU,KAAK,IAAI,EAAE;AAClC,cAAY,KAAK,KAAK,MAAM;AAAA,EAAC,GAAG,MAAM;AAAA,EAAC,CAAC;AACxC,SAAO;AACR;AAEA,SAAS,aAAqB;AAC7B,SAAOC,MAAKC,aAAY,GAAG,aAAa;AACzC;AAEA,eAAe,iBAAwC;AACtD,MAAI;AACH,UAAM,MAAM,MAAMC,UAAS,WAAW,GAAG,OAAO;AAChD,WAAO,KAAK,MAAM,GAAG;AAAA,EACtB,SAAS,KAAK;AACb,QAAK,KAA+B,SAAS,UAAU;AACtD,aAAO,EAAE,WAAW,CAAC,EAAE;AAAA,IACxB;AACA,UAAM;AAAA,EACP;AACD;AAEA,eAAe,gBAAgBC,SAAqC;AACnE,QAAM,IAAI,WAAW;AACrB,QAAMC,OAAMC,SAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3C,QAAMC,WAAU,GAAG,KAAK,UAAUH,SAAQ,MAAM,CAAC,GAAG,OAAO;AAC5D;AAGA,IAAM,kBAAN,cAA8B,MAAM;AAAA,EACnC,YAAY,SAAiC,QAAgB;AAC5D,UAAM,OAAO;AAD+B;AAAA,EAE7C;AAAA,EAF6C;AAG9C;AAEA,SAAS,gBAAgB,aAA4B;AACpD,MAAI,CAAC,YAAa;AAClB,QAAM,UAAU,iBAAiB,IAAI,WAAW;AAChD,MAAI,SAAS;AACZ,QAAI;AACH,cAAQ,QAAQ,cAAc,QAAQ;AAAA,IACvC,SAAS,GAAG;AACX,cAAQ,MAAM,wCAAwC,WAAW,YAAY,CAAC;AAAA,IAC/E;AAAA,EACD;AACD;AAKA,kBAAkB,IAAI,KAAK,OAAO,MAAM;AACvC,MAAI;AACH,UAAMA,UAAS,MAAM,eAAe;AACpC,UAAM,OAA6B,EAAE,QAAAA,QAAO;AAC5C,WAAO,EAAE,KAAK,IAAI;AAAA,EACnB,SAAS,KAAK;AACb,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,EACjD;AACD,CAAC;AAGD,kBAAkB,IAAI,KAAK,OAAO,MAAM;AACvC,QAAM,OAAQ,MAAM,EAAE,IAAI,KAAK;AAC/B,MAAI,CAAC,MAAM,QAAQ,WAAW;AAC7B,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,+BAA+B,GAAG,GAAG;AAAA,EACxE;AACA,MAAI;AACH,UAAM,cAAc,YAAY;AAC/B,YAAM,gBAAgB,KAAK,MAAM;AAAA,IAClC,CAAC;AACD,UAAM,cAAc,EAAE,IAAI,MAAM,aAAa;AAC7C,oBAAgB,eAAe,MAAS;AACxC,UAAM,OAA6B,EAAE,QAAQ,KAAK,OAAO;AACzD,WAAO,EAAE,KAAK,IAAI;AAAA,EACnB,SAAS,KAAK;AACb,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,EACjD;AACD,CAAC;AAGD,kBAAkB,KAAK,cAAc,OAAO,MAAM;AACjD,QAAM,OAAQ,MAAM,EAAE,IAAI,KAAK;AAC/B,MAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,UAAU;AACnC,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,iCAAiC,GAAG,GAAG;AAAA,EAC1E;AACA,MAAI,CAAC,KAAK,SAAS,WAAW,CAAC,KAAK,SAAS,OAAO,CAAC,KAAK,SAAS,QAAQ;AAC1E,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,8CAA8C,GAAG,GAAG;AAAA,EACvF;AACA,MAAI,CAAC,MAAM,QAAQ,KAAK,SAAS,MAAM,GAAG;AACzC,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,mCAAmC,GAAG,GAAG;AAAA,EAC5E;AACA,MAAI;AACH,UAAMA,UAAS,MAAM,cAAc,YAAY;AAC9C,YAAM,MAAM,MAAM,eAAe;AACjC,UAAI,UAAU,KAAK,IAAI,IAAI,KAAK;AAChC,YAAM,gBAAgB,GAAG;AACzB,aAAO;AAAA,IACR,CAAC;AACD,UAAM,cAAc,EAAE,IAAI,MAAM,aAAa;AAC7C,oBAAgB,eAAe,MAAS;AACxC,UAAM,OAA6B,EAAE,QAAAA,QAAO;AAC5C,WAAO,EAAE,KAAK,IAAI;AAAA,EACnB,SAAS,KAAK;AACb,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,EACjD;AACD,CAAC;AAGD,kBAAkB,OAAO,cAAc,OAAO,MAAM;AACnD,QAAM,OAAO,EAAE,IAAI,MAAM,MAAM;AAC/B,MAAI,CAAC,MAAM;AACV,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,+BAA+B,GAAG,GAAG;AAAA,EACxE;AACA,MAAI;AACH,UAAMA,UAAS,MAAM,cAAc,YAAY;AAC9C,YAAM,MAAM,MAAM,eAAe;AACjC,UAAI,CAAC,IAAI,UAAU,IAAI,GAAG;AACzB,cAAM,IAAI,gBAAgB,aAAa,IAAI,eAAe,GAAG;AAAA,MAC9D;AACA,aAAO,IAAI,UAAU,IAAI;AACzB,YAAM,gBAAgB,GAAG;AACzB,aAAO;AAAA,IACR,CAAC;AACD,UAAM,cAAc,EAAE,IAAI,MAAM,aAAa;AAC7C,oBAAgB,eAAe,MAAS;AACxC,UAAM,OAA6B,EAAE,QAAAA,QAAO;AAC5C,WAAO,EAAE,KAAK,IAAI;AAAA,EACnB,SAAS,KAAK;AACb,QAAI,eAAe,iBAAiB;AACnC,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,IAAI,QAAQ,GAAG,IAAI,MAAa;AAAA,IACnE;AACA,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,EACjD;AACD,CAAC;AAGD,kBAAkB,KAAK,+BAA+B,OAAO,MAAM;AAClE,QAAM,WAAW,EAAE,IAAI,MAAM,UAAU;AACvC,QAAM,OAAQ,MAAM,EAAE,IAAI,KAAK;AAC/B,MAAI,CAAC,MAAM,OAAO,MAAM,CAAC,MAAM,OAAO,MAAM;AAC3C,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,uCAAuC,GAAG,GAAG;AAAA,EAChF;AACA,MAAI;AACH,UAAMA,UAAS,MAAM,cAAc,YAAY;AAC9C,YAAM,MAAM,MAAM,eAAe;AACjC,UAAI,CAAC,IAAI,UAAU,QAAQ,GAAG;AAC7B,cAAM,IAAI,gBAAgB,aAAa,QAAQ,eAAe,GAAG;AAAA,MAClE;AACA,YAAM,WAAW,IAAI,UAAU,QAAQ,EAAE,OAAO,KAAK,OAAK,EAAE,OAAO,KAAK,MAAM,EAAE;AAChF,UAAI,UAAU;AACb,cAAM,IAAI,gBAAgB,UAAU,KAAK,MAAM,EAAE,iCAAiC,QAAQ,KAAK,GAAG;AAAA,MACnG;AACA,UAAI,UAAU,QAAQ,EAAE,OAAO,KAAK,KAAK,KAAK;AAC9C,YAAM,gBAAgB,GAAG;AACzB,aAAO;AAAA,IACR,CAAC;AACD,UAAM,cAAc,EAAE,IAAI,MAAM,aAAa;AAC7C,oBAAgB,eAAe,MAAS;AACxC,UAAM,OAA6B,EAAE,QAAAA,QAAO;AAC5C,WAAO,EAAE,KAAK,IAAI;AAAA,EACnB,SAAS,KAAK;AACb,QAAI,eAAe,iBAAiB;AACnC,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,IAAI,QAAQ,GAAG,IAAI,MAAmB;AAAA,IACzE;AACA,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,EACjD;AACD,CAAC;AAGD,kBAAkB,IAAI,wCAAwC,OAAO,MAAM;AAC1E,QAAM,WAAW,EAAE,IAAI,MAAM,UAAU;AACvC,QAAM,UAAU,EAAE,IAAI,MAAM,SAAS;AACrC,QAAM,OAAQ,MAAM,EAAE,IAAI,KAAK;AAC/B,MAAI,CAAC,MAAM,OAAO;AACjB,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,oBAAoB,GAAG,GAAG;AAAA,EAC7D;AACA,MAAI,KAAK,MAAM,OAAO,SAAS;AAC9B,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,wCAAwC,GAAG,GAAG;AAAA,EACjF;AACA,MAAI;AACH,UAAMA,UAAS,MAAM,cAAc,YAAY;AAC9C,YAAM,MAAM,MAAM,eAAe;AACjC,UAAI,CAAC,IAAI,UAAU,QAAQ,GAAG;AAC7B,cAAM,IAAI,gBAAgB,aAAa,QAAQ,eAAe,GAAG;AAAA,MAClE;AACA,YAAM,MAAM,IAAI,UAAU,QAAQ,EAAE,OAAO,UAAU,OAAK,EAAE,OAAO,OAAO;AAC1E,UAAI,QAAQ,IAAI;AACf,cAAM,IAAI,gBAAgB,UAAU,OAAO,4BAA4B,QAAQ,KAAK,GAAG;AAAA,MACxF;AACA,UAAI,UAAU,QAAQ,EAAE,OAAO,GAAG,IAAI,KAAK;AAC3C,YAAM,gBAAgB,GAAG;AACzB,aAAO;AAAA,IACR,CAAC;AACD,UAAM,cAAc,EAAE,IAAI,MAAM,aAAa;AAC7C,oBAAgB,eAAe,MAAS;AACxC,UAAM,OAA6B,EAAE,QAAAA,QAAO;AAC5C,WAAO,EAAE,KAAK,IAAI;AAAA,EACnB,SAAS,KAAK;AACb,QAAI,eAAe,iBAAiB;AACnC,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,IAAI,QAAQ,GAAG,IAAI,MAAa;AAAA,IACnE;AACA,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,EACjD;AACD,CAAC;AAGD,kBAAkB,OAAO,wCAAwC,OAAO,MAAM;AAC7E,QAAM,WAAW,EAAE,IAAI,MAAM,UAAU;AACvC,QAAM,UAAU,EAAE,IAAI,MAAM,SAAS;AACrC,MAAI;AACH,UAAMA,UAAS,MAAM,cAAc,YAAY;AAC9C,YAAM,MAAM,MAAM,eAAe;AACjC,UAAI,CAAC,IAAI,UAAU,QAAQ,GAAG;AAC7B,cAAM,IAAI,gBAAgB,aAAa,QAAQ,eAAe,GAAG;AAAA,MAClE;AACA,YAAM,MAAM,IAAI,UAAU,QAAQ,EAAE,OAAO,UAAU,OAAK,EAAE,OAAO,OAAO;AAC1E,UAAI,QAAQ,IAAI;AACf,cAAM,IAAI,gBAAgB,UAAU,OAAO,4BAA4B,QAAQ,KAAK,GAAG;AAAA,MACxF;AACA,UAAI,UAAU,QAAQ,EAAE,OAAO,OAAO,KAAK,CAAC;AAC5C,YAAM,gBAAgB,GAAG;AACzB,aAAO;AAAA,IACR,CAAC;AACD,UAAM,cAAc,EAAE,IAAI,MAAM,aAAa;AAC7C,oBAAgB,eAAe,MAAS;AACxC,UAAM,OAA6B,EAAE,QAAAA,QAAO;AAC5C,WAAO,EAAE,KAAK,IAAI;AAAA,EACnB,SAAS,KAAK;AACb,QAAI,eAAe,iBAAiB;AACnC,aAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,IAAI,QAAQ,GAAG,IAAI,MAAa;AAAA,IACnE;AACA,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAO,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAAA,EACjD;AACD,CAAC;;;ACjQD,SAAS,uBAAuC;;;ACPzC,SAAS,iBAAiB,IAAmD;AACnF,UAAQ,GAAG,MAAM;AAAA,IAChB,KAAK;AACJ,aAAO,EAAE,MAAM,cAAc;AAAA,IAE9B,KAAK;AACJ,aAAO,EAAE,MAAM,aAAa,WAAW,GAAG,UAAU;AAAA,IAErD,KAAK;AACJ,aAAO,EAAE,MAAM,aAAa;AAAA,IAE7B,KAAK;AACJ,aAAO,EAAE,MAAM,WAAW;AAAA,IAE3B,KAAK,iBAAiB;AACrB,YAAM,OAAO,OAAO,GAAG,OAAO;AAK9B,YAAM,OAAO,SAAS,SAASI,iBAAgB,GAAG,OAAO,IAAI;AAC7D,aAAO,EAAE,MAAM,iBAAiB,MAAM,KAAK;AAAA,IAC5C;AAAA,IAEA,KAAK;AACJ,aAAO,EAAE,MAAM,eAAe,MAAM,OAAO,GAAG,OAAO,EAAE;AAAA,IAExD,KAAK,kBAAkB;AACtB,YAAM,MAAM,GAAG;AACf,UAAI,IAAI,SAAS,cAAc;AAC9B,eAAO;AAAA,UACN,MAAM;AAAA,UACN,OAAO,EAAE,MAAM,QAAQ,cAAc,IAAI,cAAc,MAAM,IAAI,MAAM;AAAA,QACxE;AAAA,MACD;AACA,UAAI,IAAI,SAAS,kBAAkB;AAClC,eAAO;AAAA,UACN,MAAM;AAAA,UACN,OAAO,EAAE,MAAM,YAAY,cAAc,IAAI,cAAc,MAAM,IAAI,MAAM;AAAA,QAC5E;AAAA,MACD;AAEA,aAAO,EAAE,MAAM,kBAAkB,OAAO,EAAE,MAAM,QAAQ,EAAE;AAAA,IAC3D;AAAA,IAEA,KAAK;AACJ,aAAO;AAAA,QACN,MAAM;AAAA,QACN,YAAY,GAAG;AAAA,QACf,UAAU,GAAG;AAAA,QACb,MAAM,GAAG;AAAA,MACV;AAAA,IAED,KAAK;AACJ,aAAO;AAAA,QACN,MAAM;AAAA,QACN,YAAY,GAAG;AAAA,QACf,UAAU,GAAG;AAAA,QACb,aAAa,YAAY,GAAG,aAAa;AAAA,MAC1C;AAAA,IAED,KAAK;AACJ,aAAO;AAAA,QACN,MAAM;AAAA,QACN,YAAY,GAAG;AAAA,QACf,UAAU,GAAG;AAAA,QACb,SAAS,GAAG;AAAA,QACZ,MAAM,YAAY,GAAG,MAAM;AAAA,MAC5B;AAAA,IAED,KAAK;AACJ,aAAO;AAAA,QACN,MAAM;AAAA,QACN,UAAU,CAAC,GAAG,GAAG,QAAQ;AAAA,QACzB,UAAU,CAAC,GAAG,GAAG,QAAQ;AAAA,MAC1B;AAAA,IAED,KAAK;AACJ,aAAO;AAAA,QACN,MAAM;AAAA,QACN,SAAS,GAAG;AAAA,QACZ,aAAa,GAAG;AAAA,QAChB,SAAS,GAAG;AAAA,QACZ,cAAc,GAAG;AAAA,MAClB;AAAA,IAED,KAAK;AACJ,aAAO;AAAA,QACN,MAAM;AAAA,QACN,SAAS,GAAG;AAAA,QACZ,SAAS,GAAG;AAAA,QACZ,YAAY,GAAG;AAAA,MAChB;AAAA,IAED,KAAK;AACJ,aAAO,EAAE,MAAM,oBAAoB,QAAQ,GAAG,OAAO;AAAA,IAEtD,KAAK;AACJ,aAAO;AAAA,QACN,MAAM;AAAA,QACN,QAAQ,GAAG;AAAA,QACX,SAAS,GAAG;AAAA,QACZ,WAAW,GAAG;AAAA,QACd,cAAc,GAAG;AAAA,MAClB;AAAA,IAED,KAAK;AACJ,aAAO,EAAE,MAAM,wBAAwB,MAAM,GAAG,KAAK;AAAA,IAEtD,KAAK;AACJ,aAAO,EAAE,MAAM,0BAA0B,OAAO,GAAG,MAAM;AAAA,IAE1D;AACC,aAAO;AAAA,EACT;AACD;AAEA,SAAS,OAAO,SAAyE;AACxF,QAAM,OAAQ,SAA2C;AACzD,MAAI,SAAS,UAAU,SAAS,eAAe,SAAS,gBAAgB,SAAS,iBAAiB;AACjG,WAAO;AAAA,EACR;AACA,SAAO;AACR;AASA,SAASA,iBAAgB,SAAsC;AAC9D,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAM,UAAW,QAAkC;AACnD,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,SAAS;AAC5B,QAAI,SAAS,OAAO,UAAU,YAAa,MAA4B,SAAS,QAAQ;AACvF,YAAM,OAAQ,MAA6B;AAC3C,UAAI,OAAO,SAAS,SAAU,OAAM,KAAK,IAAI;AAAA,IAC9C;AAAA,EACD;AACA,SAAO,MAAM,WAAW,IAAI,SAAY,MAAM,KAAK,EAAE;AACtD;AAGA,SAAS,YAAY,QAAqC;AACzD,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,UAAW,OAAiC;AAClD,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,QAAM,QAAkB,CAAC;AACzB,aAAW,KAAK,SAAS;AACxB,QAAI,KAAK,OAAO,MAAM,YAAa,EAAwB,SAAS,QAAQ;AAC3E,YAAM,OAAQ,EAAyB;AACvC,UAAI,OAAO,SAAS,SAAU,OAAM,KAAK,IAAI;AAAA,IAC9C;AAAA,EACD;AACA,SAAO,MAAM,WAAW,IAAI,SAAY,MAAM,KAAK,EAAE;AACtD;;;ADlIA,IAAM,mBAAmB,oBAAI,IAA2B;AAExD,SAAS,oBAAoB,aAAqB,IAAwC;AACzF,QAAM,OAAO,iBAAiB,IAAI,WAAW,KAAK,QAAQ,QAAQ;AAClE,QAAM,OAAO,KAAK,KAAK,IAAI,EAAE;AAC7B,mBAAiB,IAAI,aAAa,IAAI;AAItC,QAAM,UAAU,MAAM;AACrB,QAAI,iBAAiB,IAAI,WAAW,MAAM,MAAM;AAC/C,uBAAiB,OAAO,WAAW;AAAA,IACpC;AAAA,EACD;AACA,OAAK,KAAK,SAAS,OAAO;AAC1B,SAAO;AACR;AAEO,SAAS,YAAY,YAAqC;AAChE,QAAM,MAAM,IAAI,gBAAgB,EAAE,QAAQ,YAAY,MAAM,MAAM,CAAC;AAEnE,MAAI,GAAG,cAAc,CAAC,OAAO;AAC5B,UAAM,QAAyB,CAAC;AAEhC,OAAG,GAAG,WAAW,OAAO,QAAQ;AAC/B,UAAI;AACJ,UAAI;AACH,cAAM,KAAK,MAAM,IAAI,SAAS,CAAC;AAAA,MAChC,QAAQ;AACP,aAAK,IAAI,EAAE,MAAM,SAAS,SAAS,eAAe,CAAC;AACnD;AAAA,MACD;AAEA,UAAI;AACH,cAAM,OAAO,IAAI,OAAO,GAAG;AAAA,MAC5B,SAAS,KAAK;AACb,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAK,IAAI,EAAE,MAAM,SAAS,SAAS,SAAS,IAAI,KAAK,CAAC;AAAA,MACvD;AAAA,IACD,CAAC;AAED,OAAG,GAAG,SAAS,MAAM;AACpB,aAAO,OAAO,EAAE;AAAA,IACjB,CAAC;AAAA,EACF,CAAC;AAED,SAAO;AACR;AAEA,eAAe,OAAO,IAAe,OAAwB,KAAqC;AACjG,UAAQ,IAAI,MAAM;AAAA,IACjB,KAAK,aAAa;AACjB,YAAM,yBACL,MAAM,gBAAgB,IAAI,eAAe,CAAC,CAAC,MAAM;AAQlD,2BAAqB,IAAI,OAAO,IAAI,WAAW;AAC/C,YAAM,iBAAiB,YAAY,IAAI,WAAW;AAElD,UAAI,WAAW;AACf,UAAI;AACJ,UAAI,IAAI,aAAa;AACpB,cAAM,oBAAoB,IAAI,aAAa,YAAY;AACtD,cAAI;AACH,uBAAW,MAAM,iBAAiB,cAAc,IAAI,aAAa,IAAI,WAAY;AAAA,UAClF,SAAS,KAAK;AACb,0BAAc,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,UAC9D;AAAA,QACD,CAAC;AAAA,MACF;AACA,UAAI,CAAC,YAAY,CAAC,wBAAwB;AACzC,2BAAmB,IAAI,OAAO,IAAI,WAAW;AAAA,MAC9C;AACA,UAAI,aAAa;AAChB,aAAK,IAAI,EAAE,MAAM,SAAS,SAAS,aAAa,SAAS,YAAY,CAAC;AAAA,MACvE;AACA,WAAK,IAAI,EAAE,MAAM,OAAO,SAAS,YAAY,CAAC;AAC9C;AAAA,IACD;AAAA,IAEA,KAAK,UAAU;AACd,YAAM,OAAO,MAAM;AACnB,UAAI,CAAC,MAAM;AACV,aAAK,IAAI,EAAE,MAAM,SAAS,SAAS,kBAAkB,SAAS,SAAS,CAAC;AACxE;AAAA,MACD;AAEA,UAAI,iBAAiB,IAAI,IAAI,GAAG;AAC/B,aAAK,IAAI,EAAE,MAAM,SAAS,SAAS,iCAAiC,SAAS,SAAS,CAAC;AACvF;AAAA,MACD;AACA,YAAM,UAAU,iBAAiB,IAAI,IAAI;AACzC,UAAI,CAAC,SAAS;AACb,aAAK,IAAI,EAAE,MAAM,SAAS,SAAS,gBAAgB,SAAS,SAAS,CAAC;AACtE;AAAA,MACD;AAGA,WAAK,QAAQ,QACX,OAAO,IAAI,SAAS;AAAA,QACpB,mBAAmB,IAAI;AAAA,MACxB,CAAC,EACA,MAAM,CAAC,QAAiB;AACxB,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAK,IAAI,EAAE,MAAM,SAAS,SAAS,SAAS,SAAS,CAAC;AAAA,MACvD,CAAC;AACF;AAAA,IACD;AAAA,IAEA,KAAK,SAAS;AACb,YAAM,OAAO,MAAM;AACnB,UAAI,CAAC,MAAM;AACV,aAAK,IAAI,EAAE,MAAM,SAAS,SAAS,kBAAkB,SAAS,QAAQ,CAAC;AACvE;AAAA,MACD;AACA,UAAI,iBAAiB,IAAI,IAAI,GAAG;AAC/B,aAAK,IAAI,EAAE,MAAM,SAAS,SAAS,iCAAiC,SAAS,QAAQ,CAAC;AACtF;AAAA,MACD;AACA,YAAM,UAAU,iBAAiB,IAAI,IAAI;AACzC,UAAI,CAAC,QAAS;AAEd,YAAM,QAAQ,QAAQ,MAAM;AAC5B;AAAA,IACD;AAAA,IAEA,KAAK,eAAe;AACnB,YAAM,OAAO,MAAM;AACnB,UAAI,CAAC,MAAM;AACV,aAAK,IAAI,EAAE,MAAM,SAAS,SAAS,kBAAkB,SAAS,cAAc,CAAC;AAC7E;AAAA,MACD;AACA,YAAM,oBAAoB,IAAI,aAAa,YAAY;AACtD,cAAM,UAAU,iBAAiB,IAAI,IAAI;AACzC,YAAI,CAAC,SAAS;AACb,eAAK,IAAI,EAAE,MAAM,SAAS,SAAS,gBAAgB,SAAS,cAAc,CAAC;AAC3E;AAAA,QACD;AACA,YAAI,QAAQ,QAAQ,aAAa;AAChC,eAAK,IAAI,EAAE,MAAM,SAAS,SAAS,yCAAyC,SAAS,cAAc,CAAC;AACpG;AAAA,QACD;AACA,cAAM,SAAS,MAAM,QAAQ,WAAW;AACxC,YAAI,OAAO,WAAW;AACrB,eAAK,IAAI,EAAE,MAAM,SAAS,SAAS,yBAAyB,SAAS,cAAc,CAAC;AAAA,QACrF;AAAA,MAED,CAAC;AACD;AAAA,IACD;AAAA,IAEA,KAAK,QAAQ;AACZ,YAAM,OAAO,MAAM;AACnB,UAAI,CAAC,MAAM;AACV,aAAK,IAAI,EAAE,MAAM,SAAS,SAAS,kBAAkB,SAAS,OAAO,CAAC;AACtE;AAAA,MACD;AACA,YAAM,oBAAoB,IAAI,aAAa,YAAY;AACtD,cAAM,UAAU,iBAAiB,IAAI,IAAI;AACzC,YAAI,CAAC,SAAS;AACb,eAAK,IAAI,EAAE,MAAM,SAAS,SAAS,gBAAgB,SAAS,OAAO,CAAC;AACpE;AAAA,QACD;AACA,YAAI,QAAQ,QAAQ,aAAa;AAChC,eAAK,IAAI,EAAE,MAAM,SAAS,SAAS,+BAA+B,SAAS,OAAO,CAAC;AACnF;AAAA,QACD;AACA,cAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,OAAO;AAC7C,YAAI,OAAO,WAAW;AACrB,eAAK,IAAI,EAAE,MAAM,SAAS,SAAS,kBAAkB,SAAS,OAAO,CAAC;AAAA,QACvE;AAAA,MAED,CAAC;AACD;AAAA,IACD;AAAA,IAEA,SAAS;AAER,YAAM,IAAW;AACjB,WAAK;AACL,WAAK,IAAI,EAAE,MAAM,SAAS,SAAS,kBAAkB,CAAC;AAAA,IACvD;AAAA,EACD;AACD;AAEA,SAAS,qBAAqB,IAAe,OAAwB,aAA2B;AAC/F,MAAI,MAAM,gBAAgB,eAAe,MAAM,kBAAmB;AAElE,SAAO,OAAO,EAAE;AAChB,QAAM,cAAc;AACpB,mBAAiB,cAAc,aAAa,EAAE;AAC9C,QAAM,oBAAoB,iBAAiB,kBAAkB,aAAa,MAAM;AAC/E,QAAI,MAAM,gBAAgB,YAAa;AACvC,uBAAmB,IAAI,OAAO,WAAW;AAAA,EAC1C,CAAC;AACF;AAEA,SAAS,mBAAmB,IAAe,OAAwB,aAA2B;AAC7F,QAAM,UAAU,iBAAiB,IAAI,WAAW;AAChD,MAAI,CAAC,SAAS;AACb,SAAK,IAAI,EAAE,MAAM,SAAS,SAAS,gBAAgB,SAAS,YAAY,CAAC;AACzE;AAAA,EACD;AAEA,QAAM,qBAAqB;AAC3B,QAAM,UAAU,QAAQ;AACxB,QAAM,cAAc,QAAQ,eAAe;AAM3C,MAAI;AACJ,MAAI;AAEJ,QAAM,qBAAqB,QAAQ,UAAU,CAAC,OAAO;AACpD,UAAM,UAAU,iBAAiB,EAAE;AACnC,QAAI,CAAC,QAAS;AAKd,QAAI,QAAQ,SAAS,mBAAmB,QAAQ,SAAS,aAAa;AACrE,yBAAmB,YAAY,IAAI;AACnC,8BAAwB;AAAA,IACzB,WACC,QAAQ,SAAS,oBACjB,QAAQ,MAAM,SAAS,UACvB,qBAAqB,UACrB,0BAA0B,QACzB;AACD,8BAAwB,YAAY,IAAI;AAAA,IACzC;AAEA,SAAK,IAAI;AAAA,MACR,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,IACD,CAAC;AAKD,QACC,QAAQ,SAAS,iBACjB,QAAQ,SAAS,eACjB,qBAAqB,QACpB;AACD,YAAM,MAAM,YAAY,IAAI;AAC5B,YAAM,SAA4B;AAAA,QACjC,MAAM;AAAA,QACN,cACC,0BAA0B,SACvB,KAAK,MAAM,wBAAwB,gBAAgB,IACnD;AAAA,QACJ,SAAS,KAAK,MAAM,MAAM,gBAAgB;AAAA,MAC3C;AACA,WAAK,IAAI;AAAA,QACR,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,SAAS;AAAA,MACV,CAAC;AACD,yBAAmB;AACnB,8BAAwB;AAAA,IACzB;AAQA,QACC,QAAQ,SAAS,eACjB,QAAQ,SAAS,oBACjB,QAAQ,SAAS,0BACjB,QAAQ,SAAS,0BAChB;AACD,uBAAiB,IAAI,SAAS,aAAa,WAAW;AAAA,IACvD;AAAA,EACD,CAAC;AAED,OAAK,IAAI;AAAA,IACR,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,WAAW,QAAQ;AAAA,EACpB,CAAC;AAGD,mBAAiB,IAAI,SAAS,aAAa,WAAW;AACvD;AAEA,SAAS,iBACR,IACA,SACA,aACA,aACO;AACP,QAAM,QAAQ,QAAQ,QAAQ,gBAAgB;AAC9C,MAAI,CAAC,MAAO;AACZ,QAAM,UAA0B;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,MAAM;AAAA,IACd,eAAe,MAAM;AAAA,IACrB,SAAS,MAAM;AAAA,EAChB;AACA,OAAK,IAAI;AAAA,IACR,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACD,CAAC;AACF;AAEA,SAAS,OAAO,OAAwB,IAAsB;AAC7D,QAAM,qBAAqB;AAC3B,QAAM,qBAAqB;AAC3B,QAAM,oBAAoB;AAC1B,QAAM,oBAAoB;AAC1B,MAAI,MAAM,eAAe,IAAI;AAC5B,qBAAiB,iBAAiB,MAAM,aAAa,EAAE;AAAA,EACxD;AACA,QAAM,cAAc;AACrB;AAEA,SAAS,KAAK,IAAe,KAA4B;AACxD,MAAI,GAAG,eAAe,GAAG,KAAM;AAC/B,KAAG,KAAK,KAAK,UAAU,GAAG,CAAC;AAC5B;;;AhBlWA,IAAM,MAAM,IAAIC,MAAK;AACrB,IAAM,UAAUC,SAAQ,cAAc,YAAY,GAAG,CAAC;AACtD,IAAM,UAAUC,SAAQ,QAAQ,IAAI,qBAAqBC,MAAK,SAAS,MAAM,QAAQ,CAAC;AACtF,IAAM,eAAeA,MAAK,SAAS,YAAY;AAE/C,IAAM,YAAoC;AAAA,EACzC,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AACX;AAEA,SAAS,cAAc,UAA2B;AACjD,SAAO,aAAa,UAAU,SAAS,WAAW,OAAO,KAAK,aAAa;AAC5E;AAEA,SAAS,mBAAmB,UAAsC;AACjE,MAAI;AACJ,MAAI;AACH,cAAU,mBAAmB,QAAQ;AAAA,EACtC,QAAQ;AACP,WAAO;AAAA,EACR;AAEA,QAAM,eAAe,YAAY,MAAM,eAAe,QAAQ,QAAQ,QAAQ,EAAE;AAChF,QAAM,YAAYD,SAAQ,SAAS,YAAY;AAC/C,MAAI,cAAc,WAAW,CAAC,UAAU,WAAW,GAAG,OAAO,GAAGE,IAAG,EAAE,GAAG;AACvE,WAAO;AAAA,EACR;AACA,SAAO;AACR;AAEA,eAAe,YAAY,MAA2C;AACrE,MAAI;AACH,WAAO,MAAMC,UAAS,IAAI;AAAA,EAC3B,SAAS,KAAK;AACb,UAAM,OAAQ,IAA8B;AAC5C,QAAI,SAAS,YAAY,SAAS,SAAU,QAAO;AACnD,UAAM;AAAA,EACP;AACD;AAEA,eAAe,SAAS,GAA+B;AACtD,QAAM,WAAW,IAAI,IAAI,EAAE,IAAI,GAAG,EAAE;AACpC,MAAI,cAAc,QAAQ,EAAG,QAAO,EAAE,SAAS;AAE/C,QAAM,YAAY,mBAAmB,QAAQ;AAC7C,MAAI,CAAC,UAAW,QAAO,EAAE,KAAK,sBAAsB,GAAG;AAEvD,QAAM,QAAQ,MAAM,YAAY,SAAS;AACzC,QAAM,OAAO,SAAU,MAAMA,UAAS,YAAY;AAClD,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,UAAkC;AAAA,IACvC,gBAAgB,UAAU,QAAQ,QAAQ,CAAC,KAAK;AAAA,IAChD,iBAAiB,SAAS,SAAS,WAAW,UAAU,IACrD,wCACA;AAAA,EACJ;AAEA,SAAO,IAAI,SAAS,MAA6B,EAAE,QAAQ,CAAC;AAC7D;AAEA,IAAI;AAAA,EACH;AAAA,EACA,KAAK;AAAA,IACJ,QAAQ,OAAO;AAAA,IACf,cAAc,CAAC,OAAO,QAAQ,OAAO,QAAQ;AAAA,EAC9C,CAAC;AACF;AAEA,IAAI,IAAI,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,CAAC;AAClD,IAAI,MAAM,mBAAmB,eAAe;AAC5C,IAAI,MAAM,WAAW,OAAO;AAC5B,IAAI,MAAM,sBAAsB,iBAAiB;AAEjD,IAAI,WAAW,YAAY,GAAG;AAC7B,MAAI,IAAI,KAAK,QAAQ;AACtB,OAAO;AACN,MAAI;AAAA,IAAI;AAAA,IAAK,CAAC,MACb,EAAE;AAAA,MACD;AAAA,MAEA;AAAA,IACD;AAAA,EACD;AACD;AAEA,IAAM,SAAS;AAAA,EACd;AAAA,IACC,OAAO,IAAI;AAAA,IACX,UAAU,OAAO;AAAA,IACjB,MAAM,OAAO;AAAA,EACd;AAAA,EACA,CAAC,SAAS;AACT,YAAQ,IAAI,qBAAqB,KAAK,OAAO,IAAI,KAAK,IAAI,EAAE;AAAA,EAC7D;AACD;AAEA,YAAY,MAA+B;AAE3C,eAAe,SAAS,QAA+B;AACtD,UAAQ,IAAI,6BAA6B,MAAM,GAAG;AAClD,MAAI;AACH,UAAM,iBAAiB,WAAW;AAAA,EACnC,SAAS,GAAG;AACX,YAAQ,MAAM,gCAAgC,CAAC;AAAA,EAChD;AACA,SAAO,MAAM,MAAM,QAAQ,KAAK,CAAC,CAAC;AAElC,aAAW,MAAM,QAAQ,KAAK,CAAC,GAAG,GAAI,EAAE,MAAM;AAC/C;AAEA,QAAQ,GAAG,UAAU,MAAM,KAAK,SAAS,QAAQ,CAAC;AAClD,QAAQ,GAAG,WAAW,MAAM,KAAK,SAAS,SAAS,CAAC;","names":["readFile","dirname","join","resolve","sep","Hono","stat","basename","isAbsolute","resolve","join","mkdir","readFile","writeFile","dirname","join","join","readFile","mkdir","dirname","writeFile","cache","unlink","isAbsolute","resolve","inflight","resolve","isAbsolute","unlink","app","exists","execFile","join","sep","promisify","exec","promisify","execFile","inflight","out","join","sep","app","readdir","join","getAgentDir","join","getAgentDir","readdir","app","isAbsolute","resolve","stat","basename","readdir","homedir","dirname","isAbsolute","join","resolve","Hono","readFile","writeFile","mkdir","dirname","join","Hono","getAgentDir","Hono","join","getAgentDir","readFile","config","mkdir","dirname","writeFile","extractUserText","Hono","dirname","resolve","join","sep","readFile"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/http-proxy.ts","../src/api/workspaces.ts","../src/storage/resource-writer.ts","../src/storage/workspace-registry.ts","../src/storage/workspace-stats.ts","../src/workspace-manager.ts","../src/extensions/plan/schema.ts","../src/extensions/plan/factory.ts","../src/extensions/ask_user/schema.ts","../src/extensions/ask_user/registry.ts","../src/extensions/ask_user/factory.ts","../src/storage/builtin-extension-prefs.ts","../src/extensions/index.ts","../src/extensions/ask_user/cleanup.ts","../src/ws/bridge.ts","../src/ws/extension-ui.ts","../src/api/config.ts","../src/api/files.ts","../src/api/resources.ts","../src/api/fs.ts","../src/api/model-configs.ts","../src/ws/hub.ts"],"sourcesContent":["/**\n * pi-pilot server entry.\n *\n * Boots one process that serves both HTTP (Hono) and WebSocket (`ws`) on\n * the same port. Localhost only.\n */\n\nimport type { Server as HttpServer } from \"node:http\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, extname, join, resolve, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { serve } from \"@hono/node-server\";\nimport type { Context } from \"hono\";\nimport { Hono } from \"hono\";\nimport { cors } from \"hono/cors\";\nimport { config } from \"./config.ts\";\nimport { configureHttpProxy } from \"./http-proxy.ts\";\nimport { workspacesRoute } from \"./api/workspaces.ts\";\nimport { fsRoute } from \"./api/fs.ts\";\nimport { modelConfigsRoute } from \"./api/model-configs.ts\";\nimport { attachWsHub } from \"./ws/hub.ts\";\nimport { workspaceManager } from \"./workspace-manager.ts\";\nimport { loadBuiltinPrefs } from \"./storage/builtin-extension-prefs.ts\";\n\n// Honor standard HTTPS_PROXY / HTTP_PROXY / NO_PROXY before any pi LLM call.\n// Must run early — the SDK's provider requests go through undici's global\n// dispatcher, which this installs (no-op/direct when no proxy env var is set).\nconfigureHttpProxy();\n\nconst app = new Hono();\nconst distDir = dirname(fileURLToPath(import.meta.url));\nconst webRoot = resolve(process.env.PI_PILOT_WEB_ROOT ?? join(distDir, \"..\", \"public\"));\nconst webIndexPath = join(webRoot, \"index.html\");\n\nconst mimeTypes: Record<string, string> = {\n\t\".css\": \"text/css; charset=utf-8\",\n\t\".html\": \"text/html; charset=utf-8\",\n\t\".ico\": \"image/x-icon\",\n\t\".js\": \"text/javascript; charset=utf-8\",\n\t\".json\": \"application/json; charset=utf-8\",\n\t\".map\": \"application/json; charset=utf-8\",\n\t\".png\": \"image/png\",\n\t\".svg\": \"image/svg+xml\",\n\t\".txt\": \"text/plain; charset=utf-8\",\n\t\".webp\": \"image/webp\",\n\t\".woff\": \"font/woff\",\n\t\".woff2\": \"font/woff2\",\n};\n\nfunction isApiOrWsPath(pathname: string): boolean {\n\treturn pathname === \"/api\" || pathname.startsWith(\"/api/\") || pathname === \"/ws\";\n}\n\nfunction safeResolveWebPath(pathname: string): string | undefined {\n\tlet decoded: string;\n\ttry {\n\t\tdecoded = decodeURIComponent(pathname);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tconst relativePath = decoded === \"/\" ? \"index.html\" : decoded.replace(/^\\/+/, \"\");\n\tconst candidate = resolve(webRoot, relativePath);\n\tif (candidate !== webRoot && !candidate.startsWith(`${webRoot}${sep}`)) {\n\t\treturn undefined;\n\t}\n\treturn candidate;\n}\n\nasync function readWebFile(path: string): Promise<Buffer | undefined> {\n\ttry {\n\t\treturn await readFile(path);\n\t} catch (err) {\n\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\tif (code === \"ENOENT\" || code === \"EISDIR\") return undefined;\n\t\tthrow err;\n\t}\n}\n\nasync function serveWeb(c: Context): Promise<Response> {\n\tconst pathname = new URL(c.req.url).pathname;\n\tif (isApiOrWsPath(pathname)) return c.notFound();\n\n\tconst assetPath = safeResolveWebPath(pathname);\n\tif (!assetPath) return c.text(\"invalid asset path\", 400);\n\n\tconst asset = await readWebFile(assetPath);\n\tconst body = asset ?? (await readFile(webIndexPath));\n\tconst filePath = asset ? assetPath : webIndexPath;\n\tconst headers: Record<string, string> = {\n\t\t\"Content-Type\": mimeTypes[extname(filePath)] ?? \"application/octet-stream\",\n\t\t\"Cache-Control\": asset && pathname.startsWith(\"/assets/\")\n\t\t\t? \"public, max-age=31536000, immutable\"\n\t\t\t: \"no-cache\",\n\t};\n\n\treturn new Response(body as unknown as BodyInit, { headers });\n}\n\napp.use(\n\t\"/api/*\",\n\tcors({\n\t\torigin: config.corsOrigin,\n\t\tallowMethods: [\"GET\", \"POST\", \"PUT\", \"DELETE\"],\n\t}),\n);\n\napp.get(\"/api/health\", (c) => c.json({ ok: true }));\napp.route(\"/api/workspaces\", workspacesRoute);\napp.route(\"/api/fs\", fsRoute);\napp.route(\"/api/model-configs\", modelConfigsRoute);\n\nif (existsSync(webIndexPath)) {\n\tapp.get(\"*\", serveWeb);\n} else {\n\tapp.get(\"/\", (c) =>\n\t\tc.text(\n\t\t\t\"pi-pilot server is running, but the web UI assets were not found. \" +\n\t\t\t\t\"Run `pnpm build` from the repository root, or set PI_PILOT_WEB_ROOT to a built web dist directory.\",\n\t\t\t500,\n\t\t),\n\t);\n}\n\n// Load builtin-extension on/off prefs into the in-memory cache before serving,\n// so the synchronous factory gate (extensions/index.ts) sees them on the first\n// lazily-built runtime. Top-level await blocks the listen until this resolves.\nawait loadBuiltinPrefs();\n\nconst server = serve(\n\t{\n\t\tfetch: app.fetch,\n\t\thostname: config.host,\n\t\tport: config.port,\n\t},\n\t(info) => {\n\t\tconsole.log(`[pi-pilot] http://${info.address}:${info.port}`);\n\t},\n);\n\nattachWsHub(server as unknown as HttpServer);\n\nasync function shutdown(reason: string): Promise<void> {\n\tconsole.log(`[pi-pilot] shutting down (${reason})`);\n\ttry {\n\t\tawait workspaceManager.disposeAll();\n\t} catch (e) {\n\t\tconsole.error(\"[pi-pilot] disposeAll error:\", e);\n\t}\n\tserver.close(() => process.exit(0));\n\t// Hard-kill if close hangs.\n\tsetTimeout(() => process.exit(1), 3000).unref();\n}\n\nprocess.on(\"SIGINT\", () => void shutdown(\"SIGINT\"));\nprocess.on(\"SIGTERM\", () => void shutdown(\"SIGTERM\"));\n","/**\n * Process-wide config, sourced from env vars.\n *\n * We do not read any file at startup beyond what pi's SDK does internally.\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const config = {\n\t/** HTTP + WS port. */\n\tport: Number(process.env.PI_PILOT_PORT ?? 5174),\n\n\t/** Bind address. Hard-coded localhost; do NOT make this configurable\n\t * without first reviewing the security implications of exposing bash. */\n\thost: \"127.0.0.1\",\n\n\t/** Where pi-pilot stores its own (non-pi) state. */\n\tdataDir: process.env.PI_PILOT_DATA_DIR ?? join(homedir(), \".pi\", \"webui\"),\n\n\t/** Dev origin allowed by CORS for /api. The Vite dev server. */\n\tcorsOrigin: process.env.PI_PILOT_CORS_ORIGIN ?? \"http://localhost:5173\",\n} as const;\n","/**\n * Route every outbound HTTP(S) request — including all pi LLM provider calls —\n * through the proxy named in the standard `HTTPS_PROXY` / `HTTP_PROXY` /\n * `NO_PROXY` env vars (lowercase variants honored too).\n *\n * pi's own CLI does this in `cli.ts` via `configureHttpDispatcher()`. pi-pilot\n * embeds the SDK in-process and never hits that entry, so without this the\n * provider SDKs (Anthropic / OpenAI / Gemini / Mistral — all go through the\n * global `fetch`/undici dispatcher) ignore proxy env vars entirely. We replicate\n * the dispatcher install here and call it once at startup, before the first\n * runtime is built.\n *\n * `undici` is pinned to the SDK's bundled version (8.3.0) so the dispatcher we\n * set is the same implementation the SDK's fetch reads from.\n */\n\nimport { EnvHttpProxyAgent, install, setGlobalDispatcher } from \"undici\";\n\nlet configured = false;\n\n/**\n * Install an `EnvHttpProxyAgent` as undici's global dispatcher. Idempotent.\n *\n * Safe to call unconditionally: when no proxy env var is set, the agent\n * degrades to a direct-connection `Agent`, so behavior is unchanged for users\n * who don't use a proxy. The agent reads the env at construction time, hence\n * this must run early (it does — before any LLM request).\n */\nexport function configureHttpProxy(): void {\n\tif (configured) return;\n\tconfigured = true;\n\t// allowH2:false + 300s idle timeout mirror pi's `core/http-dispatcher.ts`,\n\t// tuned for long-lived LLM streaming responses.\n\tsetGlobalDispatcher(\n\t\tnew EnvHttpProxyAgent({\n\t\t\tallowH2: false,\n\t\t\tbodyTimeout: 300_000,\n\t\t\theadersTimeout: 300_000,\n\t\t}),\n\t);\n\t// Keep `fetch` and the dispatcher on the same undici implementation (also\n\t// mirrors pi): avoids a mismatch where newer Node's bundled fetch consumes a\n\t// compressed response through the npm-undici dispatcher without decompressing.\n\tinstall?.();\n}\n","/**\n * /api/workspaces routes.\n */\n\nimport { stat } from \"node:fs/promises\";\nimport { basename, isAbsolute, resolve } from \"node:path\";\nimport { Hono } from \"hono\";\nimport type {\n\tAddWorkspaceRequest,\n\tAddWorkspaceResponse,\n\tHistoryResponse,\n\tListForkPointsResponse,\n\tListSessionsResponse,\n\tListWorkspacesResponse,\n\tOkResponse,\n} from \"@pi-pilot/shared\";\nimport { HttpError } from \"../storage/resource-writer.ts\";\nimport {\n\taddWorkspace,\n\tgetWorkspace,\n\tlistWorkspaces,\n\tremoveWorkspace,\n} from \"../storage/workspace-registry.ts\";\nimport { enrichWorkspace } from \"../storage/workspace-stats.ts\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport { mountConfigRoutes } from \"./config.ts\";\nimport { mountFilesRoute } from \"./files.ts\";\nimport { mountResourcesRoute } from \"./resources.ts\";\n\nexport const workspacesRoute = new Hono();\n\nworkspacesRoute.get(\"/\", async (c) => {\n\tconst raw = await listWorkspaces();\n\tconst workspaces = await Promise.all(raw.map(enrichWorkspace));\n\tconst body: ListWorkspacesResponse = { workspaces };\n\treturn c.json(body);\n});\n\nworkspacesRoute.get(\"/:id/sessions\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tconst sessions = await workspaceManager.listSessions(id);\n\t\tconst body: ListSessionsResponse = { sessions };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] list sessions for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.delete(\"/:id/sessions\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\tconst sessionPath = c.req.query(\"path\");\n\tif (!sessionPath) {\n\t\treturn c.json({ ok: false, error: \"path query is required\" }, 400);\n\t}\n\n\ttry {\n\t\tawait workspaceManager.deleteSession(id, sessionPath);\n\t\tconst body: OkResponse = { ok: true };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tif (err instanceof HttpError) {\n\t\t\treturn c.json(\n\t\t\t\t{ ok: false, error: err.message },\n\t\t\t\terr.status as 400 | 404 | 409 | 500,\n\t\t\t);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] delete session for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.get(\"/:id/fork-points\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tconst runtime = await workspaceManager.getOrCreate(id);\n\t\tconst raw = runtime.session.getUserMessagesForForking();\n\t\tconst points = raw.map((p) => ({\n\t\t\tentryId: p.entryId,\n\t\t\ttext: p.text.length > 120 ? p.text.slice(0, 120) + \"…\" : p.text,\n\t\t}));\n\t\tconst body: ListForkPointsResponse = { points };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] fork-points for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.get(\"/:id/history\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tawait workspaceManager.getOrCreate(id);\n\t\tconst sessionPath = c.req.query(\"sessionPath\");\n\t\tconst body: HistoryResponse = workspaceManager.getSessionHistory(id, sessionPath);\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] history for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.post(\"/\", async (c) => {\n\tconst body = (await c.req.json()) as AddWorkspaceRequest;\n\tif (!body?.path || typeof body.path !== \"string\") {\n\t\treturn c.json({ ok: false, error: \"path is required\" }, 400);\n\t}\n\tif (!isAbsolute(body.path)) {\n\t\treturn c.json({ ok: false, error: \"path must be absolute\" }, 400);\n\t}\n\tconst resolved = resolve(body.path);\n\ttry {\n\t\tconst st = await stat(resolved);\n\t\tif (!st.isDirectory()) {\n\t\t\treturn c.json({ ok: false, error: \"path is not a directory\" }, 400);\n\t\t}\n\t} catch {\n\t\treturn c.json({ ok: false, error: \"path does not exist\" }, 400);\n\t}\n\n\tconst stored = await addWorkspace({\n\t\tpath: resolved,\n\t\tname: body.name?.trim() || basename(resolved) || resolved,\n\t});\n\tconst ws = await enrichWorkspace(stored);\n\tconst res: AddWorkspaceResponse = { workspace: ws };\n\treturn c.json(res);\n});\n\nworkspacesRoute.delete(\"/:id\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t// Dispose the runtime if it was open. Best-effort.\n\tawait workspaceManager.dispose(id);\n\tawait removeWorkspace(id);\n\n\tconst body: OkResponse = { ok: true };\n\treturn c.json(body);\n});\n\nmountConfigRoutes(workspacesRoute);\nmountResourcesRoute(workspacesRoute);\nmountFilesRoute(workspacesRoute);\n","/**\n * Filesystem CRUD for skills and prompt templates.\n *\n * pi's ResourceLoader is read-only from our perspective (`reload()` re-reads\n * from disk but doesn't write). So pi-pilot writes the files itself and then\n * asks the loader to re-scan.\n *\n * Layout (matches pi's discovery rules — see docs/skills.md, docs/prompt-templates.md):\n *\n * skills:\n * user → <agentDir>/skills/<name>/SKILL.md\n * project → <workspaceCwd>/.pi/skills/<name>/SKILL.md\n *\n * prompts:\n * user → <agentDir>/prompts/<name>.md\n * project → <workspaceCwd>/.pi/prompts/<name>.md\n *\n * Safety\n * - `name` is restricted to lowercase a-z / 0-9 / hyphens (skills) and\n * additionally underscores (prompts) so it cannot escape its parent dir.\n * - `assertUnder(path, allowedRoots)` double-checks the resolved file path\n * is actually inside one of the allowed roots before any write/unlink.\n * - We never delete a directory that contains anything other than known\n * skill content via this module \\u2014 see `deleteSkill` for the precise rule.\n *\n * Frontmatter handling\n * - Known fields (name, description, disable-model-invocation /\n * argument-hint) are parsed into typed slots and exposed to the\n * editor as discrete form inputs.\n * - Unknown keys (`allowed-tools`, `metadata`, `license`, \\u2026) are\n * captured as an opaque list of raw `key: value` lines and round-\n * tripped on save. The editor never displays or rewrites them; the\n * write path concatenates them back below the known fields in the\n * order they appeared in the source. This means custom YAML survives\n * edits, but multi-line scalars / anchors / nested maps still won't\n * because the parser is line-oriented (acceptable: pi's documented\n * skill/prompt fields are all single-line scalars).\n */\n\nimport {\n\tmkdir,\n\treadFile,\n\trm,\n\tstat,\n\tunlink,\n\twriteFile,\n} from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, resolve, sep } from \"node:path\";\nimport type { ResourceScope } from \"@pi-pilot/shared\";\n\n// ---------- path helpers ----------\n\nconst SKILL_NAME_RE = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;\nconst PROMPT_NAME_RE = /^[a-z0-9_](?:[a-z0-9_-]{0,62}[a-z0-9_])?$/;\n\nfunction ensureSkillName(name: string): void {\n\tif (!SKILL_NAME_RE.test(name)) {\n\t\tthrow new HttpError(400, \"skill name must be lowercase a-z/0-9/hyphens, 1-64 chars, no leading/trailing/double hyphens\");\n\t}\n}\n\nfunction ensurePromptName(name: string): void {\n\tif (!PROMPT_NAME_RE.test(name)) {\n\t\tthrow new HttpError(400, \"prompt name must be lowercase a-z/0-9/hyphens/underscores, 1-64 chars\");\n\t}\n}\n\n/**\n * Throw if `target` does not resolve to a descendant of any path in `roots`.\n * Prevents `../../etc/passwd` style escapes even though the name regex above\n * already forbids slashes \\u2014 belt + suspenders.\n */\nfunction assertUnder(target: string, roots: string[]): void {\n\tconst t = resolve(target);\n\tconst ok = roots.some((root) => {\n\t\tconst r = resolve(root);\n\t\treturn t === r || t.startsWith(r + sep);\n\t});\n\tif (!ok) {\n\t\tthrow new HttpError(500, `refusing to touch path outside managed roots: ${target}`);\n\t}\n}\n\nfunction skillDirFor(scope: ResourceScope, name: string, roots: ResourceRoots): string {\n\tensureSkillName(name);\n\tconst base = scope === \"user\" ? roots.userSkills : roots.projectSkills;\n\treturn join(base, name);\n}\n\nfunction promptFileFor(scope: ResourceScope, name: string, roots: ResourceRoots): string {\n\tensurePromptName(name);\n\tconst base = scope === \"user\" ? roots.userPrompts : roots.projectPrompts;\n\treturn join(base, `${name}.md`);\n}\n\nexport interface ResourceRoots {\n\t/** ~/.pi/agent/skills */\n\tuserSkills: string;\n\t/** <cwd>/.pi/skills */\n\tprojectSkills: string;\n\t/** ~/.pi/agent/prompts */\n\tuserPrompts: string;\n\t/** <cwd>/.pi/prompts */\n\tprojectPrompts: string;\n}\n\nexport function resolveResourceRoots(opts: {\n\tagentDir: string;\n\tworkspaceCwd: string;\n}): ResourceRoots {\n\treturn {\n\t\tuserSkills: join(opts.agentDir, \"skills\"),\n\t\tprojectSkills: join(opts.workspaceCwd, \".pi\", \"skills\"),\n\t\tuserPrompts: join(opts.agentDir, \"prompts\"),\n\t\tprojectPrompts: join(opts.workspaceCwd, \".pi\", \"prompts\"),\n\t};\n}\n\n/** Map an absolute file path to its scope, by checking which root it sits in. */\nexport function scopeFor(absPath: string, roots: ResourceRoots): ResourceScope | undefined {\n\tconst p = resolve(absPath);\n\tfor (const [root, scope] of [\n\t\t[roots.userSkills, \"user\"],\n\t\t[roots.projectSkills, \"project\"],\n\t\t[roots.userPrompts, \"user\"],\n\t\t[roots.projectPrompts, \"project\"],\n\t] as const) {\n\t\tconst r = resolve(root);\n\t\tif (p === r || p.startsWith(r + sep)) return scope;\n\t}\n\treturn undefined;\n}\n\n// ---------- frontmatter ----------\n\n/** A frontmatter line we recognized. `raw` is the original `key: value`\n * text, used to round-trip unknown keys verbatim. */\ninterface FrontmatterLine {\n\tkey: string;\n\tvalue: string | boolean;\n\traw: string;\n}\n\ninterface ParsedFile {\n\tfrontmatter: Record<string, string | boolean>;\n\t/** Every frontmatter line in source order, with the original raw text. */\n\tlines: FrontmatterLine[];\n\tbody: string;\n}\n\n/**\n * Tiny YAML-frontmatter reader. Recognises `key: value`, `key: \"value\"`,\n * and `key: true|false`. Quoted strings honour `\\\"` and `\\\\` escapes;\n * everything else is treated as a raw scalar (trimmed). This is enough for\n * pi's documented skill/prompt frontmatter fields. Files with multi-line\n * scalars, anchors, etc. won't round-trip cleanly through pi-pilot.\n */\nfunction parseFile(content: string): ParsedFile {\n\tconst rawLines = content.split(/\\r?\\n/);\n\tif (rawLines[0] !== \"---\") {\n\t\treturn { frontmatter: {}, lines: [], body: content };\n\t}\n\tconst fm: Record<string, string | boolean> = {};\n\tconst lines: FrontmatterLine[] = [];\n\tlet i = 1;\n\twhile (i < rawLines.length && rawLines[i] !== \"---\") {\n\t\tconst line = rawLines[i] ?? \"\";\n\t\tconst m = line.match(/^([A-Za-z][A-Za-z0-9_-]*)\\s*:\\s*(.*?)\\s*$/);\n\t\tif (m) {\n\t\t\tconst key = m[1]!;\n\t\t\tconst rawVal = m[2] ?? \"\";\n\t\t\tconst value = parseScalar(rawVal);\n\t\t\tfm[key] = value;\n\t\t\tlines.push({ key, value, raw: line });\n\t\t}\n\t\ti++;\n\t}\n\tif (i >= rawLines.length) {\n\t\t// No closing `---` \\u2014 treat the whole thing as body, no frontmatter.\n\t\treturn { frontmatter: {}, lines: [], body: content };\n\t}\n\tconst body = rawLines.slice(i + 1).join(\"\\n\");\n\treturn { frontmatter: fm, lines, body };\n}\n\n/**\n * Filter `lines` (every frontmatter entry as it appeared on disk) down\n * to the entries whose keys are *not* in `knownKeys`. Used to preserve\n * arbitrary frontmatter (`allowed-tools`, `metadata`, `license`, \\u2026) on\n * write.\n *\n * Returns the raw source text of each kept line (without the trailing\n * newline). The build path emits these verbatim so values keep their\n * original quoting style.\n */\nfunction extraLines(lines: FrontmatterLine[], knownKeys: readonly string[]): string[] {\n\tconst known = new Set(knownKeys);\n\treturn lines.filter((l) => !known.has(l.key)).map((l) => l.raw);\n}\n\nfunction parseScalar(raw: string): string | boolean {\n\tif (raw === \"true\") return true;\n\tif (raw === \"false\") return false;\n\tif (raw.startsWith('\"') && raw.endsWith('\"') && raw.length >= 2) {\n\t\treturn raw.slice(1, -1).replace(/\\\\([\"\\\\])/g, \"$1\");\n\t}\n\tif (raw.startsWith(\"'\") && raw.endsWith(\"'\") && raw.length >= 2) {\n\t\treturn raw.slice(1, -1).replace(/''/g, \"'\");\n\t}\n\treturn raw;\n}\n\n/**\n * Emit a frontmatter+body file. Field order is stable so re-saves produce\n * deterministic diffs:\n *\n * 1. Known fields in the caller-supplied order (skipping undefined / \"\").\n * 2. Extra raw lines (unknown frontmatter keys captured on read) appended\n * verbatim. They keep their original ordering relative to each other.\n *\n * Boolean/string known values only; arrays and nested maps round-trip via\n * the raw `extras` channel only.\n */\nfunction buildFile(\n\tfields: Array<[string, string | boolean | undefined]>,\n\tbody: string,\n\textras: string[] = [],\n): string {\n\tconst out: string[] = [\"---\"];\n\tfor (const [key, value] of fields) {\n\t\tif (value === undefined || value === \"\") continue;\n\t\tif (typeof value === \"boolean\") {\n\t\t\tout.push(`${key}: ${value ? \"true\" : \"false\"}`);\n\t\t} else {\n\t\t\tout.push(`${key}: ${formatString(value)}`);\n\t\t}\n\t}\n\tfor (const raw of extras) out.push(raw);\n\tout.push(\"---\");\n\tconst trimmedBody = body.replace(/\\s+$/, \"\");\n\tconst text = `${out.join(\"\\n\")}\\n${trimmedBody}\\n`;\n\treturn text;\n}\n\n/** Frontmatter keys we render as form inputs. Everything else is opaque\n * passthrough. Keep these arrays here so read and write agree. */\nconst SKILL_KNOWN_KEYS = [\"name\", \"description\", \"disable-model-invocation\"] as const;\nconst PROMPT_KNOWN_KEYS = [\"description\", \"argument-hint\"] as const;\n\nfunction formatString(value: string): string {\n\t// Always double-quote so embedded `:`, `#`, leading/trailing whitespace,\n\t// and the like don't break YAML parsing. Escape `\\` and `\"`.\n\tconst escaped = value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n\treturn `\"${escaped}\"`;\n}\n\n// ---------- skills ----------\n\nexport interface CreateSkillOpts {\n\troots: ResourceRoots;\n\tscope: ResourceScope;\n\tname: string;\n\tdescription: string;\n\tbody: string;\n\tdisableModelInvocation?: boolean;\n}\n\nexport async function createSkill(opts: CreateSkillOpts): Promise<string> {\n\tconst dir = skillDirFor(opts.scope, opts.name, opts.roots);\n\tassertUnder(dir, [opts.roots.userSkills, opts.roots.projectSkills]);\n\tif (await exists(dir)) {\n\t\tthrow new HttpError(409, `skill already exists at ${dir}`);\n\t}\n\tconst file = join(dir, \"SKILL.md\");\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"name\", opts.name],\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"disable-model-invocation\", opts.disableModelInvocation],\n\t\t],\n\t\topts.body,\n\t);\n\tawait mkdir(dir, { recursive: true });\n\tawait writeFile(file, text, \"utf8\");\n\treturn file;\n}\n\nexport interface UpdateSkillOpts {\n\troots: ResourceRoots;\n\tfilePath: string;\n\tname: string;\n\tdescription: string;\n\tbody: string;\n\tdisableModelInvocation?: boolean;\n}\n\nexport async function updateSkill(opts: UpdateSkillOpts): Promise<string> {\n\tif (!isAbsolute(opts.filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(opts.filePath, [opts.roots.userSkills, opts.roots.projectSkills]);\n\tif (!(await exists(opts.filePath))) {\n\t\tthrow new HttpError(404, `skill file not found: ${opts.filePath}`);\n\t}\n\t// Round-trip arbitrary frontmatter (`allowed-tools`, `metadata`, \\u2026)\n\t// from the existing file. Re-read each save so a side edit on disk\n\t// between open and save isn't clobbered.\n\tconst existing = await readFile(opts.filePath, \"utf8\");\n\tconst extras = extraLines(parseFile(existing).lines, SKILL_KNOWN_KEYS);\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"name\", opts.name],\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"disable-model-invocation\", opts.disableModelInvocation],\n\t\t],\n\t\topts.body,\n\t\textras,\n\t);\n\tawait writeFile(opts.filePath, text, \"utf8\");\n\treturn opts.filePath;\n}\n\nexport async function deleteSkill(filePath: string, roots: ResourceRoots): Promise<void> {\n\tif (!isAbsolute(filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(filePath, [roots.userSkills, roots.projectSkills]);\n\tif (!(await exists(filePath))) {\n\t\tthrow new HttpError(404, `skill file not found: ${filePath}`);\n\t}\n\t// A skill can be either a bare .md file (root-level under skills/) or a\n\t// SKILL.md inside its own directory. Match pi's discovery: if the parent\n\t// directory is itself the skills root, delete just the .md file;\n\t// otherwise delete the parent directory (skill + scripts + assets).\n\tconst dir = dirname(filePath);\n\tconst parentIsRoot =\n\t\tresolve(dir) === resolve(roots.userSkills) ||\n\t\tresolve(dir) === resolve(roots.projectSkills);\n\tif (parentIsRoot) {\n\t\tawait unlink(filePath);\n\t\treturn;\n\t}\n\tassertUnder(dir, [roots.userSkills, roots.projectSkills]);\n\tawait rm(dir, { recursive: true, force: true });\n}\n\n// ---------- prompts ----------\n\nexport interface CreatePromptOpts {\n\troots: ResourceRoots;\n\tscope: ResourceScope;\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tbody: string;\n}\n\nexport async function createPrompt(opts: CreatePromptOpts): Promise<string> {\n\tconst file = promptFileFor(opts.scope, opts.name, opts.roots);\n\tassertUnder(file, [opts.roots.userPrompts, opts.roots.projectPrompts]);\n\tif (await exists(file)) {\n\t\tthrow new HttpError(409, `prompt already exists at ${file}`);\n\t}\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"argument-hint\", opts.argumentHint],\n\t\t],\n\t\topts.body,\n\t);\n\tawait mkdir(dirname(file), { recursive: true });\n\tawait writeFile(file, text, \"utf8\");\n\treturn file;\n}\n\nexport interface UpdatePromptOpts {\n\troots: ResourceRoots;\n\tfilePath: string;\n\t/** Stem of the new filename. If different from the existing file's stem,\n\t * the file is renamed. */\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tbody: string;\n}\n\nexport async function updatePrompt(opts: UpdatePromptOpts): Promise<string> {\n\tif (!isAbsolute(opts.filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(opts.filePath, [opts.roots.userPrompts, opts.roots.projectPrompts]);\n\tif (!(await exists(opts.filePath))) {\n\t\tthrow new HttpError(404, `prompt file not found: ${opts.filePath}`);\n\t}\n\tensurePromptName(opts.name);\n\tconst dir = dirname(opts.filePath);\n\tconst newPath = join(dir, `${opts.name}.md`);\n\tassertUnder(newPath, [opts.roots.userPrompts, opts.roots.projectPrompts]);\n\t// Preserve arbitrary frontmatter from the existing file (re-read so a\n\t// concurrent on-disk edit doesn't get silently dropped).\n\tconst existing = await readFile(opts.filePath, \"utf8\");\n\tconst extras = extraLines(parseFile(existing).lines, PROMPT_KNOWN_KEYS);\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"argument-hint\", opts.argumentHint],\n\t\t],\n\t\topts.body,\n\t\textras,\n\t);\n\tif (resolve(newPath) !== resolve(opts.filePath)) {\n\t\tif (await exists(newPath)) {\n\t\t\tthrow new HttpError(409, `prompt already exists at ${newPath}`);\n\t\t}\n\t\t// Rename is NOT a single syscall here — we have to write the new\n\t\t// content under a different name and then drop the old file. If the\n\t\t// unlink fails (perms, file lock, cross-fs handle, etc.) we must roll\n\t\t// back the new file; otherwise `loader.reload()` would surface an\n\t\t// orphaned `/oldname` prompt the user never asked to keep.\n\t\tawait writeFile(newPath, text, \"utf8\");\n\t\ttry {\n\t\t\tawait unlink(opts.filePath);\n\t\t} catch (err) {\n\t\t\tawait unlink(newPath).catch(() => undefined);\n\t\t\tthrow err;\n\t\t}\n\t\treturn newPath;\n\t}\n\tawait writeFile(opts.filePath, text, \"utf8\");\n\treturn opts.filePath;\n}\n\nexport async function deletePrompt(filePath: string, roots: ResourceRoots): Promise<void> {\n\tif (!isAbsolute(filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(filePath, [roots.userPrompts, roots.projectPrompts]);\n\tif (!(await exists(filePath))) {\n\t\tthrow new HttpError(404, `prompt file not found: ${filePath}`);\n\t}\n\tawait unlink(filePath);\n}\n\n// ---------- reads (for editor pre-fill) ----------\n\nexport interface ReadResourceFileResult {\n\tbody: string;\n\tname: string;\n\tdescription: string;\n\tdisableModelInvocation?: boolean;\n\targumentHint?: string;\n}\n\nexport async function readSkillFile(filePath: string, roots: ResourceRoots): Promise<ReadResourceFileResult> {\n\tassertUnder(filePath, [roots.userSkills, roots.projectSkills]);\n\tconst text = await readFile(filePath, \"utf8\");\n\tconst { frontmatter, body } = parseFile(text);\n\treturn {\n\t\tbody,\n\t\tname: stringOr(frontmatter.name, \"\"),\n\t\tdescription: stringOr(frontmatter.description, \"\"),\n\t\tdisableModelInvocation: booleanOr(frontmatter[\"disable-model-invocation\"], undefined),\n\t};\n}\n\nexport async function readPromptFile(filePath: string, roots: ResourceRoots): Promise<ReadResourceFileResult> {\n\tassertUnder(filePath, [roots.userPrompts, roots.projectPrompts]);\n\tconst text = await readFile(filePath, \"utf8\");\n\tconst { frontmatter, body } = parseFile(text);\n\t// Prompt's \"name\" is the filename stem, not a frontmatter field.\n\tconst stem = basename(filePath).replace(/\\.md$/, \"\");\n\treturn {\n\t\tbody,\n\t\tname: stem,\n\t\tdescription: stringOr(frontmatter.description, \"\"),\n\t\targumentHint: stringOr(frontmatter[\"argument-hint\"], undefined),\n\t};\n}\n\nfunction basename(p: string): string {\n\tconst parts = p.split(sep);\n\treturn parts.at(-1) || p;\n}\n\nfunction stringOr<T>(value: unknown, fallback: T): string | T {\n\treturn typeof value === \"string\" ? value : fallback;\n}\n\nfunction booleanOr<T>(value: unknown, fallback: T): boolean | T {\n\treturn typeof value === \"boolean\" ? value : fallback;\n}\n\n// ---------- misc ----------\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Tagged error so the route layer can map to specific HTTP statuses without\n * losing the message. Anything else thrown becomes a 500.\n */\nexport class HttpError extends Error {\n\tconstructor(public readonly status: number, message: string) {\n\t\tsuper(message);\n\t}\n}\n","/**\n * Persistence for the list of workspaces the user has registered.\n *\n * pi itself doesn't need this — it's tied to a single cwd at a time. We need\n * it because the WebUI shows a multi-workspace picker.\n *\n * File format (~/.pi/webui/workspaces.json):\n * { \"workspaces\": [{ \"id\", \"name\", \"path\", \"addedAt\" }] }\n *\n * Storage holds only the *persistent* fields. Derived telemetry\n * (gitBranch, fileCount) is added by the API layer via `enrichWorkspace`\n * — see `workspace-stats.ts`. Keeping them out of storage avoids stale\n * branch names on disk when the user switches branches outside the app.\n */\n\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { config } from \"../config.ts\";\n\n/** Persistent shape on disk. API responses augment this with derived fields. */\nexport interface StoredWorkspace {\n\tid: string;\n\tname: string;\n\tpath: string;\n\t/** ISO-8601 */\n\taddedAt: string;\n}\n\nconst REGISTRY_PATH = join(config.dataDir, \"workspaces.json\");\n\ninterface RegistryFile {\n\tworkspaces: StoredWorkspace[];\n}\n\nlet cache: RegistryFile | undefined;\n\nasync function load(): Promise<RegistryFile> {\n\tif (cache) return cache;\n\ttry {\n\t\tconst raw = await readFile(REGISTRY_PATH, \"utf8\");\n\t\tcache = JSON.parse(raw) as RegistryFile;\n\t\tif (!Array.isArray(cache.workspaces)) cache = { workspaces: [] };\n\t} catch (err) {\n\t\tif ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\tcache = { workspaces: [] };\n\t\t} else {\n\t\t\tthrow err;\n\t\t}\n\t}\n\treturn cache;\n}\n\nasync function save(): Promise<void> {\n\tif (!cache) return;\n\tawait mkdir(dirname(REGISTRY_PATH), { recursive: true });\n\tawait writeFile(REGISTRY_PATH, JSON.stringify(cache, null, 2), \"utf8\");\n}\n\nexport async function listWorkspaces(): Promise<StoredWorkspace[]> {\n\tconst r = await load();\n\treturn [...r.workspaces];\n}\n\nexport async function getWorkspace(id: string): Promise<StoredWorkspace | undefined> {\n\tconst r = await load();\n\treturn r.workspaces.find((w) => w.id === id);\n}\n\nexport async function getWorkspaceByPath(path: string): Promise<StoredWorkspace | undefined> {\n\tconst r = await load();\n\treturn r.workspaces.find((w) => w.path === path);\n}\n\nexport async function addWorkspace(input: { path: string; name: string }): Promise<StoredWorkspace> {\n\tconst r = await load();\n\tconst existing = r.workspaces.find((w) => w.path === input.path);\n\tif (existing) return existing;\n\tconst ws: StoredWorkspace = {\n\t\tid: randomUUID(),\n\t\tname: input.name,\n\t\tpath: input.path,\n\t\taddedAt: new Date().toISOString(),\n\t};\n\tr.workspaces.push(ws);\n\tawait save();\n\treturn ws;\n}\n\nexport async function removeWorkspace(id: string): Promise<boolean> {\n\tconst r = await load();\n\tconst before = r.workspaces.length;\n\tr.workspaces = r.workspaces.filter((w) => w.id !== id);\n\tif (r.workspaces.length === before) return false;\n\tawait save();\n\treturn true;\n}\n","/**\n * Derive instrument-panel telemetry from a workspace directory.\n *\n * Two probes today:\n * - current git branch (`git rev-parse --abbrev-ref HEAD`)\n * - tracked-file count (`git ls-files --cached --others --exclude-standard`)\n *\n * Both probes fail silently — non-git directories, missing `git` binary, or\n * broken repos all collapse to `null`. The UI fades the slot when both are\n * null, so a failure looks identical to \"not implemented yet\" from the\n * operator's perspective.\n *\n * In-memory 30-second TTL cache keyed by absolute path so the list endpoint\n * doesn't fork two child processes per workspace on every refresh. The TTL is\n * a deliberate trade: branch changes made outside the app will surface\n * within 30 seconds, which is the same staleness operators tolerate from\n * sidebar listings elsewhere.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport type { Workspace } from \"@pi-pilot/shared\";\nimport type { StoredWorkspace } from \"./workspace-registry.ts\";\n\nconst exec = promisify(execFile);\n\ninterface StatsCacheEntry {\n\tgitBranch: string | null;\n\tfileCount: number | null;\n\texpiresAt: number;\n}\n\nconst CACHE_TTL_MS = 30_000;\nconst cache = new Map<string, StatsCacheEntry>();\n\n/**\n * In-flight probes per path. Prevents the herd: if `enrichWorkspace` is called\n * twice for the same path before the first probe completes, both await the\n * same promise rather than each spawning two child processes.\n */\nconst inflight = new Map<string, Promise<StatsCacheEntry>>();\n\n/** Wrap a `StoredWorkspace` with derived stats. Never throws — failed probes\n * surface as nulls. */\nexport async function enrichWorkspace(ws: StoredWorkspace): Promise<Workspace> {\n\tconst stats = await getStats(ws.path);\n\treturn {\n\t\tid: ws.id,\n\t\tname: ws.name,\n\t\tpath: ws.path,\n\t\taddedAt: ws.addedAt,\n\t\tgitBranch: stats.gitBranch,\n\t\tfileCount: stats.fileCount,\n\t};\n}\n\nasync function getStats(path: string): Promise<StatsCacheEntry> {\n\tconst now = Date.now();\n\tconst cached = cache.get(path);\n\tif (cached && cached.expiresAt > now) return cached;\n\n\tconst pending = inflight.get(path);\n\tif (pending) return pending;\n\n\tconst probe = probeStats(path)\n\t\t.then((stats) => {\n\t\t\tconst entry: StatsCacheEntry = {\n\t\t\t\t...stats,\n\t\t\t\texpiresAt: Date.now() + CACHE_TTL_MS,\n\t\t\t};\n\t\t\tcache.set(path, entry);\n\t\t\treturn entry;\n\t\t})\n\t\t.finally(() => {\n\t\t\tinflight.delete(path);\n\t\t});\n\n\tinflight.set(path, probe);\n\treturn probe;\n}\n\nasync function probeStats(\n\tpath: string,\n): Promise<{ gitBranch: string | null; fileCount: number | null }> {\n\t// Run both probes in parallel; either may fail independently.\n\tconst [branchResult, filesResult] = await Promise.allSettled([\n\t\trunGit(path, [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]),\n\t\trunGit(path, [\n\t\t\t\"ls-files\",\n\t\t\t\"--cached\",\n\t\t\t\"--others\",\n\t\t\t\"--exclude-standard\",\n\t\t]),\n\t]);\n\n\tlet gitBranch: string | null = null;\n\tif (branchResult.status === \"fulfilled\") {\n\t\tconst out = branchResult.value.trim();\n\t\t// `HEAD` means detached HEAD; show the shortened commit instead, but\n\t\t// keep it terse so it still fits the BRANCH slot.\n\t\tif (out && out !== \"HEAD\") {\n\t\t\tgitBranch = out;\n\t\t} else if (out === \"HEAD\") {\n\t\t\t// Try to grab the short SHA for the detached case.\n\t\t\ttry {\n\t\t\t\tconst sha = (await runGit(path, [\"rev-parse\", \"--short\", \"HEAD\"])).trim();\n\t\t\t\tgitBranch = sha ? `@${sha}` : null;\n\t\t\t} catch {\n\t\t\t\tgitBranch = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tlet fileCount: number | null = null;\n\tif (filesResult.status === \"fulfilled\") {\n\t\tconst out = filesResult.value;\n\t\tif (out) {\n\t\t\t// `git ls-files` prints one path per line. Count non-empty lines so\n\t\t\t// a single trailing newline doesn't add a phantom file.\n\t\t\tlet n = 0;\n\t\t\tfor (let i = 0; i < out.length; i++) {\n\t\t\t\tif (out.charCodeAt(i) === 0x0a) n++;\n\t\t\t}\n\t\t\t// Add 1 when the last line doesn't end in a newline (rare).\n\t\t\tif (out.length > 0 && out.charCodeAt(out.length - 1) !== 0x0a) n++;\n\t\t\tfileCount = n;\n\t\t} else {\n\t\t\tfileCount = 0;\n\t\t}\n\t}\n\n\treturn { gitBranch, fileCount };\n}\n\n/**\n * Run `git` with the given args in `cwd`. Resolves with stdout on exit code 0.\n * 2-second timeout — slow probes are treated as failures rather than blocking\n * the list endpoint. Output capped at 5MB so a pathologically huge repo can't\n * OOM the server.\n */\nasync function runGit(cwd: string, args: string[]): Promise<string> {\n\tconst { stdout } = await exec(\"git\", args, {\n\t\tcwd,\n\t\ttimeout: 2000,\n\t\tmaxBuffer: 5 * 1024 * 1024,\n\t\t// Don't inherit GIT_DIR / GIT_WORK_TREE from the server process —\n\t\t// could leak the server's own repo into a workspace probe.\n\t\tenv: { ...process.env, GIT_DIR: undefined, GIT_WORK_TREE: undefined },\n\t});\n\treturn stdout;\n}\n","/**\n * WorkspaceManager — lazy lifecycle of one AgentSessionRuntime per workspace.\n *\n * Invariants:\n * - At most one runtime per workspaceId at any time.\n * - The runtime's cwd matches the workspace's path.\n * - Callers must call `dispose(id)` (or `disposeAll()` on shutdown) — we\n * don't currently idle-evict.\n *\n * NOT thread-safe across concurrent `getOrCreate` calls for the same id;\n * Node is single-threaded so this is fine, but be aware if you ever add\n * worker_threads.\n */\n\nimport { unlink } from \"node:fs/promises\";\nimport { isAbsolute, resolve } from \"node:path\";\nimport type { WebSocket } from \"ws\";\nimport {\n\tAgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionRuntime,\n\tcreateAgentSessionServices,\n\tgetAgentDir,\n\tSessionManager,\n\ttype ExtensionError,\n\ttype SessionInfo,\n} from \"@earendil-works/pi-coding-agent\";\nimport { HttpError } from \"./storage/resource-writer.ts\";\nimport { getWorkspace } from \"./storage/workspace-registry.ts\";\nimport { builtinExtensionFactories } from \"./extensions/index.ts\";\nimport { reconcileAfterRestart } from \"./extensions/ask_user/cleanup.ts\";\nimport {\n\tcancelPendingExcept,\n\tcancelPendingForSession,\n} from \"./extensions/ask_user/registry.ts\";\nimport { shouldForwardDetails } from \"./ws/bridge.ts\";\nimport type {\n\tSessionSummary,\n\tHistoryItem,\n\tHistoryResponse,\n\tWsExtensionError,\n\tWsServerMessage,\n} from \"@pi-pilot/shared\";\nimport { ExtensionUIBridge } from \"./ws/extension-ui.ts\";\n\n/**\n * Two-tier extension stance:\n *\n * 1. Third-party / disk pi extensions are OFF by default. The pi\n * extension system is TUI-native (renderers, ANSI themes, modal\n * dialogs, keyboard overlays) and cannot be bridged to the Web.\n * `EXTENSIONS_ENABLED` (env: `PI_PILOT_ENABLE_EXTENSIONS=1`) is the\n * escape hatch — even then no `ctx.ui.*` is bridged, so extensions\n * that only register tools / slash commands work and anything\n * UI-driven appears broken. Exported so `/api/resources` can show\n * this state in the panel.\n *\n * 2. pi-pilot's own first-party builtin extensions ARE loaded\n * unconditionally via `extensionFactories` (see `extensions/`).\n * That channel is independent of disk scanning, so they ship with\n * pi-pilot regardless of the env flag. These are not \"plugins\" in\n * the third-party sense — they're features of pi-pilot that happen\n * to use the extension API to get richer registration hooks\n * (`pi.registerTool`, `pi.registerCommand`, lifecycle events).\n */\nexport const EXTENSIONS_ENABLED = process.env.PI_PILOT_ENABLE_EXTENSIONS === \"1\";\n\n/**\n * Build a fresh runtime bound to `cwd`. Used both for initial creation and\n * for in-place session replacement (newSession / switchSession / fork).\n */\nconst createRuntime: CreateAgentSessionRuntimeFactory = async ({\n\tcwd,\n\tsessionManager,\n\tsessionStartEvent,\n}) => {\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tresourceLoaderOptions: {\n\t\t\tnoExtensions: !EXTENSIONS_ENABLED,\n\t\t\textensionFactories: builtinExtensionFactories,\n\t\t},\n\t});\n\tconst sessionResult = await createAgentSessionFromServices({\n\t\tservices,\n\t\tsessionManager,\n\t\tsessionStartEvent,\n\t});\n\treturn {\n\t\t...sessionResult,\n\t\tservices,\n\t\tdiagnostics: services.diagnostics,\n\t};\n};\n\ninterface WorkspaceState {\n\truntime: AgentSessionRuntime;\n\tbridge: ExtensionUIBridge;\n}\n\nclass WorkspaceManager {\n\tprivate readonly states = new Map<string, WorkspaceState>();\n\t/**\n\t * Subscribers live independently of `states` so the hub can register a\n\t * WebSocket *before* `getOrCreate` triggers a runtime build (which may\n\t * fire `session_start` synchronously, and any UI request from a\n\t * session_start handler would otherwise broadcast to an empty set).\n\t */\n\tprivate readonly subscribers = new Map<string, Set<WebSocket>>();\n\t/** Per-workspace lock to serialize concurrent creations. */\n\tprivate readonly pending = new Map<string, Promise<WorkspaceState>>();\n\tprivate readonly rebindListeners = new Map<string, Set<() => void>>();\n\n\tprivate getOrCreateSubscriberSet(workspaceId: string): Set<WebSocket> {\n\t\tlet set = this.subscribers.get(workspaceId);\n\t\tif (!set) {\n\t\t\tset = new Set();\n\t\t\tthis.subscribers.set(workspaceId, set);\n\t\t}\n\t\treturn set;\n\t}\n\n\tasync getOrCreate(workspaceId: string): Promise<AgentSessionRuntime> {\n\t\tconst existing = this.states.get(workspaceId);\n\t\tif (existing) return existing.runtime;\n\n\t\tconst inflight = this.pending.get(workspaceId);\n\t\tif (inflight) return (await inflight).runtime;\n\n\t\tconst p = this.build(workspaceId);\n\t\tthis.pending.set(workspaceId, p);\n\t\ttry {\n\t\t\tconst state = await p;\n\t\t\tthis.states.set(workspaceId, state);\n\t\t\treturn state.runtime;\n\t\t} finally {\n\t\t\tthis.pending.delete(workspaceId);\n\t\t}\n\t}\n\n\tprivate async build(workspaceId: string): Promise<WorkspaceState> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new Error(`Workspace not found: ${workspaceId}`);\n\n\t\t// SessionManager.continueRecent → opens the most recently used session\n\t\t// in this cwd, or starts a new one if none exists. Matches what `pi`\n\t\t// itself does on launch.\n\t\tconst sessionManager = SessionManager.continueRecent(ws.path);\n\n\t\tconst runtime = await createAgentSessionRuntime(createRuntime, {\n\t\t\tcwd: ws.path,\n\t\t\tagentDir: getAgentDir(),\n\t\t\tsessionManager,\n\t\t});\n\n\t\t// Subscriber set is owned by the manager so it can pre-exist any\n\t\t// runtime build (extensions may fire `onError` from `session_start`\n\t\t// before any client has subscribed). The bridge itself is stateless\n\t\t// — see `ws/extension-ui.ts` for why every method is a no-op.\n\t\tconst subscribers = this.getOrCreateSubscriberSet(workspaceId);\n\t\tconst bridge = new ExtensionUIBridge();\n\n\t\t// `onError` fires whenever an extension event handler throws (or pi\n\t\t// rejects a registration). We forward to every subscriber so the user\n\t\t// sees the failure as a toast instead of having it disappear into\n\t\t// server logs. Re-bound on every session replacement below.\n\t\tconst onError = (err: ExtensionError) => {\n\t\t\tconst msg: WsExtensionError = {\n\t\t\t\ttype: \"extension_error\",\n\t\t\t\tworkspaceId,\n\t\t\t\textensionPath: err.extensionPath,\n\t\t\t\tevent: err.event,\n\t\t\t\tmessage: err.error,\n\t\t\t};\n\t\t\tbroadcastTo(subscribers, msg);\n\t\t\t// Mirror to the server log so the stack survives even if every\n\t\t\t// subscriber is gone (no tab open, reconnect window, …).\n\t\t\tconsole.error(\n\t\t\t\t`[ext-error] ${workspaceId} ${err.extensionPath}@${err.event}: ${err.error}` +\n\t\t\t\t\t(err.stack ? `\\n${err.stack}` : \"\"),\n\t\t\t);\n\t\t};\n\n\t\t// Bind initially, and re-bind on every session replacement so the\n\t\t// new `AgentSession` instance also sees our UI context.\n\t\tawait runtime.session.bindExtensions({ uiContext: bridge, onError });\n\t\t// Restart cleanup: write a customMessage explaining any dangling\n\t\t// `ask_user` toolCalls left over from a previous server crash.\n\t\t// See extensions/ask_user/cleanup.ts for the why.\n\t\tsafeReconcileAskUser(workspaceId, runtime.session.sessionManager);\n\t\truntime.setRebindSession(async () => {\n\t\t\tawait runtime.session.bindExtensions({ uiContext: bridge, onError });\n\t\t\t// pi does not abort in-flight tool signals on session switch\n\t\t\t// (Spike Q3), so any `ask_user` Promise bound to the old\n\t\t\t// session would hang forever. Release them here — anything\n\t\t\t// whose sessionFile doesn't match the freshly-bound session\n\t\t\t// gets rejected. The new session may itself have post-restart\n\t\t\t// dangling calls; reconcile that too before notifying.\n\t\t\tcancelPendingExcept(runtime.session.sessionFile ?? null);\n\t\t\tsafeReconcileAskUser(workspaceId, runtime.session.sessionManager);\n\t\t\tthis.notifySessionReplaced(workspaceId);\n\t\t});\n\n\t\treturn { runtime, bridge };\n\t}\n\n\tget(workspaceId: string): AgentSessionRuntime | undefined {\n\t\treturn this.states.get(workspaceId)?.runtime;\n\t}\n\n\t/**\n\t * Register a WS connection as a subscriber for `workspaceId`. Safe to\n\t * call before `getOrCreate`; the set is lazily created so the bridge,\n\t * when later built, sees the same Set instance and any pre-existing\n\t * subscribers.\n\t */\n\taddSubscriber(workspaceId: string, ws: WebSocket): void {\n\t\tthis.getOrCreateSubscriberSet(workspaceId).add(ws);\n\t}\n\n\tremoveSubscriber(workspaceId: string, ws: WebSocket): void {\n\t\tconst set = this.subscribers.get(workspaceId);\n\t\tif (!set) return;\n\t\tset.delete(ws);\n\t\tif (set.size === 0) this.subscribers.delete(workspaceId);\n\t}\n\n\t/**\n\t * Fan a server-initiated message out to every WS subscribed to the\n\t * workspace. Used by API handlers that mutate runtime state and need\n\t * to refresh derived snapshots (e.g. `context_usage` after `setModel`,\n\t * which pi's event stream doesn't surface unless thinking-level also\n\t * clamps).\n\t */\n\tbroadcast(workspaceId: string, msg: WsServerMessage): void {\n\t\tconst set = this.subscribers.get(workspaceId);\n\t\tif (!set || set.size === 0) return;\n\t\tbroadcastTo(set, msg);\n\t}\n\n\tonSessionReplaced(workspaceId: string, listener: () => void): () => void {\n\t\tlet listeners = this.rebindListeners.get(workspaceId);\n\t\tif (!listeners) {\n\t\t\tlisteners = new Set();\n\t\t\tthis.rebindListeners.set(workspaceId, listeners);\n\t\t}\n\t\tlisteners.add(listener);\n\t\treturn () => {\n\t\t\tconst current = this.rebindListeners.get(workspaceId);\n\t\t\tif (!current) return;\n\t\t\tcurrent.delete(listener);\n\t\t\tif (current.size === 0) {\n\t\t\t\tthis.rebindListeners.delete(workspaceId);\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate notifySessionReplaced(workspaceId: string): void {\n\t\tconst listeners = this.rebindListeners.get(workspaceId);\n\t\tif (!listeners) return;\n\t\tfor (const listener of [...listeners]) {\n\t\t\ttry {\n\t\t\t\tlistener();\n\t\t\t} catch (e) {\n\t\t\t\tconsole.error(`[wm] rebind listener for ${workspaceId} failed:`, e);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync listSessions(workspaceId: string): Promise<SessionSummary[]> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new Error(`Workspace not found: ${workspaceId}`);\n\n\t\tconst sessions = await SessionManager.list(ws.path);\n\t\treturn sessions\n\t\t\t.slice()\n\t\t\t.sort((a, b) => b.modified.getTime() - a.modified.getTime())\n\t\t\t.map(toSessionSummary);\n\t}\n\n\tgetSessionHistory(workspaceId: string, sessionPath?: string): HistoryResponse {\n\t\tconst runtime = this.states.get(workspaceId)?.runtime;\n\t\tif (!runtime) return { items: [], isStreaming: false };\n\n\t\t// If the caller specifies a sessionPath, verify it matches the active\n\t\t// runtime. This prevents returning history from the wrong session when\n\t\t// the server restarted and picked a different \"recent\" session.\n\t\tif (sessionPath) {\n\t\t\tconst activeFile = runtime.session.sessionFile\n\t\t\t\t? resolve(runtime.session.sessionFile)\n\t\t\t\t: undefined;\n\t\t\tif (activeFile !== resolve(sessionPath)) {\n\t\t\t\treturn { items: [], isStreaming: false };\n\t\t\t}\n\t\t}\n\n\t\tconst isStreaming: boolean = runtime.session.isStreaming ?? false;\n\n\t\tconst branch = runtime.session.sessionManager.getBranch();\n\t\tconst items: HistoryItem[] = [];\n\t\t// toolCall args live in assistant messages; collect them so we can\n\t\t// attach them to the corresponding toolResult items.\n\t\tconst argsByCallId = new Map<string, string>();\n\n\t\tfor (const entry of branch) {\n\t\t\tif (entry.type !== \"message\") continue;\n\t\t\tconst msg = entry.message;\n\t\t\tconst role = (msg as { role: string }).role;\n\n\t\t\tif (role === \"user\") {\n\t\t\t\tconst text = extractUserText(msg as { content: unknown });\n\t\t\t\tif (text) items.push({ kind: \"user\", text });\n\t\t\t} else if (role === \"assistant\") {\n\t\t\t\tconst { text, thinking, toolCalls } = extractAssistantContent(\n\t\t\t\t\tmsg as { content: unknown[] },\n\t\t\t\t);\n\t\t\t\tfor (const tc of toolCalls) {\n\t\t\t\t\targsByCallId.set(tc.id, tc.args);\n\t\t\t\t}\n\t\t\t\tif (text || thinking) items.push({ kind: \"assistant\", text, thinking });\n\t\t\t} else if (role === \"toolResult\") {\n\t\t\t\tconst tr = msg as {\n\t\t\t\t\ttoolCallId: string;\n\t\t\t\t\ttoolName: string;\n\t\t\t\t\tcontent: unknown[];\n\t\t\t\t\tisError: boolean;\n\t\t\t\t\tdetails?: unknown;\n\t\t\t\t};\n\t\t\t\titems.push({\n\t\t\t\t\tkind: \"tool\",\n\t\t\t\t\ttoolCallId: tr.toolCallId,\n\t\t\t\t\ttoolName: tr.toolName,\n\t\t\t\t\targs: argsByCallId.get(tr.toolCallId) ?? \"\",\n\t\t\t\t\ttext: extractContentText(tr.content),\n\t\t\t\t\tisError: tr.isError,\n\t\t\t\t\t// Mirror live wire whitelist (bridge.ts): only ship details\n\t\t\t\t\t// for tools whose cards need the structured shape, so the\n\t\t\t\t\t// history payload stays small for bash / edit / read.\n\t\t\t\t\t...(shouldForwardDetails(tr.toolName) && tr.details !== undefined\n\t\t\t\t\t\t? { details: tr.details }\n\t\t\t\t\t\t: {}),\n\t\t\t\t});\n\t\t\t} else if (role === \"bashExecution\") {\n\t\t\t\tconst be = msg as {\n\t\t\t\t\tcommand: string;\n\t\t\t\t\toutput: string;\n\t\t\t\t\texitCode: number | undefined;\n\t\t\t\t};\n\t\t\t\titems.push({\n\t\t\t\t\tkind: \"bash\",\n\t\t\t\t\tcommand: be.command,\n\t\t\t\t\toutput: be.output,\n\t\t\t\t\texitCode: be.exitCode,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn { items, isStreaming };\n\t}\n\n\t/**\n\t * Delete a session JSONL file belonging to this workspace.\n\t *\n\t * Errors are tagged with HTTP semantics via HttpError so the route layer\n\t * can map them to the right status code:\n\t * - 400: sessionPath not absolute\n\t * - 404: workspace gone, or session not in this workspace's list\n\t * - 409: file is the currently-active session (caller must switch first)\n\t *\n\t * Idempotent on ENOENT: if the file is missing at unlink time (e.g. a\n\t * concurrent external delete between list and unlink), we treat it as\n\t * success — the goal state has been reached.\n\t */\n\tasync deleteSession(workspaceId: string, sessionPath: string): Promise<void> {\n\t\t// Defensive re-check even though the route already validates the\n\t\t// workspace — keeps the manager API safe for any caller (extensions,\n\t\t// tests). Mirrors listSessions/switchSession.\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new HttpError(404, `Workspace not found: ${workspaceId}`);\n\t\tif (!isAbsolute(sessionPath)) {\n\t\t\tthrow new HttpError(400, \"Session path must be absolute\");\n\t\t}\n\n\t\tconst sessions = await SessionManager.list(ws.path);\n\t\tconst resolved = resolve(sessionPath);\n\t\tconst target = sessions.find((session) => resolve(session.path) === resolved);\n\t\tif (!target) {\n\t\t\tthrow new HttpError(404, `Session not found: ${sessionPath}`);\n\t\t}\n\n\t\tconst runtime = this.states.get(workspaceId)?.runtime;\n\t\tconst activePath = runtime?.session.sessionFile\n\t\t\t? resolve(runtime.session.sessionFile)\n\t\t\t: undefined;\n\t\tif (activePath === resolved) {\n\t\t\tthrow new HttpError(\n\t\t\t\t409,\n\t\t\t\t\"Cannot delete the currently active session — switch to another session first\",\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tawait unlink(resolved);\n\t\t} catch (err) {\n\t\t\tif ((err as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[wm] deleteSession: ${resolved} was already gone at unlink time`,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync switchSession(workspaceId: string, sessionPath: string): Promise<boolean> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new Error(`Workspace not found: ${workspaceId}`);\n\t\tif (!isAbsolute(sessionPath)) {\n\t\t\tthrow new Error(\"Session path must be absolute\");\n\t\t}\n\n\t\tconst sessions = await SessionManager.list(ws.path);\n\t\tconst resolved = resolve(sessionPath);\n\t\tconst target = sessions.find((session) => resolve(session.path) === resolved);\n\t\tif (!target) {\n\t\t\tthrow new Error(`Session not found: ${sessionPath}`);\n\t\t}\n\n\t\tconst runtime = await this.getOrCreate(workspaceId);\n\t\tconst currentPath = runtime.session.sessionFile ? resolve(runtime.session.sessionFile) : undefined;\n\t\tif (currentPath === resolved) return false;\n\t\tif (runtime.session.isStreaming) {\n\t\t\tthrow new Error(\"Cannot switch sessions while the agent is streaming\");\n\t\t}\n\n\t\tconst result = await runtime.switchSession(resolved, { cwdOverride: ws.path });\n\t\treturn !result.cancelled;\n\t}\n\n\tasync dispose(workspaceId: string): Promise<void> {\n\t\tconst state = this.states.get(workspaceId);\n\t\tif (!state) return;\n\t\tthis.states.delete(workspaceId);\n\t\tthis.rebindListeners.delete(workspaceId);\n\t\tthis.subscribers.delete(workspaceId);\n\t\t// Release any ask_user Promises bound to this workspace's active\n\t\t// session *before* disposing the runtime, so the rejected\n\t\t// `execute()` Promises have a clean signal/timeout teardown\n\t\t// instead of racing the runtime's own dispose.\n\t\tcancelPendingForSession(state.runtime.session.sessionFile ?? null);\n\t\ttry {\n\t\t\tstate.bridge.dispose();\n\t\t} catch (e) {\n\t\t\tconsole.error(`[wm] dispose bridge ${workspaceId} failed:`, e);\n\t\t}\n\t\ttry {\n\t\t\tstate.runtime.session.dispose();\n\t\t} catch (e) {\n\t\t\tconsole.error(`[wm] dispose ${workspaceId} failed:`, e);\n\t\t}\n\t}\n\n\tasync disposeAll(): Promise<void> {\n\t\t// Drain in-flight builds first so any runtime that's mid-`build()`\n\t\t// when shutdown lands doesn't leak. `getOrCreate` writes to `states`\n\t\t// from its own continuation on the same promise; because it\n\t\t// registered its `await` before us, microtask ordering guarantees\n\t\t// that write runs before our snapshot below. Failed builds never\n\t\t// reached `states` — `allSettled` lets us ignore them.\n\t\tawait Promise.allSettled([...this.pending.values()]);\n\t\tconst ids = [...this.states.keys()];\n\t\tawait Promise.all(ids.map((id) => this.dispose(id)));\n\t}\n}\n\n/**\n * Run ask_user post-restart reconciliation, swallowing any error so a\n * cleanup failure doesn't brick the workspace. The worst-case fallout\n * of skipping cleanup is the LLM seeing an unmatched ask_user toolCall\n * with no explanation — it will typically ignore or re-call; not\n * load-bearing.\n */\nfunction safeReconcileAskUser(workspaceId: string, sm: SessionManager): void {\n\ttry {\n\t\treconcileAfterRestart(sm);\n\t} catch (e) {\n\t\tconsole.error(`[wm] ask_user cleanup for ${workspaceId} failed:`, e);\n\t}\n}\n\nfunction toSessionSummary(info: SessionInfo): SessionSummary {\n\tconst preview = info.firstMessage.replace(/\\s+/g, \" \").trim();\n\treturn {\n\t\tpath: info.path,\n\t\tname: info.name,\n\t\tupdatedAt: info.modified.toISOString(),\n\t\tpreview: preview ? preview.slice(0, 160) : undefined,\n\t};\n}\n\n/** Extract text from a user message (string or content blocks). */\nfunction extractUserText(msg: { content: unknown }): string {\n\tif (typeof msg.content === \"string\") return msg.content;\n\treturn extractContentText(msg.content as unknown[]);\n}\n\n/** Extract text + thinking + toolCalls from an assistant message's content blocks. */\nfunction extractAssistantContent(msg: { content: unknown[] }): {\n\ttext: string;\n\tthinking: string;\n\ttoolCalls: { id: string; args: string }[];\n} {\n\tconst textParts: string[] = [];\n\tconst thinkingParts: string[] = [];\n\tconst toolCalls: { id: string; args: string }[] = [];\n\tfor (const block of msg.content ?? []) {\n\t\tif (!block || typeof block !== \"object\") continue;\n\t\tconst b = block as { type?: string; text?: string; thinking?: string; id?: string; arguments?: unknown };\n\t\tif (b.type === \"text\" && typeof b.text === \"string\") textParts.push(b.text);\n\t\telse if (b.type === \"thinking\" && typeof b.thinking === \"string\") thinkingParts.push(b.thinking);\n\t\telse if (b.type === \"toolCall\" && typeof b.id === \"string\") {\n\t\t\ttoolCalls.push({\n\t\t\t\tid: b.id,\n\t\t\t\targs: b.arguments != null ? JSON.stringify(b.arguments) : \"\",\n\t\t\t});\n\t\t}\n\t}\n\treturn { text: textParts.join(\"\"), thinking: thinkingParts.join(\"\"), toolCalls };\n}\n\n/** Extract text from a content block array (TextContent items). */\nfunction extractContentText(content: unknown[]): string {\n\tif (!Array.isArray(content)) return \"\";\n\tconst parts: string[] = [];\n\tfor (const block of content) {\n\t\tif (block && typeof block === \"object\" && (block as { type?: string }).type === \"text\") {\n\t\t\tconst text = (block as { text?: unknown }).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\treturn parts.join(\"\");\n}\n\nexport const workspaceManager = new WorkspaceManager();\n\n/**\n * Best-effort fan-out of a server-initiated message to every connection\n * subscribed to a workspace. Mirrors `ExtensionUIBridge.broadcast` but\n * lives at the manager level so non-UI events (extension errors today;\n * extension_error variants later) can reuse it without needing a bridge\n * reference.\n */\nfunction broadcastTo(subscribers: Set<WebSocket>, msg: WsServerMessage): void {\n\tconst wire = JSON.stringify(msg);\n\tfor (const ws of subscribers) {\n\t\tif (ws.readyState !== ws.OPEN) continue;\n\t\ttry {\n\t\t\tws.send(wire);\n\t\t} catch {\n\t\t\t// Bad sockets drop on the next close event.\n\t\t}\n\t}\n}\n","/**\n * TypeBox schemas for the update_plan tool.\n *\n * Kept separate from factory.ts so future plan-related code (web shape\n * mirrors via shared types, future tooling) can import the parameter\n * shape without pulling pi runtime types into the dependency graph.\n */\n\nimport { Type, type Static } from \"typebox\";\n\nexport const planItemStatusSchema = Type.Union([\n\tType.Literal(\"pending\"),\n\tType.Literal(\"in_progress\"),\n\tType.Literal(\"completed\"),\n]);\n\nexport const planItemSchema = Type.Object({\n\tid: Type.String({ description: \"Short stable identifier for the item (kebab-case, e.g. \\\"wire-factory\\\").\" }),\n\ttitle: Type.String({ description: \"One-line description of the step. Specific and verifiable.\" }),\n\tstatus: planItemStatusSchema,\n\tnote: Type.Optional(Type.String({ description: \"Optional short context — blocker, decision, or follow-up.\" })),\n});\n\nexport const updatePlanParamsSchema = Type.Object({\n\titems: Type.Array(planItemSchema, {\n\t\tdescription: \"The full ordered plan. Always send the complete list; previous tool calls are not merged.\",\n\t}),\n});\n\nexport type PlanItem = Static<typeof planItemSchema>;\nexport type UpdatePlanParams = Static<typeof updatePlanParamsSchema>;\n","/**\n * pi-pilot's first-party plan extension.\n *\n * Registers:\n * - `update_plan` tool: agent emits the full plan (id/title/status[/note])\n * as one tool call to publish or refresh it. State machine: pending →\n * in_progress → completed. The full list is always sent — there is no\n * merging by id across calls; the most recent successful tool call IS\n * the current plan.\n * - `/plan` slash command: user-facing entry point. Expands into a user\n * message asking the agent to draft a plan and call update_plan\n * before starting work. The literal `/plan` text never reaches the\n * LLM — pi consumes the slash invocation and the handler injects the\n * rewritten message via `pi.sendUserMessage`.\n *\n * Why it's a builtin extension and not `customTools`: registering through\n * the extension API gives us the slash command (customTools can't) and\n * groups the tool + command + prompt guidelines in one place. The factory\n * runs inside the SDK's `extensionFactories` channel, which is independent\n * of the `noExtensions` disk-scan flag — so this loads even when the user\n * has third-party pi extensions turned off (the default for pi-pilot).\n *\n * Plan state is implicitly persisted in the session JSONL as tool call\n * history, so fork / new_session / replay get correct semantics for free;\n * we deliberately don't track plan state on our side.\n */\n\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport { updatePlanParamsSchema } from \"./schema.ts\";\n\nexport const planExtensionFactory: ExtensionFactory = (pi) => {\n\tpi.registerTool({\n\t\tname: \"update_plan\",\n\t\tlabel: \"Plan\",\n\t\tdescription:\n\t\t\t\"Publish or refresh the working plan for the current task. Use for any task that takes 3+ discrete steps. Always send the full ordered list; previous calls are replaced, not merged.\",\n\t\tparameters: updatePlanParamsSchema,\n\t\tpromptSnippet:\n\t\t\t\"update_plan: maintain a live checklist for multi-step tasks; update statuses as you progress.\",\n\t\tpromptGuidelines: [\n\t\t\t\"For tasks with 3+ discrete steps, call update_plan once with the full list before starting work.\",\n\t\t\t\"After completing each step, call update_plan again with refreshed statuses, then continue immediately — do not pause the turn just because the plan was updated.\",\n\t\t\t\"Plan items should be specific and verifiable (e.g. \\\"Add typebox dependency to packages/server\\\"), not vague (\\\"Set up infrastructure\\\").\",\n\t\t\t\"Exactly one item should be in_progress at a time. Mark completed only when the work is actually done.\",\n\t\t],\n\t\texecute: async (_toolCallId, params) => ({\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\ttext: `Plan updated (${params.items.length} item${params.items.length === 1 ? \"\" : \"s\"}).`,\n\t\t\t\t},\n\t\t\t],\n\t\t\tdetails: params,\n\t\t}),\n\t});\n\n\tpi.registerCommand(\"plan\", {\n\t\tdescription: \"Ask the agent to draft a plan for the current task.\",\n\t\thandler: async (args) => {\n\t\t\tconst task = args.trim();\n\t\t\tconst message = task\n\t\t\t\t? `Draft a plan for: ${task}. Use the update_plan tool to publish it before starting any work.`\n\t\t\t\t: \"Draft a plan for the current task. Use the update_plan tool to publish it before starting any work.\";\n\t\t\tpi.sendUserMessage(message);\n\t\t},\n\t});\n};\n","/**\n * TypeBox schemas for the ask_user tool.\n *\n * Kept separate from factory.ts so the registry and any future shared\n * mirrors (web shape via shared types, snapshot helpers) can import\n * parameter / answer shapes without pulling pi runtime types into the\n * dependency graph.\n *\n * The TypeBox version is forced workspace-wide via `pnpm.overrides` —\n * the `Kind` symbols here must come from the same module instance pi's\n * runtime validates against, otherwise tool registration will reject\n * our schema with a noisy mismatch error.\n */\n\nimport { Type, type Static } from \"typebox\";\n\nexport const askUserOptionSchema = Type.Object({\n\tlabel: Type.String({\n\t\tdescription: \"Short, distinct choice (1–5 words). User-facing.\",\n\t}),\n\tdescription: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Optional one-line clarification of what this choice means or implies.\",\n\t\t}),\n\t),\n});\n\n/**\n * `header` is capped at 12 chars because the web renders it as a chip\n * next to the question; longer strings get truncated visually anyway.\n * `options` is 2–4 to match common-sense UX (single-choice dialogs\n * become noisy past 4; less than 2 isn't a choice).\n * `timeoutSec` floor is 5s — anything shorter than human reaction time\n * is almost certainly a mistake.\n */\nexport const askUserParamsSchema = Type.Object({\n\tquestion: Type.String({\n\t\tdescription:\n\t\t\t\"Full question, specific and ending with '?'. Avoid yes/no when options can be more concrete.\",\n\t}),\n\theader: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: 12,\n\t\t\tdescription:\n\t\t\t\t\"Optional short chip label (≤ 12 chars), e.g. 'Auth method'. Shown next to the question.\",\n\t\t}),\n\t),\n\toptions: Type.Array(askUserOptionSchema, {\n\t\tminItems: 2,\n\t\tmaxItems: 4,\n\t\tdescription:\n\t\t\t\"2–4 mutually exclusive choices (unless multiSelect is true). Recommended option first; append ' (Recommended)' to its label.\",\n\t}),\n\tmultiSelect: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"If true, render checkboxes and allow multiple selections. Default false.\",\n\t\t}),\n\t),\n\tallowOther: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"If true, show a freeform 'Other' input as an escape hatch. Default true.\",\n\t\t}),\n\t),\n\ttimeoutSec: Type.Optional(\n\t\tType.Integer({\n\t\t\tminimum: 5,\n\t\t\tdescription:\n\t\t\t\t\"Auto-resolve as skip after this many seconds. Only set for time-sensitive questions where a stale answer would be worse than no answer.\",\n\t\t}),\n\t),\n});\n\nexport type AskUserOption = Static<typeof askUserOptionSchema>;\nexport type AskUserParams = Static<typeof askUserParamsSchema>;\n\n/**\n * Structured detail attached to the tool result. Mirrors the wire-side\n * `WsAnswerQuestion[\"answer\"]` union plus a `timeout` variant the\n * server may synthesize when the agent-supplied `timeoutSec` fires\n * before any client answered. The web reads this on history replay to\n * highlight the selected option(s) in the resolved card.\n */\nexport type AskUserAnswerDetails =\n\t| { kind: \"option\"; indices: number[]; labels: string[] }\n\t| { kind: \"other\"; text: string }\n\t| { kind: \"skip\" }\n\t| { kind: \"timeout\"; afterMs: number };\n","/**\n * Module-level registry of pending ask_user calls.\n *\n * Why module-level: pi's toolCallId is globally unique, so a single map\n * keyed by id is enough — we don't need a (workspaceId, id) tuple. This\n * keeps the factory signature compatible with `ExtensionFactory` (just\n * `(pi) => void`); the WS hub and the in-flight snapshot helper import\n * functions from this file to read / mutate the map.\n *\n * Lifecycle:\n * - `register()` called from `execute()` at tool start. Returns a\n * Promise that resolves when one of these happens:\n * 1. `resolveAnswer()` — client sent `answer_question`.\n * 2. timeoutSec fired — auto-resolves as `timeout`.\n * 3. signal abort (user pressed Stop) — execute() rejects.\n * 4. `cancelPendingExcept()` — session replaced under our feet.\n * 5. `cancelPendingForSession()` — workspace disposed.\n * - The Promise itself lives inside `execute()` (the factory owns\n * `new Promise(...)`); this module only owns the resolver/rejecter\n * side and the metadata needed for snapshot/cancel routing.\n *\n * Stale-answer policy: when `resolveAnswer()` is called with an\n * unknown toolCallId — already-answered by another tab, server-restart\n * cleanup ran, session replaced — we return `false` and the caller\n * (the WS hub) silently ignores. We never emit an error toast for this;\n * a duplicate answer is benign and the loser tab's UI catches up via\n * the normal `tool_execution_end` broadcast.\n */\n\nimport type { AskUserParams } from \"./schema.ts\";\nimport type { WsAnswerQuestion } from \"@pi-pilot/shared\";\n\nexport type Answer = WsAnswerQuestion[\"answer\"];\n\ninterface PendingEntry {\n\tresolve(answer: Answer): void;\n\treject(reason: Error): void;\n\t/** Original tool args — needed by `inFlightToolCallsSnapshot` to\n\t * replay a synthetic `tool_execution_start` on (re)subscribe. */\n\targs: AskUserParams;\n\t/** Absolute session JSONL path at register time. Used to route\n\t * cancels (session replacement) and snapshots (per-subscriber\n\t * filter). `null` for in-memory / unpersisted sessions. */\n\tsessionFile: string | null;\n}\n\nconst pending = new Map<string, PendingEntry>();\n\nexport function register(\n\ttoolCallId: string,\n\tentry: PendingEntry,\n): void {\n\tpending.set(toolCallId, entry);\n}\n\nexport function unregister(toolCallId: string): void {\n\tpending.delete(toolCallId);\n}\n\n/**\n * Resolve a pending call. Returns `false` when the toolCallId is\n * unknown or the sessionFile guard fails (stale answer from a tab\n * still bound to a replaced session).\n */\nexport function resolveAnswer(\n\ttoolCallId: string,\n\tanswer: Answer,\n\texpectedSessionFile: string | null,\n): boolean {\n\tconst entry = pending.get(toolCallId);\n\tif (!entry) return false;\n\tif (entry.sessionFile !== expectedSessionFile) return false;\n\tpending.delete(toolCallId);\n\tentry.resolve(answer);\n\treturn true;\n}\n\n/**\n * Reject every pending entry whose `sessionFile` does NOT equal\n * `keepSessionFile`. Called from the rebind callback after a session\n * replacement (newSession / fork / switchSession): pi does not abort\n * in-flight tool signals on session switch, so we have to actively\n * cancel anything bound to the now-defunct session, otherwise the\n * Promise inside `execute()` would hang forever.\n *\n * `keepSessionFile` is the file path of the freshly-bound session\n * (typically taken from `runtime.session.sessionFile` after bind).\n * `null` is a legitimate value — an unpersisted in-memory session\n * matches `null` and is kept.\n */\nexport function cancelPendingExcept(keepSessionFile: string | null): void {\n\tfor (const [id, entry] of pending) {\n\t\tif (entry.sessionFile === keepSessionFile) continue;\n\t\tpending.delete(id);\n\t\tentry.reject(new Error(\"Session replaced before answer arrived\"));\n\t}\n}\n\n/**\n * Reject every pending entry whose `sessionFile` equals the given\n * path. Called from workspace dispose; we know the runtime is going\n * away and the Promise must release.\n */\nexport function cancelPendingForSession(sessionFile: string | null): void {\n\tfor (const [id, entry] of pending) {\n\t\tif (entry.sessionFile !== sessionFile) continue;\n\t\tpending.delete(id);\n\t\tentry.reject(new Error(\"Workspace disposed\"));\n\t}\n}\n\n/**\n * Snapshot of pending entries for a session. Used by\n * `inFlightToolCallsSnapshot` to synthesize `tool_execution_start`\n * events when a client (re)subscribes mid-question — without this,\n * the question card vanishes after a tab reload because the original\n * `tool_execution_start` fired before the subscription existed.\n *\n * Returns a fresh array each call; safe for the caller to iterate\n * while the registry continues to mutate.\n */\nexport function snapshotForSession(\n\tsessionFile: string | null,\n): { toolCallId: string; args: AskUserParams }[] {\n\tconst out: { toolCallId: string; args: AskUserParams }[] = [];\n\tfor (const [id, entry] of pending) {\n\t\tif (entry.sessionFile !== sessionFile) continue;\n\t\tout.push({ toolCallId: id, args: entry.args });\n\t}\n\treturn out;\n}\n","/**\n * pi-pilot's second first-party builtin extension: ask_user.\n *\n * Registers a single tool, `ask_user`, that lets the agent pause and\n * ask the user a structured multiple-choice question. Unlike\n * `update_plan` (which is a near-instant echo), `ask_user.execute()`\n * blocks on a Promise stored in `./registry.ts` until one of:\n *\n * - the client posts a `WsAnswerQuestion` over the WS hub (the\n * normal path), or\n * - the agent-supplied `timeoutSec` fires (auto-skip), or\n * - `signal` aborts (user pressed Stop), or\n * - the registry's `cancelPending*` helpers reject us because the\n * session was replaced or the workspace disposed.\n *\n * The question itself is rendered on the web by reading `args` off the\n * normal `tool_execution_start` event — we do not add a parallel\n * \"question\" stream. The tool result text is the agent-facing summary\n * of what the user picked; `details` carries the structured form so\n * the web can highlight the chosen option on history replay.\n *\n * Why a custom tool instead of pi's `ctx.ui.select/input`: those route\n * through `ExtensionUIContext`, and pi-pilot's `ExtensionUIBridge` is\n * deliberately a no-op (TUI-only, can't be bridged to the Web). A\n * custom tool persists both the call and the result in the session\n * JSONL automatically, so fork / new_session / history replay get\n * correct semantics for free — same trick as `update_plan`.\n *\n * Restart cleanup: when pi loads a session whose tail is an unmatched\n * `ask_user` toolCall (server crashed while the Promise was pending,\n * so the toolResult never landed), `./cleanup.ts` writes a\n * `customMessage` that's visible to the LLM explaining the cancel.\n * pi SDK doesn't expose a way to forge a real `toolResult` role\n * message, so the LLM will see \"my unmatched toolCall + a user\n * message saying it was cancelled\" — acceptable; the explanation\n * keeps it from looping on a phantom in-flight call.\n */\n\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport {\n\taskUserParamsSchema,\n\ttype AskUserAnswerDetails,\n\ttype AskUserParams,\n} from \"./schema.ts\";\nimport {\n\tregister,\n\tunregister,\n\ttype Answer,\n} from \"./registry.ts\";\nimport type { AgentToolResult } from \"@earendil-works/pi-coding-agent\";\n\ntype AskUserResult = AgentToolResult<AskUserAnswerDetails>;\n\nexport const askUserExtensionFactory: ExtensionFactory = (pi) => {\n\tpi.registerTool({\n\t\tname: \"ask_user\",\n\t\tlabel: \"Ask user\",\n\t\tdescription:\n\t\t\t\"Pause and ask the user a structured multiple-choice question. Use ONLY for genuine forks where the user's preference materially changes the work (UI layout, library choice, scope boundary). Returns the user's selection (or 'skip' if they dismissed).\",\n\t\tparameters: askUserParamsSchema,\n\t\tpromptSnippet:\n\t\t\t\"ask_user: pause and ask the user a structured multiple-choice question when a decision is genuinely theirs.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Call ask_user only for genuine forks where the user's preference materially changes the work — UI layout, library choice, scope boundary. Don't ask for opinions you can defend yourself.\",\n\t\t\t\"Each ask_user call asks ONE question. If you have several, call it multiple times in order so the user sees one at a time.\",\n\t\t\t\"ask_user options must be distinct and mutually exclusive (unless multiSelect is true). 2–4 options; put the recommended option first and append ' (Recommended)' to its label.\",\n\t\t\t\"Don't follow up ask_user with 'Should I proceed?' — the answer already authorizes the next step.\",\n\t\t\t\"Only set ask_user's timeoutSec for time-sensitive questions where a stale answer would be worse than no answer; otherwise let the user take as long as they need.\",\n\t\t],\n\t\texecute: async (toolCallId, params, signal, _onUpdate, ctx) => {\n\t\t\tconst sessionFile = ctx.sessionManager.getSessionFile() ?? null;\n\t\t\tconst startedAt = Date.now();\n\n\t\t\tconst answer = await waitForAnswer({\n\t\t\t\ttoolCallId,\n\t\t\t\tparams,\n\t\t\t\tsessionFile,\n\t\t\t\tsignal,\n\t\t\t});\n\n\t\t\treturn formatResult(params, answer, startedAt);\n\t\t},\n\t});\n};\n\ninterface WaitArgs {\n\ttoolCallId: string;\n\tparams: AskUserParams;\n\tsessionFile: string | null;\n\tsignal: AbortSignal | undefined;\n}\n\n/**\n * Wire the pending Promise + the three concurrent cancel paths\n * (client answer / timeout / signal abort). Every resolution path\n * unregisters before resolving so a late event can't double-fire.\n *\n * Returns either:\n * - the client's `Answer` (from `resolveAnswer` in registry), or\n * - a synthetic `{ kind: \"timeout\", afterMs }` when the\n * agent-supplied `timeoutSec` fires.\n *\n * Throws on signal abort or session-replacement cancel — those\n * propagate out of `execute()` as a normal tool error and pi records\n * an error toolResult on its own.\n */\nfunction waitForAnswer({\n\ttoolCallId,\n\tparams,\n\tsessionFile,\n\tsignal,\n}: WaitArgs): Promise<Answer | { kind: \"timeout\"; afterMs: number }> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\n\t\tconst cleanup = () => {\n\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tunregister(toolCallId);\n\t\t};\n\n\t\tconst finishOk = (a: Answer | { kind: \"timeout\"; afterMs: number }) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tresolve(a);\n\t\t};\n\n\t\tconst finishErr = (err: Error) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(err);\n\t\t};\n\n\t\tconst onAbort = () => finishErr(new Error(\"Aborted by user\"));\n\t\tif (signal?.aborted) {\n\t\t\tfinishErr(new Error(\"Aborted by user\"));\n\t\t\treturn;\n\t\t}\n\t\tsignal?.addEventListener(\"abort\", onAbort);\n\n\t\tif (typeof params.timeoutSec === \"number\" && params.timeoutSec > 0) {\n\t\t\tconst ms = params.timeoutSec * 1000;\n\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\tfinishOk({ kind: \"timeout\", afterMs: ms });\n\t\t\t}, ms);\n\t\t}\n\n\t\tregister(toolCallId, {\n\t\t\targs: params,\n\t\t\tsessionFile,\n\t\t\tresolve: (answer) => finishOk(answer),\n\t\t\treject: (err) => finishErr(err),\n\t\t});\n\t});\n}\n\n/**\n * Build the agent-facing tool result. The `text` is what the LLM\n * sees in the toolResult content; the `details` is for the web's\n * history-replay rendering (highlighting which option chip is\n * selected). All four outcomes are non-error — even `skip` and\n * `timeout` — because we explicitly want the agent to use its best\n * judgement and keep going rather than apologize-and-re-ask.\n */\nfunction formatResult(\n\tparams: AskUserParams,\n\tanswer: Answer | { kind: \"timeout\"; afterMs: number },\n\tstartedAt: number,\n): AskUserResult {\n\tif (answer.kind === \"option\") {\n\t\tconst labels = answer.indices\n\t\t\t.map((i) => params.options[i]?.label)\n\t\t\t.filter((l): l is string => typeof l === \"string\");\n\t\tconst text =\n\t\t\tlabels.length === 1\n\t\t\t\t? `User selected: \"${labels[0]}\"${descriptionSuffix(params, answer.indices[0]!)}.`\n\t\t\t\t: `User selected: ${labels.map((l) => `\"${l}\"`).join(\", \")}.`;\n\t\tconst details: AskUserAnswerDetails = {\n\t\t\tkind: \"option\",\n\t\t\tindices: answer.indices,\n\t\t\tlabels,\n\t\t};\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails,\n\t\t};\n\t}\n\n\tif (answer.kind === \"other\") {\n\t\tconst text = `User answered (freeform): \"${answer.text}\"`;\n\t\tconst details: AskUserAnswerDetails = { kind: \"other\", text: answer.text };\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails,\n\t\t};\n\t}\n\n\tif (answer.kind === \"skip\") {\n\t\tconst text =\n\t\t\t\"User chose to skip this question. Use your best judgement and proceed; you may ask again later if it still matters.\";\n\t\tconst details: AskUserAnswerDetails = { kind: \"skip\" };\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails,\n\t\t};\n\t}\n\n\t// timeout\n\tconst waited = Math.round((Date.now() - startedAt) / 1000);\n\tconst text = `No answer within ${waited}s (agent-supplied timeout). Use your best judgement and proceed; you may ask again later if it still matters.`;\n\tconst details: AskUserAnswerDetails = {\n\t\tkind: \"timeout\",\n\t\tafterMs: answer.afterMs,\n\t};\n\treturn {\n\t\tcontent: [{ type: \"text\", text }],\n\t\tdetails,\n\t};\n}\n\nfunction descriptionSuffix(params: AskUserParams, index: number): string {\n\tconst desc = params.options[index]?.description;\n\treturn desc ? ` — ${desc}` : \"\";\n}\n","/**\n * Persistence for which first-party builtin extensions the user has turned off.\n *\n * pi-pilot's builtins (`plan`, `ask_user`) load via the SDK's `extensionFactories`\n * channel (see `extensions/index.ts`). Each factory is gated on this disabled set\n * so a toggled-off builtin registers nothing. The preference is GLOBAL — one switch\n * per builtin, applied to every workspace — matching the user's choice; these are\n * app-level agent capabilities, not per-project resources.\n *\n * File format (~/.pi/webui/builtin-extensions.json):\n * { \"disabled\": [\"plan\"] }\n *\n * `isBuiltinDisabled` must be SYNCHRONOUS because the factory gate runs inside the\n * loader's synchronous factory loop. We populate the in-memory cache once at startup\n * via `loadBuiltinPrefs()` (awaited before the server accepts requests, so the cache\n * is ready before any runtime — and therefore any factory — is built). The gate fails\n * open (treats unknown state as enabled) on the off chance it runs pre-load.\n */\n\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { config } from \"../config.ts\";\n\nconst PREFS_PATH = join(config.dataDir, \"builtin-extensions.json\");\n\ninterface PrefsFile {\n\tdisabled: string[];\n}\n\n/** In-memory source of truth. Fail-open default until `loadBuiltinPrefs` runs. */\nlet cache: PrefsFile = { disabled: [] };\n\n/**\n * Read the prefs file into the module cache. Idempotent. Call once at server\n * startup before serving so the synchronous gate has fresh data. A missing file\n * is normal (no builtin has ever been disabled). Fails open: a corrupt or\n * unreadable file resets to all-enabled (and warns) rather than crashing\n * startup over a non-critical preference.\n */\nexport async function loadBuiltinPrefs(): Promise<void> {\n\ttry {\n\t\tconst raw = await readFile(PREFS_PATH, \"utf8\");\n\t\tconst parsed = JSON.parse(raw) as Partial<PrefsFile>;\n\t\tcache = { disabled: Array.isArray(parsed.disabled) ? parsed.disabled : [] };\n\t} catch (err) {\n\t\tcache = { disabled: [] };\n\t\tif ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n\t\t\tconsole.warn(`[builtin-prefs] ignoring unreadable ${PREFS_PATH}:`, err);\n\t\t}\n\t}\n}\n\n/** True when the user has turned this builtin off. Synchronous (cache read). */\nexport function isBuiltinDisabled(id: string): boolean {\n\treturn cache.disabled.includes(id);\n}\n\n/** Snapshot of the currently disabled builtin ids. */\nexport function getDisabledBuiltins(): string[] {\n\treturn [...cache.disabled];\n}\n\n/** Flip a builtin on/off, updating the cache and persisting to disk. */\nexport async function setBuiltinEnabled(id: string, enabled: boolean): Promise<void> {\n\tconst next = new Set(cache.disabled);\n\tif (enabled) next.delete(id);\n\telse next.add(id);\n\tcache = { disabled: [...next] };\n\tawait save();\n}\n\nasync function save(): Promise<void> {\n\tawait mkdir(dirname(PREFS_PATH), { recursive: true });\n\tawait writeFile(PREFS_PATH, JSON.stringify(cache, null, 2), \"utf8\");\n}\n","/**\n * Registry of pi-pilot's first-party builtin extensions.\n *\n * These factories are passed to pi via `DefaultResourceLoaderOptions.\n * extensionFactories`, which is an independent channel from the disk\n * extension scanner (see `resource-loader.js`). The `noExtensions` flag\n * only controls disk scanning; factories registered here always load.\n *\n * Two-tier stance: third-party / disk pi extensions stay off by default\n * (rendered as \"third-party not supported\" in the Resources panel), but\n * pi-pilot ships its own first-party features via this list. To add a new\n * builtin feature, drop a new directory under `extensions/<name>/` with a\n * `factory.ts` exporting an `ExtensionFactory`, then append an entry to\n * `BUILTIN_EXTENSIONS` below.\n *\n * Per-builtin on/off: each factory is wrapped in a gate that checks the\n * user's persisted preference (`isBuiltinDisabled`). A disabled builtin\n * registers nothing. The gate is re-evaluated on every `session.reload()`\n * (which re-runs the factories), so toggling a builtin + reloading applies\n * the change live — see `storage/builtin-extension-prefs.ts` and the\n * `PUT .../resources/builtin-extensions` route. Wrapping (rather than\n * filtering the array) keeps the factory-list reference stable across\n * reloads, which the loader assumes.\n *\n * NOT a barrel file — this defines values (the manifest + factories list),\n * not a re-export of unrelated symbols.\n */\n\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport { planExtensionFactory } from \"./plan/factory.ts\";\nimport { askUserExtensionFactory } from \"./ask_user/factory.ts\";\nimport { isBuiltinDisabled } from \"../storage/builtin-extension-prefs.ts\";\n\n/** Static metadata + factory for one builtin. Drives both the gated factory\n * list and the Resources panel's friendly listing. */\nexport interface BuiltinExtensionDef {\n\t/** Stable id used by prefs + the toggle endpoint. */\n\tid: string;\n\t/** User-facing name. */\n\tname: string;\n\t/** One-line description shown in the panel. */\n\tdescription: string;\n\t/** Tool names the factory registers (for display). */\n\ttools: string[];\n\t/** Slash command names the factory registers, without \"/\" (for display). */\n\tcommands: string[];\n\tfactory: ExtensionFactory;\n}\n\nexport const BUILTIN_EXTENSIONS: BuiltinExtensionDef[] = [\n\t{\n\t\tid: \"plan\",\n\t\tname: \"Plan\",\n\t\tdescription:\n\t\t\t\"A live task checklist for multi-step work — adds the update_plan tool and the /plan command.\",\n\t\ttools: [\"update_plan\"],\n\t\tcommands: [\"plan\"],\n\t\tfactory: planExtensionFactory,\n\t},\n\t{\n\t\tid: \"ask_user\",\n\t\tname: \"Ask user\",\n\t\tdescription:\n\t\t\t\"Lets the agent pause and ask you a structured multiple-choice question — adds the ask_user tool.\",\n\t\ttools: [\"ask_user\"],\n\t\tcommands: [],\n\t\tfactory: askUserExtensionFactory,\n\t},\n];\n\n/** Wrap a factory so it registers nothing while the builtin is toggled off. */\nfunction gate(def: BuiltinExtensionDef): ExtensionFactory {\n\treturn (pi) => {\n\t\tif (isBuiltinDisabled(def.id)) return;\n\t\treturn def.factory(pi);\n\t};\n}\n\nexport const builtinExtensionFactories: ExtensionFactory[] =\n\tBUILTIN_EXTENSIONS.map(gate);\n","/**\n * Post-restart reconciliation for orphaned `ask_user` tool calls.\n *\n * Scenario: pi-pilot crashes (or is killed) while `ask_user.execute()`\n * is blocked waiting for a client answer. pi has already finalized the\n * assistant message containing the toolCall block (toolCalls get\n * persisted as part of the assistant message at `message_end`), but\n * the matching toolResult was never written because the Promise\n * inside `execute()` died with the process. When pi loads the session\n * on next boot, it sees an unmatched toolCall and feeds it straight to\n * the LLM (per Spike Q1: pi does not validate or auto-resolve dangling\n * tool calls — `session-manager.js:113–207`).\n *\n * What this function does, on first bind after such a restart:\n * 1. Walk the branch tail backwards from the leaf.\n * 2. Find every `ask_user` toolCall whose id has no matching\n * toolResult on the tail.\n * 3. If any exist, append a `customMessage` (LLM-visible per Spike\n * Q4) explaining the cancellation, listing the dangling ids.\n *\n * Why a customMessage and not a forged toolResult: pi SDK exposes\n * `appendCustomMessageEntry` (visible to LLM as a user message) and\n * `appendCustomEntry` (not visible). Neither lets us write a real\n * `role: \"toolResult\"` message — `appendMessage` accepts it in\n * theory but mixing in a synthetic toolResult would break the\n * causality pi reasoners rely on. The customMessage path is honest:\n * the LLM sees its own unmatched toolCall + a plain English note\n * explaining the session was interrupted. Documented in PROGRESS.md\n * Known gaps so future readers know why this looks asymmetric.\n *\n * Re-entrancy: if a previous restart cleanup already wrote a notice\n * for the same toolCall ids, we don't write another. The dedup key is\n * the customType `ask_user-restart-cancelled` combined with the ids\n * encoded into the message body — we scan tail entries first and\n * bail if we see one whose ids cover ours.\n */\n\nimport type { SessionManager } from \"@earendil-works/pi-coding-agent\";\n\nconst CUSTOM_TYPE = \"ask_user-restart-cancelled\";\n\nexport function reconcileAfterRestart(sessionManager: SessionManager): void {\n\tconst branch = sessionManager.getBranch();\n\tif (branch.length === 0) return;\n\n\tconst satisfied = new Set<string>();\n\tconst danglingIds: string[] = [];\n\tconst danglingAlreadyHandled = new Set<string>();\n\n\t// Walk backwards. Stop conditions:\n\t// - user message: end of the current \"agent reply\" span; anything\n\t// dangling further back would have been handled by an earlier\n\t// restart pass or doesn't matter (LLM has moved on).\n\t// - existing restart-cancellation customMessage: read the ids it\n\t// covers so we don't re-notify for the same calls.\n\tfor (let i = branch.length - 1; i >= 0; i--) {\n\t\tconst entry = branch[i]!;\n\n\t\tif (entry.type === \"custom_message\") {\n\t\t\tconst cm = entry as { customType?: string; details?: unknown };\n\t\t\tif (cm.customType === CUSTOM_TYPE) {\n\t\t\t\tconst ids = (cm.details as { ids?: unknown })?.ids;\n\t\t\t\tif (Array.isArray(ids)) {\n\t\t\t\t\tfor (const id of ids) {\n\t\t\t\t\t\tif (typeof id === \"string\") danglingAlreadyHandled.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type !== \"message\") continue;\n\t\tconst msg = entry.message as { role?: string; toolCallId?: string; content?: unknown };\n\n\t\tif (msg.role === \"toolResult\" && typeof msg.toolCallId === \"string\") {\n\t\t\tsatisfied.add(msg.toolCallId);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n\t\t\tfor (const block of msg.content) {\n\t\t\t\tif (!block || typeof block !== \"object\") continue;\n\t\t\t\tconst b = block as { type?: string; name?: string; id?: string };\n\t\t\t\tif (b.type !== \"toolCall\") continue;\n\t\t\t\tif (b.name !== \"ask_user\") continue;\n\t\t\t\tif (typeof b.id !== \"string\") continue;\n\t\t\t\tif (satisfied.has(b.id)) continue;\n\t\t\t\tif (danglingAlreadyHandled.has(b.id)) continue;\n\t\t\t\tdanglingIds.push(b.id);\n\t\t\t}\n\t\t\t// Whether we found dangling here or not, we stop after the\n\t\t\t// first assistant message: earlier turns are historical and\n\t\t\t// would have been reconciled by their own subsequent\n\t\t\t// toolResult or by an earlier cleanup run.\n\t\t\tbreak;\n\t\t}\n\n\t\tif (msg.role === \"user\") break;\n\t}\n\n\tif (danglingIds.length === 0) return;\n\n\tconst idList = danglingIds.join(\", \");\n\tconst text =\n\t\t`[pi-pilot] Your previous ask_user call(s) [${idList}] were cancelled ` +\n\t\t`because the server restarted before the user answered. Use your best ` +\n\t\t`judgement and proceed; you may re-call ask_user if the decision still matters.`;\n\n\tsessionManager.appendCustomMessageEntry(\n\t\tCUSTOM_TYPE,\n\t\ttext,\n\t\ttrue, // display in TUI too (no-op in pi-pilot, but harmless)\n\t\t{ ids: danglingIds },\n\t);\n}\n\n","/**\n * Translate a pi AgentSessionEvent into our narrow wire payload.\n *\n * Anything not in the switch is dropped (returns undefined). Adding support\n * for a new event type means: add a case here, and a matching variant in\n * `@pi-pilot/shared/ws-protocol.ts`.\n */\n\nimport type { AgentSessionEvent } from \"@earendil-works/pi-coding-agent\";\nimport type { PiEventPayload } from \"@pi-pilot/shared\";\nimport { snapshotForSession } from \"../extensions/ask_user/registry.ts\";\n\nexport function translatePiEvent(ev: AgentSessionEvent): PiEventPayload | undefined {\n\tswitch (ev.type) {\n\t\tcase \"agent_start\":\n\t\t\treturn { kind: \"agent_start\" };\n\n\t\tcase \"agent_end\":\n\t\t\treturn { kind: \"agent_end\", willRetry: ev.willRetry };\n\n\t\tcase \"turn_start\":\n\t\t\treturn { kind: \"turn_start\" };\n\n\t\tcase \"turn_end\":\n\t\t\treturn { kind: \"turn_end\" };\n\n\t\tcase \"message_start\": {\n\t\t\tconst role = roleOf(ev.message);\n\t\t\t// Carry user text on the wire so the client can reconcile its\n\t\t\t// optimistic bubble (or insert one when no optimistic push\n\t\t\t// happened — extension-command path). Other roles stream via\n\t\t\t// message_update deltas; a start-time snapshot would be stale.\n\t\t\tconst text = role === \"user\" ? extractUserText(ev.message) : undefined;\n\t\t\treturn { kind: \"message_start\", role, text };\n\t\t}\n\n\t\tcase \"message_end\":\n\t\t\treturn { kind: \"message_end\", role: roleOf(ev.message) };\n\n\t\tcase \"message_update\": {\n\t\t\tconst ame = ev.assistantMessageEvent;\n\t\t\tif (ame.type === \"text_delta\") {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"message_update\",\n\t\t\t\t\tdelta: { kind: \"text\", contentIndex: ame.contentIndex, text: ame.delta },\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (ame.type === \"thinking_delta\") {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"message_update\",\n\t\t\t\t\tdelta: { kind: \"thinking\", contentIndex: ame.contentIndex, text: ame.delta },\n\t\t\t\t};\n\t\t\t}\n\t\t\t// Other deltas (start/end, toolcall) are not surfaced in MVP.\n\t\t\treturn { kind: \"message_update\", delta: { kind: \"other\" } };\n\t\t}\n\n\t\tcase \"tool_execution_start\":\n\t\t\treturn {\n\t\t\t\tkind: \"tool_execution_start\",\n\t\t\t\ttoolCallId: ev.toolCallId,\n\t\t\t\ttoolName: ev.toolName,\n\t\t\t\targs: ev.args,\n\t\t\t};\n\n\t\tcase \"tool_execution_update\":\n\t\t\treturn {\n\t\t\t\tkind: \"tool_execution_update\",\n\t\t\t\ttoolCallId: ev.toolCallId,\n\t\t\t\ttoolName: ev.toolName,\n\t\t\t\tpartialText: extractText(ev.partialResult),\n\t\t\t};\n\n\t\tcase \"tool_execution_end\": {\n\t\t\t// Whitelist details forwarding: only tools whose web cards\n\t\t\t// need the structured shape get it on the wire. Today only\n\t\t\t// `ask_user` (so <AskUserCard>'s resolved state can read\n\t\t\t// `kind`/`indices`/`text` directly instead of regex-parsing\n\t\t\t// `formatResult`'s output). Built-in tools' details are\n\t\t\t// often large (bash stdout, edit diffs); keeping them off\n\t\t\t// the wire avoids double-transmission since `extractText`\n\t\t\t// already pulled the human-readable form.\n\t\t\t//\n\t\t\t// `ev.result` is typed `any` in the SDK; the `!== undefined`\n\t\t\t// guard mirrors `workspace-manager.ts/getSessionHistory` so\n\t\t\t// we never put a `details: undefined` field on the object —\n\t\t\t// it would JSON-stringify away on the wire but is noisy in\n\t\t\t// in-process logging.\n\t\t\tconst details = shouldForwardDetails(ev.toolName)\n\t\t\t\t? ev.result?.details\n\t\t\t\t: undefined;\n\t\t\treturn {\n\t\t\t\tkind: \"tool_execution_end\",\n\t\t\t\ttoolCallId: ev.toolCallId,\n\t\t\t\ttoolName: ev.toolName,\n\t\t\t\tisError: ev.isError,\n\t\t\t\ttext: extractText(ev.result),\n\t\t\t\t...(details !== undefined ? { details } : {}),\n\t\t\t};\n\t\t}\n\n\t\tcase \"queue_update\":\n\t\t\treturn {\n\t\t\t\tkind: \"queue_update\",\n\t\t\t\tsteering: [...ev.steering],\n\t\t\t\tfollowUp: [...ev.followUp],\n\t\t\t};\n\n\t\tcase \"auto_retry_start\":\n\t\t\treturn {\n\t\t\t\tkind: \"auto_retry_start\",\n\t\t\t\tattempt: ev.attempt,\n\t\t\t\tmaxAttempts: ev.maxAttempts,\n\t\t\t\tdelayMs: ev.delayMs,\n\t\t\t\terrorMessage: ev.errorMessage,\n\t\t\t};\n\n\t\tcase \"auto_retry_end\":\n\t\t\treturn {\n\t\t\t\tkind: \"auto_retry_end\",\n\t\t\t\tsuccess: ev.success,\n\t\t\t\tattempt: ev.attempt,\n\t\t\t\tfinalError: ev.finalError,\n\t\t\t};\n\n\t\tcase \"compaction_start\":\n\t\t\treturn { kind: \"compaction_start\", reason: ev.reason };\n\n\t\tcase \"compaction_end\":\n\t\t\treturn {\n\t\t\t\tkind: \"compaction_end\",\n\t\t\t\treason: ev.reason,\n\t\t\t\taborted: ev.aborted,\n\t\t\t\twillRetry: ev.willRetry,\n\t\t\t\terrorMessage: ev.errorMessage,\n\t\t\t};\n\n\t\tcase \"session_info_changed\":\n\t\t\treturn { kind: \"session_info_changed\", name: ev.name };\n\n\t\tcase \"thinking_level_changed\":\n\t\t\treturn { kind: \"thinking_level_changed\", level: ev.level };\n\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\n/**\n * Track unknown roles we've already warned about so a stream of messages\n * with the same novel role doesn't spam the log. Module-level — the warn\n * is informational and one per process lifetime is enough to flag the\n * mismatch during development.\n */\nconst warnedUnknownRoles = new Set<string>();\n\nfunction roleOf(message: unknown): \"user\" | \"assistant\" | \"toolResult\" | \"bashExecution\" {\n\tconst role = (message as { role?: string } | undefined)?.role;\n\tif (role === \"user\" || role === \"assistant\" || role === \"toolResult\" || role === \"bashExecution\") {\n\t\treturn role;\n\t}\n\t// Pi SDK added a message role we don't translate yet. Fall back to\n\t// \"assistant\" so the wire schema stays valid, but warn loudly — a\n\t// new role usually implies new event shapes that bridge.ts and the\n\t// chat store both need to learn about.\n\tconst key = typeof role === \"string\" ? role : `<${role === undefined ? \"missing\" : typeof role}>`;\n\tif (!warnedUnknownRoles.has(key)) {\n\t\twarnedUnknownRoles.add(key);\n\t\tconsole.warn(\n\t\t\t`[bridge] unknown message role \"${key}\" — treating as assistant. ` +\n\t\t\t\t`Pi SDK may have added a role bridge.ts and chat.ts don't handle yet.`,\n\t\t);\n\t}\n\treturn \"assistant\";\n}\n\n/**\n * Best-effort extraction of user-message text. Pi's user-role\n * `AgentMessage` has either `content: string` (single text body) or\n * `content: ContentBlock[]` (typed blocks; we keep only `TextContent`).\n * Returns `undefined` when neither shape yields text — the wire field\n * is optional so an undefined here is fine.\n */\nfunction extractUserText(message: unknown): string | undefined {\n\tif (!message || typeof message !== \"object\") return undefined;\n\tconst content = (message as { content?: unknown }).content;\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return undefined;\n\tconst parts: string[] = [];\n\tfor (const block of content) {\n\t\tif (block && typeof block === \"object\" && (block as { type?: string }).type === \"text\") {\n\t\t\tconst text = (block as { text?: unknown }).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\treturn parts.length === 0 ? undefined : parts.join(\"\");\n}\n\n/**\n * Build synthetic events that recreate an in-flight assistant turn on a\n * client that just (re)subscribed mid-stream.\n *\n * Why: when a workspace switch happens during streaming, the client wipes\n * its chat store on `subscribed`, and /history can't see the not-yet-\n * persisted assistant message (pi only commits to JSONL at message_end).\n * Live deltas that follow the rebind would land in `chat.ts`'s\n * `lastIndexOfStreamingAssistant` guard with no streaming item to attach\n * to, and silently drop. Emitting a synthetic `message_start(assistant)`\n * + accumulated `thinking` / `text` deltas reseats that anchor so the\n * subsequent live `message_update`s flow into the right item.\n *\n * Tool calls inside the in-flight assistant message are intentionally NOT\n * restored — any `tool_execution_*` events that fired during the\n * off-window are gone (pi's `session.subscribe` does not replay), and\n * the assistant `content` only carries the ToolCall block, not the\n * running/finished tool result. Pending tool restoration is a follow-up.\n *\n * Returns `undefined` when there's nothing to replay (no in-flight\n * message, non-assistant role, or only ToolCall content).\n */\nexport function inFlightAssistantSnapshot(\n\tstreamingMessage: unknown,\n): PiEventPayload[] | undefined {\n\tif (!streamingMessage || typeof streamingMessage !== \"object\") return undefined;\n\tconst m = streamingMessage as { role?: string; content?: unknown };\n\tif (m.role !== \"assistant\" || !Array.isArray(m.content)) return undefined;\n\n\tlet textAccum = \"\";\n\tlet thinkingAccum = \"\";\n\tfor (const block of m.content) {\n\t\tif (!block || typeof block !== \"object\") continue;\n\t\tconst b = block as { type?: string; text?: unknown; thinking?: unknown };\n\t\tif (b.type === \"text\" && typeof b.text === \"string\") {\n\t\t\ttextAccum += b.text;\n\t\t} else if (b.type === \"thinking\" && typeof b.thinking === \"string\") {\n\t\t\tthinkingAccum += b.thinking;\n\t\t}\n\t}\n\n\tif (!textAccum && !thinkingAccum) return undefined;\n\n\tconst events: PiEventPayload[] = [\n\t\t{ kind: \"message_start\", role: \"assistant\" },\n\t];\n\tif (thinkingAccum) {\n\t\tevents.push({\n\t\t\tkind: \"message_update\",\n\t\t\tdelta: { kind: \"thinking\", contentIndex: 0, text: thinkingAccum },\n\t\t});\n\t}\n\tif (textAccum) {\n\t\tevents.push({\n\t\t\tkind: \"message_update\",\n\t\t\tdelta: { kind: \"text\", contentIndex: 0, text: textAccum },\n\t\t});\n\t}\n\treturn events;\n}\n\n/**\n * Build synthetic `tool_execution_start` events for any pending\n * `ask_user` calls on the given session, so a client that (re)subscribes\n * mid-question still sees the interactive card.\n *\n * Why this is needed even though pi already persists the assistant\n * message with the toolCall block: a freshly-subscribed client\n * reconciles via REST `/history` (which only sees finalized\n * toolResults — for a still-pending call there isn't one), and live\n * `tool_execution_start` already fired before this client connected.\n * Without this snapshot the question card silently never appears.\n *\n * Symmetric with `inFlightAssistantSnapshot` above. Filtered by\n * sessionFile so a connection bound to workspace A doesn't see\n * workspace B's pending questions. Empty arrays are returned (rather\n * than undefined) when there's nothing to replay — the caller is a\n * `for` loop, this keeps the call site simple.\n */\nexport function inFlightToolCallsSnapshot(\n\tsessionFile: string | null,\n): PiEventPayload[] {\n\tconst pending = snapshotForSession(sessionFile);\n\treturn pending.map((p) => ({\n\t\tkind: \"tool_execution_start\",\n\t\ttoolCallId: p.toolCallId,\n\t\ttoolName: \"ask_user\",\n\t\targs: p.args,\n\t}));\n}\n\n/**\n * Tools allowed to ship `AgentToolResult.details` over the wire. Keep\n * this list tight — every entry doubles the per-result payload size\n * for that tool. New entries should be ones whose web rendering\n * genuinely benefits from the structured shape (interactive cards,\n * highlighted selections, etc.), not ones that just *could* use it.\n *\n * Exported so `workspace-manager.ts/getSessionHistory` can apply the\n * same filter on REST history — the live (`tool_execution_end`) and\n * persisted (`HistoryToolItem`) surfaces stay symmetric.\n */\nexport const DETAILS_FORWARD_WHITELIST = new Set<string>([\"ask_user\"]);\n\nexport function shouldForwardDetails(toolName: string): boolean {\n\treturn DETAILS_FORWARD_WHITELIST.has(toolName);\n}\n\n/** Best-effort extraction of human-readable text from a tool result-shaped object. */\nfunction extractText(result: unknown): string | undefined {\n\tif (!result || typeof result !== \"object\") return undefined;\n\tconst content = (result as { content?: unknown }).content;\n\tif (!Array.isArray(content)) return undefined;\n\tconst parts: string[] = [];\n\tfor (const c of content) {\n\t\tif (c && typeof c === \"object\" && (c as { type?: string }).type === \"text\") {\n\t\t\tconst text = (c as { text?: unknown }).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\treturn parts.length === 0 ? undefined : parts.join(\"\");\n}\n","/**\n * No-op implementation of pi's `ExtensionUIContext`.\n *\n * pi-pilot does not support pi extensions (see README / workspace-manager).\n * The extension system is TUI-native — custom renderers, ANSI themes,\n * keyboard overlays, modal dialogs — and cannot be bridged to the Web in\n * any meaningful way. Rather than render a half-working subset that lies\n * about what's supported, every method here is an intentional no-op or\n * returns a sensible default.\n *\n * Why keep the class at all: the pi SDK still calls\n * `bindExtensions({ uiContext })`, and the type contract requires a\n * full `ExtensionUIContext`. When `PI_PILOT_ENABLE_EXTENSIONS=1`, the\n * loader will load extensions and any UI calls land here and quietly\n * do nothing. An extension that registers tools / slash commands but\n * doesn't touch `ctx.ui` still works; everything UI-driven is dead.\n *\n * Methods covered:\n * - Dialogs: select, confirm, input, editor → resolve to default\n * - Fire-and-forget: notify, setStatus, setWidget, setTitle,\n * setEditorText, pasteToEditor → no-op\n * - TUI-only: onTerminalInput, setWorkingMessage,\n * setWorkingVisible, setWorkingIndicator,\n * setHiddenThinkingLabel, setFooter, setHeader,\n * custom, getEditorText, addAutocompleteProvider,\n * setEditorComponent, getEditorComponent, theme,\n * getAllThemes, getTheme, setTheme,\n * getToolsExpanded, setToolsExpanded\n */\n\nimport type { ExtensionUIContext } from \"@earendil-works/pi-coding-agent\";\n\nexport class ExtensionUIBridge implements ExtensionUIContext {\n\t/** Symmetric with the old bridge so workspace-manager's dispose path\n\t * can call it uniformly. There is no state to release. */\n\tdispose(): void {}\n\n\t// ============== dialog methods (resolve to default) ==============\n\n\tselect(): Promise<string | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\tconfirm(): Promise<boolean> {\n\t\treturn Promise.resolve(false);\n\t}\n\tinput(): Promise<string | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\teditor(): Promise<string | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\n\t// ============== fire-and-forget methods (no-op) ==============\n\n\tnotify(): void {}\n\tsetStatus(): void {}\n\tsetWidget(): void {}\n\tsetTitle(): void {}\n\tsetEditorText(): void {}\n\tpasteToEditor(): void {}\n\n\t// ============== TUI-only methods (no-op / defaults) ==============\n\n\tonTerminalInput(): () => void {\n\t\treturn () => {};\n\t}\n\tsetWorkingMessage(): void {}\n\tsetWorkingVisible(): void {}\n\tsetWorkingIndicator(): void {}\n\tsetHiddenThinkingLabel(): void {}\n\tsetFooter(): void {}\n\tsetHeader(): void {}\n\tasync custom<T>(): Promise<T> {\n\t\t// `custom()` is the primary TUI rendering API used by extensions to\n\t\t// draw arbitrary terminal dialogs/overlays. It cannot be bridged to\n\t\t// the Web. Match pi's documented RPC-mode behavior of returning\n\t\t// undefined; the SDK's callers handle that explicitly.\n\t\treturn undefined as unknown as T;\n\t}\n\tgetEditorText(): string {\n\t\treturn \"\";\n\t}\n\taddAutocompleteProvider(): void {}\n\tsetEditorComponent(): void {}\n\tgetEditorComponent(): undefined {\n\t\treturn undefined;\n\t}\n\tget theme(): never {\n\t\t// Extensions that read `ctx.ui.theme` directly see undefined. The\n\t\t// cast keeps the type structural.\n\t\treturn undefined as unknown as never;\n\t}\n\tgetAllThemes(): { name: string; path: string | undefined }[] {\n\t\treturn [];\n\t}\n\tgetTheme(): undefined {\n\t\treturn undefined;\n\t}\n\tsetTheme(): { success: boolean; error?: string } {\n\t\treturn { success: false, error: \"Theme switching not supported in pi-pilot\" };\n\t}\n\tgetToolsExpanded(): boolean {\n\t\treturn false;\n\t}\n\tsetToolsExpanded(): void {}\n}\n","/**\n * /api/workspaces/:id/config routes.\n *\n * Reads and mutates the runtime config (model, thinking level, tools) for a\n * workspace's active AgentSession. Mounted onto the workspaces Hono router\n * via `mountConfigRoutes`.\n */\n\nimport type { Hono, Context } from \"hono\";\nimport type { AgentSessionRuntime } from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tConfigResponse,\n\tModelInfo,\n\tPiContextUsage,\n\tSetModelRequest,\n\tSetThinkingLevelRequest,\n\tSetToolsRequest,\n\tThinkingLevel,\n\tToolInfo as ToolInfoDTO,\n} from \"@pi-pilot/shared\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport { getWorkspace } from \"../storage/workspace-registry.ts\";\n\n// ---------- helpers ----------\n\nfunction buildConfigResponse(workspaceId: string): ConfigResponse {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) throw new Error(\"runtime not initialized\");\n\n\tconst session = runtime.session;\n\tconst model = session.model;\n\n\tconst currentModel: ModelInfo | null = model\n\t\t? {\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodelId: model.id,\n\t\t\t\tname: model.name,\n\t\t\t\treasoning: model.reasoning,\n\t\t\t}\n\t\t: null;\n\n\tconst availableModels: ModelInfo[] = session.modelRegistry\n\t\t.getAvailable()\n\t\t.map((m) => ({\n\t\t\tprovider: m.provider,\n\t\t\tmodelId: m.id,\n\t\t\tname: m.name,\n\t\t\treasoning: m.reasoning,\n\t\t}));\n\n\tconst allTools: ToolInfoDTO[] = session.getAllTools().map((t) => ({\n\t\tname: t.name,\n\t\tdescription: t.description,\n\t}));\n\n\treturn {\n\t\tcurrentModel,\n\t\tthinkingLevel: session.thinkingLevel as ThinkingLevel,\n\t\tavailableThinkingLevels: session.getAvailableThinkingLevels() as ThinkingLevel[],\n\t\tactiveTools: session.getActiveToolNames(),\n\t\tavailableModels,\n\t\tallTools,\n\t};\n}\n\n/** Return 404 early if workspace does not exist in the registry. */\nasync function requireWorkspace(c: Context, id: string): Promise<boolean> {\n\tconst ws = await getWorkspace(id);\n\tif (!ws) {\n\t\tc.status(404);\n\t\tc.header(\"Content-Type\", \"application/json\");\n\t\t// We must return the body via the route handler, so we set a flag.\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n/** Return 409 if the agent is currently streaming. */\nfunction rejectIfStreaming(c: Context, workspaceId: string): boolean {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (runtime?.session.isStreaming) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/**\n * Push a fresh `context_usage` snapshot to every WS subscriber of the\n * workspace. Pi's event stream only surfaces context-usage shifts via\n * `agent_end` / `compaction_end` / `thinking_level_changed`, so a model\n * swap whose new contextWindow differs but doesn't clamp thinking level\n * would otherwise leave clients displaying the previous model's percent\n * until the next assistant turn.\n */\nfunction broadcastContextUsage(\n\tworkspaceId: string,\n\truntime: AgentSessionRuntime,\n): void {\n\tconst usage = runtime.session.getContextUsage();\n\tif (!usage) return;\n\tconst payload: PiContextUsage = {\n\t\tkind: \"context_usage\",\n\t\ttokens: usage.tokens,\n\t\tcontextWindow: usage.contextWindow,\n\t\tpercent: usage.percent,\n\t};\n\tworkspaceManager.broadcast(workspaceId, {\n\t\ttype: \"event\",\n\t\tworkspaceId,\n\t\tsessionPath: runtime.session.sessionFile ?? null,\n\t\tpayload,\n\t});\n}\n\n// ---------- mount ----------\n\nexport function mountConfigRoutes(app: Hono): void {\n\t// GET /api/workspaces/:id/config\n\tapp.get(\"/:id/config\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n\n\t// PUT /api/workspaces/:id/config/model\n\tapp.put(\"/:id/config/model\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst body = (await c.req.json()) as SetModelRequest;\n\t\tif (!body?.provider || !body?.modelId) {\n\t\t\treturn c.json({ ok: false, error: \"provider and modelId are required\" }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\tconst runtime = await workspaceManager.getOrCreate(id);\n\t\t\tif (rejectIfStreaming(c, id)) {\n\t\t\t\treturn c.json({ ok: false, error: \"cannot change model while the agent is streaming\" }, 409);\n\t\t\t}\n\n\t\t\tconst model = runtime.session.modelRegistry.getAvailable().find(\n\t\t\t\t(m) => m.provider === body.provider && m.id === body.modelId,\n\t\t\t);\n\t\t\tif (!model) {\n\t\t\t\treturn c.json({ ok: false, error: `model not found or no auth: ${body.provider}/${body.modelId}` }, 404);\n\t\t\t}\n\n\t\t\tawait runtime.session.setModel(model);\n\t\t\t// New model may have a different contextWindow. Pi's setModel only\n\t\t\t// emits an event when thinking-level gets clamped — when the new\n\t\t\t// model accepts the current level unchanged, no event fires and\n\t\t\t// the WS CTX indicator would stay pinned to the previous model's\n\t\t\t// percent until the next agent_end. Push a snapshot ourselves.\n\t\t\tbroadcastContextUsage(id, runtime);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n\n\t// PUT /api/workspaces/:id/config/thinking-level\n\tapp.put(\"/:id/config/thinking-level\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst body = (await c.req.json()) as SetThinkingLevelRequest;\n\t\tconst validLevels: ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"];\n\t\tif (!body?.level || !validLevels.includes(body.level)) {\n\t\t\treturn c.json({ ok: false, error: `level must be one of: ${validLevels.join(\", \")}` }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tif (rejectIfStreaming(c, id)) {\n\t\t\t\treturn c.json({ ok: false, error: \"cannot change thinking level while the agent is streaming\" }, 409);\n\t\t\t}\n\n\t\t\tconst runtime = workspaceManager.get(id);\n\t\t\tif (!runtime) {\n\t\t\t\treturn c.json({ ok: false, error: \"runtime not initialized\" }, 500);\n\t\t\t}\n\t\t\truntime.session.setThinkingLevel(body.level);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n\n\t// PUT /api/workspaces/:id/config/tools\n\tapp.put(\"/:id/config/tools\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst body = (await c.req.json()) as SetToolsRequest;\n\t\tif (!body?.tools || !Array.isArray(body.tools)) {\n\t\t\treturn c.json({ ok: false, error: \"tools must be an array of tool names\" }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tif (rejectIfStreaming(c, id)) {\n\t\t\t\treturn c.json({ ok: false, error: \"cannot change tools while the agent is streaming\" }, 409);\n\t\t\t}\n\n\t\t\tconst runtime = workspaceManager.get(id);\n\t\t\tif (!runtime) {\n\t\t\t\treturn c.json({ ok: false, error: \"runtime not initialized\" }, 500);\n\t\t\t}\n\t\t\truntime.session.setActiveToolsByName(body.tools);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n}\n","/**\n * /api/workspaces/:id/files/search — workspace-scoped file lookup for the\n * composer's `@` autocomplete.\n *\n * Source of truth is `git ls-files --cached --others --exclude-standard`,\n * which is fast and honors `.gitignore`. Non-git directories fall back to a\n * bounded recursive walk that skips obvious build/dep dirs. Filtering is\n * case-insensitive substring on the workspace-relative path; results are\n * ranked by where the match landed (basename beats path) and capped at\n * `limit` (default 30, max 100).\n *\n * The full file list per workspace is cached in-process with a 10-second\n * TTL so a user mashing keys in the composer doesn't fork a `git` child\n * per keystroke. Same in-flight dedup pattern as workspace-stats.ts.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { readdir } from \"node:fs/promises\";\nimport { join, relative, sep } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport type { Hono } from \"hono\";\nimport type { SearchFilesResponse, SearchFileEntry } from \"@pi-pilot/shared\";\nimport { getWorkspace } from \"../storage/workspace-registry.ts\";\n\nconst exec = promisify(execFile);\n\nconst LIST_TTL_MS = 10_000;\n/** Soft cap on the per-workspace cache. Without it, every workspace a user\n * ever opened would stay resident even after they stopped using it (the TTL\n * only re-checks on next read of the *same* key). FIFO eviction is good\n * enough — the access pattern is \"the workspace I'm composing in right\n * now\", which gets re-inserted on every miss, naturally promoting itself. */\nconst MAX_CACHED_WORKSPACES = 16;\n/** Hard ceiling on files we ever consider, across both code paths. Keeps\n * search latency bounded on pathological repos and protects against a\n * bug in the walk. */\nconst MAX_FILES_TRACKED = 20_000;\nconst DEFAULT_LIMIT = 30;\nconst MAX_LIMIT = 100;\nconst WALK_MAX_DEPTH = 8;\nconst WALK_IGNORES = new Set([\n\t\".git\",\n\t\"node_modules\",\n\t\"dist\",\n\t\"build\",\n\t\".next\",\n\t\".turbo\",\n\t\".cache\",\n\t\".vite\",\n\t\"out\",\n\t\"coverage\",\n\t\"target\",\n\t\"venv\",\n\t\".venv\",\n\t\"__pycache__\",\n\t\".DS_Store\",\n]);\n\ninterface ListCacheEntry {\n\tfiles: string[];\n\texpiresAt: number;\n}\n\nconst listCache = new Map<string, ListCacheEntry>();\nconst inflight = new Map<string, Promise<ListCacheEntry>>();\n\nasync function getFileList(workspacePath: string): Promise<string[]> {\n\tconst now = Date.now();\n\tconst cached = listCache.get(workspacePath);\n\tif (cached && cached.expiresAt > now) return cached.files;\n\n\tconst pending = inflight.get(workspacePath);\n\tif (pending) return (await pending).files;\n\n\tconst probe = probeFileList(workspacePath)\n\t\t.then((files) => {\n\t\t\tconst entry: ListCacheEntry = {\n\t\t\t\tfiles,\n\t\t\t\texpiresAt: Date.now() + LIST_TTL_MS,\n\t\t\t};\n\t\t\t// Re-insert (delete + set) so this key moves to the back of the\n\t\t\t// Map's insertion order — the FIFO evictor below treats the head\n\t\t\t// as the oldest, so this keeps the \"actively used\" workspace warm.\n\t\t\tlistCache.delete(workspacePath);\n\t\t\tlistCache.set(workspacePath, entry);\n\t\t\twhile (listCache.size > MAX_CACHED_WORKSPACES) {\n\t\t\t\tconst oldest = listCache.keys().next().value;\n\t\t\t\tif (!oldest) break;\n\t\t\t\tlistCache.delete(oldest);\n\t\t\t}\n\t\t\treturn entry;\n\t\t})\n\t\t.finally(() => inflight.delete(workspacePath));\n\tinflight.set(workspacePath, probe);\n\treturn (await probe).files;\n}\n\nasync function probeFileList(workspacePath: string): Promise<string[]> {\n\t// Try git first — fastest, respects gitignore, single subprocess.\n\ttry {\n\t\tconst { stdout } = await exec(\n\t\t\t\"git\",\n\t\t\t[\"ls-files\", \"--cached\", \"--others\", \"--exclude-standard\"],\n\t\t\t{\n\t\t\t\tcwd: workspacePath,\n\t\t\t\ttimeout: 3000,\n\t\t\t\tmaxBuffer: 16 * 1024 * 1024,\n\t\t\t\tenv: { ...process.env, GIT_DIR: undefined, GIT_WORK_TREE: undefined },\n\t\t\t},\n\t\t);\n\t\tconst lines = stdout.split(\"\\n\");\n\t\tconst out: string[] = [];\n\t\tfor (const line of lines) {\n\t\t\tif (!line) continue;\n\t\t\tout.push(line);\n\t\t\tif (out.length >= MAX_FILES_TRACKED) break;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\t// Fall through to a bounded walk.\n\t}\n\n\tconst out: string[] = [];\n\tawait walkDir(workspacePath, workspacePath, 0, out);\n\treturn out;\n}\n\nasync function walkDir(\n\troot: string,\n\tdir: string,\n\tdepth: number,\n\tout: string[],\n): Promise<void> {\n\tif (out.length >= MAX_FILES_TRACKED) return;\n\tif (depth > WALK_MAX_DEPTH) return;\n\tlet dirents;\n\ttry {\n\t\tdirents = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const d of dirents) {\n\t\tif (out.length >= MAX_FILES_TRACKED) return;\n\t\t// Surface dotfiles like .env / .eslintrc / .gitignore — only skip the\n\t\t// dotfile *directories* listed in WALK_IGNORES (.git, .next, .cache,\n\t\t// .turbo, .vite, .venv) plus the .DS_Store noise file.\n\t\tif (WALK_IGNORES.has(d.name)) continue;\n\t\tconst abs = join(dir, d.name);\n\t\tif (d.isDirectory()) {\n\t\t\tawait walkDir(root, abs, depth + 1, out);\n\t\t} else if (d.isFile()) {\n\t\t\tout.push(relative(root, abs).split(sep).join(\"/\"));\n\t\t}\n\t}\n}\n\n/** Score a relPath against a lowercase query. Higher = better. Returns\n * null if the path doesn't match at all. */\nfunction scoreMatch(relPath: string, q: string): number | null {\n\tconst lower = relPath.toLowerCase();\n\tconst slash = lower.lastIndexOf(\"/\");\n\tconst base = slash >= 0 ? lower.slice(slash + 1) : lower;\n\n\t// Tier 1: basename prefix match — best signal.\n\tif (base.startsWith(q)) return 1000 - relPath.length;\n\t// Tier 2: basename substring.\n\tconst baseIdx = base.indexOf(q);\n\tif (baseIdx >= 0) return 800 - baseIdx - relPath.length * 0.01;\n\t// Tier 3: anywhere in the path.\n\tconst pathIdx = lower.indexOf(q);\n\tif (pathIdx >= 0) return 500 - pathIdx - relPath.length * 0.01;\n\treturn null;\n}\n\nasync function ensureWorkspaceExists(id: string): Promise<string | null> {\n\tconst ws = await getWorkspace(id);\n\treturn ws ? ws.path : null;\n}\n\nexport function mountFilesRoute(app: Hono): void {\n\tapp.get(\"/:id/files/search\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst workspacePath = await ensureWorkspaceExists(id);\n\t\tif (!workspacePath) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst qRaw = (c.req.query(\"q\") ?? \"\").trim();\n\t\tconst limitRaw = c.req.query(\"limit\");\n\t\tlet limit = limitRaw ? Number(limitRaw) : DEFAULT_LIMIT;\n\t\tif (!Number.isFinite(limit) || limit <= 0) limit = DEFAULT_LIMIT;\n\t\tif (limit > MAX_LIMIT) limit = MAX_LIMIT;\n\n\t\ttry {\n\t\t\tconst all = await getFileList(workspacePath);\n\t\t\tlet entries: SearchFileEntry[];\n\t\t\tlet truncated = false;\n\t\t\tif (!qRaw) {\n\t\t\t\t// Empty query: just return the head of the list so the popover\n\t\t\t\t// has something to show before the user types.\n\t\t\t\tconst slice = all.slice(0, limit);\n\t\t\t\tentries = slice.map((relPath) => ({\n\t\t\t\t\tpath: join(workspacePath, relPath),\n\t\t\t\t\trelPath,\n\t\t\t\t}));\n\t\t\t\ttruncated = all.length > limit;\n\t\t\t} else {\n\t\t\t\tconst q = qRaw.toLowerCase();\n\t\t\t\tconst scored: { relPath: string; score: number }[] = [];\n\t\t\t\tlet matchCount = 0;\n\t\t\t\tfor (const relPath of all) {\n\t\t\t\t\tconst score = scoreMatch(relPath, q);\n\t\t\t\t\tif (score === null) continue;\n\t\t\t\t\tmatchCount++;\n\t\t\t\t\tscored.push({ relPath, score });\n\t\t\t\t}\n\t\t\t\tscored.sort((a, b) => b.score - a.score);\n\t\t\t\tconst top = scored.slice(0, limit);\n\t\t\t\tentries = top.map((e) => ({\n\t\t\t\t\tpath: join(workspacePath, e.relPath),\n\t\t\t\t\trelPath: e.relPath,\n\t\t\t\t}));\n\t\t\t\ttruncated = matchCount > limit;\n\t\t\t}\n\t\t\tconst body: SearchFilesResponse = { workspacePath, entries, truncated };\n\t\t\treturn c.json(body);\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tconsole.error(`[api/files] search for ${id} failed:`, err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n}\n","/**\n * /api/workspaces/:id/resources route family.\n *\n * GET → snapshot of skills / prompts / extensions discovered\n * by the workspace's pi `ResourceLoader` (read-only\n * inventory \\u2014 no filesystem touched here).\n * POST .../reload → ask the loader to re-scan; useful after creating\n * files outside pi-pilot or after editing.\n * GET .../skill?path → pre-fill data for the skill editor (parsed\n * frontmatter + body).\n * POST .../skills → create a new skill (directory + SKILL.md) under the\n * chosen scope, then reload + return fresh inventory.\n * PUT .../skills → update an existing skill in place.\n * DELETE .../skills → remove a skill (and its parent directory if it\n * wasn't a bare .md at the skills root).\n * GET .../prompt?path → pre-fill data for the prompt editor.\n * POST .../prompts → create a new prompt template.\n * PUT .../prompts → update; renames the file if `name` changed.\n * DELETE .../prompts → unlink the file.\n *\n * All mutation endpoints respond with the post-reload `ResourcesResponse`\n * so the client doesn't need a follow-up GET.\n */\n\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { Context, Hono } from \"hono\";\nimport { getAgentDir } from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tBuiltinExtensionInfo,\n\tCreatePromptRequest,\n\tCreateSkillRequest,\n\tExtensionInfo,\n\tExtensionLoadError,\n\tPromptInfo,\n\tResourceFileResponse,\n\tResourceScope,\n\tResourceSource,\n\tResourcesResponse,\n\tSetBuiltinExtensionRequest,\n\tSkillInfo,\n\tUpdatePromptRequest,\n\tUpdateSkillRequest,\n} from \"@pi-pilot/shared\";\nimport { getWorkspace } from \"../storage/workspace-registry.ts\";\nimport {\n\tcreatePrompt,\n\tcreateSkill,\n\tdeletePrompt,\n\tdeleteSkill,\n\tHttpError,\n\treadPromptFile,\n\treadSkillFile,\n\tresolveResourceRoots,\n\ttype ResourceRoots,\n\tscopeFor,\n\tupdatePrompt,\n\tupdateSkill,\n} from \"../storage/resource-writer.ts\";\nimport { EXTENSIONS_ENABLED, workspaceManager } from \"../workspace-manager.ts\";\nimport { BUILTIN_EXTENSIONS } from \"../extensions/index.ts\";\nimport {\n\tgetDisabledBuiltins,\n\tsetBuiltinEnabled,\n} from \"../storage/builtin-extension-prefs.ts\";\n\ninterface PiSourceInfo {\n\tscope: \"user\" | \"project\" | \"temporary\";\n\tsource: string;\n\tpath: string;\n}\n\nfunction toResourceSource(info: PiSourceInfo): ResourceSource {\n\treturn {\n\t\tscope: info.scope,\n\t\tlabel: info.source,\n\t\tpath: info.path,\n\t};\n}\n\n/**\n * Scan the two extension discovery directories to detect extension files\n * that exist on disk but were not loaded (because extensions are disabled).\n * This lets the UI tell the user \"you have extensions installed but they\n * are disabled\" rather than leaving them to wonder why nothing works.\n *\n * Only meaningful when `EXTENSIONS_ENABLED === false`; callers should skip\n * the scan when the operator opted in via `PI_PILOT_ENABLE_EXTENSIONS=1`.\n */\nasync function scanExtensionDirs(workspaceCwd: string): Promise<string[]> {\n\tconst dirs = [join(getAgentDir(), \"extensions\"), join(workspaceCwd, \".pi\", \"extensions\")];\n\tconst found: string[] = [];\n\tfor (const dir of dirs) {\n\t\ttry {\n\t\t\tconst entries = await readdir(dir, { withFileTypes: true });\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (entry.isFile() && (entry.name.endsWith(\".ts\") || entry.name.endsWith(\".js\"))) {\n\t\t\t\t\tfound.push(join(dir, entry.name));\n\t\t\t\t} else if (entry.isDirectory()) {\n\t\t\t\t\t// Could be a package-style extension; list the directory.\n\t\t\t\t\tfound.push(join(dir, entry.name));\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Directory doesn't exist or isn't readable — that's fine.\n\t\t}\n\t}\n\treturn found;\n}\n\n/** Build a ResourcesResponse from the current loader state.\n *\n * `roots` is supplied so each skill/prompt's `managed` flag can be set:\n * resources whose filePath is under one of pi-pilot's writeable roots get\n * Edit/Delete affordances; resources that came from packages, settings\n * extras, ~/.agents/ paths, or CLI flags are read-only.\n *\n * `workspaceCwd` is the workspace's root directory, used to scan for\n * disabled extensions when extensions are off.\n */\nasync function snapshot(\n\tworkspaceId: string,\n\troots: ResourceRoots,\n\tworkspaceCwd: string,\n): Promise<ResourcesResponse> {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) throw new HttpError(500, \"runtime not initialized\");\n\tconst loader = runtime.services.resourceLoader;\n\n\tconst { skills } = loader.getSkills();\n\tconst { prompts } = loader.getPrompts();\n\tconst extResult = loader.getExtensions();\n\n\t// Pi resolves command-name collisions across extensions by appending\n\t// `:N` to later registrations (two extensions registering `plan` become\n\t// invocation names `plan` and `plan:1`). We surface those resolved\n\t// names so the composer's autocomplete picks them up and a user-typed\n\t// `/plan:1` actually invokes the right handler. The runner is the only\n\t// public source of truth for resolved names — the raw per-extension\n\t// `Map<string, RegisteredCommand>.keys()` doesn't know about\n\t// cross-extension conflicts.\n\t//\n\t// `extensionRunner` becomes available after `bindExtensions()` (called\n\t// in workspace-manager.build); the `?? []` fallback is defensive for\n\t// the brief window after runtime creation but before bind.\n\tconst resolvedExtCommandsByPath = new Map<string, string[]>();\n\tconst resolvedCommands = runtime.session.extensionRunner?.getRegisteredCommands() ?? [];\n\tfor (const cmd of resolvedCommands) {\n\t\tconst key = cmd.sourceInfo.path;\n\t\tlet list = resolvedExtCommandsByPath.get(key);\n\t\tif (!list) {\n\t\t\tlist = [];\n\t\t\tresolvedExtCommandsByPath.set(key, list);\n\t\t}\n\t\tlist.push(cmd.invocationName);\n\t}\n\n\tconst skillsOut: SkillInfo[] = skills.map((s) => ({\n\t\tname: s.name,\n\t\tdescription: s.description,\n\t\tfilePath: s.filePath,\n\t\tdisableModelInvocation: s.disableModelInvocation,\n\t\tsource: toResourceSource(s.sourceInfo),\n\t\tmanaged: scopeFor(s.filePath, roots) !== undefined,\n\t}));\n\n\tconst promptsOut: PromptInfo[] = prompts.map((p) => ({\n\t\tname: p.name,\n\t\tdescription: p.description,\n\t\targumentHint: p.argumentHint,\n\t\tfilePath: p.filePath,\n\t\tcontent: p.content,\n\t\tsource: toResourceSource(p.sourceInfo),\n\t\tmanaged: scopeFor(p.filePath, roots) !== undefined,\n\t}));\n\n\tconst extensionsOut: ExtensionInfo[] = extResult.extensions.map((e) => {\n\t\t// Match on `sourceInfo.path` because that's what ResolvedCommand's\n\t\t// sourceInfo carries; `e.path` may differ for package-style\n\t\t// extensions. Fall back to the raw command keys if the runner\n\t\t// hasn't populated yet (defensive — shouldn't happen post-bind).\n\t\tconst resolved = resolvedExtCommandsByPath.get(e.sourceInfo.path);\n\t\treturn {\n\t\t\tpath: e.path,\n\t\t\tresolvedPath: e.resolvedPath,\n\t\t\tsource: toResourceSource(e.sourceInfo),\n\t\t\ttools: [...e.tools.keys()],\n\t\t\tcommands: resolved ?? [...e.commands.keys()],\n\t\t\tflags: [...e.flags.keys()],\n\t\t\tshortcuts: [...e.shortcuts.keys()],\n\t\t};\n\t});\n\n\tconst extensionErrors: ExtensionLoadError[] = extResult.errors.map((err) => ({\n\t\tpath: err.path,\n\t\terror: err.error,\n\t}));\n\n\t// Only surface \"you have extensions on disk that we skipped\" when we\n\t// actually skipped them. When the operator opted in via the env flag,\n\t// any extensions found on disk are represented by `extensions` /\n\t// `extensionErrors` instead, and surfacing them a second time here\n\t// would double-count.\n\tconst disabledExtensions = EXTENSIONS_ENABLED ? [] : await scanExtensionDirs(workspaceCwd);\n\n\t// Builtins are listed from the static manifest + global on/off prefs, not\n\t// from the live runner — so the panel reflects a just-toggled state even in\n\t// other workspaces whose runtimes haven't reloaded yet. Tools/commands are\n\t// the manifest's declared registrations (stable; collisions among our own\n\t// builtins don't happen).\n\tconst disabledBuiltins = new Set(getDisabledBuiltins());\n\tconst builtinExtensions: BuiltinExtensionInfo[] = BUILTIN_EXTENSIONS.map((d) => ({\n\t\tid: d.id,\n\t\tname: d.name,\n\t\tdescription: d.description,\n\t\tenabled: !disabledBuiltins.has(d.id),\n\t\ttools: d.tools,\n\t\tcommands: d.commands,\n\t}));\n\n\treturn {\n\t\tskills: skillsOut,\n\t\tprompts: promptsOut,\n\t\textensionsEnabled: EXTENSIONS_ENABLED,\n\t\textensions: extensionsOut,\n\t\textensionErrors,\n\t\tdisabledExtensions,\n\t\tbuiltinExtensions,\n\t};\n}\n\nasync function rootsFor(\n\tworkspaceId: string,\n): Promise<{ roots: ResourceRoots; workspaceCwd: string }> {\n\tconst ws = await getWorkspace(workspaceId);\n\tif (!ws) throw new HttpError(404, \"workspace not found\");\n\tconst roots = resolveResourceRoots({ agentDir: getAgentDir(), workspaceCwd: ws.path });\n\treturn { roots, workspaceCwd: ws.path };\n}\n\n/** Map a thrown error into a Hono JSON response. */\nfunction respondError(c: Context, err: unknown) {\n\tif (err instanceof HttpError) {\n\t\treturn c.json({ ok: false, error: err.message }, err.status as 400 | 404 | 409 | 500);\n\t}\n\tconst message = err instanceof Error ? err.message : String(err);\n\tconsole.error(`[api/resources] unexpected error:`, err);\n\treturn c.json({ ok: false, error: message }, 500);\n}\n\n/** Reload the loader so subsequent reads see the change we just made. */\nasync function reload(workspaceId: string): Promise<void> {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) throw new HttpError(500, \"runtime not initialized\");\n\tawait runtime.services.resourceLoader.reload();\n}\n\nexport function mountResourcesRoute(app: Hono): void {\n\t// ---------- GET inventory ----------\n\tapp.get(\"/:id/resources\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- POST reload ----------\n\tapp.post(\"/:id/resources/reload\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tawait reload(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- GET skill body ----------\n\tapp.get(\"/:id/resources/skill\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tconst { roots } = await rootsFor(id);\n\t\t\tconst scope = scopeFor(filePath, roots);\n\t\t\tif (scope === undefined) {\n\t\t\t\treturn c.json({ ok: false, error: \"skill file is not under a managed root\" }, 400);\n\t\t\t}\n\t\t\tconst data = await readSkillFile(filePath, roots);\n\t\t\tconst body: ResourceFileResponse = {\n\t\t\t\tfilePath,\n\t\t\t\tscope,\n\t\t\t\tname: data.name,\n\t\t\t\tdescription: data.description,\n\t\t\t\tdisableModelInvocation: data.disableModelInvocation,\n\t\t\t\tbody: data.body,\n\t\t\t};\n\t\t\treturn c.json(body);\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- POST skills (create) ----------\n\tapp.post(\"/:id/resources/skills\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as CreateSkillRequest | null;\n\t\tif (!body || !isScope(body.scope) || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"scope, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait createSkill({\n\t\t\t\troots,\n\t\t\t\tscope: body.scope,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\tbody: body.body,\n\t\t\t\tdisableModelInvocation: body.disableModelInvocation,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- PUT skills (update) ----------\n\tapp.put(\"/:id/resources/skills\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as UpdateSkillRequest | null;\n\t\tif (!body || typeof body.filePath !== \"string\" || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"filePath, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait updateSkill({\n\t\t\t\troots,\n\t\t\t\tfilePath: body.filePath,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\tbody: body.body,\n\t\t\t\tdisableModelInvocation: body.disableModelInvocation,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- DELETE skills ----------\n\tapp.delete(\"/:id/resources/skills\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait deleteSkill(filePath, roots);\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- GET prompt body ----------\n\tapp.get(\"/:id/resources/prompt\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tconst { roots } = await rootsFor(id);\n\t\t\tconst scope = scopeFor(filePath, roots);\n\t\t\tif (scope === undefined) {\n\t\t\t\treturn c.json({ ok: false, error: \"prompt file is not under a managed root\" }, 400);\n\t\t\t}\n\t\t\tconst data = await readPromptFile(filePath, roots);\n\t\t\tconst body: ResourceFileResponse = {\n\t\t\t\tfilePath,\n\t\t\t\tscope,\n\t\t\t\tname: data.name,\n\t\t\t\tdescription: data.description,\n\t\t\t\targumentHint: data.argumentHint,\n\t\t\t\tbody: data.body,\n\t\t\t};\n\t\t\treturn c.json(body);\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- POST prompts (create) ----------\n\tapp.post(\"/:id/resources/prompts\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as CreatePromptRequest | null;\n\t\tif (!body || !isScope(body.scope) || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"scope, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait createPrompt({\n\t\t\t\troots,\n\t\t\t\tscope: body.scope,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\targumentHint: body.argumentHint,\n\t\t\t\tbody: body.body,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- PUT prompts (update) ----------\n\tapp.put(\"/:id/resources/prompts\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as UpdatePromptRequest | null;\n\t\tif (!body || typeof body.filePath !== \"string\" || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"filePath, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait updatePrompt({\n\t\t\t\troots,\n\t\t\t\tfilePath: body.filePath,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\targumentHint: body.argumentHint,\n\t\t\t\tbody: body.body,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- DELETE prompts ----------\n\tapp.delete(\"/:id/resources/prompts\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait deletePrompt(filePath, roots);\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- PUT builtin extension on/off ----------\n\tapp.put(\"/:id/resources/builtin-extensions\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as SetBuiltinExtensionRequest | null;\n\t\tif (!body || typeof body.id !== \"string\" || typeof body.enabled !== \"boolean\") {\n\t\t\treturn c.json({ ok: false, error: \"id and enabled are required\" }, 400);\n\t\t}\n\t\tif (!BUILTIN_EXTENSIONS.some((d) => d.id === body.id)) {\n\t\t\treturn c.json({ ok: false, error: `unknown builtin extension: ${body.id}` }, 400);\n\t\t}\n\t\ttry {\n\t\t\tconst runtime = await workspaceManager.getOrCreate(id);\n\t\t\t// Reloading rebuilds the tool registry; doing so mid-stream would\n\t\t\t// yank tools out from under an in-flight turn (and could strand a\n\t\t\t// pending ask_user). Make the user finish first.\n\t\t\tif (runtime.session.isStreaming) {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{ ok: false, error: \"Stop the current turn before changing extensions\" },\n\t\t\t\t\t409,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Prefs are global; persist once. We only reload THIS workspace's\n\t\t\t// session so the change is live where the user is acting. Other\n\t\t\t// loaded workspaces keep their current toolset until they next\n\t\t\t// reload / restart, but their panels already reflect the new state\n\t\t\t// (snapshot reads prefs, not the live runner).\n\t\t\tawait setBuiltinEnabled(body.id, body.enabled);\n\t\t\tawait runtime.session.reload();\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n}\n\nfunction isScope(value: unknown): value is ResourceScope {\n\treturn value === \"user\" || value === \"project\";\n}\n","/**\n * /api/fs/browse — directory listing for the web's <FsBrowser>.\n *\n * Only directories are returned (we never list files). Hidden entries are\n * excluded by default; pass ?showHidden=1 to include them.\n *\n * The `path` query parameter must be absolute. If omitted, defaults to the\n * user's home directory.\n */\n\nimport { readdir } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\nimport { Hono } from \"hono\";\nimport type { BrowseFsResponse, FsEntry } from \"@pi-pilot/shared\";\n\nexport const fsRoute = new Hono();\n\nfsRoute.get(\"/browse\", async (c) => {\n\tconst rawPath = c.req.query(\"path\");\n\tconst showHidden = c.req.query(\"showHidden\") === \"1\";\n\n\tconst target = rawPath && isAbsolute(rawPath) ? resolve(rawPath) : homedir();\n\tlet dirents;\n\ttry {\n\t\tdirents = await readdir(target, { withFileTypes: true });\n\t} catch (err) {\n\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\tconst msg = code === \"EACCES\" ? \"permission denied\" : code === \"ENOENT\" ? \"not found\" : \"read failed\";\n\t\treturn c.json({ ok: false, error: msg, path: target }, 400);\n\t}\n\n\tconst entries: FsEntry[] = dirents\n\t\t.filter((d) => d.isDirectory())\n\t\t.filter((d) => showHidden || !d.name.startsWith(\".\"))\n\t\t.map((d) => ({\n\t\t\tname: d.name,\n\t\t\tpath: join(target, d.name),\n\t\t\ttype: \"dir\" as const,\n\t\t}))\n\t\t.sort((a, b) => a.name.localeCompare(b.name));\n\n\tconst parent = (() => {\n\t\tconst p = dirname(target);\n\t\treturn p === target ? null : p;\n\t})();\n\n\tconst body: BrowseFsResponse = { path: target, parent, entries };\n\treturn c.json(body);\n});\n","/**\n * /api/model-configs routes.\n *\n * Reads and writes ~/.pi/agent/models.json. After any mutation the active\n * workspace runtimes have their modelRegistry refreshed so changes take\n * effect immediately without restarting the server.\n */\n\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { Hono } from \"hono\";\nimport {\n\tgetAgentDir,\n} from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tModelsConfig,\n\tModelsConfigResponse,\n\tSetModelsConfigRequest,\n\tUpsertProviderRequest,\n\tAddModelToProviderRequest,\n\tUpdateModelRequest,\n} from \"@pi-pilot/shared\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\n\nexport const modelConfigsRoute = new Hono();\n\n// ---------- helpers ----------\n\n/**\n * Serialize all read-modify-write operations on models.json. Although\n * Node.js is single-threaded, the async gaps between readFile and\n * writeFile allow interleaving of concurrent requests — without a lock\n * a later write could silently overwrite an earlier mutation.\n */\nlet writeLock = Promise.resolve();\nfunction withWriteLock<T>(fn: () => Promise<T>): Promise<T> {\n\tconst next = writeLock.then(fn, fn);\n\twriteLock = next.then(() => {}, () => {});\n\treturn next;\n}\n\nfunction modelsPath(): string {\n\treturn join(getAgentDir(), \"models.json\");\n}\n\nasync function readModelsJson(): Promise<ModelsConfig> {\n\ttry {\n\t\tconst raw = await readFile(modelsPath(), \"utf-8\");\n\t\treturn JSON.parse(raw) as ModelsConfig;\n\t} catch (err) {\n\t\tif ((err as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n\t\t\treturn { providers: {} };\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nasync function writeModelsJson(config: ModelsConfig): Promise<void> {\n\tconst p = modelsPath();\n\tawait mkdir(dirname(p), { recursive: true });\n\tawait writeFile(p, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\n/** Lightweight error for validation failures inside withWriteLock. */\nclass ValidationError extends Error {\n\tconstructor(message: string, public readonly status: number) {\n\t\tsuper(message);\n\t}\n}\n\nfunction refreshRegistry(workspaceId?: string): void {\n\tif (!workspaceId) return;\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (runtime) {\n\t\ttry {\n\t\t\truntime.session.modelRegistry.refresh();\n\t\t} catch (e) {\n\t\t\tconsole.error(`[model-configs] refresh registry for ${workspaceId} failed:`, e);\n\t\t}\n\t}\n}\n\n// ---------- routes ----------\n\n/** GET /api/model-configs */\nmodelConfigsRoute.get(\"/\", async (c) => {\n\ttry {\n\t\tconst config = await readModelsJson();\n\t\tconst body: ModelsConfigResponse = { config };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** PUT /api/model-configs — replace entire models.json */\nmodelConfigsRoute.put(\"/\", async (c) => {\n\tconst body = (await c.req.json()) as SetModelsConfigRequest;\n\tif (!body?.config?.providers) {\n\t\treturn c.json({ ok: false, error: \"config.providers is required\" }, 400);\n\t}\n\ttry {\n\t\tawait withWriteLock(async () => {\n\t\t\tawait writeModelsJson(body.config);\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config: body.config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** POST /api/model-configs/providers — add or update a provider */\nmodelConfigsRoute.post(\"/providers\", async (c) => {\n\tconst body = (await c.req.json()) as UpsertProviderRequest;\n\tif (!body?.name || !body?.provider) {\n\t\treturn c.json({ ok: false, error: \"name and provider are required\" }, 400);\n\t}\n\tif (!body.provider.baseUrl || !body.provider.api || !body.provider.apiKey) {\n\t\treturn c.json({ ok: false, error: \"provider must have baseUrl, api, and apiKey\" }, 400);\n\t}\n\tif (!Array.isArray(body.provider.models)) {\n\t\treturn c.json({ ok: false, error: \"provider.models must be an array\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tcfg.providers[body.name] = body.provider;\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** DELETE /api/model-configs/providers?name=... — delete a provider */\nmodelConfigsRoute.delete(\"/providers\", async (c) => {\n\tconst name = c.req.query(\"name\");\n\tif (!name) {\n\t\treturn c.json({ ok: false, error: \"name query param is required\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[name]) {\n\t\t\t\tthrow new ValidationError(`provider \"${name}\" not found`, 404);\n\t\t\t}\n\t\t\tdelete cfg.providers[name];\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** POST /api/model-configs/providers/:provider/models — add a model */\nmodelConfigsRoute.post(\"/providers/:provider/models\", async (c) => {\n\tconst provider = c.req.param(\"provider\");\n\tconst body = (await c.req.json()) as AddModelToProviderRequest;\n\tif (!body?.model?.id || !body?.model?.name) {\n\t\treturn c.json({ ok: false, error: \"model.id and model.name are required\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[provider]) {\n\t\t\t\tthrow new ValidationError(`provider \"${provider}\" not found`, 404);\n\t\t\t}\n\t\t\tconst existing = cfg.providers[provider].models.find(m => m.id === body.model.id);\n\t\t\tif (existing) {\n\t\t\t\tthrow new ValidationError(`model \"${body.model.id}\" already exists in provider \"${provider}\"`, 409);\n\t\t\t}\n\t\t\tcfg.providers[provider].models.push(body.model);\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404 | 409);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** PUT /api/model-configs/providers/:provider/models/:modelId — update a model */\nmodelConfigsRoute.put(\"/providers/:provider/models/:modelId\", async (c) => {\n\tconst provider = c.req.param(\"provider\");\n\tconst modelId = c.req.param(\"modelId\");\n\tconst body = (await c.req.json()) as UpdateModelRequest;\n\tif (!body?.model) {\n\t\treturn c.json({ ok: false, error: \"model is required\" }, 400);\n\t}\n\tif (body.model.id !== modelId) {\n\t\treturn c.json({ ok: false, error: \"model.id does not match URL parameter\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[provider]) {\n\t\t\t\tthrow new ValidationError(`provider \"${provider}\" not found`, 404);\n\t\t\t}\n\t\t\tconst idx = cfg.providers[provider].models.findIndex(m => m.id === modelId);\n\t\t\tif (idx === -1) {\n\t\t\t\tthrow new ValidationError(`model \"${modelId}\" not found in provider \"${provider}\"`, 404);\n\t\t\t}\n\t\t\tcfg.providers[provider].models[idx] = body.model;\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** DELETE /api/model-configs/providers/:provider/models/:modelId — delete a model */\nmodelConfigsRoute.delete(\"/providers/:provider/models/:modelId\", async (c) => {\n\tconst provider = c.req.param(\"provider\");\n\tconst modelId = c.req.param(\"modelId\");\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[provider]) {\n\t\t\t\tthrow new ValidationError(`provider \"${provider}\" not found`, 404);\n\t\t\t}\n\t\t\tconst idx = cfg.providers[provider].models.findIndex(m => m.id === modelId);\n\t\t\tif (idx === -1) {\n\t\t\t\tthrow new ValidationError(`model \"${modelId}\" not found in provider \"${provider}\"`, 404);\n\t\t\t}\n\t\t\tcfg.providers[provider].models.splice(idx, 1);\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n","/**\n * WebSocket hub.\n *\n * One connection ↔ at most one (workspace, session) subscription. The\n * client switches subscription with another `subscribe` message; we just\n * tear down the old listener and attach a new one.\n *\n * On disconnect, all per-connection state is released. The runtime in the\n * WorkspaceManager is NOT torn down (other connections or future ones\n * may reuse it).\n *\n * Subscribed connections are registered with the workspace manager so it\n * can fan server-initiated messages (extension load errors, future\n * workspace-level events) out to them. pi extensions themselves are not\n * supported — `ctx.ui.*` calls are no-ops, so no UI request/response\n * traffic flows over the WS.\n */\n\nimport { WebSocketServer, type WebSocket } from \"ws\";\nimport type { Server } from \"node:http\";\nimport type { AgentSessionRuntime } from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tPiAssistantTiming,\n\tPiContextUsage,\n\tWsClientMessage,\n\tWsServerMessage,\n} from \"@pi-pilot/shared\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport {\n\tinFlightAssistantSnapshot,\n\tinFlightToolCallsSnapshot,\n\ttranslatePiEvent,\n} from \"./bridge.ts\";\nimport { resolveAnswer } from \"../extensions/ask_user/registry.ts\";\n\ninterface ConnectionState {\n\tworkspaceId?: string;\n\tunsubscribeSession?: () => void;\n\tunsubscribeRebind?: () => void;\n}\n\n/**\n * Per-workspace session replacement lock. Serializes new_session, fork,\n * and switchSession calls so concurrent clicks or messages don't race\n * on the same runtime.\n */\nconst replacementLocks = new Map<string, Promise<void>>();\n\nfunction withReplacementLock(workspaceId: string, fn: () => Promise<void>): Promise<void> {\n\tconst prev = replacementLocks.get(workspaceId) ?? Promise.resolve();\n\tconst next = prev.then(fn, fn);\n\treplacementLocks.set(workspaceId, next);\n\t// Clean up reference when chain settles to avoid unbounded growth.\n\t// Use .then(cleanup, cleanup) so cleanup runs on rejection too and no\n\t// unhandled-rejection leaks from the derived promise.\n\tconst cleanup = () => {\n\t\tif (replacementLocks.get(workspaceId) === next) {\n\t\t\treplacementLocks.delete(workspaceId);\n\t\t}\n\t};\n\tnext.then(cleanup, cleanup);\n\treturn next;\n}\n\nexport function attachWsHub(httpServer: Server): WebSocketServer {\n\tconst wss = new WebSocketServer({ server: httpServer, path: \"/ws\" });\n\n\twss.on(\"connection\", (ws) => {\n\t\tconst state: ConnectionState = {};\n\n\t\tws.on(\"message\", async (raw) => {\n\t\t\tlet msg: WsClientMessage;\n\t\t\ttry {\n\t\t\t\tmsg = JSON.parse(raw.toString()) as WsClientMessage;\n\t\t\t} catch {\n\t\t\t\tsend(ws, { type: \"error\", message: \"invalid JSON\" });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait handle(ws, state, msg);\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\tsend(ws, { type: \"error\", message, command: msg.type });\n\t\t\t}\n\t\t});\n\n\t\tws.on(\"close\", () => {\n\t\t\tdetach(state, ws);\n\t\t});\n\t});\n\n\treturn wss;\n}\n\nasync function handle(ws: WebSocket, state: ConnectionState, msg: WsClientMessage): Promise<void> {\n\tswitch (msg.type) {\n\t\tcase \"subscribe\": {\n\t\t\tconst hadCurrentSubscription =\n\t\t\t\tstate.workspaceId === msg.workspaceId && !!state.unsubscribeSession;\n\n\t\t\t// Register this connection as a subscriber BEFORE building the\n\t\t\t// runtime. Extensions that fire `ctx.ui.notify(...)` from a\n\t\t\t// `session_start` handler emit it synchronously during\n\t\t\t// `getOrCreate`'s bindExtensions call — if no subscriber were\n\t\t\t// registered yet, the bridge would broadcast to an empty set and\n\t\t\t// the message would be lost.\n\t\t\tensureRebindListener(ws, state, msg.workspaceId);\n\t\t\tawait workspaceManager.getOrCreate(msg.workspaceId);\n\n\t\t\tlet switched = false;\n\t\t\tlet switchError: string | undefined;\n\t\t\tif (msg.sessionPath) {\n\t\t\t\tawait withReplacementLock(msg.workspaceId, async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tswitched = await workspaceManager.switchSession(msg.workspaceId, msg.sessionPath!);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tswitchError = err instanceof Error ? err.message : String(err);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (!switched && !hadCurrentSubscription) {\n\t\t\t\tbindCurrentSession(ws, state, msg.workspaceId);\n\t\t\t}\n\t\t\tif (switchError) {\n\t\t\t\tsend(ws, { type: \"error\", message: switchError, command: \"subscribe\" });\n\t\t\t}\n\t\t\tsend(ws, { type: \"ack\", command: \"subscribe\" });\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"prompt\": {\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"prompt\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Reject prompts while a session replacement is in progress.\n\t\t\tif (replacementLocks.has(wsId)) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"session switching in progress\", command: \"prompt\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\tif (!runtime) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"runtime gone\", command: \"prompt\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Fire-and-forget; success is observable as `event` messages\n\t\t\t// streaming back through the subscription, so no `ack` is sent.\n\t\t\tvoid runtime.session\n\t\t\t\t.prompt(msg.message, {\n\t\t\t\t\tstreamingBehavior: msg.streamingBehavior,\n\t\t\t\t})\n\t\t\t\t.catch((err: unknown) => {\n\t\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\t\tsend(ws, { type: \"error\", message, command: \"prompt\" });\n\t\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"abort\": {\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"abort\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (replacementLocks.has(wsId)) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"session switching in progress\", command: \"abort\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\tif (!runtime) return;\n\t\t\t// Success is observable as an `agent_end` event; no ack needed.\n\t\t\tawait runtime.session.abort();\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"new_session\": {\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"new_session\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait withReplacementLock(msg.workspaceId, async () => {\n\t\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\t\tif (!runtime) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"runtime gone\", command: \"new_session\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (runtime.session.isStreaming) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"cannot create session while streaming\", command: \"new_session\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst result = await runtime.newSession();\n\t\t\t\tif (result.cancelled) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"new session cancelled\", command: \"new_session\" });\n\t\t\t\t}\n\t\t\t\t// Success → rebind fires automatically → client gets `subscribed`.\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"fork\": {\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"fork\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait withReplacementLock(msg.workspaceId, async () => {\n\t\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\t\tif (!runtime) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"runtime gone\", command: \"fork\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (runtime.session.isStreaming) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"cannot fork while streaming\", command: \"fork\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst result = await runtime.fork(msg.entryId);\n\t\t\t\tif (result.cancelled) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"fork cancelled\", command: \"fork\" });\n\t\t\t\t}\n\t\t\t\t// Success → rebind fires automatically → client gets `subscribed`.\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"answer_question\": {\n\t\t\t// Stale answers (toolCallId not in registry, or sessionFile\n\t\t\t// mismatch) are silently ignored — see registry.ts header\n\t\t\t// \"Stale-answer policy\". The losing tab's UI catches up via\n\t\t\t// the normal `tool_execution_end` broadcast that the winning\n\t\t\t// tab's answer triggers.\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"answer_question\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Defense-in-depth: skip during session replacement. Today\n\t\t\t// the registry's sessionFile guard already rejects answers\n\t\t\t// destined for a defunct session, so this is currently a\n\t\t\t// belt-and-suspenders match with prompt/abort. Keeping the\n\t\t\t// shape consistent across handlers means future changes to\n\t\t\t// how `sessionFile` is exposed during a transition (e.g.\n\t\t\t// transiently `null`) won't silently let an answer through.\n\t\t\tif (replacementLocks.has(wsId)) return;\n\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\tif (!runtime) return;\n\t\t\tconst activeFile = runtime.session.sessionFile ?? null;\n\t\t\tresolveAnswer(msg.toolCallId, msg.answer, activeFile);\n\t\t\treturn;\n\t\t}\n\n\t\tdefault: {\n\t\t\t// Exhaustiveness check\n\t\t\tconst _: never = msg;\n\t\t\tvoid _;\n\t\t\tsend(ws, { type: \"error\", message: \"unknown command\" });\n\t\t}\n\t}\n}\n\nfunction ensureRebindListener(ws: WebSocket, state: ConnectionState, workspaceId: string): void {\n\tif (state.workspaceId === workspaceId && state.unsubscribeRebind) return;\n\n\tdetach(state, ws);\n\tstate.workspaceId = workspaceId;\n\tworkspaceManager.addSubscriber(workspaceId, ws);\n\tstate.unsubscribeRebind = workspaceManager.onSessionReplaced(workspaceId, () => {\n\t\tif (state.workspaceId !== workspaceId) return;\n\t\tbindCurrentSession(ws, state, workspaceId);\n\t});\n}\n\nfunction bindCurrentSession(ws: WebSocket, state: ConnectionState, workspaceId: string): void {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) {\n\t\tsend(ws, { type: \"error\", message: \"runtime gone\", command: \"subscribe\" });\n\t\treturn;\n\t}\n\n\tstate.unsubscribeSession?.();\n\tconst session = runtime.session;\n\tconst sessionPath = session.sessionFile ?? null;\n\n\t// Per-binding timing state. Lives in the subscribe closure so it\n\t// auto-resets on session replacement (each new bindCurrentSession\n\t// allocates a fresh closure). Tracks the wall-clock anchors needed to\n\t// derive `PiAssistantTiming` once the assistant turn completes.\n\tlet assistantStartAt: number | undefined;\n\tlet assistantFirstTokenAt: number | undefined;\n\n\tstate.unsubscribeSession = session.subscribe((ev) => {\n\t\tconst payload = translatePiEvent(ev);\n\t\tif (!payload) return;\n\n\t\t// Capture timing anchors *before* forwarding the event so the\n\t\t// firstToken stamp is taken at the moment the delta arrives at\n\t\t// the server, not at whatever later moment the listener runs.\n\t\tif (payload.kind === \"message_start\" && payload.role === \"assistant\") {\n\t\t\tassistantStartAt = performance.now();\n\t\t\tassistantFirstTokenAt = undefined;\n\t\t} else if (\n\t\t\tpayload.kind === \"message_update\" &&\n\t\t\tpayload.delta.kind === \"text\" &&\n\t\t\tassistantStartAt !== undefined &&\n\t\t\tassistantFirstTokenAt === undefined\n\t\t) {\n\t\t\tassistantFirstTokenAt = performance.now();\n\t\t}\n\n\t\tsend(ws, {\n\t\t\ttype: \"event\",\n\t\t\tworkspaceId,\n\t\t\tsessionPath,\n\t\t\tpayload,\n\t\t});\n\n\t\t// Emit timing telemetry once the assistant message closes. Skipped\n\t\t// if `message_start` wasn't observed (defensive — should not happen\n\t\t// in a healthy event stream, but a hot-reconnect mid-turn could).\n\t\tif (\n\t\t\tpayload.kind === \"message_end\" &&\n\t\t\tpayload.role === \"assistant\" &&\n\t\t\tassistantStartAt !== undefined\n\t\t) {\n\t\t\tconst now = performance.now();\n\t\t\tconst timing: PiAssistantTiming = {\n\t\t\t\tkind: \"assistant_timing\",\n\t\t\t\tfirstTokenMs:\n\t\t\t\t\tassistantFirstTokenAt !== undefined\n\t\t\t\t\t\t? Math.round(assistantFirstTokenAt - assistantStartAt)\n\t\t\t\t\t\t: null,\n\t\t\t\ttotalMs: Math.round(now - assistantStartAt),\n\t\t\t};\n\t\t\tsend(ws, {\n\t\t\t\ttype: \"event\",\n\t\t\t\tworkspaceId,\n\t\t\t\tsessionPath,\n\t\t\t\tpayload: timing,\n\t\t\t});\n\t\t\tassistantStartAt = undefined;\n\t\t\tassistantFirstTokenAt = undefined;\n\t\t}\n\n\t\t// Context usage shifts on turn completion (new assistant usage),\n\t\t// on compaction end (token count just reset), and on model swap\n\t\t// (context window may have changed). Sample after the event so the\n\t\t// client never sees a stale window. `getContextUsage()` returns\n\t\t// undefined when there's no model or no contextWindow — in that\n\t\t// case we don't bother sending; the existing snapshot stays.\n\t\tif (\n\t\t\tpayload.kind === \"agent_end\" ||\n\t\t\tpayload.kind === \"compaction_end\" ||\n\t\t\tpayload.kind === \"session_info_changed\" ||\n\t\t\tpayload.kind === \"thinking_level_changed\"\n\t\t) {\n\t\t\tsendContextUsage(ws, runtime, workspaceId, sessionPath);\n\t\t}\n\t});\n\n\tsend(ws, {\n\t\ttype: \"subscribed\",\n\t\tworkspaceId,\n\t\tsessionPath,\n\t\tsessionId: session.sessionId,\n\t});\n\n\t// If the agent is mid-stream when we (re)bind — typical case: user\n\t// switched workspaces during streaming and came back — replay the\n\t// in-flight assistant turn so the client has a streaming item for\n\t// subsequent live deltas to attach to. bindCurrentSession is\n\t// synchronous and JS is single-threaded, so no pi event can land\n\t// between the snapshot read and the subscribe above; future deltas\n\t// flow naturally through the cb. See bridge.ts/inFlightAssistantSnapshot.\n\tconst inFlight = inFlightAssistantSnapshot(runtime.session.state.streamingMessage);\n\tif (inFlight) {\n\t\tfor (const payload of inFlight) {\n\t\t\tsend(ws, {\n\t\t\t\ttype: \"event\",\n\t\t\t\tworkspaceId,\n\t\t\t\tsessionPath,\n\t\t\t\tpayload,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Re-emit `tool_execution_start` for any pending `ask_user` calls so\n\t// a (re)subscribing tab gets the interactive question card back —\n\t// the original tool_execution_start fired before this connection\n\t// existed, and REST /history only carries finalized toolResults.\n\t// See bridge.ts/inFlightToolCallsSnapshot.\n\tfor (const payload of inFlightToolCallsSnapshot(sessionPath)) {\n\t\tsend(ws, {\n\t\t\ttype: \"event\",\n\t\t\tworkspaceId,\n\t\t\tsessionPath,\n\t\t\tpayload,\n\t\t});\n\t}\n\n\t// Prime the client with a usage snapshot so the indicator isn't blank\n\t// until the next agent_end.\n\tsendContextUsage(ws, runtime, workspaceId, sessionPath);\n}\n\nfunction sendContextUsage(\n\tws: WebSocket,\n\truntime: AgentSessionRuntime,\n\tworkspaceId: string,\n\tsessionPath: string | null,\n): void {\n\tconst usage = runtime.session.getContextUsage();\n\tif (!usage) return;\n\tconst payload: PiContextUsage = {\n\t\tkind: \"context_usage\",\n\t\ttokens: usage.tokens,\n\t\tcontextWindow: usage.contextWindow,\n\t\tpercent: usage.percent,\n\t};\n\tsend(ws, {\n\t\ttype: \"event\",\n\t\tworkspaceId,\n\t\tsessionPath,\n\t\tpayload,\n\t});\n}\n\nfunction detach(state: ConnectionState, ws?: WebSocket): void {\n\tstate.unsubscribeSession?.();\n\tstate.unsubscribeSession = undefined;\n\tstate.unsubscribeRebind?.();\n\tstate.unsubscribeRebind = undefined;\n\tif (state.workspaceId && ws) {\n\t\tworkspaceManager.removeSubscriber(state.workspaceId, ws);\n\t}\n\tstate.workspaceId = undefined;\n}\n\nfunction send(ws: WebSocket, msg: WsServerMessage): void {\n\tif (ws.readyState !== ws.OPEN) return;\n\tws.send(JSON.stringify(msg));\n}\n"],"mappings":"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","names":["readFile","dirname","join","resolve","sep","Hono","stat","basename","isAbsolute","resolve","join","mkdir","readFile","writeFile","dirname","join","join","readFile","mkdir","dirname","writeFile","cache","pending","unlink","isAbsolute","resolve","Type","resolve","text","details","mkdir","readFile","writeFile","dirname","join","join","cache","readFile","save","mkdir","dirname","writeFile","pending","inflight","resolve","extractUserText","isAbsolute","unlink","app","exists","execFile","join","sep","promisify","exec","promisify","execFile","inflight","pending","out","join","sep","app","readdir","join","getAgentDir","join","getAgentDir","readdir","app","isAbsolute","resolve","stat","basename","readdir","homedir","dirname","isAbsolute","join","resolve","Hono","readFile","writeFile","mkdir","dirname","join","Hono","getAgentDir","Hono","join","getAgentDir","readFile","config","mkdir","dirname","writeFile","Hono","dirname","resolve","join","sep","readFile"]}
|