@diologue/local-agent 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +201 -72
- package/dist/cli.mjs.map +4 -4
- package/package.json +1 -1
package/dist/cli.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/adapters/engine-locator-npm.ts", "../engine-release.json", "../src/lib/engine-bundle.ts", "../src/adapters/engine-locator-local.ts", "../src/cli.ts", "../src/config.ts", "../src/server.ts", "../src/auth.ts", "../../shared/coding-agent-types.ts", "../src/cors.ts", "../src/state.ts", "../src/
|
|
4
|
-
"sourcesContent": ["// NpmSdkLocator \u2014 current production engine path.\n//\n// Wraps @opencode-ai/sdk (the npm package), which itself spawns the\n// `opencode` binary the user has installed locally. This is the DEFAULT\n// locator while V4 (bundled engine) is in flight.\n//\n// Constructor accepts an optional `sdkLoader` so tests can inject a stub\n// without having to actually install opencode.\n\nimport { spawn } from \"node:child_process\";\n\nimport type {\n EngineHandle,\n EngineLocator,\n EngineStartOptions,\n OpencodeClient,\n OpencodeConfig,\n} from \"./engine-locator\";\n\ntype OpencodeServerHandle = {\n url: string;\n close(): void;\n};\n\ninterface SdkModule {\n createOpencodeServer(options: {\n hostname?: string;\n port?: number;\n signal?: AbortSignal;\n config?: OpencodeConfig;\n }): Promise<OpencodeServerHandle>;\n createOpencodeClient(config: {\n baseUrl: string;\n directory?: string;\n }): OpencodeClient;\n}\n\nexport interface NpmSdkLocatorOptions {\n /** Dynamic import handle for @opencode-ai/sdk. Pass-through for tests;\n * defaults to importing the real module. */\n sdkLoader?: () => Promise<SdkModule>;\n /** Override for the binary presence check. Tests stub this so we\n * don't depend on the host's opencode install. */\n checkBinary?: () => Promise<boolean>;\n}\n\nconst defaultBinaryCheck = async (): Promise<boolean> =>\n new Promise<boolean>((resolve) => {\n const child = spawn(\"opencode\", [\"--version\"], { stdio: \"ignore\" });\n child.on(\"error\", () => resolve(false));\n child.on(\"exit\", (code) => resolve(code === 0));\n });\n\nconst defaultSdkLoader = (): Promise<SdkModule> =>\n import(\"@opencode-ai/sdk\") as unknown as Promise<SdkModule>;\n\nexport class NpmSdkLocator implements EngineLocator {\n readonly name = \"npm\";\n\n constructor(private readonly options: NpmSdkLocatorOptions = {}) {}\n\n async start(options: EngineStartOptions): Promise<EngineHandle> {\n const check = this.options.checkBinary ?? defaultBinaryCheck;\n const ok = await check();\n if (!ok) {\n throw new Error(\n \"opencode binary not found on PATH. Install via \" +\n \"`curl -fsSL https://opencode.ai/install | bash` and ensure \" +\n \"`opencode --version` works in this shell. \" +\n \"(Or set LOCAL_AGENT_ENGINE=local once V4 ships.)\",\n );\n }\n\n const loadSdk = this.options.sdkLoader ?? defaultSdkLoader;\n const sdk = await loadSdk();\n const server = await sdk.createOpencodeServer({\n hostname: \"127.0.0.1\",\n port: 0,\n signal: options.signal,\n config: options.config,\n });\n const client = sdk.createOpencodeClient({ baseUrl: server.url });\n return {\n url: server.url,\n client,\n close: () => {\n server.close();\n },\n };\n }\n}\n", "{\n \"_comment\": \"Single source of truth for the engine bundle the helper expects. Both scripts/build-engine-bundle.ts and local-agent/scripts/postinstall.ts read this. Bumping `version` here is what triggers a new release.\",\n \"version\": \"0.1.0\",\n \"releaseTag\": \"engine-v0.1.0\",\n \"downloadBase\": \"https://github.com/Deplova-Ltd/AI-Personas-and-Multi-LLM/releases/download\",\n \"targets\": [\n { \"platform\": \"darwin\", \"arch\": \"arm64\", \"bunTarget\": \"bun-darwin-arm64\", \"ext\": \"\" },\n { \"platform\": \"darwin\", \"arch\": \"x64\", \"bunTarget\": \"bun-darwin-x64\", \"ext\": \"\" },\n { \"platform\": \"linux\", \"arch\": \"x64\", \"bunTarget\": \"bun-linux-x64\", \"ext\": \"\" },\n { \"platform\": \"linux\", \"arch\": \"arm64\", \"bunTarget\": \"bun-linux-arm64\", \"ext\": \"\" },\n { \"platform\": \"win32\", \"arch\": \"x64\", \"bunTarget\": \"bun-windows-x64\", \"ext\": \".exe\" }\n ]\n}\n", "// Engine-binary discovery \u2014 finds the opencode engine binary the helper\n// spawns in `serve` mode.\n//\n// Primary source: the `opencode-ai` npm package, which we declare as a\n// dependency. Its install lands a platform-native binary at\n// node_modules/opencode-ai/bin/opencode.exe (via platform-specific\n// optionalDependencies \u2014 no compile, no postinstall download on our\n// side). This is the \"zero-setup install\" path: `npm install\n// @diologue/local-agent` brings the engine automatically.\n//\n// Discovery order (first hit wins):\n// 1. LOCAL_AGENT_ENGINE_BUNDLE env var \u2014 explicit override (tests + dev)\n// 2. opencode-ai npm binary \u2014 resolved from node_modules (the default)\n// 3. <local-agent>/dist/engine/<releaseTag>/diologue-engine-\u2026 \u2014 legacy\n// self-compiled bundle (V4/V5; only present if someone built one)\n// 4. <repo>/build/diologue-engine-bundles/diologue-engine-\u2026 \u2014 legacy\n// local build\n//\n// Returning null means \"no engine on this machine\"; the caller decides\n// whether to fall back to another path or surface an error.\n\nimport { access, constants } from \"node:fs/promises\";\nimport { readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport engineReleaseRaw from \"../../engine-release.json\" with { type: \"json\" };\n\ninterface EngineRelease {\n version: string;\n releaseTag: string;\n downloadBase: string;\n targets: Array<{\n platform: NodeJS.Platform;\n arch: NodeJS.Architecture;\n bunTarget: string;\n ext: string;\n }>;\n}\n\nexport const engineRelease = engineReleaseRaw as EngineRelease;\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// local-agent/src/lib \u2192 local-agent root is two levels up.\nconst LOCAL_AGENT_ROOT = path.resolve(__dirname, \"../..\");\n// local-agent \u2192 repo root is one level up.\nconst REPO_ROOT = path.resolve(LOCAL_AGENT_ROOT, \"..\");\n\nexport const BUNDLE_DIR_INSTALLED = path.join(\n LOCAL_AGENT_ROOT,\n \"dist/engine\",\n engineRelease.releaseTag,\n);\n\nexport const BUNDLE_DIR_LOCAL_BUILD = path.join(\n REPO_ROOT,\n \"build/diologue-engine-bundles\",\n);\n\nexport const ENGINE_BUNDLE_ENV = \"LOCAL_AGENT_ENGINE_BUNDLE\" as const;\n\nexport interface ResolvedBundle {\n /** Absolute path to the executable. */\n path: string;\n /** Where it came from \u2014 useful for logging + tests. */\n source: \"env\" | \"opencode-ai\" | \"installed\" | \"local-build\";\n}\n\nconst exists = async (p: string): Promise<boolean> => {\n try {\n await access(p, constants.X_OK);\n return true;\n } catch {\n return false;\n }\n};\n\n/** Resolve the `opencode-ai` npm package's bundled binary.\n *\n * opencode-ai declares its binary in package.json `bin` and ships the\n * platform-native executable via optionalDependencies, so after\n * `npm install` it lives at <pkgdir>/bin/<binfile>. We resolve the\n * package via Node's module resolution (handles hoisting) rather than\n * hardcoding a node_modules path.\n *\n * Returns the absolute binary path, or null if opencode-ai isn't\n * installed / its binary didn't land (e.g. unsupported platform). */\nexport const resolveOpencodeAiBinary = (): string | null => {\n try {\n const require = createRequire(import.meta.url);\n const pkgJsonPath = require.resolve(\"opencode-ai/package.json\");\n const pkgDir = path.dirname(pkgJsonPath);\n const pkg = JSON.parse(readFileSync(pkgJsonPath, \"utf-8\")) as {\n bin?: string | Record<string, string>;\n };\n const binRel =\n typeof pkg.bin === \"string\" ? pkg.bin : pkg.bin?.opencode;\n if (!binRel) return null;\n return path.join(pkgDir, binRel);\n } catch {\n // Not resolvable (not installed, or no binary for this platform).\n return null;\n }\n};\n\n/** Build the canonical bundle filename for a given target. The\n * postinstall script uses this name to write the artifact; the build\n * script writes the same name. */\nexport const bundleFilename = (\n platform: NodeJS.Platform = process.platform,\n arch: NodeJS.Architecture = process.arch,\n): string => {\n const target = engineRelease.targets.find(\n (t) => t.platform === platform && t.arch === arch,\n );\n const ext = target?.ext ?? \"\";\n return `diologue-engine-${platform}-${arch}${ext}`;\n};\n\n/** Return the bundle target for this host, or undefined if unsupported. */\nexport const supportedTargetForHost = ():\n | EngineRelease[\"targets\"][number]\n | undefined => {\n return engineRelease.targets.find(\n (t) => t.platform === process.platform && t.arch === process.arch,\n );\n};\n\n/** Search for an engine bundle on the local machine. Returns null\n * when nothing is found. */\nexport const findEngineBundle = async (): Promise<ResolvedBundle | null> => {\n const fromEnv = process.env[ENGINE_BUNDLE_ENV];\n if (fromEnv) {\n if (await exists(fromEnv)) {\n return { path: fromEnv, source: \"env\" };\n }\n // env var set but file missing \u2014 fall through so the caller still\n // gets a useful \"not found\" rather than a half-broken path.\n }\n // Primary path: the opencode-ai npm binary (zero-setup install).\n const opencodeAi = resolveOpencodeAiBinary();\n if (opencodeAi && (await exists(opencodeAi))) {\n return { path: opencodeAi, source: \"opencode-ai\" };\n }\n // Legacy self-compiled bundle paths (V4/V5). Kept as fallbacks for\n // anyone who built their own; not produced by a normal install.\n const filename = bundleFilename();\n const installed = path.join(BUNDLE_DIR_INSTALLED, filename);\n if (await exists(installed)) {\n return { path: installed, source: \"installed\" };\n }\n const localBuild = path.join(BUNDLE_DIR_LOCAL_BUILD, filename);\n if (await exists(localBuild)) {\n return { path: localBuild, source: \"local-build\" };\n }\n return null;\n};\n\n/** Compose the download URL for the engine bundle on this host. */\nexport const downloadUrlForHost = (): string | null => {\n const target = supportedTargetForHost();\n if (!target) return null;\n return `${engineRelease.downloadBase}/${engineRelease.releaseTag}/${bundleFilename()}`;\n};\n", "// LocalEngineLocator \u2014 production engine path (V4 + V5).\n//\n// What this does:\n//\n// 1. BUNDLE DISCOVERY (V5) \u2014 looks for a standalone diologue-engine\n// binary that postinstall fetched, the build-engine script\n// produced locally, or LOCAL_AGENT_ENGINE_BUNDLE points at. If\n// found, we spawn it directly \u2014 no runtime dependency on the\n// end user's machine.\n//\n// 2. PREFLIGHT FALLBACK (V4) \u2014 if no bundle is present, we fall\n// back to spawning `bun` against build/diologue-engine/ (the V2\n// rebrand output). This is the dev path; production users get\n// the bundle via postinstall.\n//\n// 3. SUBPROCESS SPAWN \u2014 exec the chosen engine binary (bundle or\n// `bun <entry>`), passing `serve --port 0 --hostname 127.0.0.1`.\n// Parse stdout for the \"listening on http://...\" banner, apply\n// a startup timeout, hand back a handle that kills the\n// subprocess on close.\n//\n// 4. CLIENT WIRING \u2014 reuse @opencode-ai/sdk's createOpencodeClient\n// against the spawned engine's base URL. Same wire-format\n// implementation as the npm path, no reimplementation. Tests\n// inject a clientFactory to bypass the SDK.\n\nimport { access, constants } from \"node:fs/promises\";\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type {\n EngineHandle,\n EngineLocator,\n EngineStartOptions,\n OpencodeClient,\n OpencodeConfig,\n} from \"./engine-locator\";\nimport { findEngineBundle, type ResolvedBundle } from \"../lib/engine-bundle\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// local-agent/src/adapters \u2192 repo root is four levels up.\nconst REPO_ROOT = path.resolve(__dirname, \"../../..\");\n\nconst DEFAULT_ENGINE_DIR = path.join(REPO_ROOT, \"build/diologue-engine\");\nconst ENTRY_REL = \"packages/opencode/src/index.ts\";\nconst DEFAULT_STARTUP_TIMEOUT_MS = 30_000;\n\n/** Matches the engine's \"listening on http(s)://host:port\" banner. The\n * rebrand doesn't touch this string (it lives in serve.ts; out of V2\n * scope), so it still says \"opencode server listening on \u2026\" in v1\n * output. We accept either prefix to insulate against later string\n * edits in V2.x. */\nconst LISTENING_REGEX = /listening on (https?:\\/\\/[^\\s]+)/i;\n\nexport interface LocalEngineLocatorOptions {\n /** Directory containing the rebranded engine tree (the fallback\n * source path when no bundle is installed). Defaults to\n * <repo>/build/diologue-engine. */\n enginePath?: string;\n /** Runtime executable for the source-fallback path. Defaults to\n * \"bun\" because the vendored source uses bun-specific imports. */\n runtime?: string;\n /** Override the runtime-availability probe. Tests stub this. */\n checkRuntime?: (runtime: string) => Promise<boolean>;\n /** Override the spawn command + arguments. Tests use this to point\n * at a fake-engine script. Production code leaves this unset. */\n engineCommand?: { command: string; args: string[] };\n /** How long to wait for the listening URL on stdout before killing\n * the subprocess and throwing. Defaults to 30s. */\n startupTimeoutMs?: number;\n /** Factory that produces an OpencodeClient bound to a URL. Defaults\n * to @opencode-ai/sdk's createOpencodeClient. Tests inject a\n * hand-rolled stub so we don't depend on the SDK in unit tests. */\n clientFactory?: (baseUrl: string) => Promise<OpencodeClient>;\n /** Override the bundle discovery. Tests use this. Production code\n * leaves it unset and falls through to lib/engine-bundle.ts. */\n resolveBundle?: () => Promise<ResolvedBundle | null>;\n /** Force the source-fallback path even if a bundle is present. Used\n * by dev workflows that want to test source-tree changes directly. */\n forceSource?: boolean;\n}\n\nconst defaultRuntimeCheck = async (runtime: string): Promise<boolean> =>\n new Promise<boolean>((resolve) => {\n const child = spawn(runtime, [\"--version\"], { stdio: \"ignore\" });\n child.on(\"error\", () => resolve(false));\n child.on(\"exit\", (code) => resolve(code === 0));\n });\n\nconst exists = async (p: string): Promise<boolean> => {\n try {\n await access(p, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n\nconst parseExistingInlineConfig = (\n existing: string | undefined,\n): Record<string, unknown> => {\n if (!existing) return {};\n try {\n const parsed = JSON.parse(existing) as unknown;\n return isRecord(parsed) ? parsed : {};\n } catch {\n // If the caller provided non-JSON config content, don't pass it through: the\n // helper must inject a valid broker config or the engine cannot call back\n // through /llm-shim and no coding work will happen.\n return {};\n }\n};\n\nconst mergeInlineConfig = (\n existing: string | undefined,\n injected: OpencodeConfig | undefined,\n): string | undefined => {\n if (!injected) return existing;\n const base = parseExistingInlineConfig(existing);\n const baseProvider = isRecord(base.provider) ? base.provider : {};\n return JSON.stringify({\n ...base,\n ...injected,\n provider: {\n ...baseProvider,\n ...(injected.provider ?? {}),\n },\n });\n};\n\nconst buildEngineEnv = (\n config: OpencodeConfig | undefined,\n): NodeJS.ProcessEnv => {\n const env = { ...process.env };\n const content = mergeInlineConfig(env.OPENCODE_CONFIG_CONTENT, config);\n if (content) {\n env.OPENCODE_CONFIG_CONTENT = content;\n }\n return env;\n};\n\nconst defaultClientFactory = async (\n baseUrl: string,\n): Promise<OpencodeClient> => {\n // We still consume the SDK's HTTP client even on the local path so\n // we have a single, well-tested wire-format implementation. Cutting\n // this dependency entirely is a later pass.\n const sdk = (await import(\"@opencode-ai/sdk\")) as unknown as {\n createOpencodeClient: (cfg: { baseUrl: string }) => OpencodeClient;\n };\n return sdk.createOpencodeClient({ baseUrl });\n};\n\nexport class LocalEngineLocator implements EngineLocator {\n readonly name = \"local\";\n readonly enginePath: string;\n readonly runtime: string;\n readonly startupTimeoutMs: number;\n\n constructor(private readonly options: LocalEngineLocatorOptions = {}) {\n this.enginePath = options.enginePath ?? DEFAULT_ENGINE_DIR;\n this.runtime = options.runtime ?? \"bun\";\n this.startupTimeoutMs =\n options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;\n }\n\n /** Decide which engine path we're going to use. Returns the resolved\n * bundle when we'll spawn that directly, or null when we'll fall\n * back to the source-tree path. Test seam. */\n async resolveBundle(): Promise<ResolvedBundle | null> {\n if (this.options.forceSource) return null;\n if (this.options.resolveBundle) return this.options.resolveBundle();\n return findEngineBundle();\n }\n\n /** Run the preflight checks for the path we're about to use. Returns\n * null on success, or a human-readable error string if something's\n * wrong. Exposed so start() can reuse it and tests can assert it\n * directly. */\n async preflight(): Promise<string | null> {\n const bundle = await this.resolveBundle();\n if (bundle) {\n // Bundle path: the only requirement is that the file is\n // present and executable. findEngineBundle() already checked\n // X_OK, so nothing else to do.\n return null;\n }\n // Source-tree fallback path (dev / pre-V5 setups).\n if (!(await exists(this.enginePath))) {\n return (\n `[engine-locator/local] No engine bundle found and no source ` +\n `tree at ${this.enginePath}. Either:\\n` +\n ` - Reinstall the package so postinstall fetches a bundle, or\\n` +\n ` - Run \\`npm run rebrand-engine\\` to populate the source tree.`\n );\n }\n const entry = path.join(this.enginePath, ENTRY_REL);\n if (!(await exists(entry))) {\n return (\n `[engine-locator/local] Engine entry not found at ${entry}. ` +\n `The rebrand may have produced a partial tree \u2014 re-run ` +\n `\\`npm run rebrand-engine\\`.`\n );\n }\n const check = this.options.checkRuntime ?? defaultRuntimeCheck;\n const ok = await check(this.runtime);\n if (!ok) {\n return (\n `[engine-locator/local] No engine bundle found and runtime ` +\n `\"${this.runtime}\" is not on PATH. Either:\\n` +\n ` - Reinstall the package so postinstall fetches a bundle, or\\n` +\n ` - Install bun (https://bun.sh) for the source-tree fallback.`\n );\n }\n return null;\n }\n\n /** Build the spawn command. Tests override via `engineCommand`.\n * Otherwise we prefer a discovered bundle; failing that, we fall\n * back to `<runtime> run <entry>`. */\n private async resolveCommand(): Promise<{\n command: string;\n args: string[];\n source: \"bundle\" | \"source\" | \"override\";\n bundle?: ResolvedBundle;\n }> {\n if (this.options.engineCommand) {\n return { ...this.options.engineCommand, source: \"override\" };\n }\n const bundle = await this.resolveBundle();\n if (bundle) {\n return {\n command: bundle.path,\n args: [\"serve\", \"--port\", \"0\", \"--hostname\", \"127.0.0.1\"],\n source: \"bundle\",\n bundle,\n };\n }\n return {\n command: this.runtime,\n args: [\n \"run\",\n path.join(this.enginePath, ENTRY_REL),\n \"serve\",\n \"--port\",\n \"0\",\n \"--hostname\",\n \"127.0.0.1\",\n ],\n source: \"source\",\n };\n }\n\n async start(options: EngineStartOptions): Promise<EngineHandle> {\n const preflightErr = await this.preflight();\n if (preflightErr) throw new Error(preflightErr);\n\n const resolved = await this.resolveCommand();\n const { command, args } = resolved;\n // Only set cwd when running against the source tree; the bundle is\n // a self-contained executable and shouldn't depend on its working\n // dir (and the source tree may not even exist on a bundle-only\n // install).\n const cwd =\n resolved.source === \"source\" && (await exists(this.enginePath))\n ? this.enginePath\n : undefined;\n const child = spawn(command, args, {\n cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n // Inherit env so the engine sees any LOCAL_AGENT_* / provider\n // credentials the parent has, then inject the brokered provider as\n // inline opencode config. Without this, the default local engine path\n // does not know the diologue/diologue-routed model, so the prompt\n // fails before edit tools can create files in the selected repo.\n env: buildEngineEnv(options.config),\n });\n\n let url: string;\n try {\n url = await this.waitForListening(child, options.signal);\n } catch (err) {\n // Make sure we don't leave a dangling subprocess on startup\n // failure.\n if (!child.killed) {\n try {\n child.kill();\n } catch {\n /* best-effort */\n }\n }\n throw err;\n }\n\n const factory = this.options.clientFactory ?? defaultClientFactory;\n const client = await factory(url);\n\n return {\n url,\n client,\n close: () => {\n if (child.exitCode === null && !child.killed) {\n try {\n child.kill();\n } catch {\n /* best-effort */\n }\n }\n },\n };\n }\n\n /** Watch the subprocess until either:\n * - stdout/stderr emits a \"listening on http://...\" line \u2014 resolve\n * - the process exits before that happens \u2014 reject with stderr\n * - the startup timeout fires \u2014 reject + (caller kills the child)\n * - the abort signal fires \u2014 reject + (caller kills the child)\n */\n private waitForListening(\n child: ChildProcess,\n abort?: AbortSignal,\n ): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n let settled = false;\n let buffered = \"\";\n const tag = `${this.runtime}/${path.basename(this.enginePath)}`;\n\n const settle = (fn: () => void): void => {\n if (settled) return;\n settled = true;\n cleanup();\n fn();\n };\n\n const onStdout = (chunk: Buffer): void => {\n const text = chunk.toString(\"utf-8\");\n buffered += text;\n const match = LISTENING_REGEX.exec(buffered);\n if (match) {\n settle(() => resolve(match[1]!));\n }\n };\n // We also tee stderr into the listening matcher because some\n // logging configurations route the banner there.\n const onStderr = onStdout;\n\n const onExit = (code: number | null, signal: NodeJS.Signals | null): void => {\n settle(() =>\n reject(\n new Error(\n `[engine-locator/local] ${tag} exited before becoming ready ` +\n `(code=${code} signal=${signal ?? \"-\"}). ` +\n `Last output:\\n${truncate(buffered, 4096)}`,\n ),\n ),\n );\n };\n\n const onError = (err: Error): void => {\n settle(() =>\n reject(\n new Error(\n `[engine-locator/local] ${tag} failed to spawn: ${err.message}`,\n ),\n ),\n );\n };\n\n const timer = setTimeout(() => {\n settle(() =>\n reject(\n new Error(\n `[engine-locator/local] ${tag} did not become ready within ` +\n `${this.startupTimeoutMs}ms. Last output:\\n${truncate(buffered, 4096)}`,\n ),\n ),\n );\n }, this.startupTimeoutMs);\n\n const onAbort = (): void => {\n settle(() =>\n reject(new Error(`[engine-locator/local] startup aborted`)),\n );\n };\n\n const cleanup = (): void => {\n clearTimeout(timer);\n child.stdout?.off(\"data\", onStdout);\n child.stderr?.off(\"data\", onStderr);\n child.off(\"exit\", onExit);\n child.off(\"error\", onError);\n abort?.removeEventListener(\"abort\", onAbort);\n };\n\n child.stdout?.on(\"data\", onStdout);\n child.stderr?.on(\"data\", onStderr);\n child.once(\"exit\", onExit);\n child.once(\"error\", onError);\n if (abort) {\n if (abort.aborted) {\n onAbort();\n return;\n }\n abort.addEventListener(\"abort\", onAbort);\n }\n });\n }\n}\n\nconst truncate = (s: string, max: number): string =>\n s.length <= max ? s : s.slice(s.length - max);\n", "// Unified CLI entry point for the Diologue local agent.\n//\n// Subcommands:\n// diologue-local-agent # default: quickstart\n// diologue-local-agent quickstart [--cloud=URL] # pair + start helper\n// diologue-local-agent start [flags] # raw helper start (legacy)\n// diologue-local-agent pair <code> # pair a device (Phase A2)\n// diologue-local-agent --help # usage\n// diologue-local-agent --version # version\n//\n// The default subcommand (no args) is `quickstart` \u2014 the high-UX path that\n// most users want. Explicit subcommands are escape hatches for advanced\n// scenarios (CI, SSH-only boxes, scripted setups).\n\nimport process from \"node:process\";\n\nimport { runQuickstart } from \"./modes/quickstart\";\nimport { runStart } from \"./modes/start\";\nimport { runPair } from \"./modes/pair\";\n\n// Constants \u2014 synced with package.json on each release.\nconst HELPER_VERSION = \"0.1.0\";\nconst DEFAULT_CLOUD_URL = \"http://localhost:5000\";\n\ninterface ParsedArgs {\n subcommand: \"quickstart\" | \"start\" | \"pair\" | \"help\" | \"version\";\n flags: Map<string, string | boolean>;\n positional: string[];\n}\n\nconst parseArgs = (argv: string[]): ParsedArgs => {\n const flags = new Map<string, string | boolean>();\n const positional: string[] = [];\n let subcommand: ParsedArgs[\"subcommand\"] | null = null;\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (arg === \"--help\" || arg === \"-h\") {\n return { subcommand: \"help\", flags, positional };\n }\n if (arg === \"--version\" || arg === \"-v\") {\n return { subcommand: \"version\", flags, positional };\n }\n if (arg.startsWith(\"--\")) {\n const eq = arg.indexOf(\"=\");\n if (eq !== -1) {\n flags.set(arg.slice(2, eq), arg.slice(eq + 1));\n } else {\n // --key value form: take next argv as value if it doesn't look\n // like another flag, else treat as boolean.\n const next = argv[i + 1];\n if (next && !next.startsWith(\"--\")) {\n flags.set(arg.slice(2), next);\n i++;\n } else {\n flags.set(arg.slice(2), true);\n }\n }\n continue;\n }\n if (subcommand === null && [\"quickstart\", \"start\", \"pair\"].includes(arg)) {\n subcommand = arg as ParsedArgs[\"subcommand\"];\n continue;\n }\n positional.push(arg);\n }\n\n return {\n subcommand: subcommand ?? \"quickstart\",\n flags,\n positional,\n };\n};\n\nconst printHelp = (): void => {\n console.log(`\nDiologue Local Agent v${HELPER_VERSION}\n\nUSAGE\n diologue-local-agent [<command>] [options]\n\nCOMMANDS\n quickstart (default) Start the helper + open the browser to pair\n start Start the helper in raw token mode (no browser)\n pair <code> Pair this machine with a previously-issued code\n --help Show this message\n --version Show version\n\nQUICKSTART OPTIONS\n --cloud=<url> Cloud URL to pair with\n (default: \\${DIOLOGUE_CLOUD_URL:-${DEFAULT_CLOUD_URL}})\n --no-open Don't auto-open the browser; print the URL instead\n --port=<n> TCP port for the helper (default: 4099)\n\nSTART OPTIONS\n --token=<token> Use this pairing token instead of a random one\n --origin=<url> Allowed browser origin for CORS\n --port=<n> TCP port (default: 4099)\n --adapter=<mock|opencode> Engine adapter (default: opencode)\n\nEXAMPLES\n # New user \u2014 go from zero to chatting in 30s\n npx @diologue/local-agent --cloud=https://app.your-domain.com\n\n # Power user \u2014 manual flags\n diologue-local-agent start --port=5099 --origin=https://your.app\n\n # Pair a device (for the future tunnel transport)\n diologue-local-agent pair K9F-27R --cloud=https://app.your-domain.com\n\nENVIRONMENT\n DIOLOGUE_CLOUD_URL Default cloud URL (overridden by --cloud)\n LOCAL_AGENT_ALLOWED_ORIGIN Default CORS origin\n LOCAL_AGENT_ADAPTER \"mock\" forces the canned-response adapter\n LOCAL_AGENT_TOKEN Pin a specific pairing token (testing)\n LOCAL_AGENT_PORT Default TCP port\n\nFor docs + the source, visit:\n https://github.com/Deplova-Ltd/AI-Personas-and-Multi-LLM\n`.trim());\n};\n\nconst main = async (): Promise<void> => {\n const argv = process.argv.slice(2);\n const parsed = parseArgs(argv);\n\n switch (parsed.subcommand) {\n case \"help\":\n printHelp();\n return;\n case \"version\":\n console.log(`diologue-local-agent v${HELPER_VERSION}`);\n return;\n case \"quickstart\":\n await runQuickstart({\n cloudUrl:\n (parsed.flags.get(\"cloud\") as string | undefined) ??\n process.env.DIOLOGUE_CLOUD_URL ??\n DEFAULT_CLOUD_URL,\n port: parseFlagInt(parsed.flags.get(\"port\"), 4099),\n autoOpenBrowser: parsed.flags.get(\"no-open\") !== true,\n helperVersion: HELPER_VERSION,\n setupToken: parsed.flags.get(\"token\") as string | undefined,\n });\n return;\n case \"start\":\n await runStart({\n port: parseFlagInt(parsed.flags.get(\"port\"), 4099),\n token: parsed.flags.get(\"token\") as string | undefined,\n allowedOrigin: parsed.flags.get(\"origin\") as string | undefined,\n adapter: parsed.flags.get(\"adapter\") as\n | \"mock\"\n | \"opencode\"\n | undefined,\n });\n return;\n case \"pair\": {\n const code = parsed.positional[0];\n if (!code) {\n console.error(\"Usage: diologue-local-agent pair <code>\");\n process.exit(2);\n }\n await runPair({\n code,\n cloudUrl:\n (parsed.flags.get(\"cloud\") as string | undefined) ??\n process.env.DIOLOGUE_CLOUD_URL ??\n DEFAULT_CLOUD_URL,\n helperVersion: HELPER_VERSION,\n });\n return;\n }\n }\n};\n\nconst parseFlagInt = (\n raw: string | boolean | undefined,\n fallback: number,\n): number => {\n if (typeof raw !== \"string\") return fallback;\n const n = Number.parseInt(raw, 10);\n return Number.isFinite(n) && n > 0 ? n : fallback;\n};\n\nmain().catch((err) => {\n console.error(\"[cli] Fatal error:\", err);\n process.exit(1);\n});\n", "// Centralised env/config reads. Imported once per process via loadConfig().\n// Tests can also call loadConfig({ overrides }) to inject deterministic values\n// without touching process.env.\n\nimport { randomBytes } from \"node:crypto\";\n\nexport interface LocalAgentConfig {\n host: string; // always 127.0.0.1 in v1\n port: number;\n allowedOrigin: string;\n token: string;\n helperVersion: string;\n /** Set at process start. Surfaced in /health so the UI can detect helper\n * restarts (which invalidate any in-memory state). */\n startedAt: string;\n}\n\ninterface LoadOptions {\n overrides?: Partial<LocalAgentConfig>;\n env?: NodeJS.ProcessEnv;\n}\n\nconst DEFAULT_PORT = 4099;\nconst DEFAULT_ALLOWED_ORIGIN = \"http://localhost:5000\";\n\nconst parsePort = (raw: string | undefined): number => {\n if (!raw) {\n return DEFAULT_PORT;\n }\n const parsed = Number.parseInt(raw, 10);\n if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 65_535) {\n throw new Error(\n `LOCAL_AGENT_PORT must be a valid TCP port (1-65535), got: ${raw}`,\n );\n }\n return parsed;\n};\n\nconst generateToken = (): string => {\n // 32 bytes \u2192 64 hex chars. Plenty of entropy for a single-machine pairing\n // secret. Hex avoids URL-encoding concerns when the user pastes it.\n return randomBytes(32).toString(\"hex\");\n};\n\nexport const loadConfig = (options: LoadOptions = {}): LocalAgentConfig => {\n const env = options.env ?? process.env;\n const base: LocalAgentConfig = {\n // 127.0.0.1 is intentional and not configurable. Binding to 0.0.0.0\n // would expose the helper to the LAN, which the token alone is not\n // designed to defend against. See TODO(tunnel-cloud-auth).\n host: \"127.0.0.1\",\n port: parsePort(env.LOCAL_AGENT_PORT),\n allowedOrigin:\n (env.LOCAL_AGENT_ALLOWED_ORIGIN ?? \"\").trim() || DEFAULT_ALLOWED_ORIGIN,\n token: (env.LOCAL_AGENT_TOKEN ?? \"\").trim() || generateToken(),\n // Bumped from package.json when we ship a meaningful change. Kept inline\n // to avoid a JSON import + ESM ergonomics in tests.\n helperVersion: \"0.0.1\",\n startedAt: new Date().toISOString(),\n };\n return { ...base, ...options.overrides };\n};\n", "// HTTP server factory. Exposed as a function (rather than module-level\n// side effects) so tests can spin up isolated instances on ephemeral ports.\n\nimport express, { type Express } from \"express\";\n\nimport type { LocalAgentConfig } from \"./config\";\nimport { createAuthMiddleware } from \"./auth\";\nimport { createCorsMiddleware } from \"./cors\";\nimport { createState, type AgentState } from \"./state\";\nimport { createHealthHandler } from \"./routes/health\";\nimport { createRepoRouter } from \"./routes/repo\";\nimport {\n createAgentRouter,\n createLlmChunkRouter,\n createLlmResponseRouter,\n createPermissionRouter,\n} from \"./routes/agent\";\nimport { createLlmShimRouter } from \"./routes/llm-shim\";\nimport { buildDefaultAdapter, type OpenCodeAdapter } from \"./adapters\";\nimport { createBrokerRegistry, type BrokerRegistry } from \"./broker\";\n\nexport interface CreateServerOptions {\n config: LocalAgentConfig;\n /** Optional pre-built state so tests can pre-select a repo. */\n state?: AgentState;\n /** Optional adapter override. Tests inject deterministic mocks; in prod\n * this defaults to whatever buildDefaultAdapter() returns. */\n adapter?: OpenCodeAdapter;\n}\n\nexport interface BuiltServer {\n app: Express;\n state: AgentState;\n adapter: OpenCodeAdapter;\n brokerRegistry: BrokerRegistry;\n}\n\nexport const createApp = (options: CreateServerOptions): BuiltServer => {\n const state = options.state ?? createState();\n const adapter = options.adapter ?? buildDefaultAdapter();\n const brokerRegistry = createBrokerRegistry();\n const app = express();\n\n // Body size cap \u2014 we only ever accept small JSON envelopes (paths,\n // prompts). Anything larger is almost certainly a mistake.\n app.use(express.json({ limit: \"1mb\" }));\n\n app.use(createCorsMiddleware({ allowedOrigin: options.config.allowedOrigin }));\n app.use(createAuthMiddleware({ token: options.config.token }));\n\n app.get(\"/health\", createHealthHandler(options.config));\n app.use(\"/repo\", createRepoRouter(state));\n app.use(\"/agent\", createAgentRouter({ state, adapter, brokerRegistry }));\n app.use(\"/agent/llm-response\", createLlmResponseRouter({ brokerRegistry }));\n app.use(\"/agent/llm-chunk\", createLlmChunkRouter({ brokerRegistry }));\n app.use(\"/agent/permission\", createPermissionRouter({ adapter }));\n // OpenAI-compat shim for opencode's outbound LLM calls. NOT\n // token-protected by the pairing token middleware \u2014 uses its own\n // ephemeral apiKey bound at stream-start time. The auth middleware\n // explicitly exempts this prefix.\n app.use(\"/llm-shim\", createLlmShimRouter());\n\n // Trailing 404. Returns JSON so the browser's fetch error path stays in\n // happy territory rather than HTML-parsing a default Express page.\n app.use((req, res) => {\n res.status(404).json({ error: \"not_found\", method: req.method, path: req.path });\n });\n\n return { app, state, adapter, brokerRegistry };\n};\n", "// Pairing-token middleware.\n//\n// MVP shortcut: a single static token, generated at process start, printed to\n// the console, and pasted into the web UI. The browser supplies it as the\n// `x-local-agent-token` request header on every authenticated call.\n//\n// TODO(tunnel-cloud-auth): when the cloud tunnel ships, this is replaced by a\n// cloud-issued, rotated device credential. The middleware boundary stays the\n// same; only the verification flips to \"ask the cloud\" instead of comparing a\n// local string.\n\nimport type { NextFunction, Request, Response } from \"express\";\nimport { timingSafeEqual } from \"node:crypto\";\n\nimport { LOCAL_AGENT_TOKEN_HEADER } from \"../../shared/coding-agent-types\";\n\nexport interface AuthOptions {\n token: string;\n /** Endpoints that should NOT require the token. Matched against\n * `req.method + ' ' + req.path`. Kept small on purpose \u2014 currently just\n * `GET /health` so the UI can probe connectivity before pairing. */\n unauthenticated?: ReadonlySet<string>;\n}\n\nconst DEFAULT_UNAUTHENTICATED: ReadonlySet<string> = new Set([\"GET /health\"]);\n\n/** Prefixes the pairing-token middleware skips. The OpenAI-compat shim\n * has its own ephemeral-token scheme bound per /agent/message stream;\n * the pairing token isn't appropriate there because opencode supplies\n * its OWN apiKey value. */\nconst DEFAULT_UNAUTHENTICATED_PREFIXES: readonly string[] = [\"/llm-shim/\"];\n\n/** Constant-time comparison. Both strings are token-shaped (hex/uuid) and\n * short enough to compare byte-for-byte. Falls back to a length mismatch\n * fail so we don't even invoke timingSafeEqual on unequal-length input\n * (it throws otherwise). */\nconst tokensEqual = (a: string, b: string): boolean => {\n const ab = Buffer.from(a, \"utf8\");\n const bb = Buffer.from(b, \"utf8\");\n if (ab.length !== bb.length) {\n return false;\n }\n return timingSafeEqual(ab, bb);\n};\n\nexport const createAuthMiddleware = (options: AuthOptions) => {\n const exempt = options.unauthenticated ?? DEFAULT_UNAUTHENTICATED;\n return (req: Request, res: Response, next: NextFunction): void => {\n const key = `${req.method} ${req.path}`;\n if (exempt.has(key)) {\n next();\n return;\n }\n // Skip prefix-matched routes (currently just the LLM shim). These\n // have their own auth.\n for (const prefix of DEFAULT_UNAUTHENTICATED_PREFIXES) {\n if (req.path.startsWith(prefix)) {\n next();\n return;\n }\n }\n // CORS preflight always passes \u2014 actual security comes from the\n // allowed-origin check (see cors.ts). Without this OPTIONS would 401\n // before the browser could send the real request.\n if (req.method === \"OPTIONS\") {\n next();\n return;\n }\n const provided = req.header(LOCAL_AGENT_TOKEN_HEADER);\n if (!provided || !tokensEqual(provided, options.token)) {\n res\n .status(401)\n .json({ error: \"invalid_or_missing_token\", header: LOCAL_AGENT_TOKEN_HEADER });\n return;\n }\n next();\n };\n};\n", "// Wire types shared between:\n// - the web client (client/src/lib/local-agent-client.ts)\n// - the Diologue backend (server/coding-agent/*)\n// - the local helper (local-agent/src/*)\n//\n// Anything that crosses one of those boundaries lives here. Database row\n// shapes stay in shared/schema.ts; this file is purely for the on-the-wire\n// envelopes and event payloads.\n\n// ----------------------------------------------------------------------------\n// Local helper HTTP surface (browser \u2194 helper on 127.0.0.1:4099)\n// ----------------------------------------------------------------------------\n\nexport const LOCAL_AGENT_TOKEN_HEADER = \"x-local-agent-token\";\n\n/** GET /health */\nexport interface LocalAgentHealth {\n ok: true;\n helperVersion: string;\n // Echoed so the browser can sanity-check the binding before showing the\n // \"connected\" badge. Will always be 127.0.0.1 in v1.\n boundHost: string;\n port: number;\n // ISO timestamp the helper started. Lets the UI detect restarts that\n // invalidate the pairing token.\n startedAt: string;\n}\n\n/** POST /repo/select */\nexport interface SelectRepoRequest {\n /** Absolute path on the user's machine. The helper realpath-resolves and\n * verifies it exists and contains a `.git/` entry. */\n path: string;\n}\n\nexport interface SelectRepoResponse {\n ok: true;\n repo: RepoStatus;\n}\n\n/** GET /repo/status \u2014 response envelope. Always 200; `repo` is null when no\n * repo has been selected yet. */\nexport interface RepoStatusResponse {\n repo: RepoStatus | null;\n}\n\n/** Repo state returned by /repo/select and embedded in /repo/status. */\nexport interface RepoStatus {\n /** The canonical (realpath-resolved) path the helper will operate on. */\n path: string;\n /** Basename of `path` \u2014 convenience for the UI. */\n name: string;\n /** Output of `git rev-parse --abbrev-ref HEAD`, or null if detached. */\n branch: string | null;\n /** Output of `git rev-parse HEAD`, short form. */\n head: string | null;\n /** True when `git status --short` returns any output. */\n isDirty: boolean;\n}\n\n/** GET /repo/changed-files */\nexport interface ChangedFile {\n /** Repo-relative path. */\n path: string;\n /** Parsed from `git status --short` columns. */\n index: GitStatusCode;\n workTree: GitStatusCode;\n /** True when the file is untracked (status code \"??\"). */\n untracked: boolean;\n}\n\n/** Two-char codes from `git status --short`. */\nexport type GitStatusCode =\n | \" \" // unmodified\n | \"M\" // modified\n | \"A\" // added\n | \"D\" // deleted\n | \"R\" // renamed\n | \"C\" // copied\n | \"U\" // updated but unmerged\n | \"?\" // untracked\n | \"!\"; // ignored\n\nexport interface ChangedFilesResponse {\n files: ChangedFile[];\n}\n\n/** POST /repo/browse \u2014 the helper opens the OS folder dialog on the user's\n * machine and returns the chosen absolute path. `cancelled` is true when the\n * user dismissed the dialog. A 501 with `error` (\"no_gui\" | \"unsupported\")\n * means there's no usable desktop picker (headless/SSH) \u2014 the UI should fall\n * back to typing the path. */\nexport interface BrowseDirectoryResponse {\n path?: string;\n cancelled?: boolean;\n}\n\n/** POST /repo/create-pr \u2014 branch the working-tree changes, commit, push, and\n * open a pull request via the user's local `gh`. */\nexport interface CreatePrRequest {\n /** PR title (also the commit message). */\n title: string;\n /** PR body. Optional; defaults to a short generated note. */\n body?: string;\n}\n\nexport interface CreatePrResponse {\n /** URL of the opened pull request. */\n url: string;\n /** The branch that was created + pushed (e.g. \"diologue/add-auth-guard-x1y2\"). */\n branch: string;\n /** The base branch the PR targets. */\n base: string;\n}\n\n/** Stable error codes from /repo/create-pr so the UI can give targeted\n * guidance without parsing prose. */\nexport type CreatePrErrorCode =\n | \"no_repo_selected\"\n | \"no_remote\"\n | \"gh_unavailable\"\n | \"nothing_to_commit\"\n | \"git_command_failed\"\n | \"gh_failed\";\n\n/** GET /repo/diff */\nexport interface RepoDiffResponse {\n /** Concatenated unified diff: staged + unstaged. Untracked files are\n * represented as full-file additions. */\n unified: string;\n /** Bytes \u2014 the UI uses this to refuse to render extremely large diffs\n * without an explicit \"show anyway\" click. */\n sizeBytes: number;\n}\n\n// ----------------------------------------------------------------------------\n// Helper \u2194 browser streaming (POST /agent/message)\n// ----------------------------------------------------------------------------\n//\n// The helper streams a sequence of envelopes back to the browser over SSE.\n// All envelopes share a discriminating `type` field so the UI can switch.\n//\n// Most events are emitted by the OpenCode adapter; the `llm_request` event\n// is special \u2014 it asks the browser to make an LLM call on the helper's\n// behalf so usage flows through the cloud billing ledger.\n\nexport type AgentStreamEvent =\n | AgentTextDelta\n | AgentToolCallStart\n | AgentToolCallEnd\n | AgentDiffProposed\n | AgentPermissionRequest\n | AgentLlmRequest\n | AgentDone\n | AgentError;\n\nexport interface AgentTextDelta {\n type: \"text_delta\";\n text: string;\n}\n\nexport interface AgentToolCallStart {\n type: \"tool_call_start\";\n toolCallId: string;\n /** Adapter-defined tool name, e.g. \"shell.run\", \"fs.read\". */\n tool: string;\n /** Opaque, adapter-specific arguments. Surface verbatim in the UI. */\n args?: Record<string, unknown>;\n}\n\nexport interface AgentToolCallEnd {\n type: \"tool_call_end\";\n toolCallId: string;\n ok: boolean;\n /** Short, one-line summary for the activity row (the tool's title, e.g.\n * \"Read 42 lines\"). */\n summary?: string;\n /** The tool's full textual result \u2014 bash stdout/stderr, file-read\n * contents, edit confirmation, etc. Truncated by the helper to keep the\n * SSE + persistence bounded. Optional: older helpers omit it, and the UI\n * renders the row fine without it. */\n output?: string;\n}\n\nexport interface AgentDiffProposed {\n type: \"diff_proposed\";\n /** Full unified diff. */\n unified: string;\n /** Parsed file summary \u2014 UI uses this to render the changed-files list\n * in the apply dialog. */\n files: Array<{\n path: string;\n additions: number;\n deletions: number;\n status: \"added\" | \"modified\" | \"deleted\" | \"renamed\";\n }>;\n}\n\n/** opencode paused a tool call (bash/webfetch) and is waiting for the user\n * to approve or deny it. The turn stays open until the browser POSTs a\n * decision to /agent/permission. Only emitted in \"confirm\" mode \u2014 in \"auto\"\n * the helper auto-approves and the browser never sees this. */\nexport interface AgentPermissionRequest {\n type: \"permission_request\";\n /** opencode's permission id \u2014 echoed back to approve/deny. */\n permissionId: string;\n /** Permission category from opencode, e.g. \"bash\", \"webfetch\". */\n tool: string;\n /** Human-readable title opencode attached (e.g. \"Run command\"). */\n title?: string;\n /** The concrete action awaiting approval \u2014 the shell command, the URL \u2014\n * extracted from the permission metadata when present. */\n command?: string;\n}\n\n/** How much the user wants to gate tool execution this turn.\n * - auto: run everything without prompting (edits are reversible via\n * Keep/Revert; bash/webfetch auto-approved).\n * - confirm: edits still run freely, but bash + webfetch pause for an\n * explicit Allow/Deny. */\nexport type CodingPermissionMode = \"auto\" | \"confirm\";\n\n/** Maps to opencode's permission reply vocabulary:\n * once = allow this one call, always = allow for the rest of the session,\n * reject = deny. */\nexport type CodingPermissionResponse = \"once\" | \"always\" | \"reject\";\n\n/** POST /agent/permission \u2014 the browser approves/denies a paused tool call. */\nexport interface AgentPermissionDecisionRequest {\n sessionId: string;\n permissionId: string;\n response: CodingPermissionResponse;\n}\n\n/** Helper has hit a point where it needs an LLM call. The browser is\n * expected to forward this to the Diologue backend's coding LLM proxy\n * (so the call is metered + auth'd), then POST the completed response\n * back to POST /agent/llm-response/:requestId.\n *\n * Step 8 wires this end-to-end. The payload shape is intentionally\n * generic so it can carry tools, system prompts, etc. without\n * requiring a wire-format change for every adapter capability. */\nexport interface AgentLlmRequest {\n type: \"llm_request\";\n requestId: string;\n provider?: string;\n model?: string;\n payload: {\n messages: Array<{ role: CodingMessageRole; content: string }>;\n systemPrompt?: string;\n /** Sampling temperature. Adapters typically leave this null and let\n * the model default apply. */\n temperature?: number;\n /** Optional tool definitions for tool-use-capable models. Shape is\n * whatever provider expects \u2014 opaque to the broker. */\n tools?: unknown[];\n };\n}\n\n/** Roles accepted in brokered LLM messages and in the coding-agent\n * message persistence layer. Wider than chat's user|assistant because\n * coding agents commonly thread tool results back in. */\nexport type CodingMessageRole = \"user\" | \"assistant\" | \"system\" | \"tool\";\n\n/** What the adapter hands to the broker. Same shape as\n * AgentLlmRequest.payload + the model/provider hints that ride on the\n * envelope. Kept as its own type so the adapter signature is readable. */\nexport interface LlmBrokerRequestPayload {\n provider?: string;\n model?: string;\n systemPrompt?: string;\n temperature?: number;\n messages: Array<{ role: CodingMessageRole; content: string }>;\n tools?: unknown[];\n}\n\n/** What the browser POSTs back to /agent/llm-response/:requestId. The\n * broker resolves the adapter's pending Promise with this object. */\nexport interface LlmBrokerResponsePayload {\n text: string;\n provider: string;\n model: string;\n tokenUsage?: { input: number; output: number };\n /** Set when the cloud-side flow refuses or fails. Adapter sees an\n * Error with `message`. */\n error?: string;\n /** Tool calls emitted by the model. Shape mirrors OpenAI Chat\n * Completions so the helper's OpenAI-compat shim can pass them\n * through verbatim. Cloud-side, an Anthropic tool_use block gets\n * translated into this shape before the response is returned. */\n toolCalls?: Array<LlmBrokerToolCall>;\n /** OpenAI-style finish reason. Defaults to \"stop\". The shim uses\n * this to populate `choices[0].finish_reason` in its response. */\n finishReason?: \"stop\" | \"tool_calls\" | \"length\" | \"content_filter\";\n /** Raw provider API cost in USD. Set by the cloud after the\n * usage_ledger row is written; passed through unchanged by the\n * helper. UI uses this to show \"$0.012\" alongside the token count. */\n rawApiCostUsd?: number;\n /** Diologue-internal tokens charged for this call (after markup +\n * safety factor). The user's balance is debited by this many. */\n diologueTokens?: number;\n}\n\nexport interface LlmBrokerToolCall {\n id: string;\n type: \"function\";\n function: {\n name: string;\n /** JSON-encoded arguments, per OpenAI's wire format. */\n arguments: string;\n };\n}\n\n/** Incremental update emitted by the streaming brokered-LLM path.\n * Cloud emits these as SSE events on /api/coding/sessions/:id/llm-proxy-stream;\n * the browser forwards them to the helper at /agent/llm-chunk/:requestId. */\nexport type LlmStreamChunk =\n | { type: \"text_delta\"; text: string }\n | {\n type: \"tool_call_delta\";\n /** OpenAI streams tool-call args incrementally. We carry the\n * full id+name once, then partial arguments deltas. */\n index: number;\n id?: string;\n name?: string;\n argumentsDelta?: string;\n }\n | {\n type: \"complete\";\n /** Mirrors LlmBrokerResponsePayload \u2014 the full final payload. */\n payload: LlmBrokerResponsePayload;\n }\n | { type: \"error\"; message: string };\n\nexport interface AgentDone {\n type: \"done\";\n /** Persisted coding_messages.id for the assistant turn, if the helper\n * has reported it (browser may also create it client-side and patch). */\n messageId?: string;\n}\n\nexport interface AgentError {\n type: \"error\";\n message: string;\n /** Set when the error is one the UI can resolve by re-pairing or\n * re-selecting the repo. */\n recoverable?: boolean;\n}\n\n// ----------------------------------------------------------------------------\n// Apply changes\n// ----------------------------------------------------------------------------\n\nexport interface ApplyPatchRequest {\n /** Unified diff to apply. The helper validates with `git apply --check`\n * before writing anything. */\n unified: string;\n /** Reserved for a future \"the on-disk content hasn't drifted since the\n * diff was proposed\" check. Optional in v1 \u2014 git apply --check is\n * the only defense today. */\n baselineHash?: string;\n}\n\nexport interface ApplyPatchResponse {\n ok: true;\n /** Number of files whose contents changed on disk. Parsed from the\n * helper's git apply output. */\n filesChanged: number;\n /** Repo-relative paths that were modified. Convenience for the UI so\n * it can render \"Updated 3 files: a.ts, b.ts, c.ts\". */\n paths: string[];\n}\n\nexport interface ApplyPatchErrorResponse {\n error:\n | \"no_repo_selected\"\n | \"invalid_body\"\n | \"diff_does_not_apply\"\n | \"git_command_failed\";\n message: string;\n /** When error is \"diff_does_not_apply\", stderr from `git apply --check`\n * is surfaced so the user can see why. */\n stderr?: string;\n}\n\n// ----------------------------------------------------------------------------\n// Revert changes\n// ----------------------------------------------------------------------------\n//\n// The real coding engine (opencode) edits files in the working tree DURING\n// the turn \u2014 by the time the user sees the diff, those changes are already\n// on disk. So the UI's primary \"undo\" action is a reverse-apply, not an\n// apply. The helper runs `git apply --reverse --check` before touching the\n// tree, mirroring the apply two-phase guard.\n\nexport interface RevertPatchRequest {\n /** The same unified diff the agent produced. Reverse-applying it against\n * the current working tree restores the pre-turn state (deleting added\n * files, recreating deleted ones, undoing modifications). */\n unified: string;\n}\n\nexport interface RevertPatchResponse {\n ok: true;\n /** Number of files restored to their pre-turn state. */\n filesChanged: number;\n /** Repo-relative paths that were reverted. */\n paths: string[];\n}\n\nexport interface RevertPatchErrorResponse {\n error:\n | \"no_repo_selected\"\n | \"invalid_body\"\n | \"diff_does_not_revert\"\n | \"git_command_failed\";\n message: string;\n /** When error is \"diff_does_not_revert\", stderr from\n * `git apply --reverse --check` is surfaced so the user can see why. */\n stderr?: string;\n}\n", "// Single-origin CORS. We deliberately do NOT use the `cors` npm package \u2014\n// the policy is so narrow (one origin, one header) that hand-rolling it is\n// less ambiguous than reading docs to confirm flag combinations.\n//\n// The browser running on the user's machine is the only legitimate caller.\n// `Origin` mismatches are dropped silently at the preflight step (no CORS\n// headers returned \u2192 browser refuses the call), which is enough \u2014 we do NOT\n// rely on CORS for security, only as a defence-in-depth measure on top of\n// the token middleware.\n\nimport type { NextFunction, Request, Response } from \"express\";\n\nimport { LOCAL_AGENT_TOKEN_HEADER } from \"../../shared/coding-agent-types\";\n\nexport interface CorsOptions {\n allowedOrigin: string;\n}\n\nexport const createCorsMiddleware = (options: CorsOptions) => {\n const allowed = options.allowedOrigin;\n return (req: Request, res: Response, next: NextFunction): void => {\n const origin = req.header(\"origin\");\n if (origin && origin === allowed) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n res.setHeader(\"Access-Control-Allow-Credentials\", \"false\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET,POST,OPTIONS\");\n res.setHeader(\n \"Access-Control-Allow-Headers\",\n `Content-Type, ${LOCAL_AGENT_TOKEN_HEADER}`,\n );\n res.setHeader(\"Access-Control-Max-Age\", \"300\");\n // Private Network Access (Chrome): a page on a public/HTTPS origin\n // fetching a loopback address (this helper) triggers a PNA preflight\n // carrying `Access-Control-Request-Private-Network: true`. We must\n // echo the allow header or Chrome blocks the request outright.\n //\n // NOTE: Chrome's newer permission-based \"Local Network Access\" gate\n // ALSO requires a one-time user grant for the site \u2014 this header is\n // necessary but, on the latest Chrome, not by itself sufficient. See\n // the loopback-access note returned to the operator.\n if (\n req.header(\"access-control-request-private-network\") === \"true\"\n ) {\n res.setHeader(\"Access-Control-Allow-Private-Network\", \"true\");\n }\n }\n if (req.method === \"OPTIONS\") {\n // Preflight. If origin matched, headers above are set; if not, the\n // response carries no CORS headers and the browser rejects it.\n res.status(204).end();\n return;\n }\n next();\n };\n};\n", "// In-memory state for the helper process.\n//\n// MVP: a single selected repo per helper process. On restart the user must\n// re-select (which is consistent with the pairing token also being lost).\n//\n// We expose a small factory rather than module-level globals so tests can\n// spin up isolated state without leaking across cases.\n\nimport type { RepoStatus } from \"../../shared/coding-agent-types\";\n\nexport interface AgentState {\n getSelectedRepo(): RepoStatus | null;\n setSelectedRepo(repo: RepoStatus): void;\n clearSelectedRepo(): void;\n}\n\nexport const createState = (): AgentState => {\n let selected: RepoStatus | null = null;\n return {\n getSelectedRepo: () => selected,\n setSelectedRepo: (repo) => {\n selected = repo;\n },\n clearSelectedRepo: () => {\n selected = null;\n },\n };\n};\n", "// GET /health \u2014 unauthenticated, intentionally minimal. The UI uses this to\n// prove the helper is reachable BEFORE the user pastes a pairing token.\n// It echoes no secrets and the same payload is safe to log.\n\nimport type { Request, Response } from \"express\";\n\nimport type { LocalAgentConfig } from \"../config\";\nimport type { LocalAgentHealth } from \"../../../shared/coding-agent-types\";\n\nexport const createHealthHandler = (config: LocalAgentConfig) => {\n return (_req: Request, res: Response): void => {\n const body: LocalAgentHealth = {\n ok: true,\n helperVersion: config.helperVersion,\n boundHost: config.host,\n port: config.port,\n startedAt: config.startedAt,\n };\n res.json(body);\n };\n};\n", "// /repo/* \u2014 repo selection, status, diff, changed files.\n//\n// Every handler requires a selected repo (with the obvious exception of\n// /repo/select itself). When none has been selected the response is a 409\n// with a stable `error: \"no_repo_selected\"` code so the UI can render a\n// friendly empty state without parsing error messages.\n\nimport type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nimport type { AgentState } from \"../state\";\nimport {\n applyDiff,\n branchNameForTitle,\n canApplyDiff,\n canRevertDiff,\n commitAll,\n createBranch,\n getBranch,\n getDefaultBranch,\n getDiff,\n getHeadShort,\n getRemoteUrl,\n getStatusShort,\n isDirty,\n parseDiffPaths,\n pushBranch,\n revertDiff,\n GitCommandError,\n} from \"../lib/git\";\nimport { validateRepoPath, InvalidRepoPathError } from \"../lib/paths\";\nimport { pickDirectory } from \"../lib/pick-directory\";\nimport { createPullRequest, ghReady, GhError } from \"../lib/github\";\nimport type {\n ApplyPatchResponse,\n BrowseDirectoryResponse,\n ChangedFilesResponse,\n CreatePrResponse,\n RepoDiffResponse,\n RepoStatus,\n RepoStatusResponse,\n RevertPatchResponse,\n SelectRepoResponse,\n} from \"../../../shared/coding-agent-types\";\n\nconst selectRepoSchema = z.object({\n path: z.string().min(1),\n});\n\nconst applyPatchSchema = z.object({\n unified: z.string().min(1).max(8 * 1024 * 1024),\n baselineHash: z.string().optional(),\n});\n\nconst revertPatchSchema = z.object({\n unified: z.string().min(1).max(8 * 1024 * 1024),\n});\n\nconst createPrSchema = z.object({\n title: z.string().min(1).max(200),\n body: z.string().max(20_000).optional(),\n});\n\nconst buildRepoStatus = async (resolvedPath: string): Promise<RepoStatus> => {\n const [branch, head, dirty] = await Promise.all([\n getBranch(resolvedPath),\n getHeadShort(resolvedPath),\n isDirty(resolvedPath),\n ]);\n return {\n path: resolvedPath,\n name: path.basename(resolvedPath),\n branch,\n head,\n isDirty: dirty,\n };\n};\n\nconst sendInvalidRepoPath = (\n res: Response,\n err: InvalidRepoPathError,\n): void => {\n res.status(400).json({ error: err.code, message: err.message });\n};\n\nconst sendGitError = (res: Response, err: GitCommandError): void => {\n res.status(500).json({\n error: \"git_command_failed\",\n message: err.message,\n exitCode: err.exitCode,\n });\n};\n\nconst requireSelectedRepo = (\n state: AgentState,\n res: Response,\n): RepoStatus | null => {\n const repo = state.getSelectedRepo();\n if (!repo) {\n res.status(409).json({ error: \"no_repo_selected\" });\n return null;\n }\n return repo;\n};\n\nexport const createRepoRouter = (state: AgentState): Router => {\n const router = createRouter();\n\n router.post(\"/select\", async (req: Request, res: Response) => {\n const parsed = selectRepoSchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n try {\n const resolved = await validateRepoPath(parsed.data.path);\n const status = await buildRepoStatus(resolved);\n state.setSelectedRepo(status);\n const body: SelectRepoResponse = { ok: true, repo: status };\n res.json(body);\n } catch (err) {\n if (err instanceof InvalidRepoPathError) {\n sendInvalidRepoPath(res, err);\n return;\n }\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n // Pop the OS folder dialog on the user's machine and return the chosen path\n // (the browser can't read absolute paths itself). Does NOT select the repo \u2014\n // the UI fills the path input and the user confirms with /repo/select.\n router.post(\"/browse\", async (_req: Request, res: Response) => {\n const result = await pickDirectory();\n if (result.ok) {\n const body: BrowseDirectoryResponse = { path: result.path };\n res.json(body);\n return;\n }\n if (result.reason === \"cancelled\") {\n const body: BrowseDirectoryResponse = { cancelled: true };\n res.json(body);\n return;\n }\n // no_gui / unsupported / failed \u2014 the UI falls back to manual typing.\n res.status(501).json({\n error: result.reason,\n message:\n \"No desktop folder dialog is available on this machine. Type the repo path instead.\",\n });\n });\n\n // Branch the working-tree changes, commit, push, and open a PR via local gh.\n // This is the foundation the worktree-isolation + cloud-GitHub phases build\n // on; for now it works in place (creating a diologue/<slug> branch off the\n // current HEAD) and relies on the user's existing gh auth + git remote.\n router.post(\"/create-pr\", async (req: Request, res: Response) => {\n const repo = requireSelectedRepo(state, res);\n if (!repo) return;\n\n const parsed = createPrSchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n const { title } = parsed.data;\n const body =\n parsed.data.body ?? \"Opened from the Diologue coding agent.\";\n const cwd = repo.path;\n\n try {\n if (!(await ghReady())) {\n res.status(501).json({\n error: \"gh_unavailable\",\n message:\n \"GitHub CLI (gh) isn't installed or authenticated on this machine. Run `gh auth login`, or push the branch and open the PR manually.\",\n });\n return;\n }\n if (!(await getRemoteUrl(cwd))) {\n res.status(400).json({\n error: \"no_remote\",\n message:\n \"This repo has no `origin` remote. Add one (git remote add origin \u2026) before creating a PR.\",\n });\n return;\n }\n if (!(await isDirty(cwd))) {\n res.status(409).json({\n error: \"nothing_to_commit\",\n message: \"There are no changes in the working tree to open a PR for.\",\n });\n return;\n }\n\n // The current branch is what we merge back INTO \u2014 unless we're already\n // sitting on a diologue/* branch (e.g. a repeat click), in which case\n // target the repo's default branch instead.\n const current = (await getBranch(cwd)) ?? \"\";\n const onAgentBranch = current.startsWith(\"diologue/\");\n const base = onAgentBranch ? await getDefaultBranch(cwd) : current || \"main\";\n\n const branch = onAgentBranch ? current : branchNameForTitle(title);\n if (!onAgentBranch) {\n await createBranch(cwd, branch);\n }\n await commitAll(cwd, title);\n await pushBranch(cwd, branch);\n const url = await createPullRequest({ cwd, base, head: branch, title, body });\n\n const payload: CreatePrResponse = { url, branch, base };\n res.json(payload);\n } catch (err) {\n if (err instanceof GhError) {\n res.status(502).json({ error: err.code, message: err.message });\n return;\n }\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n router.get(\"/status\", async (_req: Request, res: Response) => {\n const repo = state.getSelectedRepo();\n if (!repo) {\n const body: RepoStatusResponse = { repo: null };\n res.json(body);\n return;\n }\n // Re-derive fresh status so the UI reflects current branch/dirty state\n // without making the user re-select.\n try {\n const refreshed = await buildRepoStatus(repo.path);\n state.setSelectedRepo(refreshed);\n const body: RepoStatusResponse = { repo: refreshed };\n res.json(body);\n } catch (err) {\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n router.get(\"/diff\", async (_req: Request, res: Response) => {\n const repo = requireSelectedRepo(state, res);\n if (!repo) {\n return;\n }\n try {\n const unified = await getDiff(repo.path);\n const body: RepoDiffResponse = {\n unified,\n sizeBytes: Buffer.byteLength(unified, \"utf8\"),\n };\n res.json(body);\n } catch (err) {\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n router.get(\"/changed-files\", async (_req: Request, res: Response) => {\n const repo = requireSelectedRepo(state, res);\n if (!repo) {\n return;\n }\n try {\n const files = await getStatusShort(repo.path);\n const body: ChangedFilesResponse = { files };\n res.json(body);\n } catch (err) {\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n // POST /repo/apply \u2014 apply a unified diff to the selected repo.\n //\n // Two-phase: `git apply --check` first; only if that succeeds do we run\n // the real `git apply`. The check phase is a no-op on the filesystem,\n // so a failing check leaves the working tree unchanged. We surface\n // stderr from the check so the user can see why a diff didn't apply.\n router.post(\"/apply\", async (req: Request, res: Response) => {\n const repo = requireSelectedRepo(state, res);\n if (!repo) return;\n\n const parsed = applyPatchSchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n\n try {\n const check = await canApplyDiff(repo.path, parsed.data.unified);\n if (!check.ok) {\n res.status(409).json({\n error: \"diff_does_not_apply\",\n message:\n \"Diff doesn't apply against the current working tree. The repo \" +\n \"may have changed since the diff was proposed.\",\n stderr: check.stderr,\n });\n return;\n }\n await applyDiff(repo.path, parsed.data.unified);\n const paths = parseDiffPaths(parsed.data.unified);\n const body: ApplyPatchResponse = {\n ok: true,\n filesChanged: paths.length,\n paths,\n };\n res.json(body);\n } catch (err) {\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n // POST /repo/revert \u2014 undo changes the coding engine already wrote.\n //\n // opencode edits the working tree directly during a turn, so \"undo\" is a\n // reverse-apply of the same diff, not an apply. Same two-phase guard as\n // /apply: `git apply --reverse --check` first; only on success do we run\n // the real reverse-apply. A failing check (e.g. the tree drifted, or the\n // changes were already reverted) leaves the working tree untouched and\n // returns 409 with stderr.\n router.post(\"/revert\", async (req: Request, res: Response) => {\n const repo = requireSelectedRepo(state, res);\n if (!repo) return;\n\n const parsed = revertPatchSchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n\n try {\n const check = await canRevertDiff(repo.path, parsed.data.unified);\n if (!check.ok) {\n res.status(409).json({\n error: \"diff_does_not_revert\",\n message:\n \"These changes can't be reverted cleanly. The working tree may \" +\n \"have changed since the agent wrote them, or they were already \" +\n \"reverted.\",\n stderr: check.stderr,\n });\n return;\n }\n await revertDiff(repo.path, parsed.data.unified);\n const paths = parseDiffPaths(parsed.data.unified);\n const body: RevertPatchResponse = {\n ok: true,\n filesChanged: paths.length,\n paths,\n };\n res.json(body);\n } catch (err) {\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n return router;\n};\n", "// Git command wrappers + status/diff parsing.\n//\n// Everything goes through execFile (NOT exec / shell). The cwd has already\n// been validated by validateRepoPath. We never accept a shell snippet, never\n// pass user input as a flag, and never interpolate user text into argv.\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport type { ChangedFile, GitStatusCode } from \"../../../shared/coding-agent-types\";\n\nconst execFileAsync = promisify(execFile);\n\nexport class GitCommandError extends Error {\n readonly stderr: string;\n readonly exitCode: number;\n constructor(args: readonly string[], exitCode: number, stderr: string) {\n super(`git ${args.join(\" \")} failed with exit ${exitCode}: ${stderr.trim()}`);\n this.name = \"GitCommandError\";\n this.stderr = stderr;\n this.exitCode = exitCode;\n }\n}\n\n/** Run a git subcommand. `args` are passed argv-style \u2014 no shell. */\nexport const runGit = async (cwd: string, args: string[]): Promise<string> => {\n try {\n const { stdout } = await execFileAsync(\"git\", args, {\n cwd,\n // Generous-ish buffer for diffs of medium-sized changes. We surface\n // a sizeBytes field so the UI can refuse to render extreme cases.\n maxBuffer: 16 * 1024 * 1024,\n // Inherit a stripped env \u2014 we don't want PAGER or GIT_* tracing.\n env: { ...process.env, GIT_PAGER: \"cat\", PAGER: \"cat\" },\n });\n return stdout;\n } catch (err: unknown) {\n if (err && typeof err === \"object\" && \"code\" in err) {\n const e = err as { code?: number; stderr?: string };\n throw new GitCommandError(args, e.code ?? -1, e.stderr ?? \"\");\n }\n throw err;\n }\n};\n\nexport const getBranch = async (cwd: string): Promise<string | null> => {\n try {\n const out = (await runGit(cwd, [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"])).trim();\n // Detached HEAD reports \"HEAD\" \u2014 surface as null so the UI can render\n // \"(detached)\" without us hard-coding that here.\n if (out === \"HEAD\") {\n return null;\n }\n return out;\n } catch {\n // Empty repo (no commits yet) \u2014 return null rather than blow up.\n return null;\n }\n};\n\nexport const getHeadShort = async (cwd: string): Promise<string | null> => {\n try {\n return (await runGit(cwd, [\"rev-parse\", \"--short\", \"HEAD\"])).trim();\n } catch {\n return null;\n }\n};\n\nexport const isDirty = async (cwd: string): Promise<boolean> => {\n const out = await runGit(cwd, [\"status\", \"--porcelain\"]);\n return out.trim().length > 0;\n};\n\n/** Parse `git status --short` output into structured rows.\n *\n * Format (per `git status` man page):\n * XY <path>\n * XY <orig_path> -> <path> (renames/copies)\n *\n * X = index status, Y = work-tree status.\n * Untracked files appear as \"?? <path>\".\n *\n * We DO NOT shell out from here \u2014 this function takes the already-captured\n * stdout so it's easy to unit-test with hand-crafted strings.\n */\nexport const parseShortStatus = (output: string): ChangedFile[] => {\n const lines = output.split(\"\\n\");\n const files: ChangedFile[] = [];\n for (const line of lines) {\n // Status lines are at least 4 chars: XY<space><path>\n if (line.length < 4) {\n continue;\n }\n const indexChar = line[0];\n const workChar = line[1];\n if (indexChar === undefined || workChar === undefined) {\n continue;\n }\n // Skip the separator at col 2 and the path follows. Handle rename\n // form by taking the post-arrow path.\n let pathPart = line.slice(3);\n const arrow = pathPart.indexOf(\" -> \");\n if (arrow !== -1) {\n pathPart = pathPart.slice(arrow + 4);\n }\n // Strip surrounding quotes git uses for paths containing special chars\n // (when `core.quotepath` is on). We don't try to fully unescape; the UI\n // will surface a sensible path either way and exact bytes aren't used\n // for any filesystem operation from here.\n if (\n pathPart.startsWith('\"') &&\n pathPart.endsWith('\"') &&\n pathPart.length >= 2\n ) {\n pathPart = pathPart.slice(1, -1);\n }\n files.push({\n path: pathPart,\n index: asGitStatusCode(indexChar),\n workTree: asGitStatusCode(workChar),\n untracked: indexChar === \"?\" && workChar === \"?\",\n });\n }\n return files;\n};\n\nconst VALID_STATUS_CODES = new Set([\" \", \"M\", \"A\", \"D\", \"R\", \"C\", \"U\", \"?\", \"!\"]);\n\nconst asGitStatusCode = (ch: string): GitStatusCode => {\n if (VALID_STATUS_CODES.has(ch)) {\n return ch as GitStatusCode;\n }\n // Unknown / typechange / etc. \u2192 treat as modified for display purposes.\n return \"M\";\n};\n\nexport const getStatusShort = async (cwd: string): Promise<ChangedFile[]> => {\n // -uall surfaces individual untracked files (otherwise dirs collapse).\n const out = await runGit(cwd, [\"status\", \"--short\", \"-uall\"]);\n return parseShortStatus(out);\n};\n\nconst runGitAllowingExit = (\n cwd: string,\n args: string[],\n allowedExitCodes: ReadonlySet<number>,\n): Promise<string> =>\n new Promise<string>((resolve, reject) => {\n execFile(\n \"git\",\n args,\n {\n cwd,\n maxBuffer: 16 * 1024 * 1024,\n env: { ...process.env, GIT_PAGER: \"cat\", PAGER: \"cat\" },\n },\n (err, stdout, stderr) => {\n if (!err) {\n resolve(stdout);\n return;\n }\n const e = err as NodeJS.ErrnoException & { code?: number };\n const exitCode = e.code ?? -1;\n if (allowedExitCodes.has(exitCode)) {\n resolve(stdout);\n return;\n }\n reject(new GitCommandError(args, exitCode, stderr));\n },\n );\n });\n\nconst getUntrackedFiles = async (cwd: string): Promise<string[]> => {\n const out = await runGit(cwd, [\n \"ls-files\",\n \"--others\",\n \"--exclude-standard\",\n \"-z\",\n ]);\n return out.split(\"\\0\").filter(Boolean);\n};\n\nconst getUntrackedFileDiff = async (\n cwd: string,\n file: string,\n): Promise<string> => {\n // `git diff --no-index` returns 1 when the two inputs differ, which is\n // success for our purpose of rendering a full-file addition.\n const diff = await runGitAllowingExit(\n cwd,\n [\"diff\", \"--no-index\", \"--\", \"/dev/null\", file],\n new Set([1]),\n );\n return diff\n .replace(/^diff --git a\\/dev\\/null b\\/(.+)$/m, \"diff --git a/$1 b/$1\")\n .replace(/^--- \\/dev\\/null$/m, \"--- /dev/null\");\n};\n\n/** Concatenated diff covering staged + unstaged tracked changes against\n * HEAD. Untracked files are appended as full-file additions so a newly\n * created page shows up in the review panel before it is staged. */\nexport const getDiff = async (cwd: string): Promise<string> => {\n const [trackedDiff, untrackedFiles] = await Promise.all([\n runGit(cwd, [\"diff\", \"HEAD\"]),\n getUntrackedFiles(cwd),\n ]);\n const untrackedDiffs = await Promise.all(\n untrackedFiles.map((file) => getUntrackedFileDiff(cwd, file)),\n );\n return [trackedDiff, ...untrackedDiffs]\n .filter((part) => part.trim().length > 0)\n .join(\"\\n\");\n};\n\n/** Run a git command, but write `input` to stdin. Used by `git apply` so\n * we can stream a unified diff in without writing it to a temp file. */\nconst runGitWithStdin = async (\n cwd: string,\n args: string[],\n input: string,\n): Promise<string> => {\n const { execFile } = await import(\"node:child_process\");\n return new Promise<string>((resolve, reject) => {\n const child = execFile(\n \"git\",\n args,\n {\n cwd,\n maxBuffer: 16 * 1024 * 1024,\n env: { ...process.env, GIT_PAGER: \"cat\", PAGER: \"cat\" },\n },\n (err, stdout, stderr) => {\n if (err) {\n const e = err as NodeJS.ErrnoException & { code?: number };\n reject(new GitCommandError(args, e.code ?? -1, stderr));\n return;\n }\n resolve(stdout);\n },\n );\n child.stdin?.write(input);\n child.stdin?.end();\n });\n};\n\n/** Returns true iff `git apply --check` accepts the diff (i.e. it would\n * apply cleanly against the current working tree). */\nexport const canApplyDiff = async (\n cwd: string,\n unified: string,\n): Promise<{ ok: true } | { ok: false; stderr: string }> => {\n try {\n await runGitWithStdin(cwd, [\"apply\", \"--check\"], unified);\n return { ok: true };\n } catch (err) {\n if (err instanceof GitCommandError) {\n return { ok: false, stderr: err.stderr };\n }\n throw err;\n }\n};\n\n/** Apply a unified diff to the working tree. Does NOT stage the changes \u2014\n * the user reviews the resulting working-tree state and commits via\n * their normal git workflow. */\nexport const applyDiff = async (\n cwd: string,\n unified: string,\n): Promise<void> => {\n await runGitWithStdin(cwd, [\"apply\"], unified);\n};\n\n/** Returns true iff `git apply --reverse --check` accepts the diff (i.e. the\n * working tree currently CONTAINS these changes and they can be cleanly\n * undone). Used by the revert flow: the coding engine already wrote the\n * changes to disk, so undoing them means reverse-applying the same diff. */\nexport const canRevertDiff = async (\n cwd: string,\n unified: string,\n): Promise<{ ok: true } | { ok: false; stderr: string }> => {\n try {\n await runGitWithStdin(cwd, [\"apply\", \"--reverse\", \"--check\"], unified);\n return { ok: true };\n } catch (err) {\n if (err instanceof GitCommandError) {\n return { ok: false, stderr: err.stderr };\n }\n throw err;\n }\n};\n\n/** Reverse-apply a unified diff, restoring the working tree to its state\n * before the diff's changes were written. Symmetric with applyDiff; does\n * NOT touch the index. */\nexport const revertDiff = async (\n cwd: string,\n unified: string,\n): Promise<void> => {\n await runGitWithStdin(cwd, [\"apply\", \"--reverse\"], unified);\n};\n\n/** Parse the per-file headers out of a unified diff. Returns repo-relative\n * paths in the order they appear. Used by the apply route to report\n * `paths` in the response without depending on git for it. */\nexport const parseDiffPaths = (unified: string): string[] => {\n const paths: string[] = [];\n for (const line of unified.split(\"\\n\")) {\n // `diff --git a/foo b/foo` \u2014 prefer the post-image path.\n const match = /^diff --git a\\/(.+) b\\/(.+)$/.exec(line);\n if (match) {\n paths.push(match[2]);\n }\n }\n return paths;\n};\n\n// ----------------------------------------------------------------------------\n// Branch / commit / push \u2014 used by the \"Create PR\" flow.\n// ----------------------------------------------------------------------------\n\n/** `git remote get-url origin`, or null when there's no `origin` remote. */\nexport const getRemoteUrl = async (cwd: string): Promise<string | null> => {\n try {\n return (await runGit(cwd, [\"remote\", \"get-url\", \"origin\"])).trim() || null;\n } catch (err) {\n if (err instanceof GitCommandError) return null;\n throw err;\n }\n};\n\n/** Best-effort default branch name (what a PR should target) \u2014 reads\n * `origin/HEAD`, falling back to \"main\" when it isn't configured. */\nexport const getDefaultBranch = async (cwd: string): Promise<string> => {\n try {\n const ref = (\n await runGit(cwd, [\"rev-parse\", \"--abbrev-ref\", \"origin/HEAD\"])\n ).trim();\n // \"origin/main\" \u2192 \"main\"\n const name = ref.replace(/^origin\\//, \"\");\n return name || \"main\";\n } catch (err) {\n if (err instanceof GitCommandError) return \"main\";\n throw err;\n }\n};\n\n/** Create + switch to a new branch from the current HEAD, carrying any\n * uncommitted working-tree changes onto it. */\nexport const createBranch = async (\n cwd: string,\n name: string,\n): Promise<void> => {\n await runGit(cwd, [\"checkout\", \"-b\", name]);\n};\n\n/** Stage everything and commit. Returns the new commit's short hash. */\nexport const commitAll = async (\n cwd: string,\n message: string,\n): Promise<string> => {\n await runGit(cwd, [\"add\", \"-A\"]);\n // -m twice would split into subject/body; we keep it to a single message.\n await runGit(cwd, [\"commit\", \"-m\", message]);\n return (await runGit(cwd, [\"rev-parse\", \"--short\", \"HEAD\"])).trim();\n};\n\n/** Push a branch to origin, setting upstream. */\nexport const pushBranch = async (\n cwd: string,\n branch: string,\n): Promise<void> => {\n await runGit(cwd, [\"push\", \"-u\", \"origin\", branch]);\n};\n\n/** Slugify a PR title into a safe `diologue/<slug>-<rand>` branch name. */\nexport const branchNameForTitle = (title: string): string => {\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 40)\n .replace(/-+$/g, \"\");\n const rand = Math.random().toString(36).slice(2, 7);\n return `diologue/${slug || \"change\"}-${rand}`;\n};\n", "// Repo-path validation.\n//\n// The helper is the sole authority on what disk locations get read or\n// written. Every path that crosses the HTTP boundary funnels through\n// validateRepoPath() before any git or filesystem operation runs against it.\n//\n// Rules:\n// 1. Must be an absolute path (no relative, no ~).\n// 2. Must exist on disk.\n// 3. Must be a directory (not a file).\n// 4. Must contain a `.git` entry (file for worktrees, dir otherwise).\n// 5. The realpath-resolved form is what gets stored & operated on, so\n// a symlinked path is transparently followed and we never end up\n// operating on a different location than the user can see.\n\nimport { access, lstat, realpath, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport class InvalidRepoPathError extends Error {\n readonly code: InvalidRepoPathCode;\n constructor(code: InvalidRepoPathCode, message: string) {\n super(message);\n this.name = \"InvalidRepoPathError\";\n this.code = code;\n }\n}\n\nexport type InvalidRepoPathCode =\n | \"not_a_string\"\n | \"empty\"\n | \"not_absolute\"\n | \"does_not_exist\"\n | \"not_a_directory\"\n | \"not_a_git_repo\";\n\nconst exists = async (p: string): Promise<boolean> => {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n};\n\nexport const validateRepoPath = async (raw: unknown): Promise<string> => {\n if (typeof raw !== \"string\") {\n throw new InvalidRepoPathError(\"not_a_string\", \"path must be a string\");\n }\n const trimmed = raw.trim();\n if (trimmed.length === 0) {\n throw new InvalidRepoPathError(\"empty\", \"path must not be empty\");\n }\n if (!path.isAbsolute(trimmed)) {\n throw new InvalidRepoPathError(\n \"not_absolute\",\n `path must be absolute (got: ${trimmed})`,\n );\n }\n\n // Resolve symlinks. If the symlink target is missing, realpath throws,\n // which we surface as does_not_exist for a tidy UI message.\n let resolved: string;\n try {\n resolved = await realpath(trimmed);\n } catch {\n throw new InvalidRepoPathError(\n \"does_not_exist\",\n `path does not exist: ${trimmed}`,\n );\n }\n\n const dirStat = await stat(resolved);\n if (!dirStat.isDirectory()) {\n throw new InvalidRepoPathError(\n \"not_a_directory\",\n `path is not a directory: ${resolved}`,\n );\n }\n\n // `.git` is a directory in normal clones, a regular file for git\n // worktrees (containing `gitdir: ...`). Either is fine \u2014 we just need\n // *something* there.\n const gitMarker = path.join(resolved, \".git\");\n if (!(await exists(gitMarker))) {\n throw new InvalidRepoPathError(\n \"not_a_git_repo\",\n `path does not contain a .git entry: ${resolved}`,\n );\n }\n // Defence in depth: confirm it's not a broken symlink pointing nowhere.\n await lstat(gitMarker);\n\n return resolved;\n};\n", "// Native \"choose folder\" dialog, run on the user's machine by the helper.\n//\n// The browser can't get an absolute filesystem path (the File System Access\n// API hides it), so when the user clicks \"Browse\u2026\" the cloud UI asks the\n// helper \u2014 which IS on their machine \u2014 to pop the OS folder picker and return\n// the chosen path. We then feed that into /repo/select.\n//\n// Everything goes through execFile (NOT a shell) to avoid quoting/injection.\n// The prompt string is a fixed constant, never user input.\n\nimport { execFile } from \"node:child_process\";\nimport { homedir } from \"node:os\";\n\nexport type PickDirectoryResult =\n | { ok: true; path: string }\n | { ok: false; reason: \"cancelled\" | \"no_gui\" | \"unsupported\" | \"failed\" };\n\ninterface DialogSpec {\n cmd: string;\n args: string[];\n}\n\nconst PROMPT = \"Select a git repository\";\n\n/** Build the picker command for a platform. Pure + exported for tests.\n * Returns null for platforms we don't have a dialog for. */\nexport const dialogSpec = (platform: NodeJS.Platform): DialogSpec | null => {\n switch (platform) {\n case \"darwin\":\n // AppleScript: choose folder \u2192 POSIX path. Cancel exits non-zero.\n return {\n cmd: \"osascript\",\n args: [\n \"-e\",\n `set theFolder to choose folder with prompt \"${PROMPT}\"`,\n \"-e\",\n \"POSIX path of theFolder\",\n ],\n };\n case \"linux\":\n // zenity is the common GTK picker; the runner falls back to kdialog.\n return {\n cmd: \"zenity\",\n args: [\"--file-selection\", \"--directory\", `--title=${PROMPT}`],\n };\n case \"win32\":\n // PowerShell's WinForms folder browser. Exits 1 (no output) on cancel.\n return {\n cmd: \"powershell\",\n args: [\n \"-NoProfile\",\n \"-Command\",\n \"Add-Type -AssemblyName System.Windows.Forms;\" +\n \"$d = New-Object System.Windows.Forms.FolderBrowserDialog;\" +\n `$d.Description = '${PROMPT}';` +\n \"if ($d.ShowDialog() -eq 'OK') { Write-Output $d.SelectedPath }\",\n ],\n };\n default:\n return null;\n }\n};\n\nconst run = (cmd: string, args: string[]): Promise<string> =>\n new Promise((resolve, reject) => {\n execFile(\n cmd,\n args,\n // Folder dialogs can sit open a while; cap it so the request can't hang\n // forever if the user wanders off. Killing it reads as \"cancelled\".\n { timeout: 120_000, windowsHide: true },\n (err, stdout) => {\n if (err) reject(err);\n else resolve(stdout);\n },\n );\n });\n\n/** Pop the OS folder dialog and return the chosen absolute path.\n * Distinguishes user-cancel from \"no GUI / tool missing\" so the UI can\n * fall back to manual typing with the right message. */\nexport const pickDirectory = async (): Promise<PickDirectoryResult> => {\n const spec = dialogSpec(process.platform);\n if (!spec) return { ok: false, reason: \"unsupported\" };\n\n const attempt = async (cmd: string, args: string[]): Promise<PickDirectoryResult> => {\n try {\n const out = (await run(cmd, args)).trim();\n // Empty output = the dialog was dismissed without a selection.\n return out ? { ok: true, path: out } : { ok: false, reason: \"cancelled\" };\n } catch (err) {\n const code = (err as { code?: string | number }).code;\n // The launcher binary isn't installed \u2192 no usable GUI on this box.\n if (code === \"ENOENT\") return { ok: false, reason: \"no_gui\" };\n // Any other non-zero exit from a present dialog is, in practice, the\n // user cancelling (osascript -128, zenity cancel, PowerShell no-OK).\n return { ok: false, reason: \"cancelled\" };\n }\n };\n\n const result = await attempt(spec.cmd, spec.args);\n // Linux fallback: if zenity isn't installed, try kdialog before giving up.\n if (\n !result.ok &&\n result.reason === \"no_gui\" &&\n process.platform === \"linux\"\n ) {\n return attempt(\"kdialog\", [\"--getexistingdirectory\", homedir()]);\n }\n return result;\n};\n", "// Thin wrapper over the GitHub CLI (`gh`), used by the \"Create PR\" flow.\n//\n// We lean on the user's existing local `gh` auth rather than building a cloud\n// OAuth integration (that's a later phase). Everything goes through execFile \u2014\n// no shell \u2014 and the title/body are passed as argv, never interpolated.\n\nimport { execFile } from \"node:child_process\";\n\nexport class GhError extends Error {\n constructor(\n message: string,\n readonly code: \"gh_unavailable\" | \"gh_failed\",\n readonly stderr?: string,\n ) {\n super(message);\n this.name = \"GhError\";\n }\n}\n\nconst run = (\n args: string[],\n cwd?: string,\n): Promise<{ stdout: string; stderr: string }> =>\n new Promise((resolve, reject) => {\n execFile(\n \"gh\",\n args,\n { cwd, timeout: 60_000, windowsHide: true },\n (err, stdout, stderr) => {\n if (err) {\n const code = (err as { code?: string | number }).code;\n if (code === \"ENOENT\") {\n reject(\n new GhError(\n \"The GitHub CLI (gh) isn't installed on this machine.\",\n \"gh_unavailable\",\n ),\n );\n return;\n }\n reject(new GhError(stderr || err.message, \"gh_failed\", stderr));\n return;\n }\n resolve({ stdout, stderr });\n },\n );\n });\n\n/** True when `gh` is installed AND authenticated. */\nexport const ghReady = async (): Promise<boolean> => {\n try {\n await run([\"auth\", \"status\"]);\n return true;\n } catch {\n return false;\n }\n};\n\nexport interface CreatePrOptions {\n cwd: string;\n base: string;\n head: string;\n title: string;\n body: string;\n}\n\n/** Open a pull request via `gh pr create` and return its URL (gh prints the\n * URL to stdout on success). */\nexport const createPullRequest = async (\n opts: CreatePrOptions,\n): Promise<string> => {\n const { stdout } = await run(\n [\n \"pr\",\n \"create\",\n \"--base\",\n opts.base,\n \"--head\",\n opts.head,\n \"--title\",\n opts.title,\n \"--body\",\n opts.body,\n ],\n opts.cwd,\n );\n // gh prints the PR URL as the last non-empty line.\n const url = stdout\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter(Boolean)\n .reverse()\n .find((l) => l.startsWith(\"http\"));\n if (!url) {\n throw new GhError(\"gh pr create didn't return a PR URL.\", \"gh_failed\", stdout);\n }\n return url;\n};\n", "// POST /agent/message \u2014 SSE stream of AgentStreamEvent envelopes from the\n// configured adapter (mock today; real opencode in Step 9).\n//\n// Wire format on the response:\n// Content-Type: text/event-stream\n// Each event: data: <json>\\n\\n\n// Terminal: data: [DONE]\\n\\n\n//\n// Why SSE rather than chunked JSON: it's what the existing app uses for\n// chat streaming (see server/routes.ts), and the browser's EventSource +\n// fetch+reader-loop patterns are already familiar to the codebase.\n\nimport type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport { z } from \"zod\";\n\nimport type {\n AgentStreamEvent,\n LlmBrokerResponsePayload,\n LlmStreamChunk,\n} from \"../../../shared/coding-agent-types\";\nimport type { AgentState } from \"../state\";\nimport type { AdapterHistoryMessage, OpenCodeAdapter } from \"../adapters\";\nimport { LlmBroker, type BrokerRegistry } from \"../broker\";\n\nconst historyRoleSchema = z.enum([\"user\", \"assistant\", \"system\", \"tool\"]);\n\nconst agentMessageBodySchema = z.object({\n sessionId: z.string().min(1),\n prompt: z.string().min(1).max(200_000),\n history: z\n .array(\n z.object({\n role: historyRoleSchema,\n content: z.string(),\n }),\n )\n .max(200)\n .optional(),\n /** Optional user-chosen routing for this turn. When absent the cloud\n * picks the default (CODING_AGENT_DEFAULT_PROVIDER/MODEL). */\n preferredProvider: z.string().min(1).max(100).optional(),\n preferredModel: z.string().min(1).max(200).optional(),\n /** Tool-gating policy. Absent \u2192 \"auto\" (run everything) so older clients\n * keep their current behaviour. */\n permissionMode: z.enum([\"auto\", \"confirm\"]).optional(),\n});\n\nconst writeEvent = (res: Response, event: AgentStreamEvent): void => {\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n};\n\nconst writeDone = (res: Response): void => {\n res.write(\"data: [DONE]\\n\\n\");\n};\n\nconst logAgentRoute = (message: string): void => {\n console.error(`[agent-route] ${message}`);\n};\n\nexport interface AgentRouterDeps {\n state: AgentState;\n adapter: OpenCodeAdapter;\n /** Registry that lets the sibling /agent/llm-response route find the\n * broker for a given session. */\n brokerRegistry: BrokerRegistry;\n}\n\nexport const createAgentRouter = (deps: AgentRouterDeps): Router => {\n const router = createRouter();\n\n router.post(\"/message\", async (req: Request, res: Response) => {\n const parsed = agentMessageBodySchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n\n const repo = deps.state.getSelectedRepo();\n if (!repo) {\n res.status(409).json({ error: \"no_repo_selected\" });\n return;\n }\n\n logAgentRoute(\n `message start session=${parsed.data.sessionId.slice(0, 8)} repo=${repo.path} promptChars=${parsed.data.prompt.length} provider=${parsed.data.preferredProvider ?? \"(default)\"} model=${parsed.data.preferredModel ?? \"(default)\"}`,\n );\n\n // SSE preamble. We set headers BEFORE any res.write so the browser\n // begins streaming immediately rather than buffering the response.\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache, no-transform\");\n res.setHeader(\"Connection\", \"keep-alive\");\n // Disable proxy buffering for nginx/Cloudflare-style frontends. This\n // matters less for localhost but costs nothing.\n res.setHeader(\"X-Accel-Buffering\", \"no\");\n res.flushHeaders?.();\n\n const controller = new AbortController();\n // We listen on `res.on(\"close\")` (NOT `req.on(\"close\")`). Express's\n // body parser consumes `req` and the request stream's 'close' fires\n // early \u2014 using it here aborts the adapter before its first yield.\n // `res.on(\"close\")` only fires once the response is torn down by the\n // client or the server, which is exactly when we want to stop.\n res.on(\"close\", () => {\n if (!res.writableEnded) {\n controller.abort();\n }\n });\n\n // Build the per-stream LLM broker and register it under this session\n // so /agent/llm-response can find it. The user-chosen provider/model\n // ride on the broker as overrides so every LLM call this turn lands\n // on the same backend.\n const broker = new LlmBroker({\n emitToStream: (event) => writeEvent(res, event),\n overrideProvider: parsed.data.preferredProvider,\n overrideModel: parsed.data.preferredModel,\n });\n deps.brokerRegistry.register(parsed.data.sessionId, broker);\n\n try {\n const history: AdapterHistoryMessage[] | undefined = parsed.data.history\n ? parsed.data.history.map((m) => ({ role: m.role, content: m.content }))\n : undefined;\n\n for await (const event of deps.adapter.streamMessage({\n sessionId: parsed.data.sessionId,\n repoPath: repo.path,\n prompt: parsed.data.prompt,\n history,\n signal: controller.signal,\n broker: (payload, observer) => broker.request(payload, observer),\n preferredProvider: parsed.data.preferredProvider,\n preferredModel: parsed.data.preferredModel,\n permissionMode: parsed.data.permissionMode,\n })) {\n if (controller.signal.aborted) {\n logAgentRoute(\n `message aborted session=${parsed.data.sessionId.slice(0, 8)}`,\n );\n break;\n }\n logAgentRoute(\n `stream event session=${parsed.data.sessionId.slice(0, 8)} type=${event.type}`,\n );\n writeEvent(res, event);\n }\n logAgentRoute(\n `message done session=${parsed.data.sessionId.slice(0, 8)}`,\n );\n writeDone(res);\n } catch (err) {\n // Abort is the expected exit when req.on('close') fired \u2014 don't\n // emit an error event for it.\n if (\n err &&\n typeof err === \"object\" &&\n \"name\" in err &&\n (err as { name?: string }).name === \"AbortError\"\n ) {\n writeDone(res);\n } else {\n const message =\n err instanceof Error ? err.message : \"Adapter stream failed\";\n logAgentRoute(\n `message error session=${parsed.data.sessionId.slice(0, 8)} error=${message}`,\n );\n writeEvent(res, { type: \"error\", message });\n writeDone(res);\n }\n } finally {\n // Tear down the broker so any in-flight requests reject rather than\n // dangle. Idempotent \u2014 safe even if the adapter already finished.\n broker.close(\"stream_closed\");\n deps.brokerRegistry.unregister(parsed.data.sessionId, broker);\n res.end();\n }\n });\n\n return router;\n};\n\n// ----------------------------------------------------------------------------\n// /agent/llm-response/:requestId\n// ----------------------------------------------------------------------------\n//\n// The browser POSTs the completed LLM response here. The route looks up\n// the broker bound to the session, fulfils the requestId, and returns\n// ok/no-match. Lives in this module rather than its own file because it\n// shares the broker dependency with the SSE route.\n\nconst llmResponseBodySchema = z.object({\n sessionId: z.string().min(1),\n text: z.string(),\n provider: z.string().min(1),\n model: z.string().min(1),\n tokenUsage: z\n .object({\n input: z.number().nonnegative(),\n output: z.number().nonnegative(),\n })\n .optional(),\n error: z.string().optional(),\n toolCalls: z\n .array(\n z.object({\n id: z.string().min(1),\n type: z.literal(\"function\"),\n function: z.object({\n name: z.string().min(1),\n arguments: z.string(),\n }),\n }),\n )\n .optional(),\n finishReason: z\n .enum([\"stop\", \"tool_calls\", \"length\", \"content_filter\"])\n .optional(),\n});\n\nexport interface LlmResponseRouterDeps {\n brokerRegistry: BrokerRegistry;\n}\n\nconst llmChunkBodySchema = z.object({\n sessionId: z.string().min(1),\n chunk: z.union([\n z.object({\n type: z.literal(\"text_delta\"),\n text: z.string(),\n }),\n z.object({\n type: z.literal(\"tool_call_delta\"),\n index: z.number().int().nonnegative(),\n id: z.string().optional(),\n name: z.string().optional(),\n argumentsDelta: z.string().optional(),\n }),\n z.object({\n type: z.literal(\"complete\"),\n payload: z.object({\n text: z.string(),\n provider: z.string(),\n model: z.string(),\n tokenUsage: z\n .object({\n input: z.number().nonnegative(),\n output: z.number().nonnegative(),\n })\n .optional(),\n finishReason: z\n .enum([\"stop\", \"tool_calls\", \"length\", \"content_filter\"])\n .optional(),\n toolCalls: z\n .array(\n z.object({\n id: z.string(),\n type: z.literal(\"function\"),\n function: z.object({\n name: z.string(),\n arguments: z.string(),\n }),\n }),\n )\n .optional(),\n }),\n }),\n z.object({\n type: z.literal(\"error\"),\n message: z.string(),\n }),\n ]),\n});\n\nexport const createLlmResponseRouter = (\n deps: LlmResponseRouterDeps,\n): Router => {\n const router = createRouter();\n\n router.post(\"/:requestId\", (req: Request, res: Response) => {\n const requestId = req.params.requestId;\n if (!requestId) {\n res.status(400).json({ error: \"missing_request_id\" });\n return;\n }\n const parsed = llmResponseBodySchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n const broker = deps.brokerRegistry.get(parsed.data.sessionId);\n if (!broker) {\n res.status(404).json({ error: \"no_active_stream_for_session\" });\n return;\n }\n if (!broker.has(requestId)) {\n res.status(404).json({ error: \"unknown_request_id\" });\n return;\n }\n if (parsed.data.error) {\n broker.fail(requestId, parsed.data.error);\n res.json({ ok: true });\n return;\n }\n const payload: LlmBrokerResponsePayload = {\n text: parsed.data.text,\n provider: parsed.data.provider,\n model: parsed.data.model,\n tokenUsage: parsed.data.tokenUsage,\n ...(parsed.data.toolCalls ? { toolCalls: parsed.data.toolCalls } : {}),\n ...(parsed.data.finishReason\n ? { finishReason: parsed.data.finishReason }\n : {}),\n };\n broker.fulfill(requestId, payload);\n res.json({ ok: true });\n });\n\n return router;\n};\n\n/** /agent/llm-chunk/:requestId \u2014 sibling of /agent/llm-response for the\n * streaming brokered-LLM path. The browser POSTs one chunk per Anthropic\n * delta as the cloud SSE emits them, and a final `complete` chunk that\n * resolves the broker's pending Promise. */\nexport const createLlmChunkRouter = (deps: LlmResponseRouterDeps): Router => {\n const router = createRouter();\n\n router.post(\"/:requestId\", (req: Request, res: Response) => {\n const requestId = req.params.requestId;\n if (!requestId) {\n res.status(400).json({ error: \"missing_request_id\" });\n return;\n }\n const parsed = llmChunkBodySchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n const broker = deps.brokerRegistry.get(parsed.data.sessionId);\n if (!broker) {\n res.status(404).json({ error: \"no_active_stream_for_session\" });\n return;\n }\n const accepted = broker.pushChunk(\n requestId,\n parsed.data.chunk as LlmStreamChunk,\n );\n if (!accepted) {\n res.status(404).json({ error: \"unknown_request_id\" });\n return;\n }\n res.json({ ok: true });\n });\n\n return router;\n};\n\n// ----------------------------------------------------------------------------\n// /agent/permission\n// ----------------------------------------------------------------------------\n//\n// In confirm mode the adapter pauses bash/webfetch and emits a\n// permission_request over the SSE stream. The browser shows Allow/Deny and\n// POSTs the decision here \u2014 on a SEPARATE request from the stream, mirroring\n// /agent/llm-response. We hand it to the adapter, which replies to opencode\n// and lets the paused turn resume.\n\nconst permissionDecisionBodySchema = z.object({\n sessionId: z.string().min(1),\n permissionId: z.string().min(1),\n response: z.enum([\"once\", \"always\", \"reject\"]),\n});\n\nexport interface PermissionRouterDeps {\n adapter: OpenCodeAdapter;\n}\n\nexport const createPermissionRouter = (deps: PermissionRouterDeps): Router => {\n const router = createRouter();\n\n router.post(\"/\", async (req: Request, res: Response) => {\n const parsed = permissionDecisionBodySchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n if (!deps.adapter.replyPermission) {\n res.status(501).json({ error: \"permissions_unsupported\" });\n return;\n }\n const matched = await deps.adapter.replyPermission(\n parsed.data.sessionId,\n parsed.data.permissionId,\n parsed.data.response,\n );\n if (!matched) {\n // No in-flight turn for this session, or the permission already\n // resolved/expired. Non-fatal \u2014 the browser just drops the prompt.\n res.status(404).json({ error: \"no_active_permission\" });\n return;\n }\n res.json({ ok: true });\n });\n\n return router;\n};\n", "// LLM broker for a single in-flight /agent/message stream.\n//\n// Lifecycle:\n// 1. The SSE route in agent.ts builds a Broker per stream.\n// 2. The route hands `broker.request(...)` to the adapter as\n// `request.broker`.\n// 3. When the adapter calls `broker.request(payload)`:\n// a. Broker generates a fresh requestId.\n// b. Broker stores `{ resolve, reject }` in a pending map.\n// c. Broker calls the SSE-emit hook (provided by the route) with an\n// `llm_request` envelope carrying the requestId + payload.\n// d. The browser receives the envelope, makes a cloud LLM call,\n// and POSTs the response to `/agent/llm-response/:requestId`\n// (with sessionId in the body for registry lookup).\n// e. That route asks the registry for the broker bound to this\n// sessionId and calls `broker.fulfill(requestId, response)`,\n// which resolves the pending promise.\n// 4. The adapter receives the response and continues iteration.\n//\n// Why this design:\n// - The helper holds no cloud credentials. The browser carries them.\n// - One broker per stream keeps requestIds unique within a stream and\n// guarantees no cross-talk between concurrent /agent/message calls.\n// - The pending map is local to each broker (and so to each stream),\n// so we cannot accidentally fulfill a request from a different stream.\n\nimport { randomUUID } from \"node:crypto\";\n\nimport type {\n AgentLlmRequest,\n LlmBrokerRequestPayload,\n LlmBrokerResponsePayload,\n LlmStreamChunk,\n} from \"../../shared/coding-agent-types\";\n\nconst logBroker = (message: string): void => {\n console.error(`[llm-broker] ${message}`);\n};\n\n/** Hook adapters supply to observe incremental tokens as they stream\n * from the cloud. When set, the broker invokes it for every chunk\n * posted to /agent/llm-chunk/:requestId; the final `complete` chunk\n * also resolves the request's Promise. */\nexport type StreamObserver = (chunk: LlmStreamChunk) => void;\n\nexport interface BrokerOptions {\n /** Emits an SSE envelope on the parent /agent/message response.\n * Implemented by the route; injected so the broker stays decoupled\n * from Express. */\n emitToStream(event: AgentLlmRequest): void;\n /** Hard ceiling on how long a single LLM brokered call may take. The\n * browser is a network hop + the cloud is a model call; 90s is\n * comfortable headroom for non-streaming completions. */\n timeoutMs?: number;\n /** Override the provider on every request that flows through this\n * broker. The user-chosen provider from the chat UI rides on the\n * AdapterRequest and lands here at broker-construction time. */\n overrideProvider?: string;\n /** Override the model. Same rationale as overrideProvider \u2014 the\n * adapter doesn't always know what the user wants (e.g. opencode\n * hard-codes our placeholder model id in its outbound HTTP). */\n overrideModel?: string;\n}\n\ninterface PendingRequest {\n resolve(value: LlmBrokerResponsePayload): void;\n reject(reason: Error): void;\n timer: NodeJS.Timeout;\n observer?: StreamObserver;\n}\n\nexport class LlmBroker {\n private readonly pending = new Map<string, PendingRequest>();\n private readonly emit: BrokerOptions[\"emitToStream\"];\n private readonly timeoutMs: number;\n private readonly overrideProvider?: string;\n private readonly overrideModel?: string;\n private closed = false;\n\n constructor(options: BrokerOptions) {\n this.emit = options.emitToStream;\n this.timeoutMs = options.timeoutMs ?? 90_000;\n this.overrideProvider = options.overrideProvider;\n this.overrideModel = options.overrideModel;\n }\n\n /** Called by the adapter when it needs an LLM call. Resolves with the\n * full LlmBrokerResponsePayload once the browser fulfils it. Rejects\n * on timeout, broker close, or explicit failure from the browser.\n *\n * Pass `observer` to receive incremental stream chunks if the browser\n * fulfils via the streaming chunk endpoint. Observer is called once\n * per chunk; the Promise still resolves with the final aggregate\n * payload when the stream completes. */\n request(\n payload: LlmBrokerRequestPayload,\n observer?: StreamObserver,\n ): Promise<LlmBrokerResponsePayload> {\n if (this.closed) {\n return Promise.reject(new Error(\"broker is closed\"));\n }\n const requestId = randomUUID();\n return new Promise<LlmBrokerResponsePayload>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(requestId);\n logBroker(\n `timeout request=${requestId.slice(0, 8)} pending=${this.pending.size} after=${this.timeoutMs}ms`,\n );\n reject(\n new Error(\n `llm_broker_timeout: no response within ${this.timeoutMs}ms`,\n ),\n );\n }, this.timeoutMs);\n // Unref long-lived production timers so a stranded broker does not\n // keep the helper alive on shutdown. Keep short test/demo timeouts\n // ref'ed so their callbacks can fire before node:test exits.\n if (this.timeoutMs >= 1_000) {\n timer.unref?.();\n }\n this.pending.set(requestId, { resolve, reject, timer, observer });\n\n // Provider/model precedence: explicit broker override > payload's\n // declared provider/model. Opencode's outbound HTTP carries our\n // placeholder model id; we override it here with the user's\n // selection so the cloud routes to the right backend.\n const effectiveProvider = this.overrideProvider ?? payload.provider;\n const effectiveModel = this.overrideModel ?? payload.model;\n const envelope: AgentLlmRequest = {\n type: \"llm_request\",\n requestId,\n provider: effectiveProvider,\n model: effectiveModel,\n payload: {\n messages: payload.messages,\n tools: payload.tools,\n temperature: payload.temperature,\n systemPrompt: payload.systemPrompt,\n },\n };\n try {\n logBroker(\n `emit request=${requestId.slice(0, 8)} provider=${effectiveProvider ?? \"(default)\"} model=${effectiveModel ?? \"(default)\"} messages=${payload.messages.length} tools=${payload.tools?.length ?? 0}`,\n );\n this.emit(envelope);\n } catch (err) {\n clearTimeout(timer);\n this.pending.delete(requestId);\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n }\n\n /** True iff this broker has a pending request with the given id. Used\n * by the registry to route /agent/llm-response/:requestId without\n * needing the caller to supply sessionId. */\n has(requestId: string): boolean {\n return this.pending.has(requestId);\n }\n\n /** Called by the /agent/llm-response route once the browser has POSTed\n * the completion back. Returns true if a pending request matched.\n *\n * If the request was registered with an observer (streaming flow),\n * the observer is also invoked with a synthesised text_delta + a\n * `complete` chunk \u2014 this makes the streaming path work even when\n * the browser fulfils via the non-streaming /agent/llm-response\n * route (e.g. for providers that don't support streaming). */\n fulfill(requestId: string, response: LlmBrokerResponsePayload): boolean {\n const entry = this.pending.get(requestId);\n if (!entry) {\n logBroker(`fulfill_miss request=${requestId.slice(0, 8)}`);\n return false;\n }\n const toolNames = (response.toolCalls ?? [])\n .map((tc) => tc.function?.name ?? \"?\")\n .join(\",\");\n logBroker(\n `fulfill request=${requestId.slice(0, 8)} text=${response.text.length} toolCalls=${response.toolCalls?.length ?? 0}${toolNames ? ` tools=[${toolNames}]` : \"\"} finish=${response.finishReason ?? \"(none)\"}`,\n );\n clearTimeout(entry.timer);\n this.pending.delete(requestId);\n if (entry.observer) {\n try {\n if (response.text) {\n entry.observer({ type: \"text_delta\", text: response.text });\n }\n // Synthesise a tool_call_delta per tool call so the observer\n // (and the shim translators downstream) see them. The\n // non-streaming fulfil path previously emitted only text, so a\n // tool-only turn \u2014 which is what every coding task produces\n // (write/edit/bash) \u2014 reached opencode as an empty message with\n // no tool calls. The agent then did nothing: no file, no diff,\n // just a metered LLM round-trip. Because we already hold the\n // complete payload, each tool call is emitted as a single delta\n // carrying id + name + full arguments (no need to chunk args).\n for (const [index, tc] of (response.toolCalls ?? []).entries()) {\n entry.observer({\n type: \"tool_call_delta\",\n index,\n id: tc.id,\n name: tc.function.name,\n argumentsDelta: tc.function.arguments,\n });\n }\n entry.observer({ type: \"complete\", payload: response });\n } catch {\n // Observer errors are isolated from broker state.\n }\n }\n entry.resolve(response);\n return true;\n }\n\n /** Called by the /agent/llm-chunk/:requestId route when the browser\n * forwards an incremental stream chunk. The observer (if any) is\n * invoked; a `complete` chunk also resolves the pending Promise. */\n pushChunk(requestId: string, chunk: LlmStreamChunk): boolean {\n const entry = this.pending.get(requestId);\n if (!entry) {\n logBroker(\n `chunk_miss request=${requestId.slice(0, 8)} type=${chunk.type}`,\n );\n return false;\n }\n logBroker(\n `chunk request=${requestId.slice(0, 8)} type=${chunk.type}${chunk.type === \"text_delta\" ? ` chars=${chunk.text.length}` : \"\"}${chunk.type === \"tool_call_delta\" ? ` index=${chunk.index} name=${chunk.name ?? \"(delta)\"} argsDelta=${chunk.argumentsDelta?.length ?? 0}` : \"\"}`,\n );\n if (entry.observer) {\n try {\n entry.observer(chunk);\n } catch {\n // Observer errors must not break the broker \u2014 adapter's loop\n // is what cares about them.\n }\n }\n if (chunk.type === \"complete\") {\n clearTimeout(entry.timer);\n this.pending.delete(requestId);\n entry.resolve(chunk.payload);\n } else if (chunk.type === \"error\") {\n clearTimeout(entry.timer);\n this.pending.delete(requestId);\n entry.reject(new Error(chunk.message));\n }\n return true;\n }\n\n /** Called by the /agent/llm-response route when the browser reports a\n * failure rather than a completion. */\n fail(requestId: string, message: string): boolean {\n const entry = this.pending.get(requestId);\n if (!entry) {\n logBroker(`fail_miss request=${requestId.slice(0, 8)}`);\n return false;\n }\n logBroker(`fail request=${requestId.slice(0, 8)} message=${message}`);\n clearTimeout(entry.timer);\n this.pending.delete(requestId);\n entry.reject(new Error(message));\n return true;\n }\n\n /** Reject all in-flight requests. Called when the parent SSE stream\n * closes so a stranded request doesn't sit forever. */\n close(reason = \"broker_closed\"): void {\n if (this.pending.size > 0) {\n logBroker(`close reason=${reason} pending=${this.pending.size}`);\n }\n this.closed = true;\n for (const [, entry] of this.pending.entries()) {\n clearTimeout(entry.timer);\n entry.reject(new Error(reason));\n }\n this.pending.clear();\n }\n\n get pendingCount(): number {\n return this.pending.size;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Registry \u2014 one broker per active stream, indexed by sessionId.\n// ----------------------------------------------------------------------------\n//\n// /agent/llm-response/:requestId arrives on a SEPARATE HTTP request from\n// the browser. It needs to find the broker that emitted the originating\n// llm_request. We index by sessionId because that's what the chat panel\n// already tracks; the response route requires sessionId in its body so\n// the lookup is O(1).\n\nexport interface BrokerRegistry {\n /** Bind a broker to a session. If a prior broker is bound for the same\n * session, it is closed (its pending requests reject) \u2014 this handles\n * the case of the browser reconnecting without a clean prior close. */\n register(sessionId: string, broker: LlmBroker): void;\n /** Remove the binding for a session. Idempotent. */\n unregister(sessionId: string, broker: LlmBroker): void;\n /** Look up the broker for a session. Returns null if nothing is bound. */\n get(sessionId: string): LlmBroker | null;\n}\n\nexport const createBrokerRegistry = (): BrokerRegistry => {\n const bySession = new Map<string, LlmBroker>();\n return {\n register(sessionId, broker) {\n const existing = bySession.get(sessionId);\n if (existing && existing !== broker) {\n existing.close(\"superseded_by_new_stream\");\n }\n bySession.set(sessionId, broker);\n },\n unregister(sessionId, broker) {\n // Only unbind if it's still THIS broker (a concurrent register\n // may have replaced it).\n if (bySession.get(sessionId) === broker) {\n bySession.delete(sessionId);\n }\n },\n get(sessionId) {\n return bySession.get(sessionId) ?? null;\n },\n };\n};\n", "// POST /llm-shim/v1/chat/completions \u2014 OpenAI-compatible endpoint that\n// opencode targets via its custom-provider config.\n//\n// Wire-level expectations from opencode (via AI SDK 6's openai provider):\n// - Accepts a Chat Completions request with messages, tools, stream.\n// - Returns either a single JSON body or SSE chunks (depending on\n// `stream`).\n// - Authentication via `Authorization: Bearer <apiKey>`.\n\nimport type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\n\nimport {\n type OpenAIChatCompletionRequest,\n translateRequest,\n translateResponse,\n} from \"../shim/openai-translator\";\nimport {\n type ResponsesApiRequest,\n ResponsesStreamState,\n responsesToChatCompletions,\n} from \"../shim/responses-translator\";\nimport {\n _activeBrokerCount,\n getActiveBrokerByToken,\n getOnlyActiveBroker,\n} from \"../shim/active-broker\";\nimport type { LlmBroker } from \"../broker\";\nimport type { LlmStreamChunk } from \"../../../shared/coding-agent-types\";\n\nconst extractBearer = (req: Request): string | null => {\n const header = req.header(\"authorization\");\n if (!header) return null;\n if (!header.toLowerCase().startsWith(\"bearer \")) return null;\n return header.slice(7).trim() || null;\n};\n\nconst sendOpenAIError = (\n res: Response,\n status: number,\n message: string,\n type = \"invalid_request_error\",\n): void => {\n // OpenAI's error envelope shape \u2014 opencode parses these.\n res.status(status).json({ error: { message, type } });\n};\n\n/** Resolve the LlmBroker for an inbound shim request, or send the 401\n * and return null. Shared by BOTH shim routes (/v1/chat/completions\n * and /v1/responses) so the auth logic can never drift between them\n * again \u2014 a previous bug shipped the placeholder fallback on only one\n * route, and opencode (which uses /v1/responses) 401'd forever.\n *\n * Resolution order:\n * 1. Exact token match (the per-stream token, if opencode could\n * carry it \u2014 which it currently can't).\n * 2. Single-active-broker fallback: opencode's @ai-sdk/openai\n * provider sends a fixed placeholder bearer for every call, so\n * when exactly one stream is live we use it. See active-broker.ts\n * getOnlyActiveBroker for the multi-stream caveat. */\nconst resolveBrokerOr401 = (\n req: Request,\n res: Response,\n): LlmBroker | null => {\n const token = extractBearer(req);\n if (!token) {\n sendOpenAIError(res, 401, \"Missing Authorization bearer\", \"auth_error\");\n return null;\n }\n const broker = getActiveBrokerByToken(token) ?? getOnlyActiveBroker();\n if (!broker) {\n const tokenPrefix =\n token.length > 12 ? `${token.slice(0, 12)}\u2026` : token;\n console.error(\n `[llm-shim] 401 on ${req.path}: bearer=\"${tokenPrefix}\" active_brokers=${_activeBrokerCount()}`,\n );\n sendOpenAIError(\n res,\n 401,\n \"No active coding-agent stream is currently authorised on this helper.\",\n \"auth_error\",\n );\n return null;\n }\n return broker;\n};\n\nexport const createLlmShimRouter = (): Router => {\n const router = createRouter();\n\n router.post(\n \"/v1/chat/completions\",\n async (req: Request, res: Response) => {\n const broker = resolveBrokerOr401(req, res);\n if (!broker) return;\n\n // Minimal request validation \u2014 leave the heavy lifting to the\n // translator. We accept extra fields gracefully (`[key]: unknown`\n // catch-all in the request type).\n const body = req.body as OpenAIChatCompletionRequest | undefined;\n if (\n !body ||\n typeof body !== \"object\" ||\n !Array.isArray(body.messages) ||\n body.messages.length === 0\n ) {\n sendOpenAIError(res, 400, \"messages[] is required and must be non-empty\");\n return;\n }\n\n const brokerRequest = translateRequest(body);\n\n const completionId = `chatcmpl-${Math.random().toString(36).slice(2, 10)}`;\n const stream = body.stream === true;\n\n // True streaming path: open the SSE response NOW, attach a\n // stream observer to the broker, forward every Anthropic delta\n // as an OpenAI chunk in real time. The final `complete` chunk\n // resolves the broker's promise \u2014 we then send the finish chunk\n // and [DONE].\n if (stream) {\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache, no-transform\");\n res.setHeader(\"Connection\", \"keep-alive\");\n res.flushHeaders?.();\n const writeOpenAi = (delta: unknown): void => {\n res.write(`data: ${JSON.stringify(delta)}\\n\\n`);\n };\n // Emit the role chunk straight away so AI-SDK consumers see\n // the assistant turn start.\n writeOpenAi({\n id: completionId,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model: body.model,\n choices: [\n { index: 0, delta: { role: \"assistant\" }, finish_reason: null },\n ],\n });\n // Forward incremental chunks. Index tracking maps Anthropic's\n // content-block index \u2192 OpenAI's tool_call index (we collapse\n // text deltas; tool_call deltas keep their index).\n // Held by the observer closure; the outer scope reads it after\n // broker.request() resolves. We type it via a single-element\n // array to dodge the closure-narrowing-to-null issue.\n const finalPayloadRef: {\n current: ReturnType<typeof translateResponse> | null;\n } = { current: null };\n const observer = (chunk: LlmStreamChunk): void => {\n switch (chunk.type) {\n case \"text_delta\":\n writeOpenAi({\n id: completionId,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model: body.model,\n choices: [\n {\n index: 0,\n delta: { content: chunk.text },\n finish_reason: null,\n },\n ],\n });\n break;\n case \"tool_call_delta\":\n writeOpenAi({\n id: completionId,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model: body.model,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: chunk.index,\n ...(chunk.id ? { id: chunk.id } : {}),\n ...(chunk.id || chunk.name\n ? { type: \"function\" as const }\n : {}),\n ...(chunk.name || chunk.argumentsDelta !== undefined\n ? {\n function: {\n ...(chunk.name ? { name: chunk.name } : {}),\n ...(chunk.argumentsDelta !== undefined\n ? { arguments: chunk.argumentsDelta }\n : {}),\n },\n }\n : {}),\n },\n ],\n },\n finish_reason: null,\n },\n ],\n });\n break;\n case \"complete\":\n finalPayloadRef.current = translateResponse(\n chunk.payload,\n completionId,\n body.model,\n );\n break;\n case \"error\":\n // Errors are surfaced by the request() Promise rejection\n // catch below; nothing to write here.\n break;\n }\n };\n try {\n await broker.request(brokerRequest, observer);\n } catch (err) {\n writeOpenAi({\n id: completionId,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model: body.model,\n choices: [\n {\n index: 0,\n delta: {},\n finish_reason: \"stop\" as const,\n },\n ],\n });\n res.write(\"data: [DONE]\\n\\n\");\n res.end();\n void err;\n return;\n }\n // Final finish chunk + [DONE].\n const finishReason =\n finalPayloadRef.current?.choices[0]?.finish_reason ?? \"stop\";\n writeOpenAi({\n id: completionId,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model: body.model,\n choices: [\n {\n index: 0,\n delta: {},\n finish_reason: finishReason,\n },\n ],\n });\n res.write(\"data: [DONE]\\n\\n\");\n res.end();\n return;\n }\n\n let brokerResponse;\n try {\n brokerResponse = await broker.request(brokerRequest);\n } catch (err) {\n sendOpenAIError(\n res,\n 502,\n err instanceof Error ? err.message : \"Broker call failed\",\n \"api_error\",\n );\n return;\n }\n\n if (brokerResponse.error) {\n sendOpenAIError(res, 502, brokerResponse.error, \"api_error\");\n return;\n }\n\n // Non-streaming response.\n const out = translateResponse(brokerResponse, completionId, body.model);\n res.json(out);\n },\n );\n\n // ----------------------------------------------------------------------\n // POST /v1/responses \u2014 OpenAI Responses API endpoint\n //\n // opencode 1.x (via @ai-sdk/openai v3+) defaults to the Responses API\n // shape rather than the older Chat Completions one. We translate the\n // request into Chat Completions, reuse the broker layer, and translate\n // streaming chunks back into Responses-API SSE events.\n // ----------------------------------------------------------------------\n router.post(\"/v1/responses\", async (req: Request, res: Response) => {\n const broker = resolveBrokerOr401(req, res);\n if (!broker) return;\n\n const body = req.body as ResponsesApiRequest | undefined;\n if (\n !body ||\n typeof body !== \"object\" ||\n !Array.isArray(body.input) ||\n body.input.length === 0\n ) {\n sendOpenAIError(res, 400, \"input[] is required and must be non-empty\");\n return;\n }\n\n const chatRequest = responsesToChatCompletions(body);\n const brokerRequest = translateRequest(chatRequest);\n const stream = body.stream === true;\n\n if (stream) {\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache, no-transform\");\n res.setHeader(\"Connection\", \"keep-alive\");\n res.flushHeaders?.();\n\n const state = new ResponsesStreamState(body.model);\n const writeEvent = (ev: { event: string; data: unknown }): void => {\n // Responses API SSE wire: an `event:` line + a `data:` line with\n // the JSON payload, separated by a blank line. The AI SDK uses\n // the event name as the type discriminator.\n res.write(`event: ${ev.event}\\n`);\n res.write(`data: ${JSON.stringify(ev.data)}\\n\\n`);\n };\n\n for (const ev of state.start()) writeEvent(ev);\n\n const observer = (chunk: LlmStreamChunk): void => {\n for (const ev of state.chunk(chunk)) writeEvent(ev);\n };\n\n try {\n await broker.request(brokerRequest, observer);\n } catch (err) {\n // Emit response.failed so opencode surfaces a clean error.\n writeEvent({\n event: \"response.failed\",\n data: {\n type: \"response.failed\",\n response: {\n id: `resp_${Math.random().toString(36).slice(2, 12)}`,\n object: \"response\",\n status: \"failed\",\n model: body.model,\n error: {\n code: \"broker_error\",\n message:\n err instanceof Error ? err.message : \"Broker call failed\",\n },\n },\n },\n });\n res.write(\"data: [DONE]\\n\\n\");\n res.end();\n return;\n }\n\n for (const ev of state.finish()) writeEvent(ev);\n res.write(\"data: [DONE]\\n\\n\");\n res.end();\n return;\n }\n\n // Non-streaming path: collect deltas through the broker, then\n // assemble a single response object. Use the existing Chat\n // Completions translator to consolidate, then re-shape into the\n // Responses API top-level response object.\n let brokerResponse;\n try {\n brokerResponse = await broker.request(brokerRequest);\n } catch (err) {\n sendOpenAIError(\n res,\n 502,\n err instanceof Error ? err.message : \"Broker call failed\",\n \"api_error\",\n );\n return;\n }\n if (brokerResponse.error) {\n sendOpenAIError(res, 502, brokerResponse.error, \"api_error\");\n return;\n }\n const completionId = `resp_${Math.random().toString(36).slice(2, 12)}`;\n const chatResponse = translateResponse(\n brokerResponse,\n completionId,\n body.model,\n );\n const text =\n chatResponse.choices[0]?.message?.content ?? \"\";\n const toolCalls = chatResponse.choices[0]?.message?.tool_calls ?? [];\n const output: Array<Record<string, unknown>> = [];\n if (text) {\n output.push({\n id: `msg_${Math.random().toString(36).slice(2, 12)}`,\n type: \"message\",\n status: \"completed\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text }],\n });\n }\n for (const tc of toolCalls) {\n output.push({\n id: `fc_${Math.random().toString(36).slice(2, 12)}`,\n type: \"function_call\",\n status: \"completed\",\n name: tc.function.name,\n call_id: tc.id,\n arguments: tc.function.arguments,\n });\n }\n res.json({\n id: completionId,\n object: \"response\",\n created_at: Math.floor(Date.now() / 1000),\n status: \"completed\",\n model: body.model,\n output,\n usage: chatResponse.usage,\n });\n });\n\n return router;\n};\n", "// Translate OpenAI Chat Completions wire format \u2194 our broker payloads.\n//\n// This is pure-function on purpose so it's covered exhaustively by unit\n// tests without spinning up Express or opencode. The shim route in\n// routes/llm-shim.ts is then ~30 lines of HTTP plumbing on top.\n//\n// Why OpenAI Chat Completions specifically: opencode's provider config\n// supports any AI-SDK provider with a custom `baseURL` + `apiKey`, and\n// every other provider speaks this wire format too via OpenAI-compatible\n// shims. Building one translator covers the common path.\n\nimport type {\n CodingMessageRole,\n LlmBrokerRequestPayload,\n LlmBrokerResponsePayload,\n} from \"../../../shared/coding-agent-types\";\n\n// ----------------------------------------------------------------------------\n// OpenAI request shapes (subset we accept)\n// ----------------------------------------------------------------------------\n\nexport interface OpenAIChatCompletionRequest {\n model: string;\n messages: OpenAIChatMessage[];\n tools?: OpenAITool[];\n tool_choice?: \"auto\" | \"none\" | \"required\" | { type: \"function\"; function: { name: string } };\n temperature?: number;\n stream?: boolean;\n /** Many clients send these; we ignore them (top_p, n, max_tokens,\n * presence_penalty, frequency_penalty, etc.). They flow through to\n * the cloud provider via `payload.tools`'s catch-all if needed. */\n [key: string]: unknown;\n}\n\nexport interface OpenAIChatMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | null;\n name?: string;\n tool_call_id?: string;\n tool_calls?: Array<{\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }>;\n}\n\nexport interface OpenAITool {\n type: \"function\";\n function: {\n name: string;\n description?: string;\n parameters?: Record<string, unknown>;\n };\n}\n\n// ----------------------------------------------------------------------------\n// Request: OpenAI \u2192 broker\n// ----------------------------------------------------------------------------\n\n/** Pulls the system message out of the OpenAI message list and returns\n * the remaining messages flattened to the broker's role union. */\nexport const translateRequest = (\n req: OpenAIChatCompletionRequest,\n): LlmBrokerRequestPayload => {\n let systemPrompt: string | undefined;\n const messages: Array<{ role: CodingMessageRole; content: string }> = [];\n\n for (const msg of req.messages) {\n if (msg.role === \"system\") {\n const text = typeof msg.content === \"string\" ? msg.content : \"\";\n systemPrompt =\n systemPrompt === undefined ? text : `${systemPrompt}\\n\\n${text}`;\n continue;\n }\n // Tool result messages and assistant messages may carry structured\n // content; we flatten to text. tool_calls on an assistant message\n // are serialised into the content so the model has them in context.\n let content = typeof msg.content === \"string\" ? msg.content : \"\";\n if (msg.role === \"assistant\" && msg.tool_calls && msg.tool_calls.length > 0) {\n const callsLine = msg.tool_calls\n .map(\n (c) =>\n `[tool_call ${c.function.name} args=${c.function.arguments}]`,\n )\n .join(\"\\n\");\n content = content ? `${content}\\n${callsLine}` : callsLine;\n }\n if (msg.role === \"tool\" && msg.tool_call_id) {\n content = `[tool_result id=${msg.tool_call_id}] ${content}`;\n }\n messages.push({ role: msg.role, content });\n }\n\n return {\n provider: undefined, // cloud picks the default \u2014 see CODING_AGENT_DEFAULT_PROVIDER\n model: req.model, // pass through for telemetry; cloud may override\n systemPrompt,\n temperature: req.temperature,\n messages,\n tools: req.tools,\n };\n};\n\n// ----------------------------------------------------------------------------\n// Response: broker \u2192 OpenAI\n// ----------------------------------------------------------------------------\n\nexport interface OpenAIChatCompletionResponse {\n id: string;\n object: \"chat.completion\";\n created: number;\n model: string;\n choices: Array<{\n index: 0;\n message: {\n role: \"assistant\";\n content: string | null;\n tool_calls?: Array<{\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }>;\n };\n finish_reason: \"stop\" | \"tool_calls\" | \"length\" | \"content_filter\";\n }>;\n usage?: {\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n };\n}\n\n/** Non-streaming response. Used when the request didn't ask for stream. */\nexport const translateResponse = (\n response: LlmBrokerResponsePayload,\n id: string,\n modelEcho: string,\n): OpenAIChatCompletionResponse => {\n const hasToolCalls = response.toolCalls && response.toolCalls.length > 0;\n const finish_reason: OpenAIChatCompletionResponse[\"choices\"][number][\"finish_reason\"] =\n response.finishReason ?? (hasToolCalls ? \"tool_calls\" : \"stop\");\n return {\n id,\n object: \"chat.completion\",\n created: Math.floor(Date.now() / 1000),\n // Echo the model the client requested. The cloud may have routed to\n // a different actual model; we surface that in `response.model` but\n // OpenAI clients expect the echoed model id to match their request.\n model: modelEcho,\n choices: [\n {\n index: 0,\n message: {\n role: \"assistant\",\n content: response.text || null,\n ...(hasToolCalls ? { tool_calls: response.toolCalls } : {}),\n },\n finish_reason,\n },\n ],\n ...(response.tokenUsage\n ? {\n usage: {\n prompt_tokens: response.tokenUsage.input,\n completion_tokens: response.tokenUsage.output,\n total_tokens:\n response.tokenUsage.input + response.tokenUsage.output,\n },\n }\n : {}),\n };\n};\n\n// ----------------------------------------------------------------------------\n// Streaming response \u2014 buffered v1\n// ----------------------------------------------------------------------------\n//\n// True end-to-end streaming would require the broker chain to be\n// streaming-aware. For v1 we BUFFER the broker result and emit it as a\n// small set of SSE chunks (role+content, optional tool_calls, [DONE]).\n// Opencode's AI SDK consumer is fine with this \u2014 it just sees the\n// stream finish quickly. Real streaming is a follow-up.\n\nexport interface OpenAIChatCompletionChunk {\n id: string;\n object: \"chat.completion.chunk\";\n created: number;\n model: string;\n choices: Array<{\n index: 0;\n delta: {\n role?: \"assistant\";\n content?: string;\n tool_calls?: Array<{\n index: number;\n id?: string;\n type?: \"function\";\n function?: { name?: string; arguments?: string };\n }>;\n };\n finish_reason: null | \"stop\" | \"tool_calls\" | \"length\" | \"content_filter\";\n }>;\n}\n\n/** Returns the sequence of SSE-event JSON payloads to send for one\n * buffered response. The caller wraps each in `data: <json>\\n\\n` and\n * appends a final `data: [DONE]\\n\\n`. */\nexport const translateResponseToChunks = (\n response: LlmBrokerResponsePayload,\n id: string,\n modelEcho: string,\n): OpenAIChatCompletionChunk[] => {\n const created = Math.floor(Date.now() / 1000);\n const baseChunk = (\n delta: OpenAIChatCompletionChunk[\"choices\"][number][\"delta\"],\n finish_reason: OpenAIChatCompletionChunk[\"choices\"][number][\"finish_reason\"] = null,\n ): OpenAIChatCompletionChunk => ({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: modelEcho,\n choices: [{ index: 0, delta, finish_reason }],\n });\n\n const chunks: OpenAIChatCompletionChunk[] = [];\n\n // 1. Role chunk (OpenAI convention).\n chunks.push(baseChunk({ role: \"assistant\" }));\n\n // 2. Content chunk(s). For now one chunk containing the whole text.\n if (response.text) {\n chunks.push(baseChunk({ content: response.text }));\n }\n\n // 3. Tool call chunks. OpenAI streams these as incremental deltas\n // per call. Since we have the full result, emit one chunk per\n // tool call containing everything at once.\n if (response.toolCalls) {\n for (let i = 0; i < response.toolCalls.length; i++) {\n const tc = response.toolCalls[i]!;\n chunks.push(\n baseChunk({\n tool_calls: [\n {\n index: i,\n id: tc.id,\n type: \"function\",\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n },\n ],\n }),\n );\n }\n }\n\n // 4. Finish-reason chunk.\n const hasToolCalls = response.toolCalls && response.toolCalls.length > 0;\n chunks.push(baseChunk({}, response.finishReason ?? (hasToolCalls ? \"tool_calls\" : \"stop\")));\n\n return chunks;\n};\n", "// Translator between OpenAI Responses API and OpenAI Chat Completions.\n//\n// Why: opencode 1.x targets the AI SDK's `@ai-sdk/openai` provider,\n// which moved to the Responses API as its default wire format. Our\n// existing shim only speaks Chat Completions. Rather than ripping out\n// the existing shim, we translate at the edge:\n//\n// Inbound: Responses request body \u2192 Chat Completions request body\n// (then reuse openai-translator.translateRequest to get a\n// broker payload \u2014 the broker layer never sees Responses\n// API at all)\n//\n// Outbound: broker stream chunks \u2192 Responses API SSE events\n// (we don't go via Chat Completions chunks here because\n// the Responses API event vocabulary is rich enough that\n// double-translation would lose context, e.g. item IDs)\n//\n// Wire references:\n// Responses API docs: https://platform.openai.com/docs/api-reference/responses\n// AI SDK v3+ openai provider: defaults to Responses API for stream\n\nimport type { OpenAIChatCompletionRequest, OpenAIChatMessage, OpenAITool } from \"./openai-translator\";\nimport type { LlmStreamChunk } from \"../../../shared/coding-agent-types\";\n\n// ---- Request shapes (what we receive on POST /v1/responses) --------\n\nexport interface ResponsesApiRequest {\n model: string;\n input: ResponsesInputItem[];\n /** Responses API uses max_output_tokens; Chat Completions used max_tokens. */\n max_output_tokens?: number;\n temperature?: number;\n /** Responses tools use a FLAT shape \u2014 name/description/parameters live\n * at the top level of the tool, not under a `function` key. */\n tools?: ResponsesTool[];\n tool_choice?: \"auto\" | \"none\" | \"required\" | { type: \"function\"; name: string };\n stream?: boolean;\n /** Whether OpenAI should persist the response server-side. We always\n * ignore this; we're not OpenAI. */\n store?: boolean;\n /** Tolerate other fields the SDK adds (parallel_tool_calls, etc.). */\n [key: string]: unknown;\n}\n\nexport type ResponsesInputItem =\n | {\n role: \"system\" | \"user\" | \"assistant\";\n content: string | ResponsesContentPart[];\n }\n | {\n type: \"function_call\";\n call_id: string;\n name: string;\n arguments: string;\n }\n | {\n type: \"function_call_output\";\n call_id: string;\n output: string;\n };\n\nexport type ResponsesContentPart =\n | { type: \"input_text\"; text: string }\n | { type: \"output_text\"; text: string }\n // Tolerate other content types (input_image, refusal, etc.) \u2014 we\n // flatten unsupported parts to empty text rather than throwing.\n | { type: string; [key: string]: unknown };\n\nexport interface ResponsesTool {\n type: \"function\";\n name: string;\n description?: string;\n parameters?: Record<string, unknown>;\n}\n\n// ---- Request: Responses \u2192 Chat Completions -------------------------\n\nconst flattenContent = (\n content: string | ResponsesContentPart[],\n): string => {\n if (typeof content === \"string\") return content;\n return content\n .map((part) => {\n if (\n part.type === \"input_text\" ||\n part.type === \"output_text\"\n ) {\n return (part as { text?: string }).text ?? \"\";\n }\n return \"\";\n })\n .join(\"\");\n};\n\n/** Translate a Responses API request into the older Chat Completions\n * shape our existing shim understands. The broker layer is downstream\n * of openai-translator.translateRequest and stays untouched. */\nexport const responsesToChatCompletions = (\n req: ResponsesApiRequest,\n): OpenAIChatCompletionRequest => {\n const messages: OpenAIChatMessage[] = [];\n\n for (const item of req.input) {\n if (\"type\" in item && item.type === \"function_call\") {\n // Assistant tool-call turn in the rolling transcript: re-emit as\n // an assistant message with a tool_calls entry.\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: [\n {\n id: item.call_id,\n type: \"function\",\n function: { name: item.name, arguments: item.arguments },\n },\n ],\n });\n continue;\n }\n if (\"type\" in item && item.type === \"function_call_output\") {\n messages.push({\n role: \"tool\",\n content: item.output,\n tool_call_id: item.call_id,\n });\n continue;\n }\n // Plain conversation turn (system/user/assistant).\n const role = item.role;\n const content = flattenContent(item.content);\n messages.push({ role, content });\n }\n\n const tools: OpenAITool[] | undefined = req.tools?.map((t) => ({\n type: \"function\",\n function: {\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n },\n }));\n\n // tool_choice: Responses' { type: \"function\", name: \"x\" } \u2192 Chat's\n // { type: \"function\", function: { name: \"x\" } }\n let toolChoice: OpenAIChatCompletionRequest[\"tool_choice\"] | undefined;\n if (typeof req.tool_choice === \"string\") {\n toolChoice = req.tool_choice;\n } else if (req.tool_choice && typeof req.tool_choice === \"object\") {\n toolChoice = {\n type: \"function\",\n function: { name: req.tool_choice.name },\n };\n }\n\n return {\n model: req.model,\n messages,\n tools,\n tool_choice: toolChoice,\n temperature: req.temperature,\n stream: req.stream,\n // Pass max_output_tokens through as max_tokens for downstream\n // providers that honour it. Chat Completions naming.\n max_tokens: req.max_output_tokens,\n };\n};\n\n// ---- Streaming response: broker chunks \u2192 Responses API events ------\n\n/** Minimal subset of the Responses API streaming event shape we emit.\n * Every event has a top-level `type` (used by the AI SDK) and an\n * optional payload. We also write the `event:` SSE field, matching\n * OpenAI's wire. */\nexport interface ResponsesStreamEvent {\n event: string;\n data: Record<string, unknown>;\n}\n\nconst randomId = (prefix: string): string =>\n `${prefix}_${Math.random().toString(36).slice(2, 12)}`;\n\n/** Stateful translator. One instance per turn. Call methods in the\n * order the broker emits chunks; `start()` first, `chunk()` per\n * delta, `finish()` last. Each method returns the SSE events to\n * write \u2014 never returns null. */\nexport class ResponsesStreamState {\n private readonly responseId: string;\n private readonly model: string;\n private readonly createdAt: number;\n private outputIndex = 0;\n\n /** Open message item carrying assistant text, if any. */\n private text:\n | { itemId: string; outputIndex: number; contentIndex: number; buffer: string }\n | null = null;\n\n /** Open function_call items, keyed by the broker's tool_call index\n * (the model's call position, not our output_index). */\n private readonly tools = new Map<\n number,\n { itemId: string; outputIndex: number; callId: string; name: string; arguments: string; emittedAdded: boolean }\n >();\n\n private finished = false;\n /** Set when broker emits `complete`. We carry usage + finish_reason\n * into the final response.completed payload. */\n private finalUsage:\n | { input_tokens?: number; output_tokens?: number; total_tokens?: number }\n | undefined;\n private finalStatus: \"completed\" | \"incomplete\" | \"failed\" = \"completed\";\n\n constructor(model: string, id?: string) {\n this.responseId = id ?? randomId(\"resp\");\n this.model = model;\n this.createdAt = Math.floor(Date.now() / 1000);\n }\n\n /** Emit the response.created + response.in_progress pair. Call once\n * before any chunks. */\n start(): ResponsesStreamEvent[] {\n const responseObj = {\n id: this.responseId,\n object: \"response\",\n created_at: this.createdAt,\n status: \"in_progress\",\n model: this.model,\n output: [] as unknown[],\n usage: null,\n };\n return [\n { event: \"response.created\", data: { type: \"response.created\", response: responseObj } },\n { event: \"response.in_progress\", data: { type: \"response.in_progress\", response: responseObj } },\n ];\n }\n\n /** Translate a single broker chunk. Some chunks emit multiple events\n * (opening a new item requires output_item.added + content_part.added\n * before the delta itself). */\n chunk(c: LlmStreamChunk): ResponsesStreamEvent[] {\n if (this.finished) return [];\n switch (c.type) {\n case \"text_delta\":\n return this.handleTextDelta(c.text);\n case \"tool_call_delta\":\n return this.handleToolCallDelta(c);\n case \"complete\":\n // The broker's complete payload carries final usage + finish\n // reason. We don't emit response.completed here yet \u2014 that\n // happens in finish(). Just capture the data so finish() has it.\n // The broker's tokenUsage is {input,output}; rename to the\n // Responses API shape on the way out.\n {\n const tu = (c.payload as { tokenUsage?: { input?: number; output?: number } })\n ?.tokenUsage;\n if (tu) {\n const input = tu.input ?? 0;\n const output = tu.output ?? 0;\n this.finalUsage = {\n input_tokens: input,\n output_tokens: output,\n total_tokens: input + output,\n };\n }\n }\n // finish_reason may be on the payload's choices[0]; we don't\n // currently distinguish \"stop\" vs \"tool_calls\" in the\n // completed event so just default to \"completed\" status.\n return [];\n case \"error\":\n // The route handler is expected to catch broker errors and\n // emit response.failed itself; nothing for us to translate.\n this.finalStatus = \"failed\";\n return [];\n }\n }\n\n /** Close any open items + emit the terminal response.completed event. */\n finish(): ResponsesStreamEvent[] {\n if (this.finished) return [];\n this.finished = true;\n const out: ResponsesStreamEvent[] = [];\n out.push(...this.closeTextItem());\n out.push(...this.closeAllToolItems());\n\n const responseObj = {\n id: this.responseId,\n object: \"response\",\n created_at: this.createdAt,\n status: this.finalStatus,\n model: this.model,\n output: [], // For brevity \u2014 the AI SDK only needs status + usage at completion.\n usage: this.finalUsage ?? null,\n };\n out.push({\n event: \"response.completed\",\n data: { type: \"response.completed\", response: responseObj },\n });\n return out;\n }\n\n // ---- internals ----\n\n private handleTextDelta(text: string): ResponsesStreamEvent[] {\n if (!this.text) {\n // Open a new message item + content part before the first delta.\n const itemId = randomId(\"msg\");\n const outputIndex = this.outputIndex++;\n const contentIndex = 0;\n this.text = { itemId, outputIndex, contentIndex, buffer: text };\n return [\n {\n event: \"response.output_item.added\",\n data: {\n type: \"response.output_item.added\",\n output_index: outputIndex,\n item: {\n id: itemId,\n type: \"message\",\n status: \"in_progress\",\n role: \"assistant\",\n content: [],\n },\n },\n },\n {\n event: \"response.content_part.added\",\n data: {\n type: \"response.content_part.added\",\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: \"\" },\n },\n },\n {\n event: \"response.output_text.delta\",\n data: {\n type: \"response.output_text.delta\",\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n delta: text,\n },\n },\n ];\n }\n this.text.buffer += text;\n return [\n {\n event: \"response.output_text.delta\",\n data: {\n type: \"response.output_text.delta\",\n item_id: this.text.itemId,\n output_index: this.text.outputIndex,\n content_index: this.text.contentIndex,\n delta: text,\n },\n },\n ];\n }\n\n private handleToolCallDelta(c: {\n type: \"tool_call_delta\";\n index: number;\n id?: string;\n name?: string;\n argumentsDelta?: string;\n }): ResponsesStreamEvent[] {\n const events: ResponsesStreamEvent[] = [];\n\n // Text and tool calls are separate output items. If text is open,\n // we have to close it before adding the tool-call item.\n events.push(...this.closeTextItem());\n\n let entry = this.tools.get(c.index);\n if (!entry) {\n entry = {\n itemId: randomId(\"fc\"),\n outputIndex: this.outputIndex++,\n callId: c.id ?? randomId(\"call\"),\n name: c.name ?? \"\",\n arguments: \"\",\n emittedAdded: false,\n };\n this.tools.set(c.index, entry);\n } else {\n // Later deltas may carry id/name we missed on the first one.\n if (c.id) entry.callId = c.id;\n if (c.name) entry.name = c.name;\n }\n\n if (!entry.emittedAdded && entry.name) {\n // We have enough to emit the item header. Delay until we have a\n // name so the event payload is well-formed.\n entry.emittedAdded = true;\n events.push({\n event: \"response.output_item.added\",\n data: {\n type: \"response.output_item.added\",\n output_index: entry.outputIndex,\n item: {\n id: entry.itemId,\n type: \"function_call\",\n status: \"in_progress\",\n name: entry.name,\n call_id: entry.callId,\n arguments: \"\",\n },\n },\n });\n }\n\n if (c.argumentsDelta !== undefined && c.argumentsDelta.length > 0) {\n entry.arguments += c.argumentsDelta;\n if (entry.emittedAdded) {\n events.push({\n event: \"response.function_call_arguments.delta\",\n data: {\n type: \"response.function_call_arguments.delta\",\n item_id: entry.itemId,\n output_index: entry.outputIndex,\n delta: c.argumentsDelta,\n },\n });\n }\n // If we haven't emitted the added event yet (name still missing),\n // we buffer the arguments and flush when we get the name. The\n // common case is name arrives in the first chunk so this is rare.\n }\n\n return events;\n }\n\n private closeTextItem(): ResponsesStreamEvent[] {\n if (!this.text) return [];\n const { itemId, outputIndex, contentIndex, buffer } = this.text;\n this.text = null;\n return [\n {\n event: \"response.output_text.done\",\n data: {\n type: \"response.output_text.done\",\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n text: buffer,\n },\n },\n {\n event: \"response.content_part.done\",\n data: {\n type: \"response.content_part.done\",\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: buffer },\n },\n },\n {\n event: \"response.output_item.done\",\n data: {\n type: \"response.output_item.done\",\n output_index: outputIndex,\n item: {\n id: itemId,\n type: \"message\",\n status: \"completed\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: buffer }],\n },\n },\n },\n ];\n }\n\n private closeAllToolItems(): ResponsesStreamEvent[] {\n const out: ResponsesStreamEvent[] = [];\n for (const entry of this.tools.values()) {\n if (!entry.emittedAdded) {\n // Edge case: we never had a name, so we couldn't open the item.\n // Open and immediately close so the SDK sees a valid sequence.\n entry.emittedAdded = true;\n out.push({\n event: \"response.output_item.added\",\n data: {\n type: \"response.output_item.added\",\n output_index: entry.outputIndex,\n item: {\n id: entry.itemId,\n type: \"function_call\",\n status: \"in_progress\",\n name: entry.name || \"unknown\",\n call_id: entry.callId,\n arguments: \"\",\n },\n },\n });\n }\n out.push({\n event: \"response.function_call_arguments.done\",\n data: {\n type: \"response.function_call_arguments.done\",\n item_id: entry.itemId,\n output_index: entry.outputIndex,\n arguments: entry.arguments,\n },\n });\n out.push({\n event: \"response.output_item.done\",\n data: {\n type: \"response.output_item.done\",\n output_index: entry.outputIndex,\n item: {\n id: entry.itemId,\n type: \"function_call\",\n status: \"completed\",\n name: entry.name || \"unknown\",\n call_id: entry.callId,\n arguments: entry.arguments,\n },\n },\n });\n }\n this.tools.clear();\n return out;\n }\n}\n", "// Active-broker tracking for the OpenAI-compat shim.\n//\n// The shim endpoint receives outbound HTTP from opencode. Opencode's\n// AI-SDK provider doesn't expose which coding session is making the\n// call \u2014 it's a stateless provider call. We need a way to route the\n// shim's request to the right LlmBroker.\n//\n// Approach:\n// Per-stream tokens. Each /agent/message stream binds its broker with\n// `bindActiveBroker(broker)`, getting back a unique token. That token\n// becomes the `apiKey` opencode supplies via the provider config (or\n// via the placeholder mechanism described in opencode-adapter.ts).\n// The shim looks up the broker by token \u2014 multiple concurrent streams\n// coexist cleanly because each has its own token.\n\nimport { randomUUID } from \"node:crypto\";\n\nimport type { LlmBroker } from \"../broker\";\n\ninterface ActiveBrokerEntry {\n token: string;\n broker: LlmBroker;\n}\n\nconst byToken = new Map<string, ActiveBrokerEntry>();\n\nexport interface ActiveBrokerHandle {\n token: string;\n release(): void;\n}\n\n/** Bind a broker as active. Returns a unique token the adapter passes\n * to opencode as the apiKey, plus a release() to call when done. */\nexport const bindActiveBroker = (broker: LlmBroker): ActiveBrokerHandle => {\n const token = `dlg-${randomUUID()}`;\n byToken.set(token, { token, broker });\n return {\n token,\n release() {\n // Only delete if it's still THIS entry. A racing rebind under the\n // same token (which would only happen if randomUUID collided \u2014\n // 1-in-2^122) is the only way someone else could hold it.\n const current = byToken.get(token);\n if (current && current.broker === broker) {\n byToken.delete(token);\n }\n },\n };\n};\n\n/** Look up the broker by token. Returns null when the token doesn't\n * match any active stream \u2014 the shim treats that as 401. */\nexport const getActiveBrokerByToken = (token: string): LlmBroker | null => {\n return byToken.get(token)?.broker ?? null;\n};\n\n/** Fallback used by the shim when the bearer token doesn't match\n * exactly. opencode's @ai-sdk/openai provider doesn't support\n * per-call apiKey overrides; the adapter sets a placeholder at\n * server-spawn time and opencode echoes it as the bearer forever.\n *\n * When exactly ONE broker is bound, we use it \u2014 that's the MVP\n * single-stream case. When two or more brokers are concurrently\n * bound, we return null so the request 401s rather than randomly\n * cross-wiring streams. Multi-stream support requires a deeper\n * refactor (per-stream opencode servers, or an SDK that accepts\n * per-call apiKey) that's outside this fix's scope. */\nexport const getOnlyActiveBroker = (): LlmBroker | null => {\n if (byToken.size !== 1) return null;\n const first = byToken.values().next().value;\n return first?.broker ?? null;\n};\n\n/** Test-only: number of currently-bound brokers. */\nexport const _activeBrokerCount = (): number => byToken.size;\n\n/** Test-only: clear all bindings. */\nexport const _resetActiveBroker = (): void => {\n byToken.clear();\n};\n", "// Deterministic mock adapter.\n//\n// Purpose:\n// 1. Lets the rest of the helper (SSE route, chat UI) be built and tested\n// before we wire the real opencode subprocess in Step 9.\n// 2. Serves as the documented fallback when the real adapter can't\n// satisfy a call (we'd rather show the user *something* than fail).\n//\n// Behaviour for one turn:\n// - Emits ~6 text_delta events that together read as a plausible\n// assistant reply referencing the user's prompt.\n// - Emits one tool_call_start + tool_call_end pair representing a\n// synthetic \"fs.read README.md\" call.\n// - Emits one diff_proposed with a small unified diff against a file\n// called `MOCK_NOTES.md` (so the apply flow can be exercised in a real\n// repo without touching tracked files).\n// - Emits done with a synthetic messageId.\n//\n// Timing is configurable so tests can run at zero delay while interactive\n// dev runs have a small pause between tokens for a more realistic feel.\n\nimport { setTimeout as delay } from \"node:timers/promises\";\n\nimport type {\n AgentStreamEvent,\n AgentDiffProposed,\n} from \"../../../shared/coding-agent-types\";\nimport type { AdapterRequest, OpenCodeAdapter } from \"./types\";\n\nexport interface MockAdapterOptions {\n /** Milliseconds between successive text_delta events. 0 makes it\n * effectively synchronous, which is what tests want. */\n tokenDelayMs?: number;\n /** Skip emitting the synthetic tool-call pair. Tests that only care\n * about text streaming use this to cut noise. */\n skipToolCall?: boolean;\n /** Skip emitting the diff_proposed event. Tests for the \"no diff yet\"\n * branch use this. */\n skipDiff?: boolean;\n /** Override the synthetic messageId in the final `done` event. Useful\n * for tests that want a stable value. */\n fixedMessageId?: string;\n /** When set and a broker is available on the AdapterRequest, the mock\n * performs ONE round-trip through the broker before emitting its\n * canned text. This exercises the Step 8 brokered-LLM bridge end to\n * end without needing the real opencode binary. */\n useBroker?: boolean;\n /** Provider hint sent to the broker. Only used when useBroker is true. */\n brokerProvider?: string;\n /** Model hint sent to the broker. Only used when useBroker is true. */\n brokerModel?: string;\n}\n\nconst RESPONSE_FRAGMENTS = [\n \"Got it \u2014 looking at\",\n \" the repo at\",\n \" {repoPath}.\",\n \"\\n\\nThis is a mock response from MockOpenCodeAdapter,\",\n \" so the actual analysis hasn't run.\",\n \" The real opencode integration lands in Step 9; until then\",\n \" every prompt produces this same canned reply plus a small synthetic\",\n \" diff so you can exercise the apply flow safely.\",\n];\n\nconst SYNTHETIC_DIFF: AgentDiffProposed = {\n type: \"diff_proposed\",\n unified: [\n \"diff --git a/MOCK_NOTES.md b/MOCK_NOTES.md\",\n \"new file mode 100644\",\n \"index 0000000..1111111\",\n \"--- /dev/null\",\n \"+++ b/MOCK_NOTES.md\",\n \"@@ -0,0 +1,3 @@\",\n \"+# Mock notes\",\n \"+\",\n \"+Created by MockOpenCodeAdapter to demonstrate the diff flow.\",\n \"\",\n ].join(\"\\n\"),\n files: [\n {\n path: \"MOCK_NOTES.md\",\n additions: 3,\n deletions: 0,\n status: \"added\",\n },\n ],\n};\n\nexport class MockOpenCodeAdapter implements OpenCodeAdapter {\n readonly name = \"mock\";\n\n constructor(private readonly options: MockAdapterOptions = {}) {}\n\n async *streamMessage(\n request: AdapterRequest,\n ): AsyncIterable<AgentStreamEvent> {\n const tokenDelay = this.options.tokenDelayMs ?? 0;\n\n // If broker-mode is on and a broker is wired, do ONE round-trip first.\n // The response text isn't streamed to the user verbatim \u2014 instead we\n // prepend a single text_delta that proves the bridge worked. The\n // rest of the canned events follow as normal.\n let brokerPrefix: string | null = null;\n if (this.options.useBroker && request.broker) {\n this.throwIfAborted(request.signal);\n try {\n const response = await request.broker({\n provider: this.options.brokerProvider,\n model: this.options.brokerModel,\n messages: [\n { role: \"user\", content: request.prompt },\n ],\n });\n brokerPrefix =\n `[brokered via ${response.provider}:${response.model}] ` +\n response.text.slice(0, 200);\n } catch (err) {\n // Don't tear down the whole turn \u2014 emit an error envelope and\n // continue with canned text so the user still sees something.\n yield {\n type: \"error\",\n message: `Brokered LLM call failed: ${(err as Error).message}`,\n recoverable: true,\n };\n }\n }\n\n if (brokerPrefix) {\n yield { type: \"text_delta\", text: brokerPrefix + \"\\n\\n\" };\n }\n\n // Text deltas. We treat the prompt itself opaquely; the only\n // substitution is {repoPath} so the response feels grounded.\n for (const fragment of RESPONSE_FRAGMENTS) {\n this.throwIfAborted(request.signal);\n const text = fragment.replace(\"{repoPath}\", request.repoPath);\n yield { type: \"text_delta\", text };\n if (tokenDelay > 0) {\n await delay(tokenDelay);\n }\n }\n\n if (!this.options.skipToolCall) {\n this.throwIfAborted(request.signal);\n const toolCallId = `mock-tool-${Date.now()}`;\n yield {\n type: \"tool_call_start\",\n toolCallId,\n tool: \"fs.read\",\n args: { path: \"README.md\" },\n };\n if (tokenDelay > 0) {\n await delay(tokenDelay);\n }\n yield {\n type: \"tool_call_end\",\n toolCallId,\n ok: true,\n summary: \"Read README.md (mock \u2014 no actual file IO performed)\",\n };\n }\n\n if (!this.options.skipDiff) {\n this.throwIfAborted(request.signal);\n yield SYNTHETIC_DIFF;\n }\n\n this.throwIfAborted(request.signal);\n yield {\n type: \"done\",\n messageId:\n this.options.fixedMessageId ??\n `mock-msg-${request.sessionId}-${Date.now()}`,\n };\n }\n\n private throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) {\n // We deliberately throw rather than emit AgentError so the SSE route\n // can distinguish \"cancelled by caller\" from \"adapter blew up.\"\n const err = new Error(\"aborted\");\n (err as Error & { name: string }).name = \"AbortError\";\n throw err;\n }\n }\n}\n", "// Translate opencode's SDK event union \u2192 our AgentStreamEvent envelope.\n//\n// Pulled out as a pure function so it's trivially unit-testable without\n// spawning opencode. The adapter feeds events into mapEvent() and forwards\n// whatever non-null AgentStreamEvent(s) come back.\n//\n// References (sdk node_modules paths):\n// - dist/gen/types.gen.d.ts \u2192 Event, Part, ToolState, EventMessagePart*\n//\n// Mapping rules:\n// - EventMessagePartUpdated with a text Part \u2192 text_delta carrying the\n// `delta` field (the chunk just added) or the whole `text` on first\n// emission if no delta is present.\n// - EventMessagePartUpdated with a tool Part transitions:\n// pending/running \u2192 tool_call_start (idempotent: we de-dup by callID\n// in the adapter so multiple updates don't spam).\n// completed \u2192 tool_call_end (ok: true) with the tool's title as\n// the summary.\n// error \u2192 tool_call_end (ok: false) with the error message.\n// - EventMessagePartUpdated with a patch Part \u2192 diff_proposed. The\n// SDK's PatchPart only carries a hash + file list; we DO NOT have\n// the unified diff text here. The adapter pairs this with a\n// subsequent client.session.diff() call to fetch the actual diff.\n// - EventSessionIdle for our session \u2192 signals end-of-turn. The\n// adapter emits a `done` event when it sees this.\n// - Everything else \u2192 ignored at v1 (reasoning parts, step-start/finish,\n// compaction, session.status, file.edited, etc.). Some of these will\n// become useful in later phases (e.g. reasoning panel) but ignoring\n// them keeps the wire shape stable.\n\nimport type {\n AgentStreamEvent,\n AgentToolCallEnd,\n AgentToolCallStart,\n} from \"../../../shared/coding-agent-types\";\n\n/** What the adapter needs to know about an event stream item to translate\n * it. We declare our own minimal interface here so the mapper isn't\n * coupled to the SDK's `Event` union (which would force a runtime import\n * in tests). The opencode SDK types are structurally compatible. */\nexport interface OpencodeStreamItem {\n type: string;\n properties?: {\n sessionID?: string;\n part?: OpencodePart;\n delta?: string;\n // For `permission.updated`, the SDK's Permission rides directly on\n // `properties` (id/type/title/metadata), not under `part`.\n id?: string;\n /** Permission category for `permission.updated` \u2014 \"bash\"/\"webfetch\"/etc. */\n type?: string;\n title?: string;\n metadata?: Record<string, unknown>;\n };\n}\n\nexport interface OpencodePart {\n id?: string;\n type: string;\n text?: string;\n callID?: string;\n tool?: string;\n state?: {\n status: \"pending\" | \"running\" | \"completed\" | \"error\";\n input?: Record<string, unknown>;\n title?: string;\n error?: string;\n /** Full textual result the model sees \u2014 bash stdout, file contents,\n * edit confirmation, etc. opencode's ToolStateCompleted carries this. */\n output?: string;\n };\n files?: string[];\n}\n\n/** Cap forwarded tool output so a `cat` of a huge file or a chatty build\n * can't bloat the SSE stream or the persisted transcript row. */\nconst MAX_TOOL_OUTPUT_CHARS = 4000;\n\nconst truncateOutput = (raw: string | undefined): string | undefined => {\n if (!raw) return undefined;\n if (raw.length <= MAX_TOOL_OUTPUT_CHARS) return raw;\n const omitted = raw.length - MAX_TOOL_OUTPUT_CHARS;\n return `${raw.slice(0, MAX_TOOL_OUTPUT_CHARS)}\\n\u2026 [${omitted} more characters truncated]`;\n};\n\n/** Per-stream state the mapper threads between calls so we can de-dup\n * repeated tool_call_start emissions for the same callID, and so the\n * caller can know when a patch was seen (and should fetch the unified\n * diff out-of-band). */\nexport interface MapState {\n ourSessionId: string;\n openCodeSessionId: string;\n /** Tool callIDs we've already announced as started. */\n startedTools: Set<string>;\n /** Tool callIDs we've already announced as ended (so a duplicate\n * completed/error event doesn't double-fire). */\n endedTools: Set<string>;\n /** Hashes of patches we've already announced. */\n announcedPatches: Set<string>;\n /** Permission ids we've already surfaced (opencode re-emits the same\n * permission as its status changes; we only announce it once). */\n announcedPermissions: Set<string>;\n}\n\nexport const createMapState = (\n ourSessionId: string,\n openCodeSessionId: string,\n): MapState => ({\n ourSessionId,\n openCodeSessionId,\n startedTools: new Set(),\n endedTools: new Set(),\n announcedPatches: new Set(),\n announcedPermissions: new Set(),\n});\n\n/** Pull the concrete action out of a permission's metadata so the UI can\n * show \"rm -rf dist\" / \"https://\u2026\" rather than an opaque id. opencode keys\n * vary by tool; we probe the common ones and fall back to nothing (the\n * title then carries the context). */\nconst extractPermissionCommand = (\n metadata: Record<string, unknown> | undefined,\n): string | undefined => {\n if (!metadata) return undefined;\n for (const key of [\"command\", \"cmd\", \"url\", \"pattern\", \"filePath\"]) {\n const v = metadata[key];\n if (typeof v === \"string\" && v) return v;\n }\n return undefined;\n};\n\nexport interface MappedEvent {\n events: AgentStreamEvent[];\n /** True when this event signals the end of the current turn. */\n done: boolean;\n /** Set when the event is a PatchPart so the adapter knows to fetch the\n * unified diff via client.session.diff(). The hash + files list ride\n * on the diff_proposed event we emit; the adapter is responsible for\n * swapping in the actual unified diff after fetching. */\n patchToFetch?: { hash: string; files: string[] };\n}\n\nconst EMPTY: MappedEvent = { events: [], done: false };\n\nexport const mapEvent = (\n item: OpencodeStreamItem,\n state: MapState,\n): MappedEvent => {\n // Filter to events for our session. The SDK's event stream is GLOBAL\n // across the opencode instance, so the helper sees events from every\n // session it has open. Sessions other than ours are ignored.\n const sid = item.properties?.sessionID;\n if (sid && sid !== state.openCodeSessionId) {\n return EMPTY;\n }\n\n if (item.type === \"session.idle\" && sid === state.openCodeSessionId) {\n return { events: [{ type: \"done\" }], done: true };\n }\n\n if (item.type === \"permission.updated\") {\n // opencode paused a tool (bash/webfetch with permission \"ask\"). Surface\n // it once; the adapter decides whether to auto-approve (auto mode) or\n // forward to the browser (confirm mode).\n const permissionId = item.properties?.id;\n if (!permissionId || state.announcedPermissions.has(permissionId)) {\n return EMPTY;\n }\n state.announcedPermissions.add(permissionId);\n return {\n events: [\n {\n type: \"permission_request\",\n permissionId,\n tool: item.properties?.type ?? \"tool\",\n title: item.properties?.title,\n command: extractPermissionCommand(item.properties?.metadata),\n },\n ],\n done: false,\n };\n }\n\n if (item.type !== \"message.part.updated\") {\n return EMPTY;\n }\n const part = item.properties?.part;\n if (!part) return EMPTY;\n\n switch (part.type) {\n case \"text\": {\n // Prefer the delta if the SDK supplies it (incremental update);\n // fall back to the full text on the first emission.\n const delta = item.properties?.delta ?? part.text ?? \"\";\n if (!delta) return EMPTY;\n return { events: [{ type: \"text_delta\", text: delta }], done: false };\n }\n case \"tool\": {\n const callId = part.callID ?? part.id ?? \"\";\n const tool = part.tool ?? \"tool\";\n const status = part.state?.status;\n if (!callId || !status) return EMPTY;\n\n if (status === \"pending\" || status === \"running\") {\n if (state.startedTools.has(callId)) {\n return EMPTY;\n }\n state.startedTools.add(callId);\n const event: AgentToolCallStart = {\n type: \"tool_call_start\",\n toolCallId: callId,\n tool,\n args: part.state?.input,\n };\n return { events: [event], done: false };\n }\n\n if (status === \"completed\" || status === \"error\") {\n if (state.endedTools.has(callId)) {\n return EMPTY;\n }\n state.endedTools.add(callId);\n const event: AgentToolCallEnd = {\n type: \"tool_call_end\",\n toolCallId: callId,\n ok: status === \"completed\",\n summary:\n status === \"error\"\n ? part.state?.error ?? \"Tool failed\"\n : part.state?.title,\n output: truncateOutput(\n status === \"error\" ? part.state?.error : part.state?.output,\n ),\n };\n return { events: [event], done: false };\n }\n return EMPTY;\n }\n case \"patch\": {\n const hash = (part as { hash?: string }).hash ?? \"\";\n if (!hash || state.announcedPatches.has(hash)) {\n return EMPTY;\n }\n state.announcedPatches.add(hash);\n const files = part.files ?? [];\n // We can't classify add/modify/delete from the patch part alone \u2014\n // it just lists changed paths. The adapter swaps in the unified\n // diff after fetching it; until then we emit a placeholder so the\n // chat panel at least shows \"Diff proposed: N files\".\n return {\n events: [\n {\n type: \"diff_proposed\",\n unified: \"\",\n files: files.map((path) => ({\n path,\n additions: 0,\n deletions: 0,\n status: \"modified\" as const,\n })),\n },\n ],\n done: false,\n patchToFetch: { hash, files },\n };\n }\n default:\n return EMPTY;\n }\n};\n", "// EngineLocator \u2014 the seam that lets us swap how the helper acquires a\n// running opencode/diologue-engine server.\n//\n// Why this exists:\n//\n// In v1 (the MVP), OpenCodeProcessAdapter loaded @opencode-ai/sdk from\n// npm and called `sdk.createOpencodeServer()` to spawn an in-process\n// server. That works, but ties us to (a) the user having `opencode` on\n// PATH and (b) shipping an npm SDK version we don't control.\n//\n// The V1-V6 vendoring track introduces a rebranded engine built from\n// vendor/opencode/ (via scripts/rebrand-engine.ts, V2). To swap the\n// adapter cleanly from \"npm SDK\" \u2192 \"local bundled engine\" we need a\n// stable abstraction the adapter can depend on. That's EngineLocator.\n//\n// Two implementations live next to this file:\n//\n// - NpmSdkLocator (engine-locator-npm.ts)\n// Current behavior. Lazily imports @opencode-ai/sdk and uses its\n// createOpencodeServer/createOpencodeClient. Still requires the\n// `opencode` binary on PATH. This is the DEFAULT for now so the\n// MVP keeps working unchanged.\n//\n// - LocalEngineLocator (engine-locator-local.ts)\n// Scaffolding for the V4 path. Validates that build/diologue-engine/\n// exists and that the runtime (bun) is available, then \u2014 until V4\n// ships the bundled engine \u2014 throws a clear \"not yet wired\" error.\n// Opt-in via LOCAL_AGENT_ENGINE=local.\n//\n// Selection:\n//\n// createEngineLocator() reads LOCAL_AGENT_ENGINE (defaults to \"npm\").\n// Tests construct locators directly via the class constructors.\n\nimport type { OpencodeStreamItem } from \"./opencode-event-mapper\";\n\n// ---- Protocol shapes (the slice of opencode's API we depend on) -----\n\n/** Subset of opencode's session/global API surface. Declared narrowly so\n * the adapter file doesn't carry the entire generated SDK in its type\n * surface. */\nexport interface OpencodeClient {\n session: {\n create(options: {\n body?: { title?: string };\n query?: { directory?: string };\n }): Promise<{ data?: { id: string } }>;\n prompt(options: {\n path: { id: string };\n query?: { directory?: string };\n body: {\n parts: Array<{ type: \"text\"; text: string }>;\n system?: string;\n model?: { providerID: string; modelID: string };\n };\n }): Promise<unknown>;\n diff(options: {\n path: { id: string };\n query?: { directory?: string };\n }): Promise<{ data?: { unified?: string } }>;\n };\n /** Reply to a pending permission request. opencode pauses bash/webfetch\n * when configured with `permission: \"ask\"`; this resumes (or rejects) it.\n * `response`: once = allow this call, always = allow for the session,\n * reject = deny. */\n postSessionIdPermissionsPermissionId(options: {\n path: { id: string; permissionID: string };\n query?: { directory?: string };\n body?: { response: \"once\" | \"always\" | \"reject\" };\n }): Promise<unknown>;\n global: {\n event(options?: {\n signal?: AbortSignal;\n }): Promise<{ stream: AsyncIterable<OpencodeStreamItem> }>;\n };\n}\n\n/** Subset of opencode's Config we set. Mostly for registering the\n * Diologue-brokered provider. */\nexport interface OpencodeConfig {\n provider?: {\n [key: string]: {\n name?: string;\n npm?: string;\n models?: {\n [modelId: string]: { id?: string; name?: string };\n };\n options?: {\n apiKey?: string;\n baseURL?: string;\n };\n };\n };\n model?: string;\n /** Tool-permission policy. opencode gates edit/bash/webfetch behind a\n * permission prompt (`permission.updated` \u2192 `permission.replied`). In\n * this headless helper there is no interactive approver, so we must set\n * these to \"allow\" or the agent can never write to the repo. */\n permission?: {\n edit?: \"ask\" | \"allow\" | \"deny\";\n bash?: \"ask\" | \"allow\" | \"deny\";\n webfetch?: \"ask\" | \"allow\" | \"deny\";\n };\n}\n\n// ---- The locator interface itself -----------------------------------\n\nexport interface EngineHandle {\n /** Base URL of the running engine HTTP server. */\n readonly url: string;\n /** Client bound to that URL. */\n readonly client: OpencodeClient;\n /** Shut the engine down. Idempotent. */\n close(): Promise<void> | void;\n}\n\nexport interface EngineStartOptions {\n /** Provider/model config to register with the engine on startup. */\n config?: OpencodeConfig;\n /** Cancellation signal honoured by the locator. */\n signal?: AbortSignal;\n}\n\nexport interface EngineLocator {\n /** Human-readable name for logs + error messages. */\n readonly name: string;\n /** Bring an engine up and return a handle. The caller is responsible\n * for caching the handle for reuse across requests. */\n start(options: EngineStartOptions): Promise<EngineHandle>;\n}\n\n// ---- Factory --------------------------------------------------------\n\nexport type EngineLocatorName = \"npm\" | \"local\";\n\nexport const ENGINE_LOCATOR_ENV = \"LOCAL_AGENT_ENGINE\" as const;\n// V5 flip: the local (vendored, postinstall-fetched) engine is now the\n// default. Users can still opt into the legacy npm path with\n// LOCAL_AGENT_ENGINE=npm for debugging while the V5 release stabilises.\nexport const DEFAULT_LOCATOR_NAME: EngineLocatorName = \"local\";\n\nconst isLocatorName = (value: string): value is EngineLocatorName =>\n value === \"npm\" || value === \"local\";\n\n/** Build an EngineLocator from a name (or from process.env.LOCAL_AGENT_ENGINE).\n * Throws on an unknown name so a typo doesn't silently fall back to npm. */\nexport const createEngineLocator = async (\n name?: string,\n): Promise<EngineLocator> => {\n const resolved = name ?? process.env[ENGINE_LOCATOR_ENV] ?? DEFAULT_LOCATOR_NAME;\n if (!isLocatorName(resolved)) {\n throw new Error(\n `[engine-locator] Unknown engine \"${resolved}\". ` +\n `Set ${ENGINE_LOCATOR_ENV} to \"npm\" or \"local\".`,\n );\n }\n // Late-imported so this module stays free of locator-specific deps.\n if (resolved === \"npm\") {\n const { NpmSdkLocator } = await import(\"./engine-locator-npm\");\n return new NpmSdkLocator();\n }\n const { LocalEngineLocator } = await import(\"./engine-locator-local\");\n return new LocalEngineLocator();\n};\n", "// OpenCodeProcessAdapter \u2014 adapter wired through the EngineLocator seam.\n//\n// Lifecycle:\n// - Asks its EngineLocator for a running engine the first time\n// streamMessage is called. Handle is cached for reuse.\n// - For each AdapterRequest, looks up (or creates) an opencode session\n// bound to the request.repoPath. This gives the engine its native\n// conversation continuity within a coding-agent session.\n// - Subscribes to the global event stream once per turn, filters to\n// our opencode session, translates events via opencode-event-mapper,\n// and yields AgentStreamEvent envelopes.\n// - On `session.idle` we stop iterating, fetch the latest unified diff\n// if any patch was proposed, and emit a final `done` event.\n// - On `close()` we shut the engine down and clear caches.\n//\n// Engine sourcing \u2014 the EngineLocator seam:\n// The adapter no longer cares HOW the engine starts; it only depends\n// on EngineHandle ({ url, client, close }). Two locators ship today:\n//\n// - NpmSdkLocator (default) \u2014 wraps @opencode-ai/sdk + the user's\n// `opencode` binary on PATH. This is what the MVP used.\n// - LocalEngineLocator (opt-in via LOCAL_AGENT_ENGINE=local) \u2014\n// targets build/diologue-engine/ from the V2 rebrand. Scaffolding\n// only in V3; V4 wires the actual spawn.\n//\n// Tests inject locators directly. The legacy `sdkLoader` option is\n// still accepted (mapped to a NpmSdkLocator under the hood) so older\n// call sites keep working.\n\nimport type {\n AgentStreamEvent,\n AgentTextDelta,\n CodingPermissionResponse,\n} from \"../../../shared/coding-agent-types\";\nimport type { AdapterRequest, OpenCodeAdapter } from \"./types\";\nimport {\n createMapState,\n mapEvent,\n type MapState,\n type OpencodeStreamItem,\n} from \"./opencode-event-mapper\";\nimport { LlmBroker } from \"../broker\";\nimport { bindActiveBroker } from \"../shim/active-broker\";\n\nimport {\n createEngineLocator,\n type EngineHandle,\n type EngineLocator,\n type OpencodeClient,\n type OpencodeConfig,\n} from \"./engine-locator\";\nimport { NpmSdkLocator } from \"./engine-locator-npm\";\n\n/** The opencode model id we register against our shim provider. Echoed\n * back to the model layer; can be anything as long as it stays stable\n * across the provider config and the session.prompt model field. */\nconst DIOLOGUE_PROVIDER_ID = \"diologue\";\nconst DIOLOGUE_MODEL_ID = \"diologue-routed\";\n\n/** After `session.prompt()` resolves (the turn finished server-side) we wait\n * this long for any trailing `patch`/`session.idle` events before ending the\n * event loop ourselves. Keeps the happy path (idle arrives) intact while\n * guaranteeing a turn can't hang forever when idle never lands. */\nconst END_OF_TURN_GRACE_MS = 600;\n\n/** In confirm mode, how long a paused bash/webfetch waits for the user before\n * the helper auto-rejects it. Without this a closed tab / walked-away user\n * would hang the turn forever (prompt() never resolves \u2192 broker leaks). */\nconst PERMISSION_DECISION_TIMEOUT_MS = 180_000;\n\n/** Tracks an in-flight turn so the sibling /agent/permission route can reach\n * the engine client + opencode session to reply to a paused tool call. */\ninterface ActiveTurn {\n client: OpencodeClient;\n openCodeSessionId: string;\n repoPath: string;\n /** permissionId \u2192 auto-reject timer, for permissions awaiting the user. */\n pending: Map<string, ReturnType<typeof setTimeout>>;\n}\n\n/** Prepended to each turn's prompt before it reaches the engine. opencode's\n * models \u2014 gpt-4.1 especially \u2014 otherwise sometimes *describe* a change\n * instead of making it, ending the turn with a text answer and no file\n * written. This pushes the agent to actually use its edit tools. It is only\n * added to the engine-bound prompt; the UI transcript and the cloud-persisted\n * message keep the user's original text. */\nconst EDIT_DIRECTIVE =\n \"You are an autonomous coding agent operating on a real git repository. \" +\n \"Carry out the request by editing files directly with your tools \" +\n \"(write / edit / patch / bash) \u2014 do not just describe the change, outline \" +\n \"steps, or print code for the user to copy. Read only what you need, make \" +\n \"the edits on disk, and finish once the change has actually been written.\";\n\nconst logAdapter = (message: string): void => {\n console.error(`[opencode-adapter] ${message}`);\n};\n\n// ----------------------------------------------------------------------------\n// Adapter\n// ----------------------------------------------------------------------------\n\nexport interface OpenCodeProcessAdapterOptions {\n /** Engine locator. Defaults to one resolved from LOCAL_AGENT_ENGINE\n * (or `npm` when unset). Tests inject directly. */\n engineLocator?: EngineLocator;\n /** Base URL of the helper itself (so the engine can reach the shim).\n * Defaults to http://127.0.0.1:LOCAL_AGENT_PORT. Tests inject. */\n helperBaseUrl?: string;\n /** @deprecated Prefer `engineLocator`. Kept for tests written before\n * the V3 refactor \u2014 silently constructs a NpmSdkLocator under the hood. */\n sdkLoader?: () => Promise<unknown>;\n}\n\nexport class OpenCodeProcessAdapter implements OpenCodeAdapter {\n readonly name = \"opencode\";\n\n private serverPromise: Promise<EngineHandle> | null = null;\n /** Maps our sessionId \u2192 opencode session id so successive turns within\n * one coding-agent session reuse the same opencode conversation. */\n private readonly sessionMap = new Map<string, string>();\n /** In-flight turns by our sessionId, so replyPermission() can reach the\n * engine client to resolve a paused tool call. */\n private readonly activeTurns = new Map<string, ActiveTurn>();\n\n constructor(private readonly options: OpenCodeProcessAdapterOptions = {}) {}\n\n private async resolveLocator(): Promise<EngineLocator> {\n if (this.options.engineLocator) {\n return this.options.engineLocator;\n }\n if (this.options.sdkLoader) {\n // Backward compatibility for tests that used the pre-V3 surface.\n return new NpmSdkLocator({\n sdkLoader: this.options.sdkLoader as never,\n });\n }\n return createEngineLocator();\n }\n\n private resolveHelperBaseUrl(): string {\n if (this.options.helperBaseUrl) return this.options.helperBaseUrl;\n const port = process.env.LOCAL_AGENT_PORT ?? \"4099\";\n return `http://127.0.0.1:${port}`;\n }\n\n private buildShimConfig(): OpencodeConfig {\n const baseURL = `${this.resolveHelperBaseUrl()}/llm-shim/v1`;\n return {\n provider: {\n [DIOLOGUE_PROVIDER_ID]: {\n name: \"Diologue (brokered)\",\n // openai-compatible AI SDK provider \u2014 present in opencode's\n // bundled providers. Any other custom-base-URL provider would\n // work too; openai is the most permissive.\n npm: \"@ai-sdk/openai\",\n models: {\n [DIOLOGUE_MODEL_ID]: {\n id: DIOLOGUE_MODEL_ID,\n name: \"Diologue brokered model\",\n },\n },\n options: {\n baseURL,\n // The real apiKey gets injected per-stream via the\n // active-broker handle. This placeholder is what opencode\n // sees at config time; we'll override via env at runtime.\n apiKey: \"placeholder-will-be-replaced\",\n },\n },\n },\n model: `${DIOLOGUE_PROVIDER_ID}/${DIOLOGUE_MODEL_ID}`,\n // Permission policy. The ADAPTER is the policy engine, not this config:\n // - edit stays \"allow\" \u2014 edits land in the working tree and are\n // reversible via the chat's Keep/Revert, so we never prompt on them.\n // - bash + webfetch are \"ask\" so opencode pauses them. In \"auto\" mode\n // the adapter auto-approves instantly (behaves like allow); in\n // \"confirm\" mode it forwards an AgentPermissionRequest to the browser\n // and resumes only once the user replies. See streamMessage().\n permission: {\n edit: \"allow\",\n bash: \"ask\",\n webfetch: \"ask\",\n },\n };\n }\n\n private async ensureServer(): Promise<EngineHandle> {\n if (this.serverPromise) {\n return this.serverPromise;\n }\n this.serverPromise = (async () => {\n const locator = await this.resolveLocator();\n logAdapter(`starting engine helperBase=${this.resolveHelperBaseUrl()}`);\n const handle = await locator.start({ config: this.buildShimConfig() });\n logAdapter(`engine ready url=${handle.url}`);\n return handle;\n })();\n try {\n return await this.serverPromise;\n } catch (err) {\n // Reset so a transient failure can be retried on the next call.\n this.serverPromise = null;\n throw err;\n }\n }\n\n private async ensureOpencodeSession(\n handle: EngineHandle,\n ourSessionId: string,\n repoPath: string,\n title: string | undefined,\n ): Promise<string> {\n const existing = this.sessionMap.get(ourSessionId);\n if (existing) {\n logAdapter(\n `reuse opencodeSession=${existing.slice(0, 8)} session=${ourSessionId.slice(0, 8)} repo=${repoPath}`,\n );\n return existing;\n }\n logAdapter(`create session=${ourSessionId.slice(0, 8)} repo=${repoPath}`);\n const created = await handle.client.session.create({\n body: { title },\n query: { directory: repoPath },\n });\n const id = created.data?.id;\n if (!id) {\n throw new Error(\"opencode session.create returned no id\");\n }\n this.sessionMap.set(ourSessionId, id);\n logAdapter(\n `created opencodeSession=${id.slice(0, 8)} session=${ourSessionId.slice(0, 8)}`,\n );\n return id;\n }\n\n /** Send a decision for a paused permission to opencode. Best-effort: a\n * failure is logged but not thrown \u2014 the worst case is the turn hangs\n * until the grace/abort path tears it down. */\n private async replyToOpencode(\n client: OpencodeClient,\n openCodeSessionId: string,\n repoPath: string,\n permissionId: string,\n response: CodingPermissionResponse,\n ): Promise<void> {\n try {\n await client.postSessionIdPermissionsPermissionId({\n path: { id: openCodeSessionId, permissionID: permissionId },\n query: { directory: repoPath },\n body: { response },\n });\n logAdapter(\n `permission reply id=${permissionId.slice(0, 8)} response=${response}`,\n );\n } catch (err) {\n logAdapter(\n `permission reply FAILED id=${permissionId.slice(0, 8)} response=${response} error=${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n /** Apply a browser decision to a paused permission. Invoked by the\n * /agent/permission route on a separate HTTP request from the SSE stream. */\n async replyPermission(\n sessionId: string,\n permissionId: string,\n response: CodingPermissionResponse,\n ): Promise<boolean> {\n const turn = this.activeTurns.get(sessionId);\n if (!turn) return false;\n const timer = turn.pending.get(permissionId);\n if (timer) {\n clearTimeout(timer);\n turn.pending.delete(permissionId);\n }\n await this.replyToOpencode(\n turn.client,\n turn.openCodeSessionId,\n turn.repoPath,\n permissionId,\n response,\n );\n return true;\n }\n\n async *streamMessage(\n request: AdapterRequest,\n ): AsyncIterable<AgentStreamEvent> {\n let handle: EngineHandle;\n try {\n handle = await this.ensureServer();\n } catch (err) {\n yield {\n type: \"error\",\n message:\n err instanceof Error\n ? err.message\n : \"Failed to start opencode server\",\n recoverable: false,\n };\n return;\n }\n\n let openCodeSessionId: string;\n try {\n openCodeSessionId = await this.ensureOpencodeSession(\n handle,\n request.sessionId,\n request.repoPath,\n request.prompt.slice(0, 80),\n );\n } catch (err) {\n yield {\n type: \"error\",\n message:\n err instanceof Error\n ? err.message\n : \"Failed to start opencode session\",\n recoverable: true,\n };\n return;\n }\n\n const mapState: MapState = createMapState(\n request.sessionId,\n openCodeSessionId,\n );\n let sawAnyPatch = false;\n let lastPatchHash = \"\";\n\n // Permission policy for this turn. opencode is configured to \"ask\" for\n // bash/webfetch; in \"auto\" we approve instantly, in \"confirm\" we forward\n // to the browser and wait. Register the turn so /agent/permission can\n // reach the client to reply.\n const permissionMode = request.permissionMode ?? \"auto\";\n const activeTurn: ActiveTurn = {\n client: handle.client,\n openCodeSessionId,\n repoPath: request.repoPath,\n pending: new Map(),\n };\n this.activeTurns.set(request.sessionId, activeTurn);\n\n // Build a stream-scoped broker that forwards to the request.broker\n // callback when opencode (via the shim) needs an LLM call. We bind\n // it as the active shim broker for the duration of this turn so the\n // /llm-shim route can find it.\n //\n // If the AdapterRequest has no broker, the shim returns 401 and\n // opencode will surface that as an error \u2014 which is the correct\n // behaviour (we can't do anything without a brokered LLM path).\n let activeBrokerHandle: { token: string; release(): void } | null = null;\n let streamScopedBroker: LlmBroker | null = null;\n if (request.broker) {\n const callback = request.broker;\n streamScopedBroker = new LlmBroker({\n emitToStream: () => {\n // Unused: we go directly through the callback rather than\n // emit-then-fulfill. The broker abstraction's emit/fulfill\n // pair is for the SSE route in agent.ts; here we just want\n // the awaitable round-trip without the SSE plumbing.\n },\n });\n // Override request() with a direct callback invocation. Preserve\n // the optional stream observer: opencode uses streaming Responses\n // API calls, and tool-only coding turns depend on observer-delivered\n // tool_call_delta chunks to execute writes/edits.\n const realCallback = callback;\n streamScopedBroker.request = async (payload, observer) => {\n return realCallback(payload, observer);\n };\n activeBrokerHandle = bindActiveBroker(streamScopedBroker);\n // Diagnostic: confirm bind fired and at what token (first 12 chars)\n // so we can correlate with the shim's 401 log on the other side.\n logAdapter(\n `bound active broker token=\"${activeBrokerHandle.token.slice(0, 12)}\u2026\" session=${request.sessionId.slice(0, 8)}`,\n );\n } else {\n logAdapter(\n `WARNING request.broker missing session=${request.sessionId.slice(0, 8)}; shim will 401`,\n );\n }\n\n // Subscribe to events FIRST, then send the prompt \u2014 otherwise we\n // could miss early deltas.\n const eventController = new AbortController();\n const stopOnAbort = () => eventController.abort();\n if (request.signal) {\n if (request.signal.aborted) {\n return;\n }\n request.signal.addEventListener(\"abort\", stopOnAbort);\n }\n\n let eventStream: AsyncIterable<OpencodeStreamItem>;\n try {\n const res = await handle.client.global.event({\n signal: eventController.signal,\n });\n eventStream = res.stream;\n logAdapter(\n `subscribed events session=${request.sessionId.slice(0, 8)} opencodeSession=${openCodeSessionId.slice(0, 8)}`,\n );\n } catch (err) {\n yield {\n type: \"error\",\n message:\n err instanceof Error\n ? err.message\n : \"Failed to subscribe to opencode events\",\n recoverable: true,\n };\n request.signal?.removeEventListener(\"abort\", stopOnAbort);\n return;\n }\n\n // Kick off the prompt. Don't await \u2014 we want to start draining events\n // immediately. Errors land in the catch below.\n //\n // The `model` field tells opencode to route this turn through the\n // diologue/diologue-routed provider we registered at server-spawn\n // time. Outbound LLM calls from opencode then hit our /llm-shim\n // endpoint, where the active-broker mapping routes them back through\n // the AdapterRequest.broker callback to the cloud (with the cloud\n // billing ledger entry).\n //\n // Note: opencode also needs the apiKey at request time. Because we\n // baked a placeholder into Config (the SDK doesn't support per-call\n // apiKey override), the shim accepts the placeholder + cross-checks\n // against the active broker token instead. See active-broker.ts.\n const promptBody: {\n parts: Array<{ type: \"text\"; text: string }>;\n model?: { providerID: string; modelID: string };\n } = {\n parts: [\n { type: \"text\", text: `${EDIT_DIRECTIVE}\\n\\n${request.prompt}` },\n ],\n };\n if (streamScopedBroker && activeBrokerHandle) {\n promptBody.model = {\n providerID: DIOLOGUE_PROVIDER_ID,\n modelID: DIOLOGUE_MODEL_ID,\n };\n }\n logAdapter(\n `prompt start session=${request.sessionId.slice(0, 8)} opencodeSession=${openCodeSessionId.slice(0, 8)} promptChars=${request.prompt.length} provider=${request.preferredProvider ?? \"(default)\"} model=${request.preferredModel ?? \"(default)\"}`,\n );\n const promptPromise = handle.client.session\n .prompt({\n path: { id: openCodeSessionId },\n query: { directory: request.repoPath },\n body: promptBody,\n })\n .then((value) => {\n logAdapter(\n `prompt accepted session=${request.sessionId.slice(0, 8)} opencodeSession=${openCodeSessionId.slice(0, 8)}`,\n );\n return value;\n })\n .catch((err: unknown) => {\n const error = err instanceof Error ? err : new Error(String(err));\n logAdapter(\n `prompt failed session=${request.sessionId.slice(0, 8)} opencodeSession=${openCodeSessionId.slice(0, 8)} error=${error.message}`,\n );\n // Promote into the iteration so the consumer sees one AgentError.\n return error;\n });\n\n // End-of-turn detection. opencode can signal completion two ways and we\n // honour whichever lands first:\n // - a `session.idle` event on the stream (mapped.done) \u2014 the happy path;\n // - `session.prompt()` resolving \u2014 the turn finished server-side.\n // Some engine builds never deliver a matching `session.idle`, so relying\n // on it alone hangs the turn forever: the UI spins, and the `finally`\n // below (which releases the active broker) never runs \u2014 that leak is what\n // made a later turn 401 with active_brokers=2. We add a stop signal driven\n // by the prompt settling, with a short grace for any trailing patch/idle.\n let stopConsuming = false;\n const stopSignal: Promise<\"stop\"> = new Promise((resolve) => {\n void promptPromise.finally(() => {\n setTimeout(() => {\n stopConsuming = true;\n resolve(\"stop\");\n }, END_OF_TURN_GRACE_MS);\n });\n });\n\n let emittedDone = false;\n const iterator = eventStream[Symbol.asyncIterator]();\n try {\n while (!stopConsuming) {\n if (request.signal?.aborted) break;\n const nextP = iterator.next();\n const next = await Promise.race([\n nextP,\n stopSignal.then(() => null),\n ]);\n if (next === null) {\n // Prompt settled + grace elapsed. The in-flight next() is now\n // abandoned; swallow its eventual (abort) rejection so it doesn't\n // surface as an unhandled rejection.\n void nextP.catch(() => undefined);\n break;\n }\n if (next.done) break; // engine event stream ended\n // The engine's global event stream wraps every opencode event as\n // `{ directory, payload: Event }`. The session events the mapper\n // understands (message.part.updated, session.idle, tool/patch parts)\n // live under `payload`. Subscribing without unwrapping was the bug\n // behind the blank chat transcript, the missing in-chat diff, and the\n // turn that never saw `session.idle` (the original hang) \u2014 every raw\n // item had no top-level `type`, so the mapper dropped all of them.\n // Unwrap defensively: fall back to the item itself if it's already\n // an un-wrapped Event (older engines / event.subscribe shape).\n const rawItem = next.value as\n | OpencodeStreamItem\n | { payload?: OpencodeStreamItem };\n const item =\n \"payload\" in rawItem && rawItem.payload\n ? rawItem.payload\n : (rawItem as OpencodeStreamItem);\n const mapped = mapEvent(item, mapState);\n if (mapped.events.length > 0 || mapped.done) {\n logAdapter(\n `event ${item.type} mapped=${mapped.events.map((event) => event.type).join(\",\") || \"(none)\"} done=${mapped.done} session=${request.sessionId.slice(0, 8)}`,\n );\n }\n for (const event of mapped.events) {\n if (event.type === \"permission_request\") {\n if (permissionMode === \"confirm\") {\n // Pause for the user. Hold the turn open until they reply via\n // /agent/permission; auto-reject if they never do.\n const timer = setTimeout(() => {\n activeTurn.pending.delete(event.permissionId);\n void this.replyToOpencode(\n handle.client,\n openCodeSessionId,\n request.repoPath,\n event.permissionId,\n \"reject\",\n );\n logAdapter(\n `permission auto-rejected (timeout) id=${event.permissionId.slice(0, 8)} session=${request.sessionId.slice(0, 8)}`,\n );\n }, PERMISSION_DECISION_TIMEOUT_MS);\n activeTurn.pending.set(event.permissionId, timer);\n yield event;\n } else {\n // Auto mode: approve immediately without bothering the browser.\n void this.replyToOpencode(\n handle.client,\n openCodeSessionId,\n request.repoPath,\n event.permissionId,\n \"once\",\n );\n }\n continue;\n }\n if (event.type === \"diff_proposed\" && mapped.patchToFetch) {\n sawAnyPatch = true;\n lastPatchHash = mapped.patchToFetch.hash;\n }\n if (event.type === \"done\") emittedDone = true;\n yield event;\n }\n if (mapped.done) {\n break;\n }\n }\n } finally {\n eventController.abort();\n try {\n await iterator.return?.();\n } catch {\n // Best-effort stream teardown.\n }\n request.signal?.removeEventListener(\"abort\", stopOnAbort);\n // Reject any permission still awaiting a decision so opencode doesn't\n // hang, then drop the turn from the active registry.\n for (const [permId, timer] of activeTurn.pending) {\n clearTimeout(timer);\n void this.replyToOpencode(\n handle.client,\n openCodeSessionId,\n request.repoPath,\n permId,\n \"reject\",\n );\n }\n activeTurn.pending.clear();\n this.activeTurns.delete(request.sessionId);\n // Always release the active broker so the next turn (or a\n // different process state) can rebind cleanly.\n activeBrokerHandle?.release();\n streamScopedBroker?.close(\"turn_ended\");\n }\n\n // Fetch the final unified diff unconditionally \u2014 some engine builds apply\n // edits without ever emitting a `patch` event, and the chat's Keep/Revert\n // card is driven entirely by this diff_proposed envelope.\n try {\n const diffRes = await handle.client.session.diff({\n path: { id: openCodeSessionId },\n query: { directory: request.repoPath },\n });\n const unified = diffRes.data?.unified ?? \"\";\n if (unified) {\n yield {\n type: \"diff_proposed\",\n unified,\n files: parseUnifiedDiffFiles(unified),\n };\n }\n } catch (err) {\n logAdapter(\n `diff fetch failed session=${request.sessionId.slice(0, 8)} error=${err instanceof Error ? err.message : String(err)}`,\n );\n // Diff fetch is best-effort. Any in-loop diff_proposed already gave the\n // UI a file list to show.\n }\n\n // Promote prompt errors (if any) into the stream as an AgentError.\n const promptResult = await promptPromise;\n if (promptResult instanceof Error) {\n yield { type: \"error\", message: promptResult.message, recoverable: true };\n } else {\n logAdapter(`turn complete session=${request.sessionId.slice(0, 8)}`);\n }\n\n // Guarantee a terminal `done` even when no matching `session.idle` ever\n // arrived, so the UI always stops streaming.\n if (!emittedDone) {\n yield { type: \"done\" };\n }\n\n void sawAnyPatch; // tracked for logging/correlation only\n void lastPatchHash; // reserved for future apply flow correlation\n }\n\n async close(): Promise<void> {\n if (!this.serverPromise) return;\n try {\n const handle = await this.serverPromise;\n await handle.close();\n } catch {\n // Best-effort.\n } finally {\n this.serverPromise = null;\n this.sessionMap.clear();\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n// Diff helpers\n// ----------------------------------------------------------------------------\n\n/** Lightweight unified-diff parser \u2014 just enough to extract the per-file\n * summary we need on `AgentDiffProposed.files`. Counts +/- lines per file\n * and infers status (added/deleted/modified) from the `diff --git` header\n * pair and the presence of /dev/null. */\nconst parseUnifiedDiffFiles = (\n unified: string,\n): Array<{\n path: string;\n additions: number;\n deletions: number;\n status: \"added\" | \"modified\" | \"deleted\" | \"renamed\";\n}> => {\n const files: Array<{\n path: string;\n additions: number;\n deletions: number;\n status: \"added\" | \"modified\" | \"deleted\" | \"renamed\";\n }> = [];\n let current: {\n path: string;\n additions: number;\n deletions: number;\n status: \"added\" | \"modified\" | \"deleted\" | \"renamed\";\n } | null = null;\n let oldPath = \"\";\n let newPath = \"\";\n\n for (const line of unified.split(\"\\n\")) {\n if (line.startsWith(\"diff --git\")) {\n if (current) files.push(current);\n // diff --git a/foo b/foo\n const match = /^diff --git a\\/(.+) b\\/(.+)$/.exec(line);\n if (match) {\n oldPath = match[1];\n newPath = match[2];\n } else {\n oldPath = \"\";\n newPath = \"\";\n }\n current = {\n path: newPath || oldPath,\n additions: 0,\n deletions: 0,\n status: oldPath === newPath ? \"modified\" : \"renamed\",\n };\n continue;\n }\n if (!current) continue;\n if (line.startsWith(\"--- /dev/null\")) {\n current.status = \"added\";\n } else if (line.startsWith(\"+++ /dev/null\")) {\n current.status = \"deleted\";\n } else if (line.startsWith(\"+\") && !line.startsWith(\"+++\")) {\n current.additions++;\n } else if (line.startsWith(\"-\") && !line.startsWith(\"---\")) {\n current.deletions++;\n }\n }\n if (current) files.push(current);\n\n // Suppress AgentDeltaText helper from being unused.\n void (null as unknown as AgentTextDelta);\n return files;\n};\n", "// Barrel \u2014 keeps `import { OpenCodeAdapter, MockOpenCodeAdapter } from\n// \"./adapters\"` short. The default adapter resolves to opencode in prod,\n// with the mock used in two scenarios:\n// 1. Explicit override via LOCAL_AGENT_ADAPTER=mock for offline demos.\n// 2. Inside tests that don't spawn the binary.\n\nexport type {\n AdapterRequest,\n AdapterHistoryMessage,\n LlmBrokerCallback,\n OpenCodeAdapter,\n} from \"./types\";\nexport { MockOpenCodeAdapter, type MockAdapterOptions } from \"./mock-adapter\";\nexport { OpenCodeProcessAdapter } from \"./opencode-adapter\";\n\nimport { MockOpenCodeAdapter } from \"./mock-adapter\";\nimport { OpenCodeProcessAdapter } from \"./opencode-adapter\";\nimport type { OpenCodeAdapter } from \"./types\";\n\n/** Returns the adapter the helper will use for new sessions. Default is\n * the real OpenCodeProcessAdapter; set `LOCAL_AGENT_ADAPTER=mock` to\n * force the canned-response mock (useful for demos with no opencode\n * binary or no provider keys configured). */\nexport const buildDefaultAdapter = (): OpenCodeAdapter => {\n const forced = (process.env.LOCAL_AGENT_ADAPTER ?? \"\").trim().toLowerCase();\n if (forced === \"mock\") {\n return new MockOpenCodeAdapter({ tokenDelayMs: 40 });\n }\n return new OpenCodeProcessAdapter();\n};\n", "// `diologue-local-agent quickstart` \u2014 the zero-friction install path.\n//\n// Flow (browser-first; cloud's /init endpoint requires auth):\n// 1. User opens <cloud>/coding-agent/quickstart in their browser.\n// 2. Page POSTs /api/coding/quickstart/init \u2192 gets a setupToken.\n// 3. Page shows a copy-paste command:\n// npx @diologue/local-agent --cloud=<cloud> --token=<setupToken>\n// 4. User pastes it in a terminal.\n// 5. Helper starts with that token (as LOCAL_AGENT_TOKEN) and the\n// cloud as LOCAL_AGENT_ALLOWED_ORIGIN.\n// 6. Helper POSTs /api/coding/quickstart/checked-in with the token.\n// 7. Browser polls /quickstart/:token until status flips to \"ready\",\n// then redirects to /coding-agent?helper=\u2026&token=\u2026 with the\n// connection auto-tested.\n//\n// On a fresh machine this is \"one command, no copy-paste of tokens\n// into the UI.\"\n\nimport { loadConfig } from \"../config\";\nimport { createApp } from \"../server\";\n\nexport interface QuickstartOptions {\n cloudUrl: string;\n port: number;\n autoOpenBrowser: boolean;\n helperVersion: string;\n /** Setup token from the browser's /coding-agent/quickstart page. When\n * absent the helper directs the user to the browser flow first. */\n setupToken?: string;\n}\n\nconst checkInQuickstart = async (\n cloudUrl: string,\n setupToken: string,\n helperVersion: string,\n port: number,\n): Promise<void> => {\n const res = await fetch(`${cloudUrl}/api/coding/quickstart/checked-in`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n setupToken,\n helperVersion,\n port,\n }),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `Cloud quickstart/checked-in returned ${res.status}: ${text || \"no body\"}`,\n );\n }\n};\n\nconst tryOpenBrowser = async (url: string): Promise<boolean> => {\n try {\n // `open` is a thin cross-platform wrapper around `open`/`xdg-open`/`start`.\n // Skips the spawn (gracefully returns false) on headless boxes.\n const open = (await import(\"open\")).default;\n await open(url);\n return true;\n } catch {\n return false;\n }\n};\n\nexport const runQuickstart = async (\n options: QuickstartOptions,\n): Promise<void> => {\n const cloudUrl = options.cloudUrl.replace(/\\/$/, \"\");\n\n // When no setup token was supplied, the user ran `npx @diologue/local-agent`\n // without going through the browser quickstart page first. Bounce them\n // there and exit \u2014 the page is the source of truth for the token because\n // its /init call is authenticated.\n if (!options.setupToken) {\n const browserUrl = `${cloudUrl}/coding-agent/quickstart`;\n console.log(\"[quickstart] No --token supplied.\");\n console.log(`[quickstart] Open the quickstart page first:`);\n console.log(`[quickstart] ${browserUrl}`);\n console.log(\"\");\n console.log(\n \"[quickstart] It'll give you the exact `npx` command to paste here.\",\n );\n if (options.autoOpenBrowser) {\n await tryOpenBrowser(browserUrl);\n }\n return;\n }\n\n console.log(`[quickstart] Cloud: ${cloudUrl}`);\n console.log(`[quickstart] Setup token: ${options.setupToken.slice(0, 12)}\u2026`);\n\n // Configure the helper to share the setup token with the browser and\n // accept requests from the cloud's origin only.\n process.env.LOCAL_AGENT_TOKEN = options.setupToken;\n process.env.LOCAL_AGENT_ALLOWED_ORIGIN = cloudUrl;\n process.env.LOCAL_AGENT_PORT = String(options.port);\n\n const config = loadConfig();\n const { app, adapter } = createApp({ config });\n\n const server = app.listen(config.port, config.host, async () => {\n const url = `http://${config.host}:${config.port}`;\n console.log(`[quickstart] Helper listening on ${url}`);\n console.log(`[quickstart] Adapter: ${adapter.name}`);\n console.log(\"[quickstart] Telling the cloud the helper is ready\u2026\");\n try {\n await checkInQuickstart(\n cloudUrl,\n options.setupToken!,\n options.helperVersion,\n config.port,\n );\n console.log(\n \"[quickstart] \u2714 Ready. The browser tab should redirect you to /coding-agent in a moment.\",\n );\n } catch (err) {\n console.error(\n `[quickstart] Couldn't notify the cloud \u2014 the browser will keep polling but won't auto-connect.`,\n );\n console.error(`[quickstart] Underlying error: ${(err as Error).message}`);\n console.error(\"\");\n console.error(\n `[quickstart] Workaround: in the browser, paste these into the connection panel:`,\n );\n console.error(`[quickstart] Helper URL: ${url}`);\n console.error(`[quickstart] Pairing token: ${options.setupToken}`);\n }\n console.log(\"\");\n console.log(\"[quickstart] Press Ctrl+C to stop the helper.\");\n });\n\n const shutdown = (signal: NodeJS.Signals) => {\n console.log(`\\n[quickstart] Received ${signal}, shutting down\u2026`);\n server.close(() => process.exit(0));\n setTimeout(() => process.exit(0), 3_000).unref();\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n};\n", "// Credential storage for the paired-device JWT.\n//\n// Primary: OS keychain via `keytar` \u2014 macOS Keychain / Linux\n// Secret Service / Windows Credential Manager. Encrypted at rest,\n// access-controlled by the OS, gold standard for desktop CLI tools.\n//\n// Fallback: `~/.diologue/credentials` file with 0600 mode. Used when\n// keytar can't load (native module missing, headless Linux with no\n// dbus, etc.). Still survives reboots; readable only by the user.\n//\n// We deliberately try-and-fallback rather than fail hard \u2014 keytar's\n// native build is fiddly enough that bricking the helper over it\n// would be hostile. The store records which backend it ended up\n// using so the UI can surface \"your credential is in a plaintext\n// file\" as a soft warning.\n\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n\nconst KEYTAR_SERVICE = \"diologue.local-agent\";\nconst KEYTAR_ACCOUNT = \"device-credential\";\n\nexport type CredentialBackend = \"keychain\" | \"file\";\n\nexport interface StoredCredential {\n jwt: string;\n deviceId: string;\n expiresAt: string; // ISO\n pairedAt: string;\n label: string;\n /** Cloud URL the helper was paired against. Helpers can talk to\n * more than one cloud over their lifetime; we keep the URL with\n * the credential so it's clear which one this JWT is valid for. */\n cloudUrl: string;\n}\n\nexport interface CredentialStore {\n load(): Promise<{ credential: StoredCredential; backend: CredentialBackend } | null>;\n save(credential: StoredCredential): Promise<{ backend: CredentialBackend }>;\n clear(): Promise<void>;\n}\n\n// ----------------------------------------------------------------------------\n// Internal: lazy keytar loader\n// ----------------------------------------------------------------------------\n\ninterface KeytarLike {\n getPassword(service: string, account: string): Promise<string | null>;\n setPassword(service: string, account: string, password: string): Promise<void>;\n deletePassword(service: string, account: string): Promise<boolean>;\n}\n\nlet keytarPromise: Promise<KeytarLike | null> | null = null;\n\nconst loadKeytar = async (): Promise<KeytarLike | null> => {\n if (keytarPromise) return keytarPromise;\n keytarPromise = (async () => {\n try {\n // Dynamic import via an indirection so TS doesn't try to resolve\n // the module at compile time (keytar is `optionalDependencies`\n // and may not be present at all). The cast through `unknown`\n // narrows the runtime shape \u2014 checked structurally below.\n const moduleName = \"keytar\";\n const mod = (await import(/* @vite-ignore */ moduleName)) as unknown as {\n default?: KeytarLike;\n } & KeytarLike;\n return (mod.default ?? mod) as KeytarLike;\n } catch {\n return null;\n }\n })();\n return keytarPromise;\n};\n\n// ----------------------------------------------------------------------------\n// Internal: file fallback location\n// ----------------------------------------------------------------------------\n\nconst fileFallbackPath = (): string => {\n // ~/.diologue/credentials \u2014 same layout as gh, hub, fly etc. use.\n const home = os.homedir();\n return path.join(home, \".diologue\", \"credentials\");\n};\n\n// ----------------------------------------------------------------------------\n// CredentialStore implementation\n// ----------------------------------------------------------------------------\n\nexport interface CredentialStoreOptions {\n /** Force a backend (for tests). Production code never sets this. */\n forceBackend?: CredentialBackend;\n /** Override the file path (for tests). */\n filePathOverride?: string;\n}\n\nexport const createCredentialStore = (\n options: CredentialStoreOptions = {},\n): CredentialStore => {\n const filePath = options.filePathOverride ?? fileFallbackPath();\n const forceBackend = options.forceBackend;\n\n const resolveBackend = async (): Promise<CredentialBackend> => {\n if (forceBackend) return forceBackend;\n const keytar = await loadKeytar();\n return keytar ? \"keychain\" : \"file\";\n };\n\n return {\n async load() {\n const backend = await resolveBackend();\n if (backend === \"keychain\") {\n const keytar = await loadKeytar();\n if (!keytar) return null;\n const raw = await keytar.getPassword(KEYTAR_SERVICE, KEYTAR_ACCOUNT);\n if (!raw) return null;\n try {\n const credential = JSON.parse(raw) as StoredCredential;\n return { credential, backend: \"keychain\" as const };\n } catch {\n return null;\n }\n }\n // File backend.\n try {\n const raw = await fs.readFile(filePath, \"utf8\");\n const credential = JSON.parse(raw) as StoredCredential;\n return { credential, backend: \"file\" as const };\n } catch {\n return null;\n }\n },\n\n async save(credential) {\n const backend = await resolveBackend();\n if (backend === \"keychain\") {\n const keytar = await loadKeytar();\n if (!keytar) {\n // Race: backend resolved to keychain but keytar disappeared.\n // Fall through to file.\n } else {\n await keytar.setPassword(\n KEYTAR_SERVICE,\n KEYTAR_ACCOUNT,\n JSON.stringify(credential),\n );\n return { backend: \"keychain\" };\n }\n }\n // File backend.\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(filePath, JSON.stringify(credential, null, 2), {\n mode: 0o600,\n });\n return { backend: \"file\" };\n },\n\n async clear() {\n const backend = await resolveBackend();\n if (backend === \"keychain\") {\n const keytar = await loadKeytar();\n if (keytar) {\n await keytar\n .deletePassword(KEYTAR_SERVICE, KEYTAR_ACCOUNT)\n .catch(() => undefined);\n }\n }\n // Always also clear the file backup, in case credentials migrated\n // between backends.\n await fs.rm(filePath, { force: true }).catch(() => undefined);\n },\n };\n};\n", "// `diologue-local-agent start` \u2014 raw helper start.\n//\n// This is the existing behaviour from index.ts, lifted into a mode\n// function so the CLI can dispatch it alongside `quickstart` and `pair`.\n// Manual flags override env vars; env vars override defaults.\n\nimport { loadConfig } from \"../config\";\nimport { createCredentialStore } from \"../lib/keychain\";\nimport { createApp } from \"../server\";\n\nexport interface StartOptions {\n port?: number;\n token?: string;\n allowedOrigin?: string;\n adapter?: \"mock\" | \"opencode\";\n}\n\nconst printTunnelStatus = async (): Promise<void> => {\n const store = createCredentialStore();\n const loaded = await store.load().catch(() => null);\n if (!loaded) {\n console.log(\"[local-agent] Tunnel: not paired.\");\n console.log(\n \"[local-agent] Run `diologue-local-agent pair <code>` to enable remote-machine use.\",\n );\n return;\n }\n const expires = new Date(loaded.credential.expiresAt);\n const daysLeft = Math.max(\n 0,\n Math.round((expires.getTime() - Date.now()) / 86_400_000),\n );\n console.log(\n `[local-agent] Tunnel: paired as \"${loaded.credential.label}\" (${loaded.credential.deviceId})`,\n );\n console.log(\n `[local-agent] Cloud: ${loaded.credential.cloudUrl} Expires in ${daysLeft}d Storage: ${loaded.backend}`,\n );\n};\n\nexport const runStart = async (options: StartOptions): Promise<void> => {\n // Apply CLI overrides via env before loadConfig reads them. Keeping\n // env as the indirection means config.ts stays the single source of\n // truth for how the helper resolves its settings.\n if (options.port) process.env.LOCAL_AGENT_PORT = String(options.port);\n if (options.token) process.env.LOCAL_AGENT_TOKEN = options.token;\n if (options.allowedOrigin)\n process.env.LOCAL_AGENT_ALLOWED_ORIGIN = options.allowedOrigin;\n if (options.adapter) process.env.LOCAL_AGENT_ADAPTER = options.adapter;\n\n const config = loadConfig();\n const { app, adapter } = createApp({ config });\n\n const server = app.listen(config.port, config.host, async () => {\n const url = `http://${config.host}:${config.port}`;\n console.log(`[local-agent] Listening on ${url}`);\n console.log(`[local-agent] Adapter: ${adapter.name}`);\n console.log(\"[local-agent] Pairing token (paste into the web UI):\");\n console.log(`[local-agent] ${config.token}`);\n console.log(`[local-agent] Allowed browser origin: ${config.allowedOrigin}`);\n await printTunnelStatus();\n console.log(\n \"[local-agent] Press Ctrl+C to stop. The token is regenerated on each restart.\",\n );\n });\n\n const shutdown = (signal: NodeJS.Signals) => {\n console.log(`\\n[local-agent] Received ${signal}, shutting down...`);\n server.close(() => process.exit(0));\n setTimeout(() => process.exit(0), 3_000).unref();\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n};\n", "// `diologue-local-agent pair <code>` \u2014 device-pairing CLI.\n//\n// Lifted from scripts/pair.ts into a mode function so the unified CLI\n// can dispatch it. Behaviour is identical: hit /api/devices/pair-complete,\n// store the resulting JWT in the credential store.\n\nimport os from \"node:os\";\n\nimport { createCredentialStore } from \"../lib/keychain\";\n\nconst detectOs = (): \"darwin\" | \"linux\" | \"win32\" | undefined => {\n const p = process.platform;\n if (p === \"darwin\" || p === \"linux\" || p === \"win32\") return p;\n return undefined;\n};\n\nconst detectLabel = (): string => {\n const hostname = os.hostname() || \"unknown-host\";\n return hostname.replace(/\\.local$/i, \"\");\n};\n\ninterface PairCompleteResponse {\n device: { id: string; label: string };\n jwt: string;\n expiresAt: string;\n}\n\nexport interface PairOptions {\n code: string;\n cloudUrl: string;\n helperVersion: string;\n}\n\nexport const runPair = async (options: PairOptions): Promise<void> => {\n const cloudUrl = options.cloudUrl.replace(/\\/$/, \"\");\n const label = process.env.DIOLOGUE_DEVICE_LABEL?.trim() || detectLabel();\n const osId = detectOs();\n\n console.log(`[pair] Cloud: ${cloudUrl}`);\n console.log(`[pair] Label: ${label}`);\n console.log(`[pair] OS: ${osId ?? \"(unknown)\"}`);\n console.log(`[pair] Code: ${options.code}`);\n console.log(\"[pair] Exchanging code for a device credential\u2026\");\n\n let response: Response;\n try {\n response = await fetch(`${cloudUrl}/api/devices/pair-complete`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n code: options.code,\n label,\n ...(osId ? { os: osId } : {}),\n helperVersion: options.helperVersion,\n }),\n });\n } catch (err) {\n console.error(\n \"[pair] Could not reach the cloud. Check --cloud / DIOLOGUE_CLOUD_URL and your network.\",\n );\n console.error(`[pair] Underlying error: ${(err as Error).message}`);\n process.exit(1);\n }\n\n if (response.status === 404) {\n console.error(\n \"[pair] Code not found. Generate a fresh one from Settings \u2192 Devices.\",\n );\n process.exit(1);\n }\n if (response.status === 410) {\n console.error(\"[pair] Code already used or expired. Generate a fresh one.\");\n process.exit(1);\n }\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n console.error(`[pair] Cloud returned ${response.status}: ${text}`);\n process.exit(1);\n }\n\n const data = (await response.json()) as PairCompleteResponse;\n if (!data.jwt || !data.device?.id) {\n console.error(\"[pair] Cloud response missing fields:\", data);\n process.exit(1);\n }\n\n const store = createCredentialStore();\n const { backend } = await store.save({\n jwt: data.jwt,\n deviceId: data.device.id,\n expiresAt: data.expiresAt,\n pairedAt: new Date().toISOString(),\n label: data.device.label,\n cloudUrl,\n });\n\n console.log(\"\");\n console.log(`[pair] \u2714 Paired as \"${data.device.label}\" (${data.device.id})`);\n console.log(`[pair] Credential expires: ${data.expiresAt}`);\n console.log(\n `[pair] Stored in: ${backend === \"keychain\" ? \"OS keychain\" : \"~/.diologue/credentials (file, 0600)\"}`,\n );\n if (backend === \"file\") {\n console.log(\n \"[pair] Note: keytar (OS keychain) couldn't load; using file fallback.\",\n );\n }\n console.log(\"\");\n console.log(\"[pair] The helper will pick this credential up on next start.\");\n console.log(\"[pair] Start it with: diologue-local-agent\");\n};\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AASA,SAAS,aAAa;AATtB,IA8CM,oBAOA,kBAGO;AAxDb;AAAA;AAAA;AA8CA,IAAM,qBAAqB,YACzB,IAAI,QAAiB,CAAC,YAAY;AAChC,YAAM,QAAQ,MAAM,YAAY,CAAC,WAAW,GAAG,EAAE,OAAO,SAAS,CAAC;AAClE,YAAM,GAAG,SAAS,MAAM,QAAQ,KAAK,CAAC;AACtC,YAAM,GAAG,QAAQ,CAAC,SAAS,QAAQ,SAAS,CAAC,CAAC;AAAA,IAChD,CAAC;AAEH,IAAM,mBAAmB,MACvB,OAAO,kBAAkB;AAEpB,IAAM,gBAAN,MAA6C;AAAA,MAGlD,YAA6B,UAAgC,CAAC,GAAG;AAApC;AAAA,MAAqC;AAAA,MAFzD,OAAO;AAAA,MAIhB,MAAM,MAAM,SAAoD;AAC9D,cAAM,QAAQ,KAAK,QAAQ,eAAe;AAC1C,cAAM,KAAK,MAAM,MAAM;AACvB,YAAI,CAAC,IAAI;AACP,gBAAM,IAAI;AAAA,YACR;AAAA,UAIF;AAAA,QACF;AAEA,cAAM,UAAU,KAAK,QAAQ,aAAa;AAC1C,cAAM,MAAM,MAAM,QAAQ;AAC1B,cAAM,SAAS,MAAM,IAAI,qBAAqB;AAAA,UAC5C,UAAU;AAAA,UACV,MAAM;AAAA,UACN,QAAQ,QAAQ;AAAA,UAChB,QAAQ,QAAQ;AAAA,QAClB,CAAC;AACD,cAAM,SAAS,IAAI,qBAAqB,EAAE,SAAS,OAAO,IAAI,CAAC;AAC/D,eAAO;AAAA,UACL,KAAK,OAAO;AAAA,UACZ;AAAA,UACA,OAAO,MAAM;AACX,mBAAO,MAAM;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA;AAAA;;;AC1FA;AAAA;AAAA;AAAA;AAAA,MACE,UAAY;AAAA,MACZ,SAAW;AAAA,MACX,YAAc;AAAA,MACd,cAAgB;AAAA,MAChB,SAAW;AAAA,QACT,EAAE,UAAY,UAAU,MAAQ,SAAS,WAAa,oBAAoB,KAAO,GAAG;AAAA,QACpF,EAAE,UAAY,UAAU,MAAQ,OAAO,WAAa,kBAAkB,KAAO,GAAG;AAAA,QAChF,EAAE,UAAY,SAAS,MAAQ,OAAO,WAAa,iBAAiB,KAAO,GAAG;AAAA,QAC9E,EAAE,UAAY,SAAS,MAAQ,SAAS,WAAa,mBAAmB,KAAO,GAAG;AAAA,QAClF,EAAE,UAAY,SAAS,MAAQ,OAAO,WAAa,mBAAmB,KAAO,OAAO;AAAA,MACtF;AAAA,IACF;AAAA;AAAA;;;ACSA,SAAS,UAAAA,SAAQ,iBAAiB;AAClC,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,OAAOC,WAAU;AACjB,SAAS,qBAAqB;AAzB9B,IAyCa,eAEP,YACA,WAGA,kBAEA,WAEO,sBAMA,wBAKA,mBASPC,SAmBO,yBAqBA,gBAsBA;AArIb;AAAA;AAAA;AA2BA;AAcO,IAAM,gBAAgB;AAE7B,IAAM,aAAa,cAAc,YAAY,GAAG;AAChD,IAAM,YAAYD,MAAK,QAAQ,UAAU;AAGzC,IAAM,mBAAmBA,MAAK,QAAQ,WAAW,OAAO;AAExD,IAAM,YAAYA,MAAK,QAAQ,kBAAkB,IAAI;AAE9C,IAAM,uBAAuBA,MAAK;AAAA,MACvC;AAAA,MACA;AAAA,MACA,cAAc;AAAA,IAChB;AAEO,IAAM,yBAAyBA,MAAK;AAAA,MACzC;AAAA,MACA;AAAA,IACF;AAEO,IAAM,oBAAoB;AASjC,IAAMC,UAAS,OAAO,MAAgC;AACpD,UAAI;AACF,cAAMF,QAAO,GAAG,UAAU,IAAI;AAC9B,eAAO;AAAA,MACT,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAYO,IAAM,0BAA0B,MAAqB;AAC1D,UAAI;AACF,cAAMG,WAAU,cAAc,YAAY,GAAG;AAC7C,cAAM,cAAcA,SAAQ,QAAQ,0BAA0B;AAC9D,cAAM,SAASF,MAAK,QAAQ,WAAW;AACvC,cAAM,MAAM,KAAK,MAAM,aAAa,aAAa,OAAO,CAAC;AAGzD,cAAM,SACJ,OAAO,IAAI,QAAQ,WAAW,IAAI,MAAM,IAAI,KAAK;AACnD,YAAI,CAAC,OAAQ,QAAO;AACpB,eAAOA,MAAK,KAAK,QAAQ,MAAM;AAAA,MACjC,QAAQ;AAEN,eAAO;AAAA,MACT;AAAA,IACF;AAKO,IAAM,iBAAiB,CAC5B,WAA4B,QAAQ,UACpC,OAA4B,QAAQ,SACzB;AACX,YAAM,SAAS,cAAc,QAAQ;AAAA,QACnC,CAAC,MAAM,EAAE,aAAa,YAAY,EAAE,SAAS;AAAA,MAC/C;AACA,YAAM,MAAM,QAAQ,OAAO;AAC3B,aAAO,mBAAmB,QAAQ,IAAI,IAAI,GAAG,GAAG;AAAA,IAClD;AAaO,IAAM,mBAAmB,YAA4C;AAC1E,YAAM,UAAU,QAAQ,IAAI,iBAAiB;AAC7C,UAAI,SAAS;AACX,YAAI,MAAMC,QAAO,OAAO,GAAG;AACzB,iBAAO,EAAE,MAAM,SAAS,QAAQ,MAAM;AAAA,QACxC;AAAA,MAGF;AAEA,YAAM,aAAa,wBAAwB;AAC3C,UAAI,cAAe,MAAMA,QAAO,UAAU,GAAI;AAC5C,eAAO,EAAE,MAAM,YAAY,QAAQ,cAAc;AAAA,MACnD;AAGA,YAAM,WAAW,eAAe;AAChC,YAAM,YAAYD,MAAK,KAAK,sBAAsB,QAAQ;AAC1D,UAAI,MAAMC,QAAO,SAAS,GAAG;AAC3B,eAAO,EAAE,MAAM,WAAW,QAAQ,YAAY;AAAA,MAChD;AACA,YAAM,aAAaD,MAAK,KAAK,wBAAwB,QAAQ;AAC7D,UAAI,MAAMC,QAAO,UAAU,GAAG;AAC5B,eAAO,EAAE,MAAM,YAAY,QAAQ,cAAc;AAAA,MACnD;AACA,aAAO;AAAA,IACT;AAAA;AAAA;;;AC/JA;AAAA;AAAA;AAAA;AA0BA,SAAS,UAAAE,SAAQ,aAAAC,kBAAiB;AAClC,SAAS,SAAAC,cAAgC;AACzC,OAAOC,WAAU;AACjB,SAAS,iBAAAC,sBAAqB;AA7B9B,IAwCMC,aACAC,YAGAC,YAEA,oBACA,WACA,4BAOA,iBA8BA,qBAOAC,SASA,UAGA,2BAeA,mBAiBA,gBAWA,sBAYO,oBAgQP;AA/ZN;AAAA;AAAA;AAsCA;AAEA,IAAMH,cAAaD,eAAc,YAAY,GAAG;AAChD,IAAME,aAAYH,MAAK,QAAQE,WAAU;AAGzC,IAAME,aAAYJ,MAAK,QAAQG,YAAW,UAAU;AAEpD,IAAM,qBAAqBH,MAAK,KAAKI,YAAW,uBAAuB;AACvE,IAAM,YAAY;AAClB,IAAM,6BAA6B;AAOnC,IAAM,kBAAkB;AA8BxB,IAAM,sBAAsB,OAAO,YACjC,IAAI,QAAiB,CAAC,YAAY;AAChC,YAAM,QAAQL,OAAM,SAAS,CAAC,WAAW,GAAG,EAAE,OAAO,SAAS,CAAC;AAC/D,YAAM,GAAG,SAAS,MAAM,QAAQ,KAAK,CAAC;AACtC,YAAM,GAAG,QAAQ,CAAC,SAAS,QAAQ,SAAS,CAAC,CAAC;AAAA,IAChD,CAAC;AAEH,IAAMM,UAAS,OAAO,MAAgC;AACpD,UAAI;AACF,cAAMR,QAAO,GAAGC,WAAU,IAAI;AAC9B,eAAO;AAAA,MACT,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAEA,IAAM,WAAW,CAAC,UAChB,QAAQ,KAAK,KAAK,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAErE,IAAM,4BAA4B,CAChC,aAC4B;AAC5B,UAAI,CAAC,SAAU,QAAO,CAAC;AACvB,UAAI;AACF,cAAM,SAAS,KAAK,MAAM,QAAQ;AAClC,eAAO,SAAS,MAAM,IAAI,SAAS,CAAC;AAAA,MACtC,QAAQ;AAIN,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AAEA,IAAM,oBAAoB,CACxB,UACA,aACuB;AACvB,UAAI,CAAC,SAAU,QAAO;AACtB,YAAM,OAAO,0BAA0B,QAAQ;AAC/C,YAAM,eAAe,SAAS,KAAK,QAAQ,IAAI,KAAK,WAAW,CAAC;AAChE,aAAO,KAAK,UAAU;AAAA,QACpB,GAAG;AAAA,QACH,GAAG;AAAA,QACH,UAAU;AAAA,UACR,GAAG;AAAA,UACH,GAAI,SAAS,YAAY,CAAC;AAAA,QAC5B;AAAA,MACF,CAAC;AAAA,IACH;AAEA,IAAM,iBAAiB,CACrB,WACsB;AACtB,YAAM,MAAM,EAAE,GAAG,QAAQ,IAAI;AAC7B,YAAM,UAAU,kBAAkB,IAAI,yBAAyB,MAAM;AACrE,UAAI,SAAS;AACX,YAAI,0BAA0B;AAAA,MAChC;AACA,aAAO;AAAA,IACT;AAEA,IAAM,uBAAuB,OAC3B,YAC4B;AAI5B,YAAM,MAAO,MAAM,OAAO,kBAAkB;AAG5C,aAAO,IAAI,qBAAqB,EAAE,QAAQ,CAAC;AAAA,IAC7C;AAEO,IAAM,qBAAN,MAAkD;AAAA,MAMvD,YAA6B,UAAqC,CAAC,GAAG;AAAzC;AAC3B,aAAK,aAAa,QAAQ,cAAc;AACxC,aAAK,UAAU,QAAQ,WAAW;AAClC,aAAK,mBACH,QAAQ,oBAAoB;AAAA,MAChC;AAAA,MAVS,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA;AAAA;AAAA;AAAA,MAYT,MAAM,gBAAgD;AACpD,YAAI,KAAK,QAAQ,YAAa,QAAO;AACrC,YAAI,KAAK,QAAQ,cAAe,QAAO,KAAK,QAAQ,cAAc;AAClE,eAAO,iBAAiB;AAAA,MAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,MAAM,YAAoC;AACxC,cAAM,SAAS,MAAM,KAAK,cAAc;AACxC,YAAI,QAAQ;AAIV,iBAAO;AAAA,QACT;AAEA,YAAI,CAAE,MAAMO,QAAO,KAAK,UAAU,GAAI;AACpC,iBACE,uEACW,KAAK,UAAU;AAAA;AAAA;AAAA,QAI9B;AACA,cAAM,QAAQL,MAAK,KAAK,KAAK,YAAY,SAAS;AAClD,YAAI,CAAE,MAAMK,QAAO,KAAK,GAAI;AAC1B,iBACE,oDAAoD,KAAK;AAAA,QAI7D;AACA,cAAM,QAAQ,KAAK,QAAQ,gBAAgB;AAC3C,cAAM,KAAK,MAAM,MAAM,KAAK,OAAO;AACnC,YAAI,CAAC,IAAI;AACP,iBACE,8DACI,KAAK,OAAO;AAAA;AAAA;AAAA,QAIpB;AACA,eAAO;AAAA,MACT;AAAA;AAAA;AAAA;AAAA,MAKA,MAAc,iBAKX;AACD,YAAI,KAAK,QAAQ,eAAe;AAC9B,iBAAO,EAAE,GAAG,KAAK,QAAQ,eAAe,QAAQ,WAAW;AAAA,QAC7D;AACA,cAAM,SAAS,MAAM,KAAK,cAAc;AACxC,YAAI,QAAQ;AACV,iBAAO;AAAA,YACL,SAAS,OAAO;AAAA,YAChB,MAAM,CAAC,SAAS,UAAU,KAAK,cAAc,WAAW;AAAA,YACxD,QAAQ;AAAA,YACR;AAAA,UACF;AAAA,QACF;AACA,eAAO;AAAA,UACL,SAAS,KAAK;AAAA,UACd,MAAM;AAAA,YACJ;AAAA,YACAL,MAAK,KAAK,KAAK,YAAY,SAAS;AAAA,YACpC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,UACA,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,MAEA,MAAM,MAAM,SAAoD;AAC9D,cAAM,eAAe,MAAM,KAAK,UAAU;AAC1C,YAAI,aAAc,OAAM,IAAI,MAAM,YAAY;AAE9C,cAAM,WAAW,MAAM,KAAK,eAAe;AAC3C,cAAM,EAAE,SAAS,KAAK,IAAI;AAK1B,cAAM,MACJ,SAAS,WAAW,YAAa,MAAMK,QAAO,KAAK,UAAU,IACzD,KAAK,aACL;AACN,cAAM,QAAQN,OAAM,SAAS,MAAM;AAAA,UACjC;AAAA,UACA,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMhC,KAAK,eAAe,QAAQ,MAAM;AAAA,QACpC,CAAC;AAED,YAAI;AACJ,YAAI;AACF,gBAAM,MAAM,KAAK,iBAAiB,OAAO,QAAQ,MAAM;AAAA,QACzD,SAAS,KAAK;AAGZ,cAAI,CAAC,MAAM,QAAQ;AACjB,gBAAI;AACF,oBAAM,KAAK;AAAA,YACb,QAAQ;AAAA,YAER;AAAA,UACF;AACA,gBAAM;AAAA,QACR;AAEA,cAAM,UAAU,KAAK,QAAQ,iBAAiB;AAC9C,cAAM,SAAS,MAAM,QAAQ,GAAG;AAEhC,eAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA,OAAO,MAAM;AACX,gBAAI,MAAM,aAAa,QAAQ,CAAC,MAAM,QAAQ;AAC5C,kBAAI;AACF,sBAAM,KAAK;AAAA,cACb,QAAQ;AAAA,cAER;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQQ,iBACN,OACA,OACiB;AACjB,eAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC9C,cAAI,UAAU;AACd,cAAI,WAAW;AACf,gBAAM,MAAM,GAAG,KAAK,OAAO,IAAIC,MAAK,SAAS,KAAK,UAAU,CAAC;AAE7D,gBAAM,SAAS,CAAC,OAAyB;AACvC,gBAAI,QAAS;AACb,sBAAU;AACV,oBAAQ;AACR,eAAG;AAAA,UACL;AAEA,gBAAM,WAAW,CAAC,UAAwB;AACxC,kBAAM,OAAO,MAAM,SAAS,OAAO;AACnC,wBAAY;AACZ,kBAAM,QAAQ,gBAAgB,KAAK,QAAQ;AAC3C,gBAAI,OAAO;AACT,qBAAO,MAAM,QAAQ,MAAM,CAAC,CAAE,CAAC;AAAA,YACjC;AAAA,UACF;AAGA,gBAAM,WAAW;AAEjB,gBAAM,SAAS,CAAC,MAAqB,WAAwC;AAC3E;AAAA,cAAO,MACL;AAAA,gBACE,IAAI;AAAA,kBACF,0BAA0B,GAAG,uCAClB,IAAI,WAAW,UAAU,GAAG;AAAA,EACpB,SAAS,UAAU,IAAI,CAAC;AAAA,gBAC7C;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAEA,gBAAM,UAAU,CAAC,QAAqB;AACpC;AAAA,cAAO,MACL;AAAA,gBACE,IAAI;AAAA,kBACF,0BAA0B,GAAG,qBAAqB,IAAI,OAAO;AAAA,gBAC/D;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAEA,gBAAM,QAAQ,WAAW,MAAM;AAC7B;AAAA,cAAO,MACL;AAAA,gBACE,IAAI;AAAA,kBACF,0BAA0B,GAAG,gCACxB,KAAK,gBAAgB;AAAA,EAAqB,SAAS,UAAU,IAAI,CAAC;AAAA,gBACzE;AAAA,cACF;AAAA,YACF;AAAA,UACF,GAAG,KAAK,gBAAgB;AAExB,gBAAM,UAAU,MAAY;AAC1B;AAAA,cAAO,MACL,OAAO,IAAI,MAAM,wCAAwC,CAAC;AAAA,YAC5D;AAAA,UACF;AAEA,gBAAM,UAAU,MAAY;AAC1B,yBAAa,KAAK;AAClB,kBAAM,QAAQ,IAAI,QAAQ,QAAQ;AAClC,kBAAM,QAAQ,IAAI,QAAQ,QAAQ;AAClC,kBAAM,IAAI,QAAQ,MAAM;AACxB,kBAAM,IAAI,SAAS,OAAO;AAC1B,mBAAO,oBAAoB,SAAS,OAAO;AAAA,UAC7C;AAEA,gBAAM,QAAQ,GAAG,QAAQ,QAAQ;AACjC,gBAAM,QAAQ,GAAG,QAAQ,QAAQ;AACjC,gBAAM,KAAK,QAAQ,MAAM;AACzB,gBAAM,KAAK,SAAS,OAAO;AAC3B,cAAI,OAAO;AACT,gBAAI,MAAM,SAAS;AACjB,sBAAQ;AACR;AAAA,YACF;AACA,kBAAM,iBAAiB,SAAS,OAAO;AAAA,UACzC;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,IAAM,WAAW,CAAC,GAAW,QAC3B,EAAE,UAAU,MAAM,IAAI,EAAE,MAAM,EAAE,SAAS,GAAG;AAAA;AAAA;;;AClZ9C,OAAOM,cAAa;;;ACVpB,SAAS,mBAAmB;AAkB5B,IAAM,eAAe;AACrB,IAAM,yBAAyB;AAE/B,IAAM,YAAY,CAAC,QAAoC;AACrD,MAAI,CAAC,KAAK;AACR,WAAO;AAAA,EACT;AACA,QAAM,SAAS,OAAO,SAAS,KAAK,EAAE;AACtC,MAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,KAAK,SAAS,OAAQ;AAC9D,UAAM,IAAI;AAAA,MACR,6DAA6D,GAAG;AAAA,IAClE;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,gBAAgB,MAAc;AAGlC,SAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AACvC;AAEO,IAAM,aAAa,CAAC,UAAuB,CAAC,MAAwB;AACzE,QAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,QAAM,OAAyB;AAAA;AAAA;AAAA;AAAA,IAI7B,MAAM;AAAA,IACN,MAAM,UAAU,IAAI,gBAAgB;AAAA,IACpC,gBACG,IAAI,8BAA8B,IAAI,KAAK,KAAK;AAAA,IACnD,QAAQ,IAAI,qBAAqB,IAAI,KAAK,KAAK,cAAc;AAAA;AAAA;AAAA,IAG7D,eAAe;AAAA,IACf,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AACA,SAAO,EAAE,GAAG,MAAM,GAAG,QAAQ,UAAU;AACzC;;;AC1DA,OAAO,aAA+B;;;ACStC,SAAS,uBAAuB;;;ACCzB,IAAM,2BAA2B;;;ADWxC,IAAM,0BAA+C,oBAAI,IAAI,CAAC,aAAa,CAAC;AAM5E,IAAM,mCAAsD,CAAC,YAAY;AAMzE,IAAM,cAAc,CAAC,GAAW,MAAuB;AACrD,QAAM,KAAK,OAAO,KAAK,GAAG,MAAM;AAChC,QAAM,KAAK,OAAO,KAAK,GAAG,MAAM;AAChC,MAAI,GAAG,WAAW,GAAG,QAAQ;AAC3B,WAAO;AAAA,EACT;AACA,SAAO,gBAAgB,IAAI,EAAE;AAC/B;AAEO,IAAM,uBAAuB,CAAC,YAAyB;AAC5D,QAAM,SAAS,QAAQ,mBAAmB;AAC1C,SAAO,CAAC,KAAc,KAAe,SAA6B;AAChE,UAAM,MAAM,GAAG,IAAI,MAAM,IAAI,IAAI,IAAI;AACrC,QAAI,OAAO,IAAI,GAAG,GAAG;AACnB,WAAK;AACL;AAAA,IACF;AAGA,eAAW,UAAU,kCAAkC;AACrD,UAAI,IAAI,KAAK,WAAW,MAAM,GAAG;AAC/B,aAAK;AACL;AAAA,MACF;AAAA,IACF;AAIA,QAAI,IAAI,WAAW,WAAW;AAC5B,WAAK;AACL;AAAA,IACF;AACA,UAAM,WAAW,IAAI,OAAO,wBAAwB;AACpD,QAAI,CAAC,YAAY,CAAC,YAAY,UAAU,QAAQ,KAAK,GAAG;AACtD,UACG,OAAO,GAAG,EACV,KAAK,EAAE,OAAO,4BAA4B,QAAQ,yBAAyB,CAAC;AAC/E;AAAA,IACF;AACA,SAAK;AAAA,EACP;AACF;;;AE3DO,IAAM,uBAAuB,CAAC,YAAyB;AAC5D,QAAM,UAAU,QAAQ;AACxB,SAAO,CAAC,KAAc,KAAe,SAA6B;AAChE,UAAM,SAAS,IAAI,OAAO,QAAQ;AAClC,QAAI,UAAU,WAAW,SAAS;AAChC,UAAI,UAAU,+BAA+B,MAAM;AACnD,UAAI,UAAU,QAAQ,QAAQ;AAC9B,UAAI,UAAU,oCAAoC,OAAO;AACzD,UAAI,UAAU,gCAAgC,kBAAkB;AAChE,UAAI;AAAA,QACF;AAAA,QACA,iBAAiB,wBAAwB;AAAA,MAC3C;AACA,UAAI,UAAU,0BAA0B,KAAK;AAU7C,UACE,IAAI,OAAO,wCAAwC,MAAM,QACzD;AACA,YAAI,UAAU,wCAAwC,MAAM;AAAA,MAC9D;AAAA,IACF;AACA,QAAI,IAAI,WAAW,WAAW;AAG5B,UAAI,OAAO,GAAG,EAAE,IAAI;AACpB;AAAA,IACF;AACA,SAAK;AAAA,EACP;AACF;;;ACvCO,IAAM,cAAc,MAAkB;AAC3C,MAAI,WAA8B;AAClC,SAAO;AAAA,IACL,iBAAiB,MAAM;AAAA,IACvB,iBAAiB,CAAC,SAAS;AACzB,iBAAW;AAAA,IACb;AAAA,IACA,mBAAmB,MAAM;AACvB,iBAAW;AAAA,IACb;AAAA,EACF;AACF;;;AClBO,IAAM,sBAAsB,CAAC,WAA6B;AAC/D,SAAO,CAAC,MAAe,QAAwB;AAC7C,UAAM,OAAyB;AAAA,MAC7B,IAAI;AAAA,MACJ,eAAe,OAAO;AAAA,MACtB,WAAW,OAAO;AAAA,MAClB,MAAM,OAAO;AAAA,MACb,WAAW,OAAO;AAAA,IACpB;AACA,QAAI,KAAK,IAAI;AAAA,EACf;AACF;;;ACZA,SAAS,UAAU,oBAAoB;AACvC,OAAOC,WAAU;AACjB,SAAS,SAAS;;;ACJlB,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAI1B,IAAM,gBAAgB,UAAU,QAAQ;AAEjC,IAAM,kBAAN,cAA8B,MAAM;AAAA,EAChC;AAAA,EACA;AAAA,EACT,YAAY,MAAyB,UAAkB,QAAgB;AACrE,UAAM,OAAO,KAAK,KAAK,GAAG,CAAC,qBAAqB,QAAQ,KAAK,OAAO,KAAK,CAAC,EAAE;AAC5E,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,WAAW;AAAA,EAClB;AACF;AAGO,IAAM,SAAS,OAAO,KAAa,SAAoC;AAC5E,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,cAAc,OAAO,MAAM;AAAA,MAClD;AAAA;AAAA;AAAA,MAGA,WAAW,KAAK,OAAO;AAAA;AAAA,MAEvB,KAAK,EAAE,GAAG,QAAQ,KAAK,WAAW,OAAO,OAAO,MAAM;AAAA,IACxD,CAAC;AACD,WAAO;AAAA,EACT,SAAS,KAAc;AACrB,QAAI,OAAO,OAAO,QAAQ,YAAY,UAAU,KAAK;AACnD,YAAM,IAAI;AACV,YAAM,IAAI,gBAAgB,MAAM,EAAE,QAAQ,IAAI,EAAE,UAAU,EAAE;AAAA,IAC9D;AACA,UAAM;AAAA,EACR;AACF;AAEO,IAAM,YAAY,OAAO,QAAwC;AACtE,MAAI;AACF,UAAM,OAAO,MAAM,OAAO,KAAK,CAAC,aAAa,gBAAgB,MAAM,CAAC,GAAG,KAAK;AAG5E,QAAI,QAAQ,QAAQ;AAClB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,eAAe,OAAO,QAAwC;AACzE,MAAI;AACF,YAAQ,MAAM,OAAO,KAAK,CAAC,aAAa,WAAW,MAAM,CAAC,GAAG,KAAK;AAAA,EACpE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,UAAU,OAAO,QAAkC;AAC9D,QAAM,MAAM,MAAM,OAAO,KAAK,CAAC,UAAU,aAAa,CAAC;AACvD,SAAO,IAAI,KAAK,EAAE,SAAS;AAC7B;AAcO,IAAM,mBAAmB,CAAC,WAAkC;AACjE,QAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,QAAM,QAAuB,CAAC;AAC9B,aAAW,QAAQ,OAAO;AAExB,QAAI,KAAK,SAAS,GAAG;AACnB;AAAA,IACF;AACA,UAAM,YAAY,KAAK,CAAC;AACxB,UAAM,WAAW,KAAK,CAAC;AACvB,QAAI,cAAc,UAAa,aAAa,QAAW;AACrD;AAAA,IACF;AAGA,QAAI,WAAW,KAAK,MAAM,CAAC;AAC3B,UAAM,QAAQ,SAAS,QAAQ,MAAM;AACrC,QAAI,UAAU,IAAI;AAChB,iBAAW,SAAS,MAAM,QAAQ,CAAC;AAAA,IACrC;AAKA,QACE,SAAS,WAAW,GAAG,KACvB,SAAS,SAAS,GAAG,KACrB,SAAS,UAAU,GACnB;AACA,iBAAW,SAAS,MAAM,GAAG,EAAE;AAAA,IACjC;AACA,UAAM,KAAK;AAAA,MACT,MAAM;AAAA,MACN,OAAO,gBAAgB,SAAS;AAAA,MAChC,UAAU,gBAAgB,QAAQ;AAAA,MAClC,WAAW,cAAc,OAAO,aAAa;AAAA,IAC/C,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,IAAM,qBAAqB,oBAAI,IAAI,CAAC,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG,CAAC;AAEhF,IAAM,kBAAkB,CAAC,OAA8B;AACrD,MAAI,mBAAmB,IAAI,EAAE,GAAG;AAC9B,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEO,IAAM,iBAAiB,OAAO,QAAwC;AAE3E,QAAM,MAAM,MAAM,OAAO,KAAK,CAAC,UAAU,WAAW,OAAO,CAAC;AAC5D,SAAO,iBAAiB,GAAG;AAC7B;AAEA,IAAM,qBAAqB,CACzB,KACA,MACA,qBAEA,IAAI,QAAgB,CAAC,SAAS,WAAW;AACvC;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,MACE;AAAA,MACA,WAAW,KAAK,OAAO;AAAA,MACvB,KAAK,EAAE,GAAG,QAAQ,KAAK,WAAW,OAAO,OAAO,MAAM;AAAA,IACxD;AAAA,IACA,CAAC,KAAK,QAAQ,WAAW;AACvB,UAAI,CAAC,KAAK;AACR,gBAAQ,MAAM;AACd;AAAA,MACF;AACA,YAAM,IAAI;AACV,YAAM,WAAW,EAAE,QAAQ;AAC3B,UAAI,iBAAiB,IAAI,QAAQ,GAAG;AAClC,gBAAQ,MAAM;AACd;AAAA,MACF;AACA,aAAO,IAAI,gBAAgB,MAAM,UAAU,MAAM,CAAC;AAAA,IACpD;AAAA,EACF;AACF,CAAC;AAEH,IAAM,oBAAoB,OAAO,QAAmC;AAClE,QAAM,MAAM,MAAM,OAAO,KAAK;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,SAAO,IAAI,MAAM,IAAI,EAAE,OAAO,OAAO;AACvC;AAEA,IAAM,uBAAuB,OAC3B,KACA,SACoB;AAGpB,QAAM,OAAO,MAAM;AAAA,IACjB;AAAA,IACA,CAAC,QAAQ,cAAc,MAAM,aAAa,IAAI;AAAA,IAC9C,oBAAI,IAAI,CAAC,CAAC,CAAC;AAAA,EACb;AACA,SAAO,KACJ,QAAQ,sCAAsC,sBAAsB,EACpE,QAAQ,sBAAsB,eAAe;AAClD;AAKO,IAAM,UAAU,OAAO,QAAiC;AAC7D,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,QAAQ,IAAI;AAAA,IACtD,OAAO,KAAK,CAAC,QAAQ,MAAM,CAAC;AAAA,IAC5B,kBAAkB,GAAG;AAAA,EACvB,CAAC;AACD,QAAM,iBAAiB,MAAM,QAAQ;AAAA,IACnC,eAAe,IAAI,CAAC,SAAS,qBAAqB,KAAK,IAAI,CAAC;AAAA,EAC9D;AACA,SAAO,CAAC,aAAa,GAAG,cAAc,EACnC,OAAO,CAAC,SAAS,KAAK,KAAK,EAAE,SAAS,CAAC,EACvC,KAAK,IAAI;AACd;AAIA,IAAM,kBAAkB,OACtB,KACA,MACA,UACoB;AACpB,QAAM,EAAE,UAAAC,UAAS,IAAI,MAAM,OAAO,oBAAoB;AACtD,SAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC9C,UAAM,QAAQA;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,QACE;AAAA,QACA,WAAW,KAAK,OAAO;AAAA,QACvB,KAAK,EAAE,GAAG,QAAQ,KAAK,WAAW,OAAO,OAAO,MAAM;AAAA,MACxD;AAAA,MACA,CAAC,KAAK,QAAQ,WAAW;AACvB,YAAI,KAAK;AACP,gBAAM,IAAI;AACV,iBAAO,IAAI,gBAAgB,MAAM,EAAE,QAAQ,IAAI,MAAM,CAAC;AACtD;AAAA,QACF;AACA,gBAAQ,MAAM;AAAA,MAChB;AAAA,IACF;AACA,UAAM,OAAO,MAAM,KAAK;AACxB,UAAM,OAAO,IAAI;AAAA,EACnB,CAAC;AACH;AAIO,IAAM,eAAe,OAC1B,KACA,YAC0D;AAC1D,MAAI;AACF,UAAM,gBAAgB,KAAK,CAAC,SAAS,SAAS,GAAG,OAAO;AACxD,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB,SAAS,KAAK;AACZ,QAAI,eAAe,iBAAiB;AAClC,aAAO,EAAE,IAAI,OAAO,QAAQ,IAAI,OAAO;AAAA,IACzC;AACA,UAAM;AAAA,EACR;AACF;AAKO,IAAM,YAAY,OACvB,KACA,YACkB;AAClB,QAAM,gBAAgB,KAAK,CAAC,OAAO,GAAG,OAAO;AAC/C;AAMO,IAAM,gBAAgB,OAC3B,KACA,YAC0D;AAC1D,MAAI;AACF,UAAM,gBAAgB,KAAK,CAAC,SAAS,aAAa,SAAS,GAAG,OAAO;AACrE,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB,SAAS,KAAK;AACZ,QAAI,eAAe,iBAAiB;AAClC,aAAO,EAAE,IAAI,OAAO,QAAQ,IAAI,OAAO;AAAA,IACzC;AACA,UAAM;AAAA,EACR;AACF;AAKO,IAAM,aAAa,OACxB,KACA,YACkB;AAClB,QAAM,gBAAgB,KAAK,CAAC,SAAS,WAAW,GAAG,OAAO;AAC5D;AAKO,IAAM,iBAAiB,CAAC,YAA8B;AAC3D,QAAM,QAAkB,CAAC;AACzB,aAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AAEtC,UAAM,QAAQ,+BAA+B,KAAK,IAAI;AACtD,QAAI,OAAO;AACT,YAAM,KAAK,MAAM,CAAC,CAAC;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;AAOO,IAAM,eAAe,OAAO,QAAwC;AACzE,MAAI;AACF,YAAQ,MAAM,OAAO,KAAK,CAAC,UAAU,WAAW,QAAQ,CAAC,GAAG,KAAK,KAAK;AAAA,EACxE,SAAS,KAAK;AACZ,QAAI,eAAe,gBAAiB,QAAO;AAC3C,UAAM;AAAA,EACR;AACF;AAIO,IAAM,mBAAmB,OAAO,QAAiC;AACtE,MAAI;AACF,UAAM,OACJ,MAAM,OAAO,KAAK,CAAC,aAAa,gBAAgB,aAAa,CAAC,GAC9D,KAAK;AAEP,UAAM,OAAO,IAAI,QAAQ,aAAa,EAAE;AACxC,WAAO,QAAQ;AAAA,EACjB,SAAS,KAAK;AACZ,QAAI,eAAe,gBAAiB,QAAO;AAC3C,UAAM;AAAA,EACR;AACF;AAIO,IAAM,eAAe,OAC1B,KACA,SACkB;AAClB,QAAM,OAAO,KAAK,CAAC,YAAY,MAAM,IAAI,CAAC;AAC5C;AAGO,IAAM,YAAY,OACvB,KACA,YACoB;AACpB,QAAM,OAAO,KAAK,CAAC,OAAO,IAAI,CAAC;AAE/B,QAAM,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,CAAC;AAC3C,UAAQ,MAAM,OAAO,KAAK,CAAC,aAAa,WAAW,MAAM,CAAC,GAAG,KAAK;AACpE;AAGO,IAAM,aAAa,OACxB,KACA,WACkB;AAClB,QAAM,OAAO,KAAK,CAAC,QAAQ,MAAM,UAAU,MAAM,CAAC;AACpD;AAGO,IAAM,qBAAqB,CAAC,UAA0B;AAC3D,QAAM,OAAO,MACV,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE,EACtB,MAAM,GAAG,EAAE,EACX,QAAQ,QAAQ,EAAE;AACrB,QAAM,OAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC;AAClD,SAAO,YAAY,QAAQ,QAAQ,IAAI,IAAI;AAC7C;;;ACjXA,SAAS,QAAQ,OAAO,UAAU,YAAY;AAC9C,OAAO,UAAU;AAEV,IAAM,uBAAN,cAAmC,MAAM;AAAA,EACrC;AAAA,EACT,YAAY,MAA2B,SAAiB;AACtD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AACF;AAUA,IAAM,SAAS,OAAO,MAAgC;AACpD,MAAI;AACF,UAAM,OAAO,CAAC;AACd,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,mBAAmB,OAAO,QAAkC;AACvE,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,qBAAqB,gBAAgB,uBAAuB;AAAA,EACxE;AACA,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,QAAQ,WAAW,GAAG;AACxB,UAAM,IAAI,qBAAqB,SAAS,wBAAwB;AAAA,EAClE;AACA,MAAI,CAAC,KAAK,WAAW,OAAO,GAAG;AAC7B,UAAM,IAAI;AAAA,MACR;AAAA,MACA,+BAA+B,OAAO;AAAA,IACxC;AAAA,EACF;AAIA,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,SAAS,OAAO;AAAA,EACnC,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,MACA,wBAAwB,OAAO;AAAA,IACjC;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,KAAK,QAAQ;AACnC,MAAI,CAAC,QAAQ,YAAY,GAAG;AAC1B,UAAM,IAAI;AAAA,MACR;AAAA,MACA,4BAA4B,QAAQ;AAAA,IACtC;AAAA,EACF;AAKA,QAAM,YAAY,KAAK,KAAK,UAAU,MAAM;AAC5C,MAAI,CAAE,MAAM,OAAO,SAAS,GAAI;AAC9B,UAAM,IAAI;AAAA,MACR;AAAA,MACA,uCAAuC,QAAQ;AAAA,IACjD;AAAA,EACF;AAEA,QAAM,MAAM,SAAS;AAErB,SAAO;AACT;;;ACnFA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,eAAe;AAWxB,IAAM,SAAS;AAIR,IAAM,aAAa,CAAC,aAAiD;AAC1E,UAAQ,UAAU;AAAA,IAChB,KAAK;AAEH,aAAO;AAAA,QACL,KAAK;AAAA,QACL,MAAM;AAAA,UACJ;AAAA,UACA,+CAA+C,MAAM;AAAA,UACrD;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,KAAK;AAEH,aAAO;AAAA,QACL,KAAK;AAAA,QACL,MAAM,CAAC,oBAAoB,eAAe,WAAW,MAAM,EAAE;AAAA,MAC/D;AAAA,IACF,KAAK;AAEH,aAAO;AAAA,QACL,KAAK;AAAA,QACL,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA,0HAEuB,MAAM;AAAA,QAE/B;AAAA,MACF;AAAA,IACF;AACE,aAAO;AAAA,EACX;AACF;AAEA,IAAM,MAAM,CAAC,KAAa,SACxB,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC/B,EAAAA;AAAA,IACE;AAAA,IACA;AAAA;AAAA;AAAA,IAGA,EAAE,SAAS,MAAS,aAAa,KAAK;AAAA,IACtC,CAAC,KAAK,WAAW;AACf,UAAI,IAAK,QAAO,GAAG;AAAA,UACd,SAAQ,MAAM;AAAA,IACrB;AAAA,EACF;AACF,CAAC;AAKI,IAAM,gBAAgB,YAA0C;AACrE,QAAM,OAAO,WAAW,QAAQ,QAAQ;AACxC,MAAI,CAAC,KAAM,QAAO,EAAE,IAAI,OAAO,QAAQ,cAAc;AAErD,QAAM,UAAU,OAAO,KAAa,SAAiD;AACnF,QAAI;AACF,YAAM,OAAO,MAAM,IAAI,KAAK,IAAI,GAAG,KAAK;AAExC,aAAO,MAAM,EAAE,IAAI,MAAM,MAAM,IAAI,IAAI,EAAE,IAAI,OAAO,QAAQ,YAAY;AAAA,IAC1E,SAAS,KAAK;AACZ,YAAM,OAAQ,IAAmC;AAEjD,UAAI,SAAS,SAAU,QAAO,EAAE,IAAI,OAAO,QAAQ,SAAS;AAG5D,aAAO,EAAE,IAAI,OAAO,QAAQ,YAAY;AAAA,IAC1C;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,QAAQ,KAAK,KAAK,KAAK,IAAI;AAEhD,MACE,CAAC,OAAO,MACR,OAAO,WAAW,YAClB,QAAQ,aAAa,SACrB;AACA,WAAO,QAAQ,WAAW,CAAC,0BAA0B,QAAQ,CAAC,CAAC;AAAA,EACjE;AACA,SAAO;AACT;;;ACxGA,SAAS,YAAAC,iBAAgB;AAElB,IAAM,UAAN,cAAsB,MAAM;AAAA,EACjC,YACE,SACS,MACA,QACT;AACA,UAAM,OAAO;AAHJ;AACA;AAGT,SAAK,OAAO;AAAA,EACd;AACF;AAEA,IAAMC,OAAM,CACV,MACA,QAEA,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC/B,EAAAD;AAAA,IACE;AAAA,IACA;AAAA,IACA,EAAE,KAAK,SAAS,KAAQ,aAAa,KAAK;AAAA,IAC1C,CAAC,KAAK,QAAQ,WAAW;AACvB,UAAI,KAAK;AACP,cAAM,OAAQ,IAAmC;AACjD,YAAI,SAAS,UAAU;AACrB;AAAA,YACE,IAAI;AAAA,cACF;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA;AAAA,QACF;AACA,eAAO,IAAI,QAAQ,UAAU,IAAI,SAAS,aAAa,MAAM,CAAC;AAC9D;AAAA,MACF;AACA,cAAQ,EAAE,QAAQ,OAAO,CAAC;AAAA,IAC5B;AAAA,EACF;AACF,CAAC;AAGI,IAAM,UAAU,YAA8B;AACnD,MAAI;AACF,UAAMC,KAAI,CAAC,QAAQ,QAAQ,CAAC;AAC5B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,IAAM,oBAAoB,OAC/B,SACoB;AACpB,QAAM,EAAE,OAAO,IAAI,MAAMA;AAAA,IACvB;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA,KAAK;AAAA,IACP;AAAA,IACA,KAAK;AAAA,EACP;AAEA,QAAM,MAAM,OACT,MAAM,IAAI,EACV,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO,EACd,QAAQ,EACR,KAAK,CAAC,MAAM,EAAE,WAAW,MAAM,CAAC;AACnC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,QAAQ,wCAAwC,aAAa,MAAM;AAAA,EAC/E;AACA,SAAO;AACT;;;AJlDA,IAAM,mBAAmB,EAAE,OAAO;AAAA,EAChC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AACxB,CAAC;AAED,IAAM,mBAAmB,EAAE,OAAO;AAAA,EAChC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,OAAO,IAAI;AAAA,EAC9C,cAAc,EAAE,OAAO,EAAE,SAAS;AACpC,CAAC;AAED,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACjC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,OAAO,IAAI;AAChD,CAAC;AAED,IAAM,iBAAiB,EAAE,OAAO;AAAA,EAC9B,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAChC,MAAM,EAAE,OAAO,EAAE,IAAI,GAAM,EAAE,SAAS;AACxC,CAAC;AAED,IAAM,kBAAkB,OAAO,iBAA8C;AAC3E,QAAM,CAAC,QAAQ,MAAM,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC9C,UAAU,YAAY;AAAA,IACtB,aAAa,YAAY;AAAA,IACzB,QAAQ,YAAY;AAAA,EACtB,CAAC;AACD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAMC,MAAK,SAAS,YAAY;AAAA,IAChC;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EACX;AACF;AAEA,IAAM,sBAAsB,CAC1B,KACA,QACS;AACT,MAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,IAAI,MAAM,SAAS,IAAI,QAAQ,CAAC;AAChE;AAEA,IAAM,eAAe,CAAC,KAAe,QAA+B;AAClE,MAAI,OAAO,GAAG,EAAE,KAAK;AAAA,IACnB,OAAO;AAAA,IACP,SAAS,IAAI;AAAA,IACb,UAAU,IAAI;AAAA,EAChB,CAAC;AACH;AAEA,IAAM,sBAAsB,CAC1B,OACA,QACsB;AACtB,QAAM,OAAO,MAAM,gBAAgB;AACnC,MAAI,CAAC,MAAM;AACT,QAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,mBAAmB,CAAC;AAClD,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,IAAM,mBAAmB,CAAC,UAA8B;AAC7D,QAAM,SAAS,aAAa;AAE5B,SAAO,KAAK,WAAW,OAAO,KAAc,QAAkB;AAC5D,UAAM,SAAS,iBAAiB,UAAU,IAAI,IAAI;AAClD,QAAI,CAAC,OAAO,SAAS;AACnB,UACG,OAAO,GAAG,EACV,KAAK,EAAE,OAAO,gBAAgB,QAAQ,OAAO,MAAM,OAAO,CAAC;AAC9D;AAAA,IACF;AACA,QAAI;AACF,YAAM,WAAW,MAAM,iBAAiB,OAAO,KAAK,IAAI;AACxD,YAAM,SAAS,MAAM,gBAAgB,QAAQ;AAC7C,YAAM,gBAAgB,MAAM;AAC5B,YAAM,OAA2B,EAAE,IAAI,MAAM,MAAM,OAAO;AAC1D,UAAI,KAAK,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,UAAI,eAAe,sBAAsB;AACvC,4BAAoB,KAAK,GAAG;AAC5B;AAAA,MACF;AACA,UAAI,eAAe,iBAAiB;AAClC,qBAAa,KAAK,GAAG;AACrB;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AAKD,SAAO,KAAK,WAAW,OAAO,MAAe,QAAkB;AAC7D,UAAM,SAAS,MAAM,cAAc;AACnC,QAAI,OAAO,IAAI;AACb,YAAM,OAAgC,EAAE,MAAM,OAAO,KAAK;AAC1D,UAAI,KAAK,IAAI;AACb;AAAA,IACF;AACA,QAAI,OAAO,WAAW,aAAa;AACjC,YAAM,OAAgC,EAAE,WAAW,KAAK;AACxD,UAAI,KAAK,IAAI;AACb;AAAA,IACF;AAEA,QAAI,OAAO,GAAG,EAAE,KAAK;AAAA,MACnB,OAAO,OAAO;AAAA,MACd,SACE;AAAA,IACJ,CAAC;AAAA,EACH,CAAC;AAMD,SAAO,KAAK,cAAc,OAAO,KAAc,QAAkB;AAC/D,UAAM,OAAO,oBAAoB,OAAO,GAAG;AAC3C,QAAI,CAAC,KAAM;AAEX,UAAM,SAAS,eAAe,UAAU,IAAI,IAAI;AAChD,QAAI,CAAC,OAAO,SAAS;AACnB,UACG,OAAO,GAAG,EACV,KAAK,EAAE,OAAO,gBAAgB,QAAQ,OAAO,MAAM,OAAO,CAAC;AAC9D;AAAA,IACF;AACA,UAAM,EAAE,MAAM,IAAI,OAAO;AACzB,UAAM,OACJ,OAAO,KAAK,QAAQ;AACtB,UAAM,MAAM,KAAK;AAEjB,QAAI;AACF,UAAI,CAAE,MAAM,QAAQ,GAAI;AACtB,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO;AAAA,UACP,SACE;AAAA,QACJ,CAAC;AACD;AAAA,MACF;AACA,UAAI,CAAE,MAAM,aAAa,GAAG,GAAI;AAC9B,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO;AAAA,UACP,SACE;AAAA,QACJ,CAAC;AACD;AAAA,MACF;AACA,UAAI,CAAE,MAAM,QAAQ,GAAG,GAAI;AACzB,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO;AAAA,UACP,SAAS;AAAA,QACX,CAAC;AACD;AAAA,MACF;AAKA,YAAM,UAAW,MAAM,UAAU,GAAG,KAAM;AAC1C,YAAM,gBAAgB,QAAQ,WAAW,WAAW;AACpD,YAAM,OAAO,gBAAgB,MAAM,iBAAiB,GAAG,IAAI,WAAW;AAEtE,YAAM,SAAS,gBAAgB,UAAU,mBAAmB,KAAK;AACjE,UAAI,CAAC,eAAe;AAClB,cAAM,aAAa,KAAK,MAAM;AAAA,MAChC;AACA,YAAM,UAAU,KAAK,KAAK;AAC1B,YAAM,WAAW,KAAK,MAAM;AAC5B,YAAM,MAAM,MAAM,kBAAkB,EAAE,KAAK,MAAM,MAAM,QAAQ,OAAO,KAAK,CAAC;AAE5E,YAAM,UAA4B,EAAE,KAAK,QAAQ,KAAK;AACtD,UAAI,KAAK,OAAO;AAAA,IAClB,SAAS,KAAK;AACZ,UAAI,eAAe,SAAS;AAC1B,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,IAAI,MAAM,SAAS,IAAI,QAAQ,CAAC;AAC9D;AAAA,MACF;AACA,UAAI,eAAe,iBAAiB;AAClC,qBAAa,KAAK,GAAG;AACrB;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AAED,SAAO,IAAI,WAAW,OAAO,MAAe,QAAkB;AAC5D,UAAM,OAAO,MAAM,gBAAgB;AACnC,QAAI,CAAC,MAAM;AACT,YAAM,OAA2B,EAAE,MAAM,KAAK;AAC9C,UAAI,KAAK,IAAI;AACb;AAAA,IACF;AAGA,QAAI;AACF,YAAM,YAAY,MAAM,gBAAgB,KAAK,IAAI;AACjD,YAAM,gBAAgB,SAAS;AAC/B,YAAM,OAA2B,EAAE,MAAM,UAAU;AACnD,UAAI,KAAK,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,UAAI,eAAe,iBAAiB;AAClC,qBAAa,KAAK,GAAG;AACrB;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AAED,SAAO,IAAI,SAAS,OAAO,MAAe,QAAkB;AAC1D,UAAM,OAAO,oBAAoB,OAAO,GAAG;AAC3C,QAAI,CAAC,MAAM;AACT;AAAA,IACF;AACA,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,KAAK,IAAI;AACvC,YAAM,OAAyB;AAAA,QAC7B;AAAA,QACA,WAAW,OAAO,WAAW,SAAS,MAAM;AAAA,MAC9C;AACA,UAAI,KAAK,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,UAAI,eAAe,iBAAiB;AAClC,qBAAa,KAAK,GAAG;AACrB;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AAED,SAAO,IAAI,kBAAkB,OAAO,MAAe,QAAkB;AACnE,UAAM,OAAO,oBAAoB,OAAO,GAAG;AAC3C,QAAI,CAAC,MAAM;AACT;AAAA,IACF;AACA,QAAI;AACF,YAAM,QAAQ,MAAM,eAAe,KAAK,IAAI;AAC5C,YAAM,OAA6B,EAAE,MAAM;AAC3C,UAAI,KAAK,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,UAAI,eAAe,iBAAiB;AAClC,qBAAa,KAAK,GAAG;AACrB;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AAQD,SAAO,KAAK,UAAU,OAAO,KAAc,QAAkB;AAC3D,UAAM,OAAO,oBAAoB,OAAO,GAAG;AAC3C,QAAI,CAAC,KAAM;AAEX,UAAM,SAAS,iBAAiB,UAAU,IAAI,IAAI;AAClD,QAAI,CAAC,OAAO,SAAS;AACnB,UACG,OAAO,GAAG,EACV,KAAK,EAAE,OAAO,gBAAgB,QAAQ,OAAO,MAAM,OAAO,CAAC;AAC9D;AAAA,IACF;AAEA,QAAI;AACF,YAAM,QAAQ,MAAM,aAAa,KAAK,MAAM,OAAO,KAAK,OAAO;AAC/D,UAAI,CAAC,MAAM,IAAI;AACb,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO;AAAA,UACP,SACE;AAAA,UAEF,QAAQ,MAAM;AAAA,QAChB,CAAC;AACD;AAAA,MACF;AACA,YAAM,UAAU,KAAK,MAAM,OAAO,KAAK,OAAO;AAC9C,YAAM,QAAQ,eAAe,OAAO,KAAK,OAAO;AAChD,YAAM,OAA2B;AAAA,QAC/B,IAAI;AAAA,QACJ,cAAc,MAAM;AAAA,QACpB;AAAA,MACF;AACA,UAAI,KAAK,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,UAAI,eAAe,iBAAiB;AAClC,qBAAa,KAAK,GAAG;AACrB;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AAUD,SAAO,KAAK,WAAW,OAAO,KAAc,QAAkB;AAC5D,UAAM,OAAO,oBAAoB,OAAO,GAAG;AAC3C,QAAI,CAAC,KAAM;AAEX,UAAM,SAAS,kBAAkB,UAAU,IAAI,IAAI;AACnD,QAAI,CAAC,OAAO,SAAS;AACnB,UACG,OAAO,GAAG,EACV,KAAK,EAAE,OAAO,gBAAgB,QAAQ,OAAO,MAAM,OAAO,CAAC;AAC9D;AAAA,IACF;AAEA,QAAI;AACF,YAAM,QAAQ,MAAM,cAAc,KAAK,MAAM,OAAO,KAAK,OAAO;AAChE,UAAI,CAAC,MAAM,IAAI;AACb,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO;AAAA,UACP,SACE;AAAA,UAGF,QAAQ,MAAM;AAAA,QAChB,CAAC;AACD;AAAA,MACF;AACA,YAAM,WAAW,KAAK,MAAM,OAAO,KAAK,OAAO;AAC/C,YAAM,QAAQ,eAAe,OAAO,KAAK,OAAO;AAChD,YAAM,OAA4B;AAAA,QAChC,IAAI;AAAA,QACJ,cAAc,MAAM;AAAA,QACpB;AAAA,MACF;AACA,UAAI,KAAK,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,UAAI,eAAe,iBAAiB;AAClC,qBAAa,KAAK,GAAG;AACrB;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AAED,SAAO;AACT;;;AK9XA,SAAS,UAAUC,qBAAoB;AACvC,SAAS,KAAAC,UAAS;;;ACYlB,SAAS,kBAAkB;AAS3B,IAAM,YAAY,CAAC,YAA0B;AAC3C,UAAQ,MAAM,gBAAgB,OAAO,EAAE;AACzC;AAkCO,IAAM,YAAN,MAAgB;AAAA,EACJ,UAAU,oBAAI,IAA4B;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT,SAAS;AAAA,EAEjB,YAAY,SAAwB;AAClC,SAAK,OAAO,QAAQ;AACpB,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,mBAAmB,QAAQ;AAChC,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,QACE,SACA,UACmC;AACnC,QAAI,KAAK,QAAQ;AACf,aAAO,QAAQ,OAAO,IAAI,MAAM,kBAAkB,CAAC;AAAA,IACrD;AACA,UAAM,YAAY,WAAW;AAC7B,WAAO,IAAI,QAAkC,CAAC,SAAS,WAAW;AAChE,YAAM,QAAQ,WAAW,MAAM;AAC7B,aAAK,QAAQ,OAAO,SAAS;AAC7B;AAAA,UACE,mBAAmB,UAAU,MAAM,GAAG,CAAC,CAAC,YAAY,KAAK,QAAQ,IAAI,UAAU,KAAK,SAAS;AAAA,QAC/F;AACA;AAAA,UACE,IAAI;AAAA,YACF,0CAA0C,KAAK,SAAS;AAAA,UAC1D;AAAA,QACF;AAAA,MACF,GAAG,KAAK,SAAS;AAIjB,UAAI,KAAK,aAAa,KAAO;AAC3B,cAAM,QAAQ;AAAA,MAChB;AACA,WAAK,QAAQ,IAAI,WAAW,EAAE,SAAS,QAAQ,OAAO,SAAS,CAAC;AAMhE,YAAM,oBAAoB,KAAK,oBAAoB,QAAQ;AAC3D,YAAM,iBAAiB,KAAK,iBAAiB,QAAQ;AACrD,YAAM,WAA4B;AAAA,QAChC,MAAM;AAAA,QACN;AAAA,QACA,UAAU;AAAA,QACV,OAAO;AAAA,QACP,SAAS;AAAA,UACP,UAAU,QAAQ;AAAA,UAClB,OAAO,QAAQ;AAAA,UACf,aAAa,QAAQ;AAAA,UACrB,cAAc,QAAQ;AAAA,QACxB;AAAA,MACF;AACA,UAAI;AACF;AAAA,UACE,gBAAgB,UAAU,MAAM,GAAG,CAAC,CAAC,aAAa,qBAAqB,WAAW,UAAU,kBAAkB,WAAW,aAAa,QAAQ,SAAS,MAAM,UAAU,QAAQ,OAAO,UAAU,CAAC;AAAA,QACnM;AACA,aAAK,KAAK,QAAQ;AAAA,MACpB,SAAS,KAAK;AACZ,qBAAa,KAAK;AAClB,aAAK,QAAQ,OAAO,SAAS;AAC7B,eAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,MAC5D;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,WAA4B;AAC9B,WAAO,KAAK,QAAQ,IAAI,SAAS;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,QAAQ,WAAmB,UAA6C;AACtE,UAAM,QAAQ,KAAK,QAAQ,IAAI,SAAS;AACxC,QAAI,CAAC,OAAO;AACV,gBAAU,wBAAwB,UAAU,MAAM,GAAG,CAAC,CAAC,EAAE;AACzD,aAAO;AAAA,IACT;AACA,UAAM,aAAa,SAAS,aAAa,CAAC,GACvC,IAAI,CAAC,OAAO,GAAG,UAAU,QAAQ,GAAG,EACpC,KAAK,GAAG;AACX;AAAA,MACE,mBAAmB,UAAU,MAAM,GAAG,CAAC,CAAC,SAAS,SAAS,KAAK,MAAM,cAAc,SAAS,WAAW,UAAU,CAAC,GAAG,YAAY,WAAW,SAAS,MAAM,EAAE,WAAW,SAAS,gBAAgB,QAAQ;AAAA,IAC3M;AACA,iBAAa,MAAM,KAAK;AACxB,SAAK,QAAQ,OAAO,SAAS;AAC7B,QAAI,MAAM,UAAU;AAClB,UAAI;AACF,YAAI,SAAS,MAAM;AACjB,gBAAM,SAAS,EAAE,MAAM,cAAc,MAAM,SAAS,KAAK,CAAC;AAAA,QAC5D;AAUA,mBAAW,CAAC,OAAO,EAAE,MAAM,SAAS,aAAa,CAAC,GAAG,QAAQ,GAAG;AAC9D,gBAAM,SAAS;AAAA,YACb,MAAM;AAAA,YACN;AAAA,YACA,IAAI,GAAG;AAAA,YACP,MAAM,GAAG,SAAS;AAAA,YAClB,gBAAgB,GAAG,SAAS;AAAA,UAC9B,CAAC;AAAA,QACH;AACA,cAAM,SAAS,EAAE,MAAM,YAAY,SAAS,SAAS,CAAC;AAAA,MACxD,QAAQ;AAAA,MAER;AAAA,IACF;AACA,UAAM,QAAQ,QAAQ;AACtB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,WAAmB,OAAgC;AAC3D,UAAM,QAAQ,KAAK,QAAQ,IAAI,SAAS;AACxC,QAAI,CAAC,OAAO;AACV;AAAA,QACE,sBAAsB,UAAU,MAAM,GAAG,CAAC,CAAC,SAAS,MAAM,IAAI;AAAA,MAChE;AACA,aAAO;AAAA,IACT;AACA;AAAA,MACE,iBAAiB,UAAU,MAAM,GAAG,CAAC,CAAC,SAAS,MAAM,IAAI,GAAG,MAAM,SAAS,eAAe,UAAU,MAAM,KAAK,MAAM,KAAK,EAAE,GAAG,MAAM,SAAS,oBAAoB,UAAU,MAAM,KAAK,SAAS,MAAM,QAAQ,SAAS,cAAc,MAAM,gBAAgB,UAAU,CAAC,KAAK,EAAE;AAAA,IAC/Q;AACA,QAAI,MAAM,UAAU;AAClB,UAAI;AACF,cAAM,SAAS,KAAK;AAAA,MACtB,QAAQ;AAAA,MAGR;AAAA,IACF;AACA,QAAI,MAAM,SAAS,YAAY;AAC7B,mBAAa,MAAM,KAAK;AACxB,WAAK,QAAQ,OAAO,SAAS;AAC7B,YAAM,QAAQ,MAAM,OAAO;AAAA,IAC7B,WAAW,MAAM,SAAS,SAAS;AACjC,mBAAa,MAAM,KAAK;AACxB,WAAK,QAAQ,OAAO,SAAS;AAC7B,YAAM,OAAO,IAAI,MAAM,MAAM,OAAO,CAAC;AAAA,IACvC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAIA,KAAK,WAAmB,SAA0B;AAChD,UAAM,QAAQ,KAAK,QAAQ,IAAI,SAAS;AACxC,QAAI,CAAC,OAAO;AACV,gBAAU,qBAAqB,UAAU,MAAM,GAAG,CAAC,CAAC,EAAE;AACtD,aAAO;AAAA,IACT;AACA,cAAU,gBAAgB,UAAU,MAAM,GAAG,CAAC,CAAC,YAAY,OAAO,EAAE;AACpE,iBAAa,MAAM,KAAK;AACxB,SAAK,QAAQ,OAAO,SAAS;AAC7B,UAAM,OAAO,IAAI,MAAM,OAAO,CAAC;AAC/B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAIA,MAAM,SAAS,iBAAuB;AACpC,QAAI,KAAK,QAAQ,OAAO,GAAG;AACzB,gBAAU,gBAAgB,MAAM,YAAY,KAAK,QAAQ,IAAI,EAAE;AAAA,IACjE;AACA,SAAK,SAAS;AACd,eAAW,CAAC,EAAE,KAAK,KAAK,KAAK,QAAQ,QAAQ,GAAG;AAC9C,mBAAa,MAAM,KAAK;AACxB,YAAM,OAAO,IAAI,MAAM,MAAM,CAAC;AAAA,IAChC;AACA,SAAK,QAAQ,MAAM;AAAA,EACrB;AAAA,EAEA,IAAI,eAAuB;AACzB,WAAO,KAAK,QAAQ;AAAA,EACtB;AACF;AAuBO,IAAM,uBAAuB,MAAsB;AACxD,QAAM,YAAY,oBAAI,IAAuB;AAC7C,SAAO;AAAA,IACL,SAAS,WAAW,QAAQ;AAC1B,YAAM,WAAW,UAAU,IAAI,SAAS;AACxC,UAAI,YAAY,aAAa,QAAQ;AACnC,iBAAS,MAAM,0BAA0B;AAAA,MAC3C;AACA,gBAAU,IAAI,WAAW,MAAM;AAAA,IACjC;AAAA,IACA,WAAW,WAAW,QAAQ;AAG5B,UAAI,UAAU,IAAI,SAAS,MAAM,QAAQ;AACvC,kBAAU,OAAO,SAAS;AAAA,MAC5B;AAAA,IACF;AAAA,IACA,IAAI,WAAW;AACb,aAAO,UAAU,IAAI,SAAS,KAAK;AAAA,IACrC;AAAA,EACF;AACF;;;AD3SA,IAAM,oBAAoBC,GAAE,KAAK,CAAC,QAAQ,aAAa,UAAU,MAAM,CAAC;AAExE,IAAM,yBAAyBA,GAAE,OAAO;AAAA,EACtC,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,QAAQA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAO;AAAA,EACrC,SAASA,GACN;AAAA,IACCA,GAAE,OAAO;AAAA,MACP,MAAM;AAAA,MACN,SAASA,GAAE,OAAO;AAAA,IACpB,CAAC;AAAA,EACH,EACC,IAAI,GAAG,EACP,SAAS;AAAA;AAAA;AAAA,EAGZ,mBAAmBA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACvD,gBAAgBA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA;AAAA;AAAA,EAGpD,gBAAgBA,GAAE,KAAK,CAAC,QAAQ,SAAS,CAAC,EAAE,SAAS;AACvD,CAAC;AAED,IAAM,aAAa,CAAC,KAAe,UAAkC;AACnE,MAAI,MAAM,SAAS,KAAK,UAAU,KAAK,CAAC;AAAA;AAAA,CAAM;AAChD;AAEA,IAAM,YAAY,CAAC,QAAwB;AACzC,MAAI,MAAM,kBAAkB;AAC9B;AAEA,IAAM,gBAAgB,CAAC,YAA0B;AAC/C,UAAQ,MAAM,iBAAiB,OAAO,EAAE;AAC1C;AAUO,IAAM,oBAAoB,CAAC,SAAkC;AAClE,QAAM,SAASC,cAAa;AAE5B,SAAO,KAAK,YAAY,OAAO,KAAc,QAAkB;AAC7D,UAAM,SAAS,uBAAuB,UAAU,IAAI,IAAI;AACxD,QAAI,CAAC,OAAO,SAAS;AACnB,UACG,OAAO,GAAG,EACV,KAAK,EAAE,OAAO,gBAAgB,QAAQ,OAAO,MAAM,OAAO,CAAC;AAC9D;AAAA,IACF;AAEA,UAAM,OAAO,KAAK,MAAM,gBAAgB;AACxC,QAAI,CAAC,MAAM;AACT,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,mBAAmB,CAAC;AAClD;AAAA,IACF;AAEA;AAAA,MACE,yBAAyB,OAAO,KAAK,UAAU,MAAM,GAAG,CAAC,CAAC,SAAS,KAAK,IAAI,gBAAgB,OAAO,KAAK,OAAO,MAAM,aAAa,OAAO,KAAK,qBAAqB,WAAW,UAAU,OAAO,KAAK,kBAAkB,WAAW;AAAA,IACnO;AAIA,QAAI,UAAU,gBAAgB,mBAAmB;AACjD,QAAI,UAAU,iBAAiB,wBAAwB;AACvD,QAAI,UAAU,cAAc,YAAY;AAGxC,QAAI,UAAU,qBAAqB,IAAI;AACvC,QAAI,eAAe;AAEnB,UAAM,aAAa,IAAI,gBAAgB;AAMvC,QAAI,GAAG,SAAS,MAAM;AACpB,UAAI,CAAC,IAAI,eAAe;AACtB,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF,CAAC;AAMD,UAAM,SAAS,IAAI,UAAU;AAAA,MAC3B,cAAc,CAAC,UAAU,WAAW,KAAK,KAAK;AAAA,MAC9C,kBAAkB,OAAO,KAAK;AAAA,MAC9B,eAAe,OAAO,KAAK;AAAA,IAC7B,CAAC;AACD,SAAK,eAAe,SAAS,OAAO,KAAK,WAAW,MAAM;AAE1D,QAAI;AACF,YAAM,UAA+C,OAAO,KAAK,UAC7D,OAAO,KAAK,QAAQ,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,QAAQ,EAAE,IACrE;AAEJ,uBAAiB,SAAS,KAAK,QAAQ,cAAc;AAAA,QACnD,WAAW,OAAO,KAAK;AAAA,QACvB,UAAU,KAAK;AAAA,QACf,QAAQ,OAAO,KAAK;AAAA,QACpB;AAAA,QACA,QAAQ,WAAW;AAAA,QACnB,QAAQ,CAAC,SAAS,aAAa,OAAO,QAAQ,SAAS,QAAQ;AAAA,QAC/D,mBAAmB,OAAO,KAAK;AAAA,QAC/B,gBAAgB,OAAO,KAAK;AAAA,QAC5B,gBAAgB,OAAO,KAAK;AAAA,MAC9B,CAAC,GAAG;AACF,YAAI,WAAW,OAAO,SAAS;AAC7B;AAAA,YACE,2BAA2B,OAAO,KAAK,UAAU,MAAM,GAAG,CAAC,CAAC;AAAA,UAC9D;AACA;AAAA,QACF;AACA;AAAA,UACE,wBAAwB,OAAO,KAAK,UAAU,MAAM,GAAG,CAAC,CAAC,SAAS,MAAM,IAAI;AAAA,QAC9E;AACA,mBAAW,KAAK,KAAK;AAAA,MACvB;AACA;AAAA,QACE,wBAAwB,OAAO,KAAK,UAAU,MAAM,GAAG,CAAC,CAAC;AAAA,MAC3D;AACA,gBAAU,GAAG;AAAA,IACf,SAAS,KAAK;AAGZ,UACE,OACA,OAAO,QAAQ,YACf,UAAU,OACT,IAA0B,SAAS,cACpC;AACA,kBAAU,GAAG;AAAA,MACf,OAAO;AACL,cAAM,UACJ,eAAe,QAAQ,IAAI,UAAU;AACvC;AAAA,UACE,yBAAyB,OAAO,KAAK,UAAU,MAAM,GAAG,CAAC,CAAC,UAAU,OAAO;AAAA,QAC7E;AACA,mBAAW,KAAK,EAAE,MAAM,SAAS,QAAQ,CAAC;AAC1C,kBAAU,GAAG;AAAA,MACf;AAAA,IACF,UAAE;AAGA,aAAO,MAAM,eAAe;AAC5B,WAAK,eAAe,WAAW,OAAO,KAAK,WAAW,MAAM;AAC5D,UAAI,IAAI;AAAA,IACV;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAWA,IAAM,wBAAwBD,GAAE,OAAO;AAAA,EACrC,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,MAAMA,GAAE,OAAO;AAAA,EACf,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC1B,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,YAAYA,GACT,OAAO;AAAA,IACN,OAAOA,GAAE,OAAO,EAAE,YAAY;AAAA,IAC9B,QAAQA,GAAE,OAAO,EAAE,YAAY;AAAA,EACjC,CAAC,EACA,SAAS;AAAA,EACZ,OAAOA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,WAAWA,GACR;AAAA,IACCA,GAAE,OAAO;AAAA,MACP,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,MACpB,MAAMA,GAAE,QAAQ,UAAU;AAAA,MAC1B,UAAUA,GAAE,OAAO;AAAA,QACjB,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QACtB,WAAWA,GAAE,OAAO;AAAA,MACtB,CAAC;AAAA,IACH,CAAC;AAAA,EACH,EACC,SAAS;AAAA,EACZ,cAAcA,GACX,KAAK,CAAC,QAAQ,cAAc,UAAU,gBAAgB,CAAC,EACvD,SAAS;AACd,CAAC;AAMD,IAAM,qBAAqBA,GAAE,OAAO;AAAA,EAClC,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,OAAOA,GAAE,MAAM;AAAA,IACbA,GAAE,OAAO;AAAA,MACP,MAAMA,GAAE,QAAQ,YAAY;AAAA,MAC5B,MAAMA,GAAE,OAAO;AAAA,IACjB,CAAC;AAAA,IACDA,GAAE,OAAO;AAAA,MACP,MAAMA,GAAE,QAAQ,iBAAiB;AAAA,MACjC,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,MACpC,IAAIA,GAAE,OAAO,EAAE,SAAS;AAAA,MACxB,MAAMA,GAAE,OAAO,EAAE,SAAS;AAAA,MAC1B,gBAAgBA,GAAE,OAAO,EAAE,SAAS;AAAA,IACtC,CAAC;AAAA,IACDA,GAAE,OAAO;AAAA,MACP,MAAMA,GAAE,QAAQ,UAAU;AAAA,MAC1B,SAASA,GAAE,OAAO;AAAA,QAChB,MAAMA,GAAE,OAAO;AAAA,QACf,UAAUA,GAAE,OAAO;AAAA,QACnB,OAAOA,GAAE,OAAO;AAAA,QAChB,YAAYA,GACT,OAAO;AAAA,UACN,OAAOA,GAAE,OAAO,EAAE,YAAY;AAAA,UAC9B,QAAQA,GAAE,OAAO,EAAE,YAAY;AAAA,QACjC,CAAC,EACA,SAAS;AAAA,QACZ,cAAcA,GACX,KAAK,CAAC,QAAQ,cAAc,UAAU,gBAAgB,CAAC,EACvD,SAAS;AAAA,QACZ,WAAWA,GACR;AAAA,UACCA,GAAE,OAAO;AAAA,YACP,IAAIA,GAAE,OAAO;AAAA,YACb,MAAMA,GAAE,QAAQ,UAAU;AAAA,YAC1B,UAAUA,GAAE,OAAO;AAAA,cACjB,MAAMA,GAAE,OAAO;AAAA,cACf,WAAWA,GAAE,OAAO;AAAA,YACtB,CAAC;AAAA,UACH,CAAC;AAAA,QACH,EACC,SAAS;AAAA,MACd,CAAC;AAAA,IACH,CAAC;AAAA,IACDA,GAAE,OAAO;AAAA,MACP,MAAMA,GAAE,QAAQ,OAAO;AAAA,MACvB,SAASA,GAAE,OAAO;AAAA,IACpB,CAAC;AAAA,EACH,CAAC;AACH,CAAC;AAEM,IAAM,0BAA0B,CACrC,SACW;AACX,QAAM,SAASC,cAAa;AAE5B,SAAO,KAAK,eAAe,CAAC,KAAc,QAAkB;AAC1D,UAAM,YAAY,IAAI,OAAO;AAC7B,QAAI,CAAC,WAAW;AACd,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,qBAAqB,CAAC;AACpD;AAAA,IACF;AACA,UAAM,SAAS,sBAAsB,UAAU,IAAI,IAAI;AACvD,QAAI,CAAC,OAAO,SAAS;AACnB,UACG,OAAO,GAAG,EACV,KAAK,EAAE,OAAO,gBAAgB,QAAQ,OAAO,MAAM,OAAO,CAAC;AAC9D;AAAA,IACF;AACA,UAAM,SAAS,KAAK,eAAe,IAAI,OAAO,KAAK,SAAS;AAC5D,QAAI,CAAC,QAAQ;AACX,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,+BAA+B,CAAC;AAC9D;AAAA,IACF;AACA,QAAI,CAAC,OAAO,IAAI,SAAS,GAAG;AAC1B,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,qBAAqB,CAAC;AACpD;AAAA,IACF;AACA,QAAI,OAAO,KAAK,OAAO;AACrB,aAAO,KAAK,WAAW,OAAO,KAAK,KAAK;AACxC,UAAI,KAAK,EAAE,IAAI,KAAK,CAAC;AACrB;AAAA,IACF;AACA,UAAM,UAAoC;AAAA,MACxC,MAAM,OAAO,KAAK;AAAA,MAClB,UAAU,OAAO,KAAK;AAAA,MACtB,OAAO,OAAO,KAAK;AAAA,MACnB,YAAY,OAAO,KAAK;AAAA,MACxB,GAAI,OAAO,KAAK,YAAY,EAAE,WAAW,OAAO,KAAK,UAAU,IAAI,CAAC;AAAA,MACpE,GAAI,OAAO,KAAK,eACZ,EAAE,cAAc,OAAO,KAAK,aAAa,IACzC,CAAC;AAAA,IACP;AACA,WAAO,QAAQ,WAAW,OAAO;AACjC,QAAI,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EACvB,CAAC;AAED,SAAO;AACT;AAMO,IAAM,uBAAuB,CAAC,SAAwC;AAC3E,QAAM,SAASA,cAAa;AAE5B,SAAO,KAAK,eAAe,CAAC,KAAc,QAAkB;AAC1D,UAAM,YAAY,IAAI,OAAO;AAC7B,QAAI,CAAC,WAAW;AACd,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,qBAAqB,CAAC;AACpD;AAAA,IACF;AACA,UAAM,SAAS,mBAAmB,UAAU,IAAI,IAAI;AACpD,QAAI,CAAC,OAAO,SAAS;AACnB,UACG,OAAO,GAAG,EACV,KAAK,EAAE,OAAO,gBAAgB,QAAQ,OAAO,MAAM,OAAO,CAAC;AAC9D;AAAA,IACF;AACA,UAAM,SAAS,KAAK,eAAe,IAAI,OAAO,KAAK,SAAS;AAC5D,QAAI,CAAC,QAAQ;AACX,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,+BAA+B,CAAC;AAC9D;AAAA,IACF;AACA,UAAM,WAAW,OAAO;AAAA,MACtB;AAAA,MACA,OAAO,KAAK;AAAA,IACd;AACA,QAAI,CAAC,UAAU;AACb,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,qBAAqB,CAAC;AACpD;AAAA,IACF;AACA,QAAI,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EACvB,CAAC;AAED,SAAO;AACT;AAYA,IAAM,+BAA+BD,GAAE,OAAO;AAAA,EAC5C,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,cAAcA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC9B,UAAUA,GAAE,KAAK,CAAC,QAAQ,UAAU,QAAQ,CAAC;AAC/C,CAAC;AAMM,IAAM,yBAAyB,CAAC,SAAuC;AAC5E,QAAM,SAASC,cAAa;AAE5B,SAAO,KAAK,KAAK,OAAO,KAAc,QAAkB;AACtD,UAAM,SAAS,6BAA6B,UAAU,IAAI,IAAI;AAC9D,QAAI,CAAC,OAAO,SAAS;AACnB,UACG,OAAO,GAAG,EACV,KAAK,EAAE,OAAO,gBAAgB,QAAQ,OAAO,MAAM,OAAO,CAAC;AAC9D;AAAA,IACF;AACA,QAAI,CAAC,KAAK,QAAQ,iBAAiB;AACjC,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,0BAA0B,CAAC;AACzD;AAAA,IACF;AACA,UAAM,UAAU,MAAM,KAAK,QAAQ;AAAA,MACjC,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,IACd;AACA,QAAI,CAAC,SAAS;AAGZ,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,uBAAuB,CAAC;AACtD;AAAA,IACF;AACA,QAAI,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EACvB,CAAC;AAED,SAAO;AACT;;;AErZA,SAAS,UAAUC,qBAAoB;;;ACmDhC,IAAM,mBAAmB,CAC9B,QAC4B;AAC5B,MAAI;AACJ,QAAM,WAAgE,CAAC;AAEvE,aAAW,OAAO,IAAI,UAAU;AAC9B,QAAI,IAAI,SAAS,UAAU;AACzB,YAAM,OAAO,OAAO,IAAI,YAAY,WAAW,IAAI,UAAU;AAC7D,qBACE,iBAAiB,SAAY,OAAO,GAAG,YAAY;AAAA;AAAA,EAAO,IAAI;AAChE;AAAA,IACF;AAIA,QAAI,UAAU,OAAO,IAAI,YAAY,WAAW,IAAI,UAAU;AAC9D,QAAI,IAAI,SAAS,eAAe,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;AAC3E,YAAM,YAAY,IAAI,WACnB;AAAA,QACC,CAAC,MACC,cAAc,EAAE,SAAS,IAAI,SAAS,EAAE,SAAS,SAAS;AAAA,MAC9D,EACC,KAAK,IAAI;AACZ,gBAAU,UAAU,GAAG,OAAO;AAAA,EAAK,SAAS,KAAK;AAAA,IACnD;AACA,QAAI,IAAI,SAAS,UAAU,IAAI,cAAc;AAC3C,gBAAU,mBAAmB,IAAI,YAAY,KAAK,OAAO;AAAA,IAC3D;AACA,aAAS,KAAK,EAAE,MAAM,IAAI,MAAM,QAAQ,CAAC;AAAA,EAC3C;AAEA,SAAO;AAAA,IACL,UAAU;AAAA;AAAA,IACV,OAAO,IAAI;AAAA;AAAA,IACX;AAAA,IACA,aAAa,IAAI;AAAA,IACjB;AAAA,IACA,OAAO,IAAI;AAAA,EACb;AACF;AAgCO,IAAM,oBAAoB,CAC/B,UACA,IACA,cACiC;AACjC,QAAM,eAAe,SAAS,aAAa,SAAS,UAAU,SAAS;AACvE,QAAM,gBACJ,SAAS,iBAAiB,eAAe,eAAe;AAC1D,SAAO;AAAA,IACL;AAAA,IACA,QAAQ;AAAA,IACR,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA;AAAA;AAAA;AAAA,IAIrC,OAAO;AAAA,IACP,SAAS;AAAA,MACP;AAAA,QACE,OAAO;AAAA,QACP,SAAS;AAAA,UACP,MAAM;AAAA,UACN,SAAS,SAAS,QAAQ;AAAA,UAC1B,GAAI,eAAe,EAAE,YAAY,SAAS,UAAU,IAAI,CAAC;AAAA,QAC3D;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA,GAAI,SAAS,aACT;AAAA,MACE,OAAO;AAAA,QACL,eAAe,SAAS,WAAW;AAAA,QACnC,mBAAmB,SAAS,WAAW;AAAA,QACvC,cACE,SAAS,WAAW,QAAQ,SAAS,WAAW;AAAA,MACpD;AAAA,IACF,IACA,CAAC;AAAA,EACP;AACF;;;AC9FA,IAAM,iBAAiB,CACrB,YACW;AACX,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,SAAO,QACJ,IAAI,CAAC,SAAS;AACb,QACE,KAAK,SAAS,gBACd,KAAK,SAAS,eACd;AACA,aAAQ,KAA2B,QAAQ;AAAA,IAC7C;AACA,WAAO;AAAA,EACT,CAAC,EACA,KAAK,EAAE;AACZ;AAKO,IAAM,6BAA6B,CACxC,QACgC;AAChC,QAAM,WAAgC,CAAC;AAEvC,aAAW,QAAQ,IAAI,OAAO;AAC5B,QAAI,UAAU,QAAQ,KAAK,SAAS,iBAAiB;AAGnD,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,SAAS;AAAA,QACT,YAAY;AAAA,UACV;AAAA,YACE,IAAI,KAAK;AAAA,YACT,MAAM;AAAA,YACN,UAAU,EAAE,MAAM,KAAK,MAAM,WAAW,KAAK,UAAU;AAAA,UACzD;AAAA,QACF;AAAA,MACF,CAAC;AACD;AAAA,IACF;AACA,QAAI,UAAU,QAAQ,KAAK,SAAS,wBAAwB;AAC1D,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,SAAS,KAAK;AAAA,QACd,cAAc,KAAK;AAAA,MACrB,CAAC;AACD;AAAA,IACF;AAEA,UAAM,OAAO,KAAK;AAClB,UAAM,UAAU,eAAe,KAAK,OAAO;AAC3C,aAAS,KAAK,EAAE,MAAM,QAAQ,CAAC;AAAA,EACjC;AAEA,QAAM,QAAkC,IAAI,OAAO,IAAI,CAAC,OAAO;AAAA,IAC7D,MAAM;AAAA,IACN,UAAU;AAAA,MACR,MAAM,EAAE;AAAA,MACR,aAAa,EAAE;AAAA,MACf,YAAY,EAAE;AAAA,IAChB;AAAA,EACF,EAAE;AAIF,MAAI;AACJ,MAAI,OAAO,IAAI,gBAAgB,UAAU;AACvC,iBAAa,IAAI;AAAA,EACnB,WAAW,IAAI,eAAe,OAAO,IAAI,gBAAgB,UAAU;AACjE,iBAAa;AAAA,MACX,MAAM;AAAA,MACN,UAAU,EAAE,MAAM,IAAI,YAAY,KAAK;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,IAAI;AAAA,IACX;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,aAAa,IAAI;AAAA,IACjB,QAAQ,IAAI;AAAA;AAAA;AAAA,IAGZ,YAAY,IAAI;AAAA,EAClB;AACF;AAaA,IAAM,WAAW,CAAC,WAChB,GAAG,MAAM,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AAM/C,IAAM,uBAAN,MAA2B;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACT,cAAc;AAAA;AAAA,EAGd,OAEG;AAAA;AAAA;AAAA,EAIM,QAAQ,oBAAI,IAG3B;AAAA,EAEM,WAAW;AAAA;AAAA;AAAA,EAGX;AAAA,EAGA,cAAqD;AAAA,EAE7D,YAAY,OAAe,IAAa;AACtC,SAAK,aAAa,MAAM,SAAS,MAAM;AACvC,SAAK,QAAQ;AACb,SAAK,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,EAC/C;AAAA;AAAA;AAAA,EAIA,QAAgC;AAC9B,UAAM,cAAc;AAAA,MAClB,IAAI,KAAK;AAAA,MACT,QAAQ;AAAA,MACR,YAAY,KAAK;AAAA,MACjB,QAAQ;AAAA,MACR,OAAO,KAAK;AAAA,MACZ,QAAQ,CAAC;AAAA,MACT,OAAO;AAAA,IACT;AACA,WAAO;AAAA,MACL,EAAE,OAAO,oBAAoB,MAAM,EAAE,MAAM,oBAAoB,UAAU,YAAY,EAAE;AAAA,MACvF,EAAE,OAAO,wBAAwB,MAAM,EAAE,MAAM,wBAAwB,UAAU,YAAY,EAAE;AAAA,IACjG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,GAA2C;AAC/C,QAAI,KAAK,SAAU,QAAO,CAAC;AAC3B,YAAQ,EAAE,MAAM;AAAA,MACd,KAAK;AACH,eAAO,KAAK,gBAAgB,EAAE,IAAI;AAAA,MACpC,KAAK;AACH,eAAO,KAAK,oBAAoB,CAAC;AAAA,MACnC,KAAK;AAMH;AACE,gBAAM,KAAM,EAAE,SACV;AACJ,cAAI,IAAI;AACN,kBAAM,QAAQ,GAAG,SAAS;AAC1B,kBAAM,SAAS,GAAG,UAAU;AAC5B,iBAAK,aAAa;AAAA,cAChB,cAAc;AAAA,cACd,eAAe;AAAA,cACf,cAAc,QAAQ;AAAA,YACxB;AAAA,UACF;AAAA,QACF;AAIA,eAAO,CAAC;AAAA,MACV,KAAK;AAGH,aAAK,cAAc;AACnB,eAAO,CAAC;AAAA,IACZ;AAAA,EACF;AAAA;AAAA,EAGA,SAAiC;AAC/B,QAAI,KAAK,SAAU,QAAO,CAAC;AAC3B,SAAK,WAAW;AAChB,UAAM,MAA8B,CAAC;AACrC,QAAI,KAAK,GAAG,KAAK,cAAc,CAAC;AAChC,QAAI,KAAK,GAAG,KAAK,kBAAkB,CAAC;AAEpC,UAAM,cAAc;AAAA,MAClB,IAAI,KAAK;AAAA,MACT,QAAQ;AAAA,MACR,YAAY,KAAK;AAAA,MACjB,QAAQ,KAAK;AAAA,MACb,OAAO,KAAK;AAAA,MACZ,QAAQ,CAAC;AAAA;AAAA,MACT,OAAO,KAAK,cAAc;AAAA,IAC5B;AACA,QAAI,KAAK;AAAA,MACP,OAAO;AAAA,MACP,MAAM,EAAE,MAAM,sBAAsB,UAAU,YAAY;AAAA,IAC5D,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA,EAIQ,gBAAgB,MAAsC;AAC5D,QAAI,CAAC,KAAK,MAAM;AAEd,YAAM,SAAS,SAAS,KAAK;AAC7B,YAAM,cAAc,KAAK;AACzB,YAAM,eAAe;AACrB,WAAK,OAAO,EAAE,QAAQ,aAAa,cAAc,QAAQ,KAAK;AAC9D,aAAO;AAAA,QACL;AAAA,UACE,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,cAAc;AAAA,YACd,MAAM;AAAA,cACJ,IAAI;AAAA,cACJ,MAAM;AAAA,cACN,QAAQ;AAAA,cACR,MAAM;AAAA,cACN,SAAS,CAAC;AAAA,YACZ;AAAA,UACF;AAAA,QACF;AAAA,QACA;AAAA,UACE,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,SAAS;AAAA,YACT,cAAc;AAAA,YACd,eAAe;AAAA,YACf,MAAM,EAAE,MAAM,eAAe,MAAM,GAAG;AAAA,UACxC;AAAA,QACF;AAAA,QACA;AAAA,UACE,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,SAAS;AAAA,YACT,cAAc;AAAA,YACd,eAAe;AAAA,YACf,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,SAAK,KAAK,UAAU;AACpB,WAAO;AAAA,MACL;AAAA,QACE,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,SAAS,KAAK,KAAK;AAAA,UACnB,cAAc,KAAK,KAAK;AAAA,UACxB,eAAe,KAAK,KAAK;AAAA,UACzB,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,oBAAoB,GAMD;AACzB,UAAM,SAAiC,CAAC;AAIxC,WAAO,KAAK,GAAG,KAAK,cAAc,CAAC;AAEnC,QAAI,QAAQ,KAAK,MAAM,IAAI,EAAE,KAAK;AAClC,QAAI,CAAC,OAAO;AACV,cAAQ;AAAA,QACN,QAAQ,SAAS,IAAI;AAAA,QACrB,aAAa,KAAK;AAAA,QAClB,QAAQ,EAAE,MAAM,SAAS,MAAM;AAAA,QAC/B,MAAM,EAAE,QAAQ;AAAA,QAChB,WAAW;AAAA,QACX,cAAc;AAAA,MAChB;AACA,WAAK,MAAM,IAAI,EAAE,OAAO,KAAK;AAAA,IAC/B,OAAO;AAEL,UAAI,EAAE,GAAI,OAAM,SAAS,EAAE;AAC3B,UAAI,EAAE,KAAM,OAAM,OAAO,EAAE;AAAA,IAC7B;AAEA,QAAI,CAAC,MAAM,gBAAgB,MAAM,MAAM;AAGrC,YAAM,eAAe;AACrB,aAAO,KAAK;AAAA,QACV,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,cAAc,MAAM;AAAA,UACpB,MAAM;AAAA,YACJ,IAAI,MAAM;AAAA,YACV,MAAM;AAAA,YACN,QAAQ;AAAA,YACR,MAAM,MAAM;AAAA,YACZ,SAAS,MAAM;AAAA,YACf,WAAW;AAAA,UACb;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,EAAE,mBAAmB,UAAa,EAAE,eAAe,SAAS,GAAG;AACjE,YAAM,aAAa,EAAE;AACrB,UAAI,MAAM,cAAc;AACtB,eAAO,KAAK;AAAA,UACV,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,SAAS,MAAM;AAAA,YACf,cAAc,MAAM;AAAA,YACpB,OAAO,EAAE;AAAA,UACX;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IAIF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,gBAAwC;AAC9C,QAAI,CAAC,KAAK,KAAM,QAAO,CAAC;AACxB,UAAM,EAAE,QAAQ,aAAa,cAAc,OAAO,IAAI,KAAK;AAC3D,SAAK,OAAO;AACZ,WAAO;AAAA,MACL;AAAA,QACE,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,SAAS;AAAA,UACT,cAAc;AAAA,UACd,eAAe;AAAA,UACf,MAAM;AAAA,QACR;AAAA,MACF;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,SAAS;AAAA,UACT,cAAc;AAAA,UACd,eAAe;AAAA,UACf,MAAM,EAAE,MAAM,eAAe,MAAM,OAAO;AAAA,QAC5C;AAAA,MACF;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,cAAc;AAAA,UACd,MAAM;AAAA,YACJ,IAAI;AAAA,YACJ,MAAM;AAAA,YACN,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,SAAS,CAAC,EAAE,MAAM,eAAe,MAAM,OAAO,CAAC;AAAA,UACjD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,oBAA4C;AAClD,UAAM,MAA8B,CAAC;AACrC,eAAW,SAAS,KAAK,MAAM,OAAO,GAAG;AACvC,UAAI,CAAC,MAAM,cAAc;AAGvB,cAAM,eAAe;AACrB,YAAI,KAAK;AAAA,UACP,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,cAAc,MAAM;AAAA,YACpB,MAAM;AAAA,cACJ,IAAI,MAAM;AAAA,cACV,MAAM;AAAA,cACN,QAAQ;AAAA,cACR,MAAM,MAAM,QAAQ;AAAA,cACpB,SAAS,MAAM;AAAA,cACf,WAAW;AAAA,YACb;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AACA,UAAI,KAAK;AAAA,QACP,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,SAAS,MAAM;AAAA,UACf,cAAc,MAAM;AAAA,UACpB,WAAW,MAAM;AAAA,QACnB;AAAA,MACF,CAAC;AACD,UAAI,KAAK;AAAA,QACP,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,cAAc,MAAM;AAAA,UACpB,MAAM;AAAA,YACJ,IAAI,MAAM;AAAA,YACV,MAAM;AAAA,YACN,QAAQ;AAAA,YACR,MAAM,MAAM,QAAQ;AAAA,YACpB,SAAS,MAAM;AAAA,YACf,WAAW,MAAM;AAAA,UACnB;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AACA,SAAK,MAAM,MAAM;AACjB,WAAO;AAAA,EACT;AACF;;;AC/fA,SAAS,cAAAC,mBAAkB;AAS3B,IAAM,UAAU,oBAAI,IAA+B;AAS5C,IAAM,mBAAmB,CAAC,WAA0C;AACzE,QAAM,QAAQ,OAAOA,YAAW,CAAC;AACjC,UAAQ,IAAI,OAAO,EAAE,OAAO,OAAO,CAAC;AACpC,SAAO;AAAA,IACL;AAAA,IACA,UAAU;AAIR,YAAM,UAAU,QAAQ,IAAI,KAAK;AACjC,UAAI,WAAW,QAAQ,WAAW,QAAQ;AACxC,gBAAQ,OAAO,KAAK;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AACF;AAIO,IAAM,yBAAyB,CAAC,UAAoC;AACzE,SAAO,QAAQ,IAAI,KAAK,GAAG,UAAU;AACvC;AAaO,IAAM,sBAAsB,MAAwB;AACzD,MAAI,QAAQ,SAAS,EAAG,QAAO;AAC/B,QAAM,QAAQ,QAAQ,OAAO,EAAE,KAAK,EAAE;AACtC,SAAO,OAAO,UAAU;AAC1B;AAGO,IAAM,qBAAqB,MAAc,QAAQ;;;AH5CxD,IAAM,gBAAgB,CAAC,QAAgC;AACrD,QAAM,SAAS,IAAI,OAAO,eAAe;AACzC,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,CAAC,OAAO,YAAY,EAAE,WAAW,SAAS,EAAG,QAAO;AACxD,SAAO,OAAO,MAAM,CAAC,EAAE,KAAK,KAAK;AACnC;AAEA,IAAM,kBAAkB,CACtB,KACA,QACA,SACA,OAAO,4BACE;AAET,MAAI,OAAO,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,KAAK,EAAE,CAAC;AACtD;AAeA,IAAM,qBAAqB,CACzB,KACA,QACqB;AACrB,QAAM,QAAQ,cAAc,GAAG;AAC/B,MAAI,CAAC,OAAO;AACV,oBAAgB,KAAK,KAAK,gCAAgC,YAAY;AACtE,WAAO;AAAA,EACT;AACA,QAAM,SAAS,uBAAuB,KAAK,KAAK,oBAAoB;AACpE,MAAI,CAAC,QAAQ;AACX,UAAM,cACJ,MAAM,SAAS,KAAK,GAAG,MAAM,MAAM,GAAG,EAAE,CAAC,WAAM;AACjD,YAAQ;AAAA,MACN,qBAAqB,IAAI,IAAI,aAAa,WAAW,oBAAoB,mBAAmB,CAAC;AAAA,IAC/F;AACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,IAAM,sBAAsB,MAAc;AAC/C,QAAM,SAASC,cAAa;AAE5B,SAAO;AAAA,IACL;AAAA,IACA,OAAO,KAAc,QAAkB;AACrC,YAAM,SAAS,mBAAmB,KAAK,GAAG;AAC1C,UAAI,CAAC,OAAQ;AAKb,YAAM,OAAO,IAAI;AACjB,UACE,CAAC,QACD,OAAO,SAAS,YAChB,CAAC,MAAM,QAAQ,KAAK,QAAQ,KAC5B,KAAK,SAAS,WAAW,GACzB;AACA,wBAAgB,KAAK,KAAK,8CAA8C;AACxE;AAAA,MACF;AAEA,YAAM,gBAAgB,iBAAiB,IAAI;AAE3C,YAAM,eAAe,YAAY,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACxE,YAAM,SAAS,KAAK,WAAW;AAO/B,UAAI,QAAQ;AACV,YAAI,UAAU,gBAAgB,mBAAmB;AACjD,YAAI,UAAU,iBAAiB,wBAAwB;AACvD,YAAI,UAAU,cAAc,YAAY;AACxC,YAAI,eAAe;AACnB,cAAM,cAAc,CAAC,UAAyB;AAC5C,cAAI,MAAM,SAAS,KAAK,UAAU,KAAK,CAAC;AAAA;AAAA,CAAM;AAAA,QAChD;AAGA,oBAAY;AAAA,UACV,IAAI;AAAA,UACJ,QAAQ;AAAA,UACR,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,UACrC,OAAO,KAAK;AAAA,UACZ,SAAS;AAAA,YACP,EAAE,OAAO,GAAG,OAAO,EAAE,MAAM,YAAY,GAAG,eAAe,KAAK;AAAA,UAChE;AAAA,QACF,CAAC;AAOD,cAAM,kBAEF,EAAE,SAAS,KAAK;AACpB,cAAM,WAAW,CAAC,UAAgC;AAChD,kBAAQ,MAAM,MAAM;AAAA,YAClB,KAAK;AACH,0BAAY;AAAA,gBACV,IAAI;AAAA,gBACJ,QAAQ;AAAA,gBACR,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,gBACrC,OAAO,KAAK;AAAA,gBACZ,SAAS;AAAA,kBACP;AAAA,oBACE,OAAO;AAAA,oBACP,OAAO,EAAE,SAAS,MAAM,KAAK;AAAA,oBAC7B,eAAe;AAAA,kBACjB;AAAA,gBACF;AAAA,cACF,CAAC;AACD;AAAA,YACF,KAAK;AACH,0BAAY;AAAA,gBACV,IAAI;AAAA,gBACJ,QAAQ;AAAA,gBACR,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,gBACrC,OAAO,KAAK;AAAA,gBACZ,SAAS;AAAA,kBACP;AAAA,oBACE,OAAO;AAAA,oBACP,OAAO;AAAA,sBACL,YAAY;AAAA,wBACV;AAAA,0BACE,OAAO,MAAM;AAAA,0BACb,GAAI,MAAM,KAAK,EAAE,IAAI,MAAM,GAAG,IAAI,CAAC;AAAA,0BACnC,GAAI,MAAM,MAAM,MAAM,OAClB,EAAE,MAAM,WAAoB,IAC5B,CAAC;AAAA,0BACL,GAAI,MAAM,QAAQ,MAAM,mBAAmB,SACvC;AAAA,4BACE,UAAU;AAAA,8BACR,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,8BACzC,GAAI,MAAM,mBAAmB,SACzB,EAAE,WAAW,MAAM,eAAe,IAClC,CAAC;AAAA,4BACP;AAAA,0BACF,IACA,CAAC;AAAA,wBACP;AAAA,sBACF;AAAA,oBACF;AAAA,oBACA,eAAe;AAAA,kBACjB;AAAA,gBACF;AAAA,cACF,CAAC;AACD;AAAA,YACF,KAAK;AACH,8BAAgB,UAAU;AAAA,gBACxB,MAAM;AAAA,gBACN;AAAA,gBACA,KAAK;AAAA,cACP;AACA;AAAA,YACF,KAAK;AAGH;AAAA,UACJ;AAAA,QACF;AACA,YAAI;AACF,gBAAM,OAAO,QAAQ,eAAe,QAAQ;AAAA,QAC9C,SAAS,KAAK;AACZ,sBAAY;AAAA,YACV,IAAI;AAAA,YACJ,QAAQ;AAAA,YACR,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,YACrC,OAAO,KAAK;AAAA,YACZ,SAAS;AAAA,cACP;AAAA,gBACE,OAAO;AAAA,gBACP,OAAO,CAAC;AAAA,gBACR,eAAe;AAAA,cACjB;AAAA,YACF;AAAA,UACF,CAAC;AACD,cAAI,MAAM,kBAAkB;AAC5B,cAAI,IAAI;AACR,eAAK;AACL;AAAA,QACF;AAEA,cAAM,eACJ,gBAAgB,SAAS,QAAQ,CAAC,GAAG,iBAAiB;AACxD,oBAAY;AAAA,UACV,IAAI;AAAA,UACJ,QAAQ;AAAA,UACR,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,UACrC,OAAO,KAAK;AAAA,UACZ,SAAS;AAAA,YACP;AAAA,cACE,OAAO;AAAA,cACP,OAAO,CAAC;AAAA,cACR,eAAe;AAAA,YACjB;AAAA,UACF;AAAA,QACF,CAAC;AACD,YAAI,MAAM,kBAAkB;AAC5B,YAAI,IAAI;AACR;AAAA,MACF;AAEA,UAAI;AACJ,UAAI;AACF,yBAAiB,MAAM,OAAO,QAAQ,aAAa;AAAA,MACrD,SAAS,KAAK;AACZ;AAAA,UACE;AAAA,UACA;AAAA,UACA,eAAe,QAAQ,IAAI,UAAU;AAAA,UACrC;AAAA,QACF;AACA;AAAA,MACF;AAEA,UAAI,eAAe,OAAO;AACxB,wBAAgB,KAAK,KAAK,eAAe,OAAO,WAAW;AAC3D;AAAA,MACF;AAGA,YAAM,MAAM,kBAAkB,gBAAgB,cAAc,KAAK,KAAK;AACtE,UAAI,KAAK,GAAG;AAAA,IACd;AAAA,EACF;AAUA,SAAO,KAAK,iBAAiB,OAAO,KAAc,QAAkB;AAClE,UAAM,SAAS,mBAAmB,KAAK,GAAG;AAC1C,QAAI,CAAC,OAAQ;AAEb,UAAM,OAAO,IAAI;AACjB,QACE,CAAC,QACD,OAAO,SAAS,YAChB,CAAC,MAAM,QAAQ,KAAK,KAAK,KACzB,KAAK,MAAM,WAAW,GACtB;AACA,sBAAgB,KAAK,KAAK,2CAA2C;AACrE;AAAA,IACF;AAEA,UAAM,cAAc,2BAA2B,IAAI;AACnD,UAAM,gBAAgB,iBAAiB,WAAW;AAClD,UAAM,SAAS,KAAK,WAAW;AAE/B,QAAI,QAAQ;AACV,UAAI,UAAU,gBAAgB,mBAAmB;AACjD,UAAI,UAAU,iBAAiB,wBAAwB;AACvD,UAAI,UAAU,cAAc,YAAY;AACxC,UAAI,eAAe;AAEnB,YAAM,QAAQ,IAAI,qBAAqB,KAAK,KAAK;AACjD,YAAMC,cAAa,CAAC,OAA+C;AAIjE,YAAI,MAAM,UAAU,GAAG,KAAK;AAAA,CAAI;AAChC,YAAI,MAAM,SAAS,KAAK,UAAU,GAAG,IAAI,CAAC;AAAA;AAAA,CAAM;AAAA,MAClD;AAEA,iBAAW,MAAM,MAAM,MAAM,EAAG,CAAAA,YAAW,EAAE;AAE7C,YAAM,WAAW,CAAC,UAAgC;AAChD,mBAAW,MAAM,MAAM,MAAM,KAAK,EAAG,CAAAA,YAAW,EAAE;AAAA,MACpD;AAEA,UAAI;AACF,cAAM,OAAO,QAAQ,eAAe,QAAQ;AAAA,MAC9C,SAAS,KAAK;AAEZ,QAAAA,YAAW;AAAA,UACT,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,UAAU;AAAA,cACR,IAAI,QAAQ,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,cACnD,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,OAAO,KAAK;AAAA,cACZ,OAAO;AAAA,gBACL,MAAM;AAAA,gBACN,SACE,eAAe,QAAQ,IAAI,UAAU;AAAA,cACzC;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AACD,YAAI,MAAM,kBAAkB;AAC5B,YAAI,IAAI;AACR;AAAA,MACF;AAEA,iBAAW,MAAM,MAAM,OAAO,EAAG,CAAAA,YAAW,EAAE;AAC9C,UAAI,MAAM,kBAAkB;AAC5B,UAAI,IAAI;AACR;AAAA,IACF;AAMA,QAAI;AACJ,QAAI;AACF,uBAAiB,MAAM,OAAO,QAAQ,aAAa;AAAA,IACrD,SAAS,KAAK;AACZ;AAAA,QACE;AAAA,QACA;AAAA,QACA,eAAe,QAAQ,IAAI,UAAU;AAAA,QACrC;AAAA,MACF;AACA;AAAA,IACF;AACA,QAAI,eAAe,OAAO;AACxB,sBAAgB,KAAK,KAAK,eAAe,OAAO,WAAW;AAC3D;AAAA,IACF;AACA,UAAM,eAAe,QAAQ,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACpE,UAAM,eAAe;AAAA,MACnB;AAAA,MACA;AAAA,MACA,KAAK;AAAA,IACP;AACA,UAAM,OACJ,aAAa,QAAQ,CAAC,GAAG,SAAS,WAAW;AAC/C,UAAM,YAAY,aAAa,QAAQ,CAAC,GAAG,SAAS,cAAc,CAAC;AACnE,UAAM,SAAyC,CAAC;AAChD,QAAI,MAAM;AACR,aAAO,KAAK;AAAA,QACV,IAAI,OAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,QAClD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,SAAS,CAAC,EAAE,MAAM,eAAe,KAAK,CAAC;AAAA,MACzC,CAAC;AAAA,IACH;AACA,eAAW,MAAM,WAAW;AAC1B,aAAO,KAAK;AAAA,QACV,IAAI,MAAM,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,QACjD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,MAAM,GAAG,SAAS;AAAA,QAClB,SAAS,GAAG;AAAA,QACZ,WAAW,GAAG,SAAS;AAAA,MACzB,CAAC;AAAA,IACH;AACA,QAAI,KAAK;AAAA,MACP,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,MACxC,QAAQ;AAAA,MACR,OAAO,KAAK;AAAA,MACZ;AAAA,MACA,OAAO,aAAa;AAAA,IACtB,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AACT;;;AI/YA,SAAS,cAAc,aAAa;AAgCpC,IAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,iBAAoC;AAAA,EACxC,MAAM;AAAA,EACN,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AAAA,EACX,OAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,WAAW;AAAA,MACX,WAAW;AAAA,MACX,QAAQ;AAAA,IACV;AAAA,EACF;AACF;AAEO,IAAM,sBAAN,MAAqD;AAAA,EAG1D,YAA6B,UAA8B,CAAC,GAAG;AAAlC;AAAA,EAAmC;AAAA,EAFvD,OAAO;AAAA,EAIhB,OAAO,cACL,SACiC;AACjC,UAAM,aAAa,KAAK,QAAQ,gBAAgB;AAMhD,QAAI,eAA8B;AAClC,QAAI,KAAK,QAAQ,aAAa,QAAQ,QAAQ;AAC5C,WAAK,eAAe,QAAQ,MAAM;AAClC,UAAI;AACF,cAAM,WAAW,MAAM,QAAQ,OAAO;AAAA,UACpC,UAAU,KAAK,QAAQ;AAAA,UACvB,OAAO,KAAK,QAAQ;AAAA,UACpB,UAAU;AAAA,YACR,EAAE,MAAM,QAAQ,SAAS,QAAQ,OAAO;AAAA,UAC1C;AAAA,QACF,CAAC;AACD,uBACE,iBAAiB,SAAS,QAAQ,IAAI,SAAS,KAAK,OACpD,SAAS,KAAK,MAAM,GAAG,GAAG;AAAA,MAC9B,SAAS,KAAK;AAGZ,cAAM;AAAA,UACJ,MAAM;AAAA,UACN,SAAS,6BAA8B,IAAc,OAAO;AAAA,UAC5D,aAAa;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAEA,QAAI,cAAc;AAChB,YAAM,EAAE,MAAM,cAAc,MAAM,eAAe,OAAO;AAAA,IAC1D;AAIA,eAAW,YAAY,oBAAoB;AACzC,WAAK,eAAe,QAAQ,MAAM;AAClC,YAAM,OAAO,SAAS,QAAQ,cAAc,QAAQ,QAAQ;AAC5D,YAAM,EAAE,MAAM,cAAc,KAAK;AACjC,UAAI,aAAa,GAAG;AAClB,cAAM,MAAM,UAAU;AAAA,MACxB;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,QAAQ,cAAc;AAC9B,WAAK,eAAe,QAAQ,MAAM;AAClC,YAAM,aAAa,aAAa,KAAK,IAAI,CAAC;AAC1C,YAAM;AAAA,QACJ,MAAM;AAAA,QACN;AAAA,QACA,MAAM;AAAA,QACN,MAAM,EAAE,MAAM,YAAY;AAAA,MAC5B;AACA,UAAI,aAAa,GAAG;AAClB,cAAM,MAAM,UAAU;AAAA,MACxB;AACA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN;AAAA,QACA,IAAI;AAAA,QACJ,SAAS;AAAA,MACX;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,QAAQ,UAAU;AAC1B,WAAK,eAAe,QAAQ,MAAM;AAClC,YAAM;AAAA,IACR;AAEA,SAAK,eAAe,QAAQ,MAAM;AAClC,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,WACE,KAAK,QAAQ,kBACb,YAAY,QAAQ,SAAS,IAAI,KAAK,IAAI,CAAC;AAAA,IAC/C;AAAA,EACF;AAAA,EAEQ,eAAe,QAA4B;AACjD,QAAI,QAAQ,SAAS;AAGnB,YAAM,MAAM,IAAI,MAAM,SAAS;AAC/B,MAAC,IAAiC,OAAO;AACzC,YAAM;AAAA,IACR;AAAA,EACF;AACF;;;AC7GA,IAAM,wBAAwB;AAE9B,IAAM,iBAAiB,CAAC,QAAgD;AACtE,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,UAAU,sBAAuB,QAAO;AAChD,QAAM,UAAU,IAAI,SAAS;AAC7B,SAAO,GAAG,IAAI,MAAM,GAAG,qBAAqB,CAAC;AAAA,UAAQ,OAAO;AAC9D;AAqBO,IAAM,iBAAiB,CAC5B,cACA,uBACc;AAAA,EACd;AAAA,EACA;AAAA,EACA,cAAc,oBAAI,IAAI;AAAA,EACtB,YAAY,oBAAI,IAAI;AAAA,EACpB,kBAAkB,oBAAI,IAAI;AAAA,EAC1B,sBAAsB,oBAAI,IAAI;AAChC;AAMA,IAAM,2BAA2B,CAC/B,aACuB;AACvB,MAAI,CAAC,SAAU,QAAO;AACtB,aAAW,OAAO,CAAC,WAAW,OAAO,OAAO,WAAW,UAAU,GAAG;AAClE,UAAM,IAAI,SAAS,GAAG;AACtB,QAAI,OAAO,MAAM,YAAY,EAAG,QAAO;AAAA,EACzC;AACA,SAAO;AACT;AAaA,IAAM,QAAqB,EAAE,QAAQ,CAAC,GAAG,MAAM,MAAM;AAE9C,IAAM,WAAW,CACtB,MACA,UACgB;AAIhB,QAAM,MAAM,KAAK,YAAY;AAC7B,MAAI,OAAO,QAAQ,MAAM,mBAAmB;AAC1C,WAAO;AAAA,EACT;AAEA,MAAI,KAAK,SAAS,kBAAkB,QAAQ,MAAM,mBAAmB;AACnE,WAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,GAAG,MAAM,KAAK;AAAA,EAClD;AAEA,MAAI,KAAK,SAAS,sBAAsB;AAItC,UAAM,eAAe,KAAK,YAAY;AACtC,QAAI,CAAC,gBAAgB,MAAM,qBAAqB,IAAI,YAAY,GAAG;AACjE,aAAO;AAAA,IACT;AACA,UAAM,qBAAqB,IAAI,YAAY;AAC3C,WAAO;AAAA,MACL,QAAQ;AAAA,QACN;AAAA,UACE,MAAM;AAAA,UACN;AAAA,UACA,MAAM,KAAK,YAAY,QAAQ;AAAA,UAC/B,OAAO,KAAK,YAAY;AAAA,UACxB,SAAS,yBAAyB,KAAK,YAAY,QAAQ;AAAA,QAC7D;AAAA,MACF;AAAA,MACA,MAAM;AAAA,IACR;AAAA,EACF;AAEA,MAAI,KAAK,SAAS,wBAAwB;AACxC,WAAO;AAAA,EACT;AACA,QAAM,OAAO,KAAK,YAAY;AAC9B,MAAI,CAAC,KAAM,QAAO;AAElB,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK,QAAQ;AAGX,YAAM,QAAQ,KAAK,YAAY,SAAS,KAAK,QAAQ;AACrD,UAAI,CAAC,MAAO,QAAO;AACnB,aAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,cAAc,MAAM,MAAM,CAAC,GAAG,MAAM,MAAM;AAAA,IACtE;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,SAAS,KAAK,UAAU,KAAK,MAAM;AACzC,YAAM,OAAO,KAAK,QAAQ;AAC1B,YAAM,SAAS,KAAK,OAAO;AAC3B,UAAI,CAAC,UAAU,CAAC,OAAQ,QAAO;AAE/B,UAAI,WAAW,aAAa,WAAW,WAAW;AAChD,YAAI,MAAM,aAAa,IAAI,MAAM,GAAG;AAClC,iBAAO;AAAA,QACT;AACA,cAAM,aAAa,IAAI,MAAM;AAC7B,cAAM,QAA4B;AAAA,UAChC,MAAM;AAAA,UACN,YAAY;AAAA,UACZ;AAAA,UACA,MAAM,KAAK,OAAO;AAAA,QACpB;AACA,eAAO,EAAE,QAAQ,CAAC,KAAK,GAAG,MAAM,MAAM;AAAA,MACxC;AAEA,UAAI,WAAW,eAAe,WAAW,SAAS;AAChD,YAAI,MAAM,WAAW,IAAI,MAAM,GAAG;AAChC,iBAAO;AAAA,QACT;AACA,cAAM,WAAW,IAAI,MAAM;AAC3B,cAAM,QAA0B;AAAA,UAC9B,MAAM;AAAA,UACN,YAAY;AAAA,UACZ,IAAI,WAAW;AAAA,UACf,SACE,WAAW,UACP,KAAK,OAAO,SAAS,gBACrB,KAAK,OAAO;AAAA,UAClB,QAAQ;AAAA,YACN,WAAW,UAAU,KAAK,OAAO,QAAQ,KAAK,OAAO;AAAA,UACvD;AAAA,QACF;AACA,eAAO,EAAE,QAAQ,CAAC,KAAK,GAAG,MAAM,MAAM;AAAA,MACxC;AACA,aAAO;AAAA,IACT;AAAA,IACA,KAAK,SAAS;AACZ,YAAM,OAAQ,KAA2B,QAAQ;AACjD,UAAI,CAAC,QAAQ,MAAM,iBAAiB,IAAI,IAAI,GAAG;AAC7C,eAAO;AAAA,MACT;AACA,YAAM,iBAAiB,IAAI,IAAI;AAC/B,YAAM,QAAQ,KAAK,SAAS,CAAC;AAK7B,aAAO;AAAA,QACL,QAAQ;AAAA,UACN;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,OAAO,MAAM,IAAI,CAACC,WAAU;AAAA,cAC1B,MAAAA;AAAA,cACA,WAAW;AAAA,cACX,WAAW;AAAA,cACX,QAAQ;AAAA,YACV,EAAE;AAAA,UACJ;AAAA,QACF;AAAA,QACA,MAAM;AAAA,QACN,cAAc,EAAE,MAAM,MAAM;AAAA,MAC9B;AAAA,IACF;AAAA,IACA;AACE,aAAO;AAAA,EACX;AACF;;;ACtIO,IAAM,qBAAqB;AAI3B,IAAM,uBAA0C;AAEvD,IAAM,gBAAgB,CAAC,UACrB,UAAU,SAAS,UAAU;AAIxB,IAAM,sBAAsB,OACjC,SAC2B;AAC3B,QAAM,WAAW,QAAQ,QAAQ,IAAI,kBAAkB,KAAK;AAC5D,MAAI,CAAC,cAAc,QAAQ,GAAG;AAC5B,UAAM,IAAI;AAAA,MACR,oCAAoC,QAAQ,UACnC,kBAAkB;AAAA,IAC7B;AAAA,EACF;AAEA,MAAI,aAAa,OAAO;AACtB,UAAM,EAAE,eAAAC,eAAc,IAAI,MAAM;AAChC,WAAO,IAAIA,eAAc;AAAA,EAC3B;AACA,QAAM,EAAE,oBAAAC,oBAAmB,IAAI,MAAM;AACrC,SAAO,IAAIA,oBAAmB;AAChC;;;AChHA;AAKA,IAAM,uBAAuB;AAC7B,IAAM,oBAAoB;AAM1B,IAAM,uBAAuB;AAK7B,IAAM,iCAAiC;AAkBvC,IAAM,iBACJ;AAMF,IAAM,aAAa,CAAC,YAA0B;AAC5C,UAAQ,MAAM,sBAAsB,OAAO,EAAE;AAC/C;AAkBO,IAAM,yBAAN,MAAwD;AAAA,EAW7D,YAA6B,UAAyC,CAAC,GAAG;AAA7C;AAAA,EAA8C;AAAA,EAVlE,OAAO;AAAA,EAER,gBAA8C;AAAA;AAAA;AAAA,EAGrC,aAAa,oBAAI,IAAoB;AAAA;AAAA;AAAA,EAGrC,cAAc,oBAAI,IAAwB;AAAA,EAI3D,MAAc,iBAAyC;AACrD,QAAI,KAAK,QAAQ,eAAe;AAC9B,aAAO,KAAK,QAAQ;AAAA,IACtB;AACA,QAAI,KAAK,QAAQ,WAAW;AAE1B,aAAO,IAAI,cAAc;AAAA,QACvB,WAAW,KAAK,QAAQ;AAAA,MAC1B,CAAC;AAAA,IACH;AACA,WAAO,oBAAoB;AAAA,EAC7B;AAAA,EAEQ,uBAA+B;AACrC,QAAI,KAAK,QAAQ,cAAe,QAAO,KAAK,QAAQ;AACpD,UAAM,OAAO,QAAQ,IAAI,oBAAoB;AAC7C,WAAO,oBAAoB,IAAI;AAAA,EACjC;AAAA,EAEQ,kBAAkC;AACxC,UAAM,UAAU,GAAG,KAAK,qBAAqB,CAAC;AAC9C,WAAO;AAAA,MACL,UAAU;AAAA,QACR,CAAC,oBAAoB,GAAG;AAAA,UACtB,MAAM;AAAA;AAAA;AAAA;AAAA,UAIN,KAAK;AAAA,UACL,QAAQ;AAAA,YACN,CAAC,iBAAiB,GAAG;AAAA,cACnB,IAAI;AAAA,cACJ,MAAM;AAAA,YACR;AAAA,UACF;AAAA,UACA,SAAS;AAAA,YACP;AAAA;AAAA;AAAA;AAAA,YAIA,QAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,MACA,OAAO,GAAG,oBAAoB,IAAI,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQnD,YAAY;AAAA,QACV,MAAM;AAAA,QACN,MAAM;AAAA,QACN,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,eAAsC;AAClD,QAAI,KAAK,eAAe;AACtB,aAAO,KAAK;AAAA,IACd;AACA,SAAK,iBAAiB,YAAY;AAChC,YAAM,UAAU,MAAM,KAAK,eAAe;AAC1C,iBAAW,8BAA8B,KAAK,qBAAqB,CAAC,EAAE;AACtE,YAAM,SAAS,MAAM,QAAQ,MAAM,EAAE,QAAQ,KAAK,gBAAgB,EAAE,CAAC;AACrE,iBAAW,oBAAoB,OAAO,GAAG,EAAE;AAC3C,aAAO;AAAA,IACT,GAAG;AACH,QAAI;AACF,aAAO,MAAM,KAAK;AAAA,IACpB,SAAS,KAAK;AAEZ,WAAK,gBAAgB;AACrB,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAc,sBACZ,QACA,cACA,UACA,OACiB;AACjB,UAAM,WAAW,KAAK,WAAW,IAAI,YAAY;AACjD,QAAI,UAAU;AACZ;AAAA,QACE,yBAAyB,SAAS,MAAM,GAAG,CAAC,CAAC,YAAY,aAAa,MAAM,GAAG,CAAC,CAAC,SAAS,QAAQ;AAAA,MACpG;AACA,aAAO;AAAA,IACT;AACA,eAAW,kBAAkB,aAAa,MAAM,GAAG,CAAC,CAAC,SAAS,QAAQ,EAAE;AACxE,UAAM,UAAU,MAAM,OAAO,OAAO,QAAQ,OAAO;AAAA,MACjD,MAAM,EAAE,MAAM;AAAA,MACd,OAAO,EAAE,WAAW,SAAS;AAAA,IAC/B,CAAC;AACD,UAAM,KAAK,QAAQ,MAAM;AACzB,QAAI,CAAC,IAAI;AACP,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AACA,SAAK,WAAW,IAAI,cAAc,EAAE;AACpC;AAAA,MACE,2BAA2B,GAAG,MAAM,GAAG,CAAC,CAAC,YAAY,aAAa,MAAM,GAAG,CAAC,CAAC;AAAA,IAC/E;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBACZ,QACA,mBACA,UACA,cACA,UACe;AACf,QAAI;AACF,YAAM,OAAO,qCAAqC;AAAA,QAChD,MAAM,EAAE,IAAI,mBAAmB,cAAc,aAAa;AAAA,QAC1D,OAAO,EAAE,WAAW,SAAS;AAAA,QAC7B,MAAM,EAAE,SAAS;AAAA,MACnB,CAAC;AACD;AAAA,QACE,uBAAuB,aAAa,MAAM,GAAG,CAAC,CAAC,aAAa,QAAQ;AAAA,MACtE;AAAA,IACF,SAAS,KAAK;AACZ;AAAA,QACE,8BAA8B,aAAa,MAAM,GAAG,CAAC,CAAC,aAAa,QAAQ,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACvI;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA,EAIA,MAAM,gBACJ,WACA,cACA,UACkB;AAClB,UAAM,OAAO,KAAK,YAAY,IAAI,SAAS;AAC3C,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,QAAQ,KAAK,QAAQ,IAAI,YAAY;AAC3C,QAAI,OAAO;AACT,mBAAa,KAAK;AAClB,WAAK,QAAQ,OAAO,YAAY;AAAA,IAClC;AACA,UAAM,KAAK;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL;AAAA,MACA;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,cACL,SACiC;AACjC,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,KAAK,aAAa;AAAA,IACnC,SAAS,KAAK;AACZ,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,SACE,eAAe,QACX,IAAI,UACJ;AAAA,QACN,aAAa;AAAA,MACf;AACA;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,0BAAoB,MAAM,KAAK;AAAA,QAC7B;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ,OAAO,MAAM,GAAG,EAAE;AAAA,MAC5B;AAAA,IACF,SAAS,KAAK;AACZ,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,SACE,eAAe,QACX,IAAI,UACJ;AAAA,QACN,aAAa;AAAA,MACf;AACA;AAAA,IACF;AAEA,UAAM,WAAqB;AAAA,MACzB,QAAQ;AAAA,MACR;AAAA,IACF;AACA,QAAI,cAAc;AAClB,QAAI,gBAAgB;AAMpB,UAAM,iBAAiB,QAAQ,kBAAkB;AACjD,UAAM,aAAyB;AAAA,MAC7B,QAAQ,OAAO;AAAA,MACf;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB,SAAS,oBAAI,IAAI;AAAA,IACnB;AACA,SAAK,YAAY,IAAI,QAAQ,WAAW,UAAU;AAUlD,QAAI,qBAAgE;AACpE,QAAI,qBAAuC;AAC3C,QAAI,QAAQ,QAAQ;AAClB,YAAM,WAAW,QAAQ;AACzB,2BAAqB,IAAI,UAAU;AAAA,QACjC,cAAc,MAAM;AAAA,QAKpB;AAAA,MACF,CAAC;AAKD,YAAM,eAAe;AACrB,yBAAmB,UAAU,OAAO,SAAS,aAAa;AACxD,eAAO,aAAa,SAAS,QAAQ;AAAA,MACvC;AACA,2BAAqB,iBAAiB,kBAAkB;AAGxD;AAAA,QACE,8BAA8B,mBAAmB,MAAM,MAAM,GAAG,EAAE,CAAC,mBAAc,QAAQ,UAAU,MAAM,GAAG,CAAC,CAAC;AAAA,MAChH;AAAA,IACF,OAAO;AACL;AAAA,QACE,0CAA0C,QAAQ,UAAU,MAAM,GAAG,CAAC,CAAC;AAAA,MACzE;AAAA,IACF;AAIA,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,UAAM,cAAc,MAAM,gBAAgB,MAAM;AAChD,QAAI,QAAQ,QAAQ;AAClB,UAAI,QAAQ,OAAO,SAAS;AAC1B;AAAA,MACF;AACA,cAAQ,OAAO,iBAAiB,SAAS,WAAW;AAAA,IACtD;AAEA,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,MAAM,OAAO,OAAO,OAAO,MAAM;AAAA,QAC3C,QAAQ,gBAAgB;AAAA,MAC1B,CAAC;AACD,oBAAc,IAAI;AAClB;AAAA,QACE,6BAA6B,QAAQ,UAAU,MAAM,GAAG,CAAC,CAAC,oBAAoB,kBAAkB,MAAM,GAAG,CAAC,CAAC;AAAA,MAC7G;AAAA,IACF,SAAS,KAAK;AACZ,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,SACE,eAAe,QACX,IAAI,UACJ;AAAA,QACN,aAAa;AAAA,MACf;AACA,cAAQ,QAAQ,oBAAoB,SAAS,WAAW;AACxD;AAAA,IACF;AAgBA,UAAM,aAGF;AAAA,MACF,OAAO;AAAA,QACL,EAAE,MAAM,QAAQ,MAAM,GAAG,cAAc;AAAA;AAAA,EAAO,QAAQ,MAAM,GAAG;AAAA,MACjE;AAAA,IACF;AACA,QAAI,sBAAsB,oBAAoB;AAC5C,iBAAW,QAAQ;AAAA,QACjB,YAAY;AAAA,QACZ,SAAS;AAAA,MACX;AAAA,IACF;AACA;AAAA,MACE,wBAAwB,QAAQ,UAAU,MAAM,GAAG,CAAC,CAAC,oBAAoB,kBAAkB,MAAM,GAAG,CAAC,CAAC,gBAAgB,QAAQ,OAAO,MAAM,aAAa,QAAQ,qBAAqB,WAAW,UAAU,QAAQ,kBAAkB,WAAW;AAAA,IACjP;AACA,UAAM,gBAAgB,OAAO,OAAO,QACjC,OAAO;AAAA,MACN,MAAM,EAAE,IAAI,kBAAkB;AAAA,MAC9B,OAAO,EAAE,WAAW,QAAQ,SAAS;AAAA,MACrC,MAAM;AAAA,IACR,CAAC,EACA,KAAK,CAAC,UAAU;AACf;AAAA,QACE,2BAA2B,QAAQ,UAAU,MAAM,GAAG,CAAC,CAAC,oBAAoB,kBAAkB,MAAM,GAAG,CAAC,CAAC;AAAA,MAC3G;AACA,aAAO;AAAA,IACT,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE;AAAA,QACE,yBAAyB,QAAQ,UAAU,MAAM,GAAG,CAAC,CAAC,oBAAoB,kBAAkB,MAAM,GAAG,CAAC,CAAC,UAAU,MAAM,OAAO;AAAA,MAChI;AAEA,aAAO;AAAA,IACT,CAAC;AAWH,QAAI,gBAAgB;AACpB,UAAM,aAA8B,IAAI,QAAQ,CAAC,YAAY;AAC3D,WAAK,cAAc,QAAQ,MAAM;AAC/B,mBAAW,MAAM;AACf,0BAAgB;AAChB,kBAAQ,MAAM;AAAA,QAChB,GAAG,oBAAoB;AAAA,MACzB,CAAC;AAAA,IACH,CAAC;AAED,QAAI,cAAc;AAClB,UAAM,WAAW,YAAY,OAAO,aAAa,EAAE;AACnD,QAAI;AACF,aAAO,CAAC,eAAe;AACrB,YAAI,QAAQ,QAAQ,QAAS;AAC7B,cAAM,QAAQ,SAAS,KAAK;AAC5B,cAAM,OAAO,MAAM,QAAQ,KAAK;AAAA,UAC9B;AAAA,UACA,WAAW,KAAK,MAAM,IAAI;AAAA,QAC5B,CAAC;AACD,YAAI,SAAS,MAAM;AAIjB,eAAK,MAAM,MAAM,MAAM,MAAS;AAChC;AAAA,QACF;AACA,YAAI,KAAK,KAAM;AAUf,cAAM,UAAU,KAAK;AAGrB,cAAM,OACJ,aAAa,WAAW,QAAQ,UAC5B,QAAQ,UACP;AACP,cAAM,SAAS,SAAS,MAAM,QAAQ;AACtC,YAAI,OAAO,OAAO,SAAS,KAAK,OAAO,MAAM;AAC3C;AAAA,YACE,SAAS,KAAK,IAAI,WAAW,OAAO,OAAO,IAAI,CAAC,UAAU,MAAM,IAAI,EAAE,KAAK,GAAG,KAAK,QAAQ,SAAS,OAAO,IAAI,YAAY,QAAQ,UAAU,MAAM,GAAG,CAAC,CAAC;AAAA,UAC1J;AAAA,QACF;AACA,mBAAW,SAAS,OAAO,QAAQ;AACjC,cAAI,MAAM,SAAS,sBAAsB;AACvC,gBAAI,mBAAmB,WAAW;AAGhC,oBAAM,QAAQ,WAAW,MAAM;AAC7B,2BAAW,QAAQ,OAAO,MAAM,YAAY;AAC5C,qBAAK,KAAK;AAAA,kBACR,OAAO;AAAA,kBACP;AAAA,kBACA,QAAQ;AAAA,kBACR,MAAM;AAAA,kBACN;AAAA,gBACF;AACA;AAAA,kBACE,yCAAyC,MAAM,aAAa,MAAM,GAAG,CAAC,CAAC,YAAY,QAAQ,UAAU,MAAM,GAAG,CAAC,CAAC;AAAA,gBAClH;AAAA,cACF,GAAG,8BAA8B;AACjC,yBAAW,QAAQ,IAAI,MAAM,cAAc,KAAK;AAChD,oBAAM;AAAA,YACR,OAAO;AAEL,mBAAK,KAAK;AAAA,gBACR,OAAO;AAAA,gBACP;AAAA,gBACA,QAAQ;AAAA,gBACR,MAAM;AAAA,gBACN;AAAA,cACF;AAAA,YACF;AACA;AAAA,UACF;AACA,cAAI,MAAM,SAAS,mBAAmB,OAAO,cAAc;AACzD,0BAAc;AACd,4BAAgB,OAAO,aAAa;AAAA,UACtC;AACA,cAAI,MAAM,SAAS,OAAQ,eAAc;AACzC,gBAAM;AAAA,QACR;AACA,YAAI,OAAO,MAAM;AACf;AAAA,QACF;AAAA,MACF;AAAA,IACF,UAAE;AACA,sBAAgB,MAAM;AACtB,UAAI;AACF,cAAM,SAAS,SAAS;AAAA,MAC1B,QAAQ;AAAA,MAER;AACA,cAAQ,QAAQ,oBAAoB,SAAS,WAAW;AAGxD,iBAAW,CAAC,QAAQ,KAAK,KAAK,WAAW,SAAS;AAChD,qBAAa,KAAK;AAClB,aAAK,KAAK;AAAA,UACR,OAAO;AAAA,UACP;AAAA,UACA,QAAQ;AAAA,UACR;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,iBAAW,QAAQ,MAAM;AACzB,WAAK,YAAY,OAAO,QAAQ,SAAS;AAGzC,0BAAoB,QAAQ;AAC5B,0BAAoB,MAAM,YAAY;AAAA,IACxC;AAKA,QAAI;AACF,YAAM,UAAU,MAAM,OAAO,OAAO,QAAQ,KAAK;AAAA,QAC/C,MAAM,EAAE,IAAI,kBAAkB;AAAA,QAC9B,OAAO,EAAE,WAAW,QAAQ,SAAS;AAAA,MACvC,CAAC;AACD,YAAM,UAAU,QAAQ,MAAM,WAAW;AACzC,UAAI,SAAS;AACX,cAAM;AAAA,UACJ,MAAM;AAAA,UACN;AAAA,UACA,OAAO,sBAAsB,OAAO;AAAA,QACtC;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ;AAAA,QACE,6BAA6B,QAAQ,UAAU,MAAM,GAAG,CAAC,CAAC,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACtH;AAAA,IAGF;AAGA,UAAM,eAAe,MAAM;AAC3B,QAAI,wBAAwB,OAAO;AACjC,YAAM,EAAE,MAAM,SAAS,SAAS,aAAa,SAAS,aAAa,KAAK;AAAA,IAC1E,OAAO;AACL,iBAAW,yBAAyB,QAAQ,UAAU,MAAM,GAAG,CAAC,CAAC,EAAE;AAAA,IACrE;AAIA,QAAI,CAAC,aAAa;AAChB,YAAM,EAAE,MAAM,OAAO;AAAA,IACvB;AAEA,SAAK;AACL,SAAK;AAAA,EACP;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,cAAe;AACzB,QAAI;AACF,YAAM,SAAS,MAAM,KAAK;AAC1B,YAAM,OAAO,MAAM;AAAA,IACrB,QAAQ;AAAA,IAER,UAAE;AACA,WAAK,gBAAgB;AACrB,WAAK,WAAW,MAAM;AAAA,IACxB;AAAA,EACF;AACF;AAUA,IAAM,wBAAwB,CAC5B,YAMI;AACJ,QAAM,QAKD,CAAC;AACN,MAAI,UAKO;AACX,MAAI,UAAU;AACd,MAAI,UAAU;AAEd,aAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACtC,QAAI,KAAK,WAAW,YAAY,GAAG;AACjC,UAAI,QAAS,OAAM,KAAK,OAAO;AAE/B,YAAM,QAAQ,+BAA+B,KAAK,IAAI;AACtD,UAAI,OAAO;AACT,kBAAU,MAAM,CAAC;AACjB,kBAAU,MAAM,CAAC;AAAA,MACnB,OAAO;AACL,kBAAU;AACV,kBAAU;AAAA,MACZ;AACA,gBAAU;AAAA,QACR,MAAM,WAAW;AAAA,QACjB,WAAW;AAAA,QACX,WAAW;AAAA,QACX,QAAQ,YAAY,UAAU,aAAa;AAAA,MAC7C;AACA;AAAA,IACF;AACA,QAAI,CAAC,QAAS;AACd,QAAI,KAAK,WAAW,eAAe,GAAG;AACpC,cAAQ,SAAS;AAAA,IACnB,WAAW,KAAK,WAAW,eAAe,GAAG;AAC3C,cAAQ,SAAS;AAAA,IACnB,WAAW,KAAK,WAAW,GAAG,KAAK,CAAC,KAAK,WAAW,KAAK,GAAG;AAC1D,cAAQ;AAAA,IACV,WAAW,KAAK,WAAW,GAAG,KAAK,CAAC,KAAK,WAAW,KAAK,GAAG;AAC1D,cAAQ;AAAA,IACV;AAAA,EACF;AACA,MAAI,QAAS,OAAM,KAAK,OAAO;AAI/B,SAAO;AACT;;;AC1rBO,IAAM,sBAAsB,MAAuB;AACxD,QAAM,UAAU,QAAQ,IAAI,uBAAuB,IAAI,KAAK,EAAE,YAAY;AAC1E,MAAI,WAAW,QAAQ;AACrB,WAAO,IAAI,oBAAoB,EAAE,cAAc,GAAG,CAAC;AAAA,EACrD;AACA,SAAO,IAAI,uBAAuB;AACpC;;;ArBQO,IAAM,YAAY,CAAC,YAA8C;AACtE,QAAM,QAAQ,QAAQ,SAAS,YAAY;AAC3C,QAAM,UAAU,QAAQ,WAAW,oBAAoB;AACvD,QAAM,iBAAiB,qBAAqB;AAC5C,QAAM,MAAM,QAAQ;AAIpB,MAAI,IAAI,QAAQ,KAAK,EAAE,OAAO,MAAM,CAAC,CAAC;AAEtC,MAAI,IAAI,qBAAqB,EAAE,eAAe,QAAQ,OAAO,cAAc,CAAC,CAAC;AAC7E,MAAI,IAAI,qBAAqB,EAAE,OAAO,QAAQ,OAAO,MAAM,CAAC,CAAC;AAE7D,MAAI,IAAI,WAAW,oBAAoB,QAAQ,MAAM,CAAC;AACtD,MAAI,IAAI,SAAS,iBAAiB,KAAK,CAAC;AACxC,MAAI,IAAI,UAAU,kBAAkB,EAAE,OAAO,SAAS,eAAe,CAAC,CAAC;AACvE,MAAI,IAAI,uBAAuB,wBAAwB,EAAE,eAAe,CAAC,CAAC;AAC1E,MAAI,IAAI,oBAAoB,qBAAqB,EAAE,eAAe,CAAC,CAAC;AACpE,MAAI,IAAI,qBAAqB,uBAAuB,EAAE,QAAQ,CAAC,CAAC;AAKhE,MAAI,IAAI,aAAa,oBAAoB,CAAC;AAI1C,MAAI,IAAI,CAAC,KAAK,QAAQ;AACpB,QAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,aAAa,QAAQ,IAAI,QAAQ,MAAM,IAAI,KAAK,CAAC;AAAA,EACjF,CAAC;AAED,SAAO,EAAE,KAAK,OAAO,SAAS,eAAe;AAC/C;;;AsBtCA,IAAM,oBAAoB,OACxB,UACA,YACA,eACA,SACkB;AAClB,QAAM,MAAM,MAAM,MAAM,GAAG,QAAQ,qCAAqC;AAAA,IACtE,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,UAAM,IAAI;AAAA,MACR,wCAAwC,IAAI,MAAM,KAAK,QAAQ,SAAS;AAAA,IAC1E;AAAA,EACF;AACF;AAEA,IAAM,iBAAiB,OAAO,QAAkC;AAC9D,MAAI;AAGF,UAAM,QAAQ,MAAM,OAAO,MAAM,GAAG;AACpC,UAAM,KAAK,GAAG;AACd,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,gBAAgB,OAC3B,YACkB;AAClB,QAAM,WAAW,QAAQ,SAAS,QAAQ,OAAO,EAAE;AAMnD,MAAI,CAAC,QAAQ,YAAY;AACvB,UAAM,aAAa,GAAG,QAAQ;AAC9B,YAAQ,IAAI,mCAAmC;AAC/C,YAAQ,IAAI,8CAA8C;AAC1D,YAAQ,IAAI,kBAAkB,UAAU,EAAE;AAC1C,YAAQ,IAAI,EAAE;AACd,YAAQ;AAAA,MACN;AAAA,IACF;AACA,QAAI,QAAQ,iBAAiB;AAC3B,YAAM,eAAe,UAAU;AAAA,IACjC;AACA;AAAA,EACF;AAEA,UAAQ,IAAI,uBAAuB,QAAQ,EAAE;AAC7C,UAAQ,IAAI,6BAA6B,QAAQ,WAAW,MAAM,GAAG,EAAE,CAAC,QAAG;AAI3E,UAAQ,IAAI,oBAAoB,QAAQ;AACxC,UAAQ,IAAI,6BAA6B;AACzC,UAAQ,IAAI,mBAAmB,OAAO,QAAQ,IAAI;AAElD,QAAM,SAAS,WAAW;AAC1B,QAAM,EAAE,KAAK,QAAQ,IAAI,UAAU,EAAE,OAAO,CAAC;AAE7C,QAAM,SAAS,IAAI,OAAO,OAAO,MAAM,OAAO,MAAM,YAAY;AAC9D,UAAM,MAAM,UAAU,OAAO,IAAI,IAAI,OAAO,IAAI;AAChD,YAAQ,IAAI,oCAAoC,GAAG,EAAE;AACrD,YAAQ,IAAI,yBAAyB,QAAQ,IAAI,EAAE;AACnD,YAAQ,IAAI,0DAAqD;AACjE,QAAI;AACF,YAAM;AAAA,QACJ;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,OAAO;AAAA,MACT;AACA,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,MAAM,kCAAmC,IAAc,OAAO,EAAE;AACxE,cAAQ,MAAM,EAAE;AAChB,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,MAAM,kCAAkC,GAAG,EAAE;AACrD,cAAQ,MAAM,kCAAkC,QAAQ,UAAU,EAAE;AAAA,IACtE;AACA,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,+CAA+C;AAAA,EAC7D,CAAC;AAED,QAAM,WAAW,CAAC,WAA2B;AAC3C,YAAQ,IAAI;AAAA,wBAA2B,MAAM,uBAAkB;AAC/D,WAAO,MAAM,MAAM,QAAQ,KAAK,CAAC,CAAC;AAClC,eAAW,MAAM,QAAQ,KAAK,CAAC,GAAG,GAAK,EAAE,MAAM;AAAA,EACjD;AACA,UAAQ,GAAG,UAAU,QAAQ;AAC7B,UAAQ,GAAG,WAAW,QAAQ;AAChC;;;AC5HA,SAAS,YAAY,UAAU;AAC/B,OAAOC,WAAU;AACjB,OAAO,QAAQ;AAEf,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AAgCvB,IAAI,gBAAmD;AAEvD,IAAM,aAAa,YAAwC;AACzD,MAAI,cAAe,QAAO;AAC1B,mBAAiB,YAAY;AAC3B,QAAI;AAKF,YAAM,aAAa;AACnB,YAAM,MAAO,MAAM;AAAA;AAAA,QAA0B;AAAA;AAG7C,aAAQ,IAAI,WAAW;AAAA,IACzB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,GAAG;AACH,SAAO;AACT;AAMA,IAAM,mBAAmB,MAAc;AAErC,QAAM,OAAO,GAAG,QAAQ;AACxB,SAAOA,MAAK,KAAK,MAAM,aAAa,aAAa;AACnD;AAaO,IAAM,wBAAwB,CACnC,UAAkC,CAAC,MACf;AACpB,QAAM,WAAW,QAAQ,oBAAoB,iBAAiB;AAC9D,QAAM,eAAe,QAAQ;AAE7B,QAAM,iBAAiB,YAAwC;AAC7D,QAAI,aAAc,QAAO;AACzB,UAAM,SAAS,MAAM,WAAW;AAChC,WAAO,SAAS,aAAa;AAAA,EAC/B;AAEA,SAAO;AAAA,IACL,MAAM,OAAO;AACX,YAAM,UAAU,MAAM,eAAe;AACrC,UAAI,YAAY,YAAY;AAC1B,cAAM,SAAS,MAAM,WAAW;AAChC,YAAI,CAAC,OAAQ,QAAO;AACpB,cAAM,MAAM,MAAM,OAAO,YAAY,gBAAgB,cAAc;AACnE,YAAI,CAAC,IAAK,QAAO;AACjB,YAAI;AACF,gBAAM,aAAa,KAAK,MAAM,GAAG;AACjC,iBAAO,EAAE,YAAY,SAAS,WAAoB;AAAA,QACpD,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,UAAI;AACF,cAAM,MAAM,MAAM,GAAG,SAAS,UAAU,MAAM;AAC9C,cAAM,aAAa,KAAK,MAAM,GAAG;AACjC,eAAO,EAAE,YAAY,SAAS,OAAgB;AAAA,MAChD,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,MAAM,KAAK,YAAY;AACrB,YAAM,UAAU,MAAM,eAAe;AACrC,UAAI,YAAY,YAAY;AAC1B,cAAM,SAAS,MAAM,WAAW;AAChC,YAAI,CAAC,QAAQ;AAAA,QAGb,OAAO;AACL,gBAAM,OAAO;AAAA,YACX;AAAA,YACA;AAAA,YACA,KAAK,UAAU,UAAU;AAAA,UAC3B;AACA,iBAAO,EAAE,SAAS,WAAW;AAAA,QAC/B;AAAA,MACF;AAEA,YAAM,GAAG,MAAMA,MAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC1D,YAAM,GAAG,UAAU,UAAU,KAAK,UAAU,YAAY,MAAM,CAAC,GAAG;AAAA,QAChE,MAAM;AAAA,MACR,CAAC;AACD,aAAO,EAAE,SAAS,OAAO;AAAA,IAC3B;AAAA,IAEA,MAAM,QAAQ;AACZ,YAAM,UAAU,MAAM,eAAe;AACrC,UAAI,YAAY,YAAY;AAC1B,cAAM,SAAS,MAAM,WAAW;AAChC,YAAI,QAAQ;AACV,gBAAM,OACH,eAAe,gBAAgB,cAAc,EAC7C,MAAM,MAAM,MAAS;AAAA,QAC1B;AAAA,MACF;AAGA,YAAM,GAAG,GAAG,UAAU,EAAE,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM,MAAS;AAAA,IAC9D;AAAA,EACF;AACF;;;AC3JA,IAAM,oBAAoB,YAA2B;AACnD,QAAM,QAAQ,sBAAsB;AACpC,QAAM,SAAS,MAAM,MAAM,KAAK,EAAE,MAAM,MAAM,IAAI;AAClD,MAAI,CAAC,QAAQ;AACX,YAAQ,IAAI,mCAAmC;AAC/C,YAAQ;AAAA,MACN;AAAA,IACF;AACA;AAAA,EACF;AACA,QAAM,UAAU,IAAI,KAAK,OAAO,WAAW,SAAS;AACpD,QAAM,WAAW,KAAK;AAAA,IACpB;AAAA,IACA,KAAK,OAAO,QAAQ,QAAQ,IAAI,KAAK,IAAI,KAAK,KAAU;AAAA,EAC1D;AACA,UAAQ;AAAA,IACN,oCAAoC,OAAO,WAAW,KAAK,MAAM,OAAO,WAAW,QAAQ;AAAA,EAC7F;AACA,UAAQ;AAAA,IACN,0BAA0B,OAAO,WAAW,QAAQ,iBAAiB,QAAQ,gBAAgB,OAAO,OAAO;AAAA,EAC7G;AACF;AAEO,IAAM,WAAW,OAAO,YAAyC;AAItE,MAAI,QAAQ,KAAM,SAAQ,IAAI,mBAAmB,OAAO,QAAQ,IAAI;AACpE,MAAI,QAAQ,MAAO,SAAQ,IAAI,oBAAoB,QAAQ;AAC3D,MAAI,QAAQ;AACV,YAAQ,IAAI,6BAA6B,QAAQ;AACnD,MAAI,QAAQ,QAAS,SAAQ,IAAI,sBAAsB,QAAQ;AAE/D,QAAM,SAAS,WAAW;AAC1B,QAAM,EAAE,KAAK,QAAQ,IAAI,UAAU,EAAE,OAAO,CAAC;AAE7C,QAAM,SAAS,IAAI,OAAO,OAAO,MAAM,OAAO,MAAM,YAAY;AAC9D,UAAM,MAAM,UAAU,OAAO,IAAI,IAAI,OAAO,IAAI;AAChD,YAAQ,IAAI,8BAA8B,GAAG,EAAE;AAC/C,YAAQ,IAAI,0BAA0B,QAAQ,IAAI,EAAE;AACpD,YAAQ,IAAI,sDAAsD;AAClE,YAAQ,IAAI,mBAAmB,OAAO,KAAK,EAAE;AAC7C,YAAQ,IAAI,yCAAyC,OAAO,aAAa,EAAE;AAC3E,UAAM,kBAAkB;AACxB,YAAQ;AAAA,MACN;AAAA,IACF;AAAA,EACF,CAAC;AAED,QAAM,WAAW,CAAC,WAA2B;AAC3C,YAAQ,IAAI;AAAA,yBAA4B,MAAM,oBAAoB;AAClE,WAAO,MAAM,MAAM,QAAQ,KAAK,CAAC,CAAC;AAClC,eAAW,MAAM,QAAQ,KAAK,CAAC,GAAG,GAAK,EAAE,MAAM;AAAA,EACjD;AACA,UAAQ,GAAG,UAAU,QAAQ;AAC7B,UAAQ,GAAG,WAAW,QAAQ;AAChC;;;ACnEA,OAAOC,SAAQ;AAIf,IAAM,WAAW,MAAgD;AAC/D,QAAM,IAAI,QAAQ;AAClB,MAAI,MAAM,YAAY,MAAM,WAAW,MAAM,QAAS,QAAO;AAC7D,SAAO;AACT;AAEA,IAAM,cAAc,MAAc;AAChC,QAAM,WAAWC,IAAG,SAAS,KAAK;AAClC,SAAO,SAAS,QAAQ,aAAa,EAAE;AACzC;AAcO,IAAM,UAAU,OAAO,YAAwC;AACpE,QAAM,WAAW,QAAQ,SAAS,QAAQ,OAAO,EAAE;AACnD,QAAM,QAAQ,QAAQ,IAAI,uBAAuB,KAAK,KAAK,YAAY;AACvE,QAAM,OAAO,SAAS;AAEtB,UAAQ,IAAI,mBAAmB,QAAQ,EAAE;AACzC,UAAQ,IAAI,mBAAmB,KAAK,EAAE;AACtC,UAAQ,IAAI,mBAAmB,QAAQ,WAAW,EAAE;AACpD,UAAQ,IAAI,mBAAmB,QAAQ,IAAI,EAAE;AAC7C,UAAQ,IAAI,sDAAiD;AAE7D,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,MAAM,GAAG,QAAQ,8BAA8B;AAAA,MAC9D,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU;AAAA,QACnB,MAAM,QAAQ;AAAA,QACd;AAAA,QACA,GAAI,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;AAAA,QAC3B,eAAe,QAAQ;AAAA,MACzB,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,MAAM,4BAA6B,IAAc,OAAO,EAAE;AAClE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,SAAS,WAAW,KAAK;AAC3B,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,SAAS,WAAW,KAAK;AAC3B,YAAQ,MAAM,4DAA4D;AAC1E,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,YAAQ,MAAM,yBAAyB,SAAS,MAAM,KAAK,IAAI,EAAE;AACjE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,MAAI,CAAC,KAAK,OAAO,CAAC,KAAK,QAAQ,IAAI;AACjC,YAAQ,MAAM,yCAAyC,IAAI;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,QAAQ,sBAAsB;AACpC,QAAM,EAAE,QAAQ,IAAI,MAAM,MAAM,KAAK;AAAA,IACnC,KAAK,KAAK;AAAA,IACV,UAAU,KAAK,OAAO;AAAA,IACtB,WAAW,KAAK;AAAA,IAChB,WAAU,oBAAI,KAAK,GAAE,YAAY;AAAA,IACjC,OAAO,KAAK,OAAO;AAAA,IACnB;AAAA,EACF,CAAC;AAED,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,4BAAuB,KAAK,OAAO,KAAK,MAAM,KAAK,OAAO,EAAE,GAAG;AAC3E,UAAQ,IAAI,gCAAgC,KAAK,SAAS,EAAE;AAC5D,UAAQ;AAAA,IACN,uBAAuB,YAAY,aAAa,gBAAgB,sCAAsC;AAAA,EACxG;AACA,MAAI,YAAY,QAAQ;AACtB,YAAQ;AAAA,MACN;AAAA,IACF;AAAA,EACF;AACA,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,+DAA+D;AAC3E,UAAQ,IAAI,4CAA4C;AAC1D;;;A3BzFA,IAAM,iBAAiB;AACvB,IAAM,oBAAoB;AAQ1B,IAAM,YAAY,CAAC,SAA+B;AAChD,QAAM,QAAQ,oBAAI,IAA8B;AAChD,QAAM,aAAuB,CAAC;AAC9B,MAAI,aAA8C;AAElD,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAClB,QAAI,QAAQ,YAAY,QAAQ,MAAM;AACpC,aAAO,EAAE,YAAY,QAAQ,OAAO,WAAW;AAAA,IACjD;AACA,QAAI,QAAQ,eAAe,QAAQ,MAAM;AACvC,aAAO,EAAE,YAAY,WAAW,OAAO,WAAW;AAAA,IACpD;AACA,QAAI,IAAI,WAAW,IAAI,GAAG;AACxB,YAAM,KAAK,IAAI,QAAQ,GAAG;AAC1B,UAAI,OAAO,IAAI;AACb,cAAM,IAAI,IAAI,MAAM,GAAG,EAAE,GAAG,IAAI,MAAM,KAAK,CAAC,CAAC;AAAA,MAC/C,OAAO;AAGL,cAAM,OAAO,KAAK,IAAI,CAAC;AACvB,YAAI,QAAQ,CAAC,KAAK,WAAW,IAAI,GAAG;AAClC,gBAAM,IAAI,IAAI,MAAM,CAAC,GAAG,IAAI;AAC5B;AAAA,QACF,OAAO;AACL,gBAAM,IAAI,IAAI,MAAM,CAAC,GAAG,IAAI;AAAA,QAC9B;AAAA,MACF;AACA;AAAA,IACF;AACA,QAAI,eAAe,QAAQ,CAAC,cAAc,SAAS,MAAM,EAAE,SAAS,GAAG,GAAG;AACxE,mBAAa;AACb;AAAA,IACF;AACA,eAAW,KAAK,GAAG;AAAA,EACrB;AAEA,SAAO;AAAA,IACL,YAAY,cAAc;AAAA,IAC1B;AAAA,IACA;AAAA,EACF;AACF;AAEA,IAAM,YAAY,MAAY;AAC5B,UAAQ,IAAI;AAAA,wBACU,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4DAcsB,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6B3E,KAAK,CAAC;AACR;AAEA,IAAM,OAAO,YAA2B;AACtC,QAAM,OAAOC,SAAQ,KAAK,MAAM,CAAC;AACjC,QAAM,SAAS,UAAU,IAAI;AAE7B,UAAQ,OAAO,YAAY;AAAA,IACzB,KAAK;AACH,gBAAU;AACV;AAAA,IACF,KAAK;AACH,cAAQ,IAAI,yBAAyB,cAAc,EAAE;AACrD;AAAA,IACF,KAAK;AACH,YAAM,cAAc;AAAA,QAClB,UACG,OAAO,MAAM,IAAI,OAAO,KACzBA,SAAQ,IAAI,sBACZ;AAAA,QACF,MAAM,aAAa,OAAO,MAAM,IAAI,MAAM,GAAG,IAAI;AAAA,QACjD,iBAAiB,OAAO,MAAM,IAAI,SAAS,MAAM;AAAA,QACjD,eAAe;AAAA,QACf,YAAY,OAAO,MAAM,IAAI,OAAO;AAAA,MACtC,CAAC;AACD;AAAA,IACF,KAAK;AACH,YAAM,SAAS;AAAA,QACb,MAAM,aAAa,OAAO,MAAM,IAAI,MAAM,GAAG,IAAI;AAAA,QACjD,OAAO,OAAO,MAAM,IAAI,OAAO;AAAA,QAC/B,eAAe,OAAO,MAAM,IAAI,QAAQ;AAAA,QACxC,SAAS,OAAO,MAAM,IAAI,SAAS;AAAA,MAIrC,CAAC;AACD;AAAA,IACF,KAAK,QAAQ;AACX,YAAM,OAAO,OAAO,WAAW,CAAC;AAChC,UAAI,CAAC,MAAM;AACT,gBAAQ,MAAM,yCAAyC;AACvD,QAAAA,SAAQ,KAAK,CAAC;AAAA,MAChB;AACA,YAAM,QAAQ;AAAA,QACZ;AAAA,QACA,UACG,OAAO,MAAM,IAAI,OAAO,KACzBA,SAAQ,IAAI,sBACZ;AAAA,QACF,eAAe;AAAA,MACjB,CAAC;AACD;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAM,eAAe,CACnB,KACA,aACW;AACX,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,IAAI,OAAO,SAAS,KAAK,EAAE;AACjC,SAAO,OAAO,SAAS,CAAC,KAAK,IAAI,IAAI,IAAI;AAC3C;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,MAAM,sBAAsB,GAAG;AACvC,EAAAA,SAAQ,KAAK,CAAC;AAChB,CAAC;",
|
|
6
|
-
"names": ["access", "path", "exists", "require", "access", "constants", "spawn", "path", "fileURLToPath", "__filename", "__dirname", "REPO_ROOT", "exists", "process", "path", "
|
|
3
|
+
"sources": ["../src/adapters/engine-locator-npm.ts", "../engine-release.json", "../src/lib/engine-bundle.ts", "../src/adapters/engine-locator-local.ts", "../src/cli.ts", "../src/config.ts", "../src/server.ts", "../src/auth.ts", "../../shared/coding-agent-types.ts", "../src/cors.ts", "../src/state.ts", "../src/worktree.ts", "../src/lib/git.ts", "../src/routes/health.ts", "../src/routes/repo.ts", "../src/lib/paths.ts", "../src/lib/pick-directory.ts", "../src/lib/github.ts", "../src/routes/agent.ts", "../src/broker.ts", "../src/routes/llm-shim.ts", "../src/shim/openai-translator.ts", "../src/shim/responses-translator.ts", "../src/shim/active-broker.ts", "../src/adapters/mock-adapter.ts", "../src/adapters/opencode-event-mapper.ts", "../src/adapters/engine-locator.ts", "../src/adapters/opencode-adapter.ts", "../src/adapters/index.ts", "../src/modes/quickstart.ts", "../src/lib/keychain.ts", "../src/modes/start.ts", "../src/modes/pair.ts"],
|
|
4
|
+
"sourcesContent": ["// NpmSdkLocator \u2014 current production engine path.\n//\n// Wraps @opencode-ai/sdk (the npm package), which itself spawns the\n// `opencode` binary the user has installed locally. This is the DEFAULT\n// locator while V4 (bundled engine) is in flight.\n//\n// Constructor accepts an optional `sdkLoader` so tests can inject a stub\n// without having to actually install opencode.\n\nimport { spawn } from \"node:child_process\";\n\nimport type {\n EngineHandle,\n EngineLocator,\n EngineStartOptions,\n OpencodeClient,\n OpencodeConfig,\n} from \"./engine-locator\";\n\ntype OpencodeServerHandle = {\n url: string;\n close(): void;\n};\n\ninterface SdkModule {\n createOpencodeServer(options: {\n hostname?: string;\n port?: number;\n signal?: AbortSignal;\n config?: OpencodeConfig;\n }): Promise<OpencodeServerHandle>;\n createOpencodeClient(config: {\n baseUrl: string;\n directory?: string;\n }): OpencodeClient;\n}\n\nexport interface NpmSdkLocatorOptions {\n /** Dynamic import handle for @opencode-ai/sdk. Pass-through for tests;\n * defaults to importing the real module. */\n sdkLoader?: () => Promise<SdkModule>;\n /** Override for the binary presence check. Tests stub this so we\n * don't depend on the host's opencode install. */\n checkBinary?: () => Promise<boolean>;\n}\n\nconst defaultBinaryCheck = async (): Promise<boolean> =>\n new Promise<boolean>((resolve) => {\n const child = spawn(\"opencode\", [\"--version\"], { stdio: \"ignore\" });\n child.on(\"error\", () => resolve(false));\n child.on(\"exit\", (code) => resolve(code === 0));\n });\n\nconst defaultSdkLoader = (): Promise<SdkModule> =>\n import(\"@opencode-ai/sdk\") as unknown as Promise<SdkModule>;\n\nexport class NpmSdkLocator implements EngineLocator {\n readonly name = \"npm\";\n\n constructor(private readonly options: NpmSdkLocatorOptions = {}) {}\n\n async start(options: EngineStartOptions): Promise<EngineHandle> {\n const check = this.options.checkBinary ?? defaultBinaryCheck;\n const ok = await check();\n if (!ok) {\n throw new Error(\n \"opencode binary not found on PATH. Install via \" +\n \"`curl -fsSL https://opencode.ai/install | bash` and ensure \" +\n \"`opencode --version` works in this shell. \" +\n \"(Or set LOCAL_AGENT_ENGINE=local once V4 ships.)\",\n );\n }\n\n const loadSdk = this.options.sdkLoader ?? defaultSdkLoader;\n const sdk = await loadSdk();\n const server = await sdk.createOpencodeServer({\n hostname: \"127.0.0.1\",\n port: 0,\n signal: options.signal,\n config: options.config,\n });\n const client = sdk.createOpencodeClient({ baseUrl: server.url });\n return {\n url: server.url,\n client,\n close: () => {\n server.close();\n },\n };\n }\n}\n", "{\n \"_comment\": \"Single source of truth for the engine bundle the helper expects. Both scripts/build-engine-bundle.ts and local-agent/scripts/postinstall.ts read this. Bumping `version` here is what triggers a new release.\",\n \"version\": \"0.1.0\",\n \"releaseTag\": \"engine-v0.1.0\",\n \"downloadBase\": \"https://github.com/Deplova-Ltd/AI-Personas-and-Multi-LLM/releases/download\",\n \"targets\": [\n { \"platform\": \"darwin\", \"arch\": \"arm64\", \"bunTarget\": \"bun-darwin-arm64\", \"ext\": \"\" },\n { \"platform\": \"darwin\", \"arch\": \"x64\", \"bunTarget\": \"bun-darwin-x64\", \"ext\": \"\" },\n { \"platform\": \"linux\", \"arch\": \"x64\", \"bunTarget\": \"bun-linux-x64\", \"ext\": \"\" },\n { \"platform\": \"linux\", \"arch\": \"arm64\", \"bunTarget\": \"bun-linux-arm64\", \"ext\": \"\" },\n { \"platform\": \"win32\", \"arch\": \"x64\", \"bunTarget\": \"bun-windows-x64\", \"ext\": \".exe\" }\n ]\n}\n", "// Engine-binary discovery \u2014 finds the opencode engine binary the helper\n// spawns in `serve` mode.\n//\n// Primary source: the `opencode-ai` npm package, which we declare as a\n// dependency. Its install lands a platform-native binary at\n// node_modules/opencode-ai/bin/opencode.exe (via platform-specific\n// optionalDependencies \u2014 no compile, no postinstall download on our\n// side). This is the \"zero-setup install\" path: `npm install\n// @diologue/local-agent` brings the engine automatically.\n//\n// Discovery order (first hit wins):\n// 1. LOCAL_AGENT_ENGINE_BUNDLE env var \u2014 explicit override (tests + dev)\n// 2. opencode-ai npm binary \u2014 resolved from node_modules (the default)\n// 3. <local-agent>/dist/engine/<releaseTag>/diologue-engine-\u2026 \u2014 legacy\n// self-compiled bundle (V4/V5; only present if someone built one)\n// 4. <repo>/build/diologue-engine-bundles/diologue-engine-\u2026 \u2014 legacy\n// local build\n//\n// Returning null means \"no engine on this machine\"; the caller decides\n// whether to fall back to another path or surface an error.\n\nimport { access, constants } from \"node:fs/promises\";\nimport { readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport engineReleaseRaw from \"../../engine-release.json\" with { type: \"json\" };\n\ninterface EngineRelease {\n version: string;\n releaseTag: string;\n downloadBase: string;\n targets: Array<{\n platform: NodeJS.Platform;\n arch: NodeJS.Architecture;\n bunTarget: string;\n ext: string;\n }>;\n}\n\nexport const engineRelease = engineReleaseRaw as EngineRelease;\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// local-agent/src/lib \u2192 local-agent root is two levels up.\nconst LOCAL_AGENT_ROOT = path.resolve(__dirname, \"../..\");\n// local-agent \u2192 repo root is one level up.\nconst REPO_ROOT = path.resolve(LOCAL_AGENT_ROOT, \"..\");\n\nexport const BUNDLE_DIR_INSTALLED = path.join(\n LOCAL_AGENT_ROOT,\n \"dist/engine\",\n engineRelease.releaseTag,\n);\n\nexport const BUNDLE_DIR_LOCAL_BUILD = path.join(\n REPO_ROOT,\n \"build/diologue-engine-bundles\",\n);\n\nexport const ENGINE_BUNDLE_ENV = \"LOCAL_AGENT_ENGINE_BUNDLE\" as const;\n\nexport interface ResolvedBundle {\n /** Absolute path to the executable. */\n path: string;\n /** Where it came from \u2014 useful for logging + tests. */\n source: \"env\" | \"opencode-ai\" | \"installed\" | \"local-build\";\n}\n\nconst exists = async (p: string): Promise<boolean> => {\n try {\n await access(p, constants.X_OK);\n return true;\n } catch {\n return false;\n }\n};\n\n/** Resolve the `opencode-ai` npm package's bundled binary.\n *\n * opencode-ai declares its binary in package.json `bin` and ships the\n * platform-native executable via optionalDependencies, so after\n * `npm install` it lives at <pkgdir>/bin/<binfile>. We resolve the\n * package via Node's module resolution (handles hoisting) rather than\n * hardcoding a node_modules path.\n *\n * Returns the absolute binary path, or null if opencode-ai isn't\n * installed / its binary didn't land (e.g. unsupported platform). */\nexport const resolveOpencodeAiBinary = (): string | null => {\n try {\n const require = createRequire(import.meta.url);\n const pkgJsonPath = require.resolve(\"opencode-ai/package.json\");\n const pkgDir = path.dirname(pkgJsonPath);\n const pkg = JSON.parse(readFileSync(pkgJsonPath, \"utf-8\")) as {\n bin?: string | Record<string, string>;\n };\n const binRel =\n typeof pkg.bin === \"string\" ? pkg.bin : pkg.bin?.opencode;\n if (!binRel) return null;\n return path.join(pkgDir, binRel);\n } catch {\n // Not resolvable (not installed, or no binary for this platform).\n return null;\n }\n};\n\n/** Build the canonical bundle filename for a given target. The\n * postinstall script uses this name to write the artifact; the build\n * script writes the same name. */\nexport const bundleFilename = (\n platform: NodeJS.Platform = process.platform,\n arch: NodeJS.Architecture = process.arch,\n): string => {\n const target = engineRelease.targets.find(\n (t) => t.platform === platform && t.arch === arch,\n );\n const ext = target?.ext ?? \"\";\n return `diologue-engine-${platform}-${arch}${ext}`;\n};\n\n/** Return the bundle target for this host, or undefined if unsupported. */\nexport const supportedTargetForHost = ():\n | EngineRelease[\"targets\"][number]\n | undefined => {\n return engineRelease.targets.find(\n (t) => t.platform === process.platform && t.arch === process.arch,\n );\n};\n\n/** Search for an engine bundle on the local machine. Returns null\n * when nothing is found. */\nexport const findEngineBundle = async (): Promise<ResolvedBundle | null> => {\n const fromEnv = process.env[ENGINE_BUNDLE_ENV];\n if (fromEnv) {\n if (await exists(fromEnv)) {\n return { path: fromEnv, source: \"env\" };\n }\n // env var set but file missing \u2014 fall through so the caller still\n // gets a useful \"not found\" rather than a half-broken path.\n }\n // Primary path: the opencode-ai npm binary (zero-setup install).\n const opencodeAi = resolveOpencodeAiBinary();\n if (opencodeAi && (await exists(opencodeAi))) {\n return { path: opencodeAi, source: \"opencode-ai\" };\n }\n // Legacy self-compiled bundle paths (V4/V5). Kept as fallbacks for\n // anyone who built their own; not produced by a normal install.\n const filename = bundleFilename();\n const installed = path.join(BUNDLE_DIR_INSTALLED, filename);\n if (await exists(installed)) {\n return { path: installed, source: \"installed\" };\n }\n const localBuild = path.join(BUNDLE_DIR_LOCAL_BUILD, filename);\n if (await exists(localBuild)) {\n return { path: localBuild, source: \"local-build\" };\n }\n return null;\n};\n\n/** Compose the download URL for the engine bundle on this host. */\nexport const downloadUrlForHost = (): string | null => {\n const target = supportedTargetForHost();\n if (!target) return null;\n return `${engineRelease.downloadBase}/${engineRelease.releaseTag}/${bundleFilename()}`;\n};\n", "// LocalEngineLocator \u2014 production engine path (V4 + V5).\n//\n// What this does:\n//\n// 1. BUNDLE DISCOVERY (V5) \u2014 looks for a standalone diologue-engine\n// binary that postinstall fetched, the build-engine script\n// produced locally, or LOCAL_AGENT_ENGINE_BUNDLE points at. If\n// found, we spawn it directly \u2014 no runtime dependency on the\n// end user's machine.\n//\n// 2. PREFLIGHT FALLBACK (V4) \u2014 if no bundle is present, we fall\n// back to spawning `bun` against build/diologue-engine/ (the V2\n// rebrand output). This is the dev path; production users get\n// the bundle via postinstall.\n//\n// 3. SUBPROCESS SPAWN \u2014 exec the chosen engine binary (bundle or\n// `bun <entry>`), passing `serve --port 0 --hostname 127.0.0.1`.\n// Parse stdout for the \"listening on http://...\" banner, apply\n// a startup timeout, hand back a handle that kills the\n// subprocess on close.\n//\n// 4. CLIENT WIRING \u2014 reuse @opencode-ai/sdk's createOpencodeClient\n// against the spawned engine's base URL. Same wire-format\n// implementation as the npm path, no reimplementation. Tests\n// inject a clientFactory to bypass the SDK.\n\nimport { access, constants } from \"node:fs/promises\";\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type {\n EngineHandle,\n EngineLocator,\n EngineStartOptions,\n OpencodeClient,\n OpencodeConfig,\n} from \"./engine-locator\";\nimport { findEngineBundle, type ResolvedBundle } from \"../lib/engine-bundle\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// local-agent/src/adapters \u2192 repo root is four levels up.\nconst REPO_ROOT = path.resolve(__dirname, \"../../..\");\n\nconst DEFAULT_ENGINE_DIR = path.join(REPO_ROOT, \"build/diologue-engine\");\nconst ENTRY_REL = \"packages/opencode/src/index.ts\";\nconst DEFAULT_STARTUP_TIMEOUT_MS = 30_000;\n\n/** Matches the engine's \"listening on http(s)://host:port\" banner. The\n * rebrand doesn't touch this string (it lives in serve.ts; out of V2\n * scope), so it still says \"opencode server listening on \u2026\" in v1\n * output. We accept either prefix to insulate against later string\n * edits in V2.x. */\nconst LISTENING_REGEX = /listening on (https?:\\/\\/[^\\s]+)/i;\n\nexport interface LocalEngineLocatorOptions {\n /** Directory containing the rebranded engine tree (the fallback\n * source path when no bundle is installed). Defaults to\n * <repo>/build/diologue-engine. */\n enginePath?: string;\n /** Runtime executable for the source-fallback path. Defaults to\n * \"bun\" because the vendored source uses bun-specific imports. */\n runtime?: string;\n /** Override the runtime-availability probe. Tests stub this. */\n checkRuntime?: (runtime: string) => Promise<boolean>;\n /** Override the spawn command + arguments. Tests use this to point\n * at a fake-engine script. Production code leaves this unset. */\n engineCommand?: { command: string; args: string[] };\n /** How long to wait for the listening URL on stdout before killing\n * the subprocess and throwing. Defaults to 30s. */\n startupTimeoutMs?: number;\n /** Factory that produces an OpencodeClient bound to a URL. Defaults\n * to @opencode-ai/sdk's createOpencodeClient. Tests inject a\n * hand-rolled stub so we don't depend on the SDK in unit tests. */\n clientFactory?: (baseUrl: string) => Promise<OpencodeClient>;\n /** Override the bundle discovery. Tests use this. Production code\n * leaves it unset and falls through to lib/engine-bundle.ts. */\n resolveBundle?: () => Promise<ResolvedBundle | null>;\n /** Force the source-fallback path even if a bundle is present. Used\n * by dev workflows that want to test source-tree changes directly. */\n forceSource?: boolean;\n}\n\nconst defaultRuntimeCheck = async (runtime: string): Promise<boolean> =>\n new Promise<boolean>((resolve) => {\n const child = spawn(runtime, [\"--version\"], { stdio: \"ignore\" });\n child.on(\"error\", () => resolve(false));\n child.on(\"exit\", (code) => resolve(code === 0));\n });\n\nconst exists = async (p: string): Promise<boolean> => {\n try {\n await access(p, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n\nconst parseExistingInlineConfig = (\n existing: string | undefined,\n): Record<string, unknown> => {\n if (!existing) return {};\n try {\n const parsed = JSON.parse(existing) as unknown;\n return isRecord(parsed) ? parsed : {};\n } catch {\n // If the caller provided non-JSON config content, don't pass it through: the\n // helper must inject a valid broker config or the engine cannot call back\n // through /llm-shim and no coding work will happen.\n return {};\n }\n};\n\nconst mergeInlineConfig = (\n existing: string | undefined,\n injected: OpencodeConfig | undefined,\n): string | undefined => {\n if (!injected) return existing;\n const base = parseExistingInlineConfig(existing);\n const baseProvider = isRecord(base.provider) ? base.provider : {};\n return JSON.stringify({\n ...base,\n ...injected,\n provider: {\n ...baseProvider,\n ...(injected.provider ?? {}),\n },\n });\n};\n\nconst buildEngineEnv = (\n config: OpencodeConfig | undefined,\n): NodeJS.ProcessEnv => {\n const env = { ...process.env };\n const content = mergeInlineConfig(env.OPENCODE_CONFIG_CONTENT, config);\n if (content) {\n env.OPENCODE_CONFIG_CONTENT = content;\n }\n return env;\n};\n\nconst defaultClientFactory = async (\n baseUrl: string,\n): Promise<OpencodeClient> => {\n // We still consume the SDK's HTTP client even on the local path so\n // we have a single, well-tested wire-format implementation. Cutting\n // this dependency entirely is a later pass.\n const sdk = (await import(\"@opencode-ai/sdk\")) as unknown as {\n createOpencodeClient: (cfg: { baseUrl: string }) => OpencodeClient;\n };\n return sdk.createOpencodeClient({ baseUrl });\n};\n\nexport class LocalEngineLocator implements EngineLocator {\n readonly name = \"local\";\n readonly enginePath: string;\n readonly runtime: string;\n readonly startupTimeoutMs: number;\n\n constructor(private readonly options: LocalEngineLocatorOptions = {}) {\n this.enginePath = options.enginePath ?? DEFAULT_ENGINE_DIR;\n this.runtime = options.runtime ?? \"bun\";\n this.startupTimeoutMs =\n options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;\n }\n\n /** Decide which engine path we're going to use. Returns the resolved\n * bundle when we'll spawn that directly, or null when we'll fall\n * back to the source-tree path. Test seam. */\n async resolveBundle(): Promise<ResolvedBundle | null> {\n if (this.options.forceSource) return null;\n if (this.options.resolveBundle) return this.options.resolveBundle();\n return findEngineBundle();\n }\n\n /** Run the preflight checks for the path we're about to use. Returns\n * null on success, or a human-readable error string if something's\n * wrong. Exposed so start() can reuse it and tests can assert it\n * directly. */\n async preflight(): Promise<string | null> {\n const bundle = await this.resolveBundle();\n if (bundle) {\n // Bundle path: the only requirement is that the file is\n // present and executable. findEngineBundle() already checked\n // X_OK, so nothing else to do.\n return null;\n }\n // Source-tree fallback path (dev / pre-V5 setups).\n if (!(await exists(this.enginePath))) {\n return (\n `[engine-locator/local] No engine bundle found and no source ` +\n `tree at ${this.enginePath}. Either:\\n` +\n ` - Reinstall the package so postinstall fetches a bundle, or\\n` +\n ` - Run \\`npm run rebrand-engine\\` to populate the source tree.`\n );\n }\n const entry = path.join(this.enginePath, ENTRY_REL);\n if (!(await exists(entry))) {\n return (\n `[engine-locator/local] Engine entry not found at ${entry}. ` +\n `The rebrand may have produced a partial tree \u2014 re-run ` +\n `\\`npm run rebrand-engine\\`.`\n );\n }\n const check = this.options.checkRuntime ?? defaultRuntimeCheck;\n const ok = await check(this.runtime);\n if (!ok) {\n return (\n `[engine-locator/local] No engine bundle found and runtime ` +\n `\"${this.runtime}\" is not on PATH. Either:\\n` +\n ` - Reinstall the package so postinstall fetches a bundle, or\\n` +\n ` - Install bun (https://bun.sh) for the source-tree fallback.`\n );\n }\n return null;\n }\n\n /** Build the spawn command. Tests override via `engineCommand`.\n * Otherwise we prefer a discovered bundle; failing that, we fall\n * back to `<runtime> run <entry>`. */\n private async resolveCommand(): Promise<{\n command: string;\n args: string[];\n source: \"bundle\" | \"source\" | \"override\";\n bundle?: ResolvedBundle;\n }> {\n if (this.options.engineCommand) {\n return { ...this.options.engineCommand, source: \"override\" };\n }\n const bundle = await this.resolveBundle();\n if (bundle) {\n return {\n command: bundle.path,\n args: [\"serve\", \"--port\", \"0\", \"--hostname\", \"127.0.0.1\"],\n source: \"bundle\",\n bundle,\n };\n }\n return {\n command: this.runtime,\n args: [\n \"run\",\n path.join(this.enginePath, ENTRY_REL),\n \"serve\",\n \"--port\",\n \"0\",\n \"--hostname\",\n \"127.0.0.1\",\n ],\n source: \"source\",\n };\n }\n\n async start(options: EngineStartOptions): Promise<EngineHandle> {\n const preflightErr = await this.preflight();\n if (preflightErr) throw new Error(preflightErr);\n\n const resolved = await this.resolveCommand();\n const { command, args } = resolved;\n // Only set cwd when running against the source tree; the bundle is\n // a self-contained executable and shouldn't depend on its working\n // dir (and the source tree may not even exist on a bundle-only\n // install).\n const cwd =\n resolved.source === \"source\" && (await exists(this.enginePath))\n ? this.enginePath\n : undefined;\n const child = spawn(command, args, {\n cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n // Inherit env so the engine sees any LOCAL_AGENT_* / provider\n // credentials the parent has, then inject the brokered provider as\n // inline opencode config. Without this, the default local engine path\n // does not know the diologue/diologue-routed model, so the prompt\n // fails before edit tools can create files in the selected repo.\n env: buildEngineEnv(options.config),\n });\n\n let url: string;\n try {\n url = await this.waitForListening(child, options.signal);\n } catch (err) {\n // Make sure we don't leave a dangling subprocess on startup\n // failure.\n if (!child.killed) {\n try {\n child.kill();\n } catch {\n /* best-effort */\n }\n }\n throw err;\n }\n\n const factory = this.options.clientFactory ?? defaultClientFactory;\n const client = await factory(url);\n\n return {\n url,\n client,\n close: () => {\n if (child.exitCode === null && !child.killed) {\n try {\n child.kill();\n } catch {\n /* best-effort */\n }\n }\n },\n };\n }\n\n /** Watch the subprocess until either:\n * - stdout/stderr emits a \"listening on http://...\" line \u2014 resolve\n * - the process exits before that happens \u2014 reject with stderr\n * - the startup timeout fires \u2014 reject + (caller kills the child)\n * - the abort signal fires \u2014 reject + (caller kills the child)\n */\n private waitForListening(\n child: ChildProcess,\n abort?: AbortSignal,\n ): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n let settled = false;\n let buffered = \"\";\n const tag = `${this.runtime}/${path.basename(this.enginePath)}`;\n\n const settle = (fn: () => void): void => {\n if (settled) return;\n settled = true;\n cleanup();\n fn();\n };\n\n const onStdout = (chunk: Buffer): void => {\n const text = chunk.toString(\"utf-8\");\n buffered += text;\n const match = LISTENING_REGEX.exec(buffered);\n if (match) {\n settle(() => resolve(match[1]!));\n }\n };\n // We also tee stderr into the listening matcher because some\n // logging configurations route the banner there.\n const onStderr = onStdout;\n\n const onExit = (code: number | null, signal: NodeJS.Signals | null): void => {\n settle(() =>\n reject(\n new Error(\n `[engine-locator/local] ${tag} exited before becoming ready ` +\n `(code=${code} signal=${signal ?? \"-\"}). ` +\n `Last output:\\n${truncate(buffered, 4096)}`,\n ),\n ),\n );\n };\n\n const onError = (err: Error): void => {\n settle(() =>\n reject(\n new Error(\n `[engine-locator/local] ${tag} failed to spawn: ${err.message}`,\n ),\n ),\n );\n };\n\n const timer = setTimeout(() => {\n settle(() =>\n reject(\n new Error(\n `[engine-locator/local] ${tag} did not become ready within ` +\n `${this.startupTimeoutMs}ms. Last output:\\n${truncate(buffered, 4096)}`,\n ),\n ),\n );\n }, this.startupTimeoutMs);\n\n const onAbort = (): void => {\n settle(() =>\n reject(new Error(`[engine-locator/local] startup aborted`)),\n );\n };\n\n const cleanup = (): void => {\n clearTimeout(timer);\n child.stdout?.off(\"data\", onStdout);\n child.stderr?.off(\"data\", onStderr);\n child.off(\"exit\", onExit);\n child.off(\"error\", onError);\n abort?.removeEventListener(\"abort\", onAbort);\n };\n\n child.stdout?.on(\"data\", onStdout);\n child.stderr?.on(\"data\", onStderr);\n child.once(\"exit\", onExit);\n child.once(\"error\", onError);\n if (abort) {\n if (abort.aborted) {\n onAbort();\n return;\n }\n abort.addEventListener(\"abort\", onAbort);\n }\n });\n }\n}\n\nconst truncate = (s: string, max: number): string =>\n s.length <= max ? s : s.slice(s.length - max);\n", "// Unified CLI entry point for the Diologue local agent.\n//\n// Subcommands:\n// diologue-local-agent # default: quickstart\n// diologue-local-agent quickstart [--cloud=URL] # pair + start helper\n// diologue-local-agent start [flags] # raw helper start (legacy)\n// diologue-local-agent pair <code> # pair a device (Phase A2)\n// diologue-local-agent --help # usage\n// diologue-local-agent --version # version\n//\n// The default subcommand (no args) is `quickstart` \u2014 the high-UX path that\n// most users want. Explicit subcommands are escape hatches for advanced\n// scenarios (CI, SSH-only boxes, scripted setups).\n\nimport process from \"node:process\";\n\nimport { runQuickstart } from \"./modes/quickstart\";\nimport { runStart } from \"./modes/start\";\nimport { runPair } from \"./modes/pair\";\n\n// Constants \u2014 synced with package.json on each release.\nconst HELPER_VERSION = \"0.1.0\";\nconst DEFAULT_CLOUD_URL = \"http://localhost:5000\";\n\ninterface ParsedArgs {\n subcommand: \"quickstart\" | \"start\" | \"pair\" | \"help\" | \"version\";\n flags: Map<string, string | boolean>;\n positional: string[];\n}\n\nconst parseArgs = (argv: string[]): ParsedArgs => {\n const flags = new Map<string, string | boolean>();\n const positional: string[] = [];\n let subcommand: ParsedArgs[\"subcommand\"] | null = null;\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (arg === \"--help\" || arg === \"-h\") {\n return { subcommand: \"help\", flags, positional };\n }\n if (arg === \"--version\" || arg === \"-v\") {\n return { subcommand: \"version\", flags, positional };\n }\n if (arg.startsWith(\"--\")) {\n const eq = arg.indexOf(\"=\");\n if (eq !== -1) {\n flags.set(arg.slice(2, eq), arg.slice(eq + 1));\n } else {\n // --key value form: take next argv as value if it doesn't look\n // like another flag, else treat as boolean.\n const next = argv[i + 1];\n if (next && !next.startsWith(\"--\")) {\n flags.set(arg.slice(2), next);\n i++;\n } else {\n flags.set(arg.slice(2), true);\n }\n }\n continue;\n }\n if (subcommand === null && [\"quickstart\", \"start\", \"pair\"].includes(arg)) {\n subcommand = arg as ParsedArgs[\"subcommand\"];\n continue;\n }\n positional.push(arg);\n }\n\n return {\n subcommand: subcommand ?? \"quickstart\",\n flags,\n positional,\n };\n};\n\nconst printHelp = (): void => {\n console.log(`\nDiologue Local Agent v${HELPER_VERSION}\n\nUSAGE\n diologue-local-agent [<command>] [options]\n\nCOMMANDS\n quickstart (default) Start the helper + open the browser to pair\n start Start the helper in raw token mode (no browser)\n pair <code> Pair this machine with a previously-issued code\n --help Show this message\n --version Show version\n\nQUICKSTART OPTIONS\n --cloud=<url> Cloud URL to pair with\n (default: \\${DIOLOGUE_CLOUD_URL:-${DEFAULT_CLOUD_URL}})\n --no-open Don't auto-open the browser; print the URL instead\n --port=<n> TCP port for the helper (default: 4099)\n\nSTART OPTIONS\n --token=<token> Use this pairing token instead of a random one\n --origin=<url> Allowed browser origin for CORS\n --port=<n> TCP port (default: 4099)\n --adapter=<mock|opencode> Engine adapter (default: opencode)\n\nEXAMPLES\n # New user \u2014 go from zero to chatting in 30s\n npx @diologue/local-agent --cloud=https://app.your-domain.com\n\n # Power user \u2014 manual flags\n diologue-local-agent start --port=5099 --origin=https://your.app\n\n # Pair a device (for the future tunnel transport)\n diologue-local-agent pair K9F-27R --cloud=https://app.your-domain.com\n\nENVIRONMENT\n DIOLOGUE_CLOUD_URL Default cloud URL (overridden by --cloud)\n LOCAL_AGENT_ALLOWED_ORIGIN Default CORS origin\n LOCAL_AGENT_ADAPTER \"mock\" forces the canned-response adapter\n LOCAL_AGENT_TOKEN Pin a specific pairing token (testing)\n LOCAL_AGENT_PORT Default TCP port\n\nFor docs + the source, visit:\n https://github.com/Deplova-Ltd/AI-Personas-and-Multi-LLM\n`.trim());\n};\n\nconst main = async (): Promise<void> => {\n const argv = process.argv.slice(2);\n const parsed = parseArgs(argv);\n\n switch (parsed.subcommand) {\n case \"help\":\n printHelp();\n return;\n case \"version\":\n console.log(`diologue-local-agent v${HELPER_VERSION}`);\n return;\n case \"quickstart\":\n await runQuickstart({\n cloudUrl:\n (parsed.flags.get(\"cloud\") as string | undefined) ??\n process.env.DIOLOGUE_CLOUD_URL ??\n DEFAULT_CLOUD_URL,\n port: parseFlagInt(parsed.flags.get(\"port\"), 4099),\n autoOpenBrowser: parsed.flags.get(\"no-open\") !== true,\n helperVersion: HELPER_VERSION,\n setupToken: parsed.flags.get(\"token\") as string | undefined,\n });\n return;\n case \"start\":\n await runStart({\n port: parseFlagInt(parsed.flags.get(\"port\"), 4099),\n token: parsed.flags.get(\"token\") as string | undefined,\n allowedOrigin: parsed.flags.get(\"origin\") as string | undefined,\n adapter: parsed.flags.get(\"adapter\") as\n | \"mock\"\n | \"opencode\"\n | undefined,\n });\n return;\n case \"pair\": {\n const code = parsed.positional[0];\n if (!code) {\n console.error(\"Usage: diologue-local-agent pair <code>\");\n process.exit(2);\n }\n await runPair({\n code,\n cloudUrl:\n (parsed.flags.get(\"cloud\") as string | undefined) ??\n process.env.DIOLOGUE_CLOUD_URL ??\n DEFAULT_CLOUD_URL,\n helperVersion: HELPER_VERSION,\n });\n return;\n }\n }\n};\n\nconst parseFlagInt = (\n raw: string | boolean | undefined,\n fallback: number,\n): number => {\n if (typeof raw !== \"string\") return fallback;\n const n = Number.parseInt(raw, 10);\n return Number.isFinite(n) && n > 0 ? n : fallback;\n};\n\nmain().catch((err) => {\n console.error(\"[cli] Fatal error:\", err);\n process.exit(1);\n});\n", "// Centralised env/config reads. Imported once per process via loadConfig().\n// Tests can also call loadConfig({ overrides }) to inject deterministic values\n// without touching process.env.\n\nimport { randomBytes } from \"node:crypto\";\n\nexport interface LocalAgentConfig {\n host: string; // always 127.0.0.1 in v1\n port: number;\n allowedOrigin: string;\n token: string;\n helperVersion: string;\n /** Set at process start. Surfaced in /health so the UI can detect helper\n * restarts (which invalidate any in-memory state). */\n startedAt: string;\n}\n\ninterface LoadOptions {\n overrides?: Partial<LocalAgentConfig>;\n env?: NodeJS.ProcessEnv;\n}\n\nconst DEFAULT_PORT = 4099;\nconst DEFAULT_ALLOWED_ORIGIN = \"http://localhost:5000\";\n\nconst parsePort = (raw: string | undefined): number => {\n if (!raw) {\n return DEFAULT_PORT;\n }\n const parsed = Number.parseInt(raw, 10);\n if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 65_535) {\n throw new Error(\n `LOCAL_AGENT_PORT must be a valid TCP port (1-65535), got: ${raw}`,\n );\n }\n return parsed;\n};\n\nconst generateToken = (): string => {\n // 32 bytes \u2192 64 hex chars. Plenty of entropy for a single-machine pairing\n // secret. Hex avoids URL-encoding concerns when the user pastes it.\n return randomBytes(32).toString(\"hex\");\n};\n\nexport const loadConfig = (options: LoadOptions = {}): LocalAgentConfig => {\n const env = options.env ?? process.env;\n const base: LocalAgentConfig = {\n // 127.0.0.1 is intentional and not configurable. Binding to 0.0.0.0\n // would expose the helper to the LAN, which the token alone is not\n // designed to defend against. See TODO(tunnel-cloud-auth).\n host: \"127.0.0.1\",\n port: parsePort(env.LOCAL_AGENT_PORT),\n allowedOrigin:\n (env.LOCAL_AGENT_ALLOWED_ORIGIN ?? \"\").trim() || DEFAULT_ALLOWED_ORIGIN,\n token: (env.LOCAL_AGENT_TOKEN ?? \"\").trim() || generateToken(),\n // Bumped from package.json when we ship a meaningful change. Kept inline\n // to avoid a JSON import + ESM ergonomics in tests.\n helperVersion: \"0.0.1\",\n startedAt: new Date().toISOString(),\n };\n return { ...base, ...options.overrides };\n};\n", "// HTTP server factory. Exposed as a function (rather than module-level\n// side effects) so tests can spin up isolated instances on ephemeral ports.\n\nimport express, { type Express } from \"express\";\n\nimport type { LocalAgentConfig } from \"./config\";\nimport { createAuthMiddleware } from \"./auth\";\nimport { createCorsMiddleware } from \"./cors\";\nimport { createState, type AgentState } from \"./state\";\nimport {\n createWorktreeManager,\n type WorktreeManager,\n} from \"./worktree\";\nimport { createHealthHandler } from \"./routes/health\";\nimport { createRepoRouter } from \"./routes/repo\";\nimport {\n createAgentRouter,\n createLlmChunkRouter,\n createLlmResponseRouter,\n createPermissionRouter,\n} from \"./routes/agent\";\nimport { createLlmShimRouter } from \"./routes/llm-shim\";\nimport { buildDefaultAdapter, type OpenCodeAdapter } from \"./adapters\";\nimport { createBrokerRegistry, type BrokerRegistry } from \"./broker\";\n\nexport interface CreateServerOptions {\n config: LocalAgentConfig;\n /** Optional pre-built state so tests can pre-select a repo. */\n state?: AgentState;\n /** Optional adapter override. Tests inject deterministic mocks; in prod\n * this defaults to whatever buildDefaultAdapter() returns. */\n adapter?: OpenCodeAdapter;\n}\n\nexport interface BuiltServer {\n app: Express;\n state: AgentState;\n adapter: OpenCodeAdapter;\n brokerRegistry: BrokerRegistry;\n worktrees: WorktreeManager;\n}\n\nexport const createApp = (options: CreateServerOptions): BuiltServer => {\n const state = options.state ?? createState();\n const adapter = options.adapter ?? buildDefaultAdapter();\n const brokerRegistry = createBrokerRegistry();\n const worktrees = createWorktreeManager();\n const app = express();\n\n // Body size cap \u2014 we only ever accept small JSON envelopes (paths,\n // prompts). Anything larger is almost certainly a mistake.\n app.use(express.json({ limit: \"1mb\" }));\n\n app.use(createCorsMiddleware({ allowedOrigin: options.config.allowedOrigin }));\n app.use(createAuthMiddleware({ token: options.config.token }));\n\n app.get(\"/health\", createHealthHandler(options.config));\n app.use(\"/repo\", createRepoRouter(state, worktrees));\n app.use(\n \"/agent\",\n createAgentRouter({ state, adapter, brokerRegistry, worktrees }),\n );\n app.use(\"/agent/llm-response\", createLlmResponseRouter({ brokerRegistry }));\n app.use(\"/agent/llm-chunk\", createLlmChunkRouter({ brokerRegistry }));\n app.use(\"/agent/permission\", createPermissionRouter({ adapter }));\n // OpenAI-compat shim for opencode's outbound LLM calls. NOT\n // token-protected by the pairing token middleware \u2014 uses its own\n // ephemeral apiKey bound at stream-start time. The auth middleware\n // explicitly exempts this prefix.\n app.use(\"/llm-shim\", createLlmShimRouter());\n\n // Trailing 404. Returns JSON so the browser's fetch error path stays in\n // happy territory rather than HTML-parsing a default Express page.\n app.use((req, res) => {\n res.status(404).json({ error: \"not_found\", method: req.method, path: req.path });\n });\n\n return { app, state, adapter, brokerRegistry, worktrees };\n};\n", "// Pairing-token middleware.\n//\n// MVP shortcut: a single static token, generated at process start, printed to\n// the console, and pasted into the web UI. The browser supplies it as the\n// `x-local-agent-token` request header on every authenticated call.\n//\n// TODO(tunnel-cloud-auth): when the cloud tunnel ships, this is replaced by a\n// cloud-issued, rotated device credential. The middleware boundary stays the\n// same; only the verification flips to \"ask the cloud\" instead of comparing a\n// local string.\n\nimport type { NextFunction, Request, Response } from \"express\";\nimport { timingSafeEqual } from \"node:crypto\";\n\nimport { LOCAL_AGENT_TOKEN_HEADER } from \"../../shared/coding-agent-types\";\n\nexport interface AuthOptions {\n token: string;\n /** Endpoints that should NOT require the token. Matched against\n * `req.method + ' ' + req.path`. Kept small on purpose \u2014 currently just\n * `GET /health` so the UI can probe connectivity before pairing. */\n unauthenticated?: ReadonlySet<string>;\n}\n\nconst DEFAULT_UNAUTHENTICATED: ReadonlySet<string> = new Set([\"GET /health\"]);\n\n/** Prefixes the pairing-token middleware skips. The OpenAI-compat shim\n * has its own ephemeral-token scheme bound per /agent/message stream;\n * the pairing token isn't appropriate there because opencode supplies\n * its OWN apiKey value. */\nconst DEFAULT_UNAUTHENTICATED_PREFIXES: readonly string[] = [\"/llm-shim/\"];\n\n/** Constant-time comparison. Both strings are token-shaped (hex/uuid) and\n * short enough to compare byte-for-byte. Falls back to a length mismatch\n * fail so we don't even invoke timingSafeEqual on unequal-length input\n * (it throws otherwise). */\nconst tokensEqual = (a: string, b: string): boolean => {\n const ab = Buffer.from(a, \"utf8\");\n const bb = Buffer.from(b, \"utf8\");\n if (ab.length !== bb.length) {\n return false;\n }\n return timingSafeEqual(ab, bb);\n};\n\nexport const createAuthMiddleware = (options: AuthOptions) => {\n const exempt = options.unauthenticated ?? DEFAULT_UNAUTHENTICATED;\n return (req: Request, res: Response, next: NextFunction): void => {\n const key = `${req.method} ${req.path}`;\n if (exempt.has(key)) {\n next();\n return;\n }\n // Skip prefix-matched routes (currently just the LLM shim). These\n // have their own auth.\n for (const prefix of DEFAULT_UNAUTHENTICATED_PREFIXES) {\n if (req.path.startsWith(prefix)) {\n next();\n return;\n }\n }\n // CORS preflight always passes \u2014 actual security comes from the\n // allowed-origin check (see cors.ts). Without this OPTIONS would 401\n // before the browser could send the real request.\n if (req.method === \"OPTIONS\") {\n next();\n return;\n }\n const provided = req.header(LOCAL_AGENT_TOKEN_HEADER);\n if (!provided || !tokensEqual(provided, options.token)) {\n res\n .status(401)\n .json({ error: \"invalid_or_missing_token\", header: LOCAL_AGENT_TOKEN_HEADER });\n return;\n }\n next();\n };\n};\n", "// Wire types shared between:\n// - the web client (client/src/lib/local-agent-client.ts)\n// - the Diologue backend (server/coding-agent/*)\n// - the local helper (local-agent/src/*)\n//\n// Anything that crosses one of those boundaries lives here. Database row\n// shapes stay in shared/schema.ts; this file is purely for the on-the-wire\n// envelopes and event payloads.\n\n// ----------------------------------------------------------------------------\n// Local helper HTTP surface (browser \u2194 helper on 127.0.0.1:4099)\n// ----------------------------------------------------------------------------\n\nexport const LOCAL_AGENT_TOKEN_HEADER = \"x-local-agent-token\";\n\n/** GET /health */\nexport interface LocalAgentHealth {\n ok: true;\n helperVersion: string;\n // Echoed so the browser can sanity-check the binding before showing the\n // \"connected\" badge. Will always be 127.0.0.1 in v1.\n boundHost: string;\n port: number;\n // ISO timestamp the helper started. Lets the UI detect restarts that\n // invalidate the pairing token.\n startedAt: string;\n}\n\n/** POST /repo/select */\nexport interface SelectRepoRequest {\n /** Absolute path on the user's machine. The helper realpath-resolves and\n * verifies it exists and contains a `.git/` entry. */\n path: string;\n}\n\nexport interface SelectRepoResponse {\n ok: true;\n repo: RepoStatus;\n}\n\n/** GET /repo/status \u2014 response envelope. Always 200; `repo` is null when no\n * repo has been selected yet. */\nexport interface RepoStatusResponse {\n repo: RepoStatus | null;\n}\n\n/** Repo state returned by /repo/select and embedded in /repo/status. */\nexport interface RepoStatus {\n /** The canonical (realpath-resolved) path the helper will operate on. */\n path: string;\n /** Basename of `path` \u2014 convenience for the UI. */\n name: string;\n /** Output of `git rev-parse --abbrev-ref HEAD`, or null if detached. */\n branch: string | null;\n /** Output of `git rev-parse HEAD`, short form. */\n head: string | null;\n /** True when `git status --short` returns any output. */\n isDirty: boolean;\n}\n\n/** GET /repo/changed-files */\nexport interface ChangedFile {\n /** Repo-relative path. */\n path: string;\n /** Parsed from `git status --short` columns. */\n index: GitStatusCode;\n workTree: GitStatusCode;\n /** True when the file is untracked (status code \"??\"). */\n untracked: boolean;\n}\n\n/** Two-char codes from `git status --short`. */\nexport type GitStatusCode =\n | \" \" // unmodified\n | \"M\" // modified\n | \"A\" // added\n | \"D\" // deleted\n | \"R\" // renamed\n | \"C\" // copied\n | \"U\" // updated but unmerged\n | \"?\" // untracked\n | \"!\"; // ignored\n\nexport interface ChangedFilesResponse {\n files: ChangedFile[];\n}\n\n/** POST /repo/browse \u2014 the helper opens the OS folder dialog on the user's\n * machine and returns the chosen absolute path. `cancelled` is true when the\n * user dismissed the dialog. A 501 with `error` (\"no_gui\" | \"unsupported\")\n * means there's no usable desktop picker (headless/SSH) \u2014 the UI should fall\n * back to typing the path. */\nexport interface BrowseDirectoryResponse {\n path?: string;\n cancelled?: boolean;\n}\n\n/** POST /repo/create-pr \u2014 branch the working-tree changes, commit, push, and\n * open a pull request via the user's local `gh`. */\nexport interface CreatePrRequest {\n /** PR title (also the commit message). */\n title: string;\n /** PR body. Optional; defaults to a short generated note. */\n body?: string;\n /** When set, the PR is created from this session's worktree branch. */\n sessionId?: string;\n}\n\nexport interface CreatePrResponse {\n /** URL of the opened pull request. */\n url: string;\n /** The branch that was created + pushed (e.g. \"diologue/add-auth-guard-x1y2\"). */\n branch: string;\n /** The base branch the PR targets. */\n base: string;\n}\n\n/** Stable error codes from /repo/create-pr so the UI can give targeted\n * guidance without parsing prose. */\nexport type CreatePrErrorCode =\n | \"no_repo_selected\"\n | \"no_remote\"\n | \"gh_unavailable\"\n | \"nothing_to_commit\"\n | \"git_command_failed\"\n | \"gh_failed\";\n\n/** GET /repo/diff */\nexport interface RepoDiffResponse {\n /** Concatenated unified diff: staged + unstaged. Untracked files are\n * represented as full-file additions. */\n unified: string;\n /** Bytes \u2014 the UI uses this to refuse to render extremely large diffs\n * without an explicit \"show anyway\" click. */\n sizeBytes: number;\n}\n\n// ----------------------------------------------------------------------------\n// Helper \u2194 browser streaming (POST /agent/message)\n// ----------------------------------------------------------------------------\n//\n// The helper streams a sequence of envelopes back to the browser over SSE.\n// All envelopes share a discriminating `type` field so the UI can switch.\n//\n// Most events are emitted by the OpenCode adapter; the `llm_request` event\n// is special \u2014 it asks the browser to make an LLM call on the helper's\n// behalf so usage flows through the cloud billing ledger.\n\nexport type AgentStreamEvent =\n | AgentTextDelta\n | AgentToolCallStart\n | AgentToolCallEnd\n | AgentDiffProposed\n | AgentPermissionRequest\n | AgentLlmRequest\n | AgentDone\n | AgentError;\n\nexport interface AgentTextDelta {\n type: \"text_delta\";\n text: string;\n}\n\nexport interface AgentToolCallStart {\n type: \"tool_call_start\";\n toolCallId: string;\n /** Adapter-defined tool name, e.g. \"shell.run\", \"fs.read\". */\n tool: string;\n /** Opaque, adapter-specific arguments. Surface verbatim in the UI. */\n args?: Record<string, unknown>;\n}\n\nexport interface AgentToolCallEnd {\n type: \"tool_call_end\";\n toolCallId: string;\n ok: boolean;\n /** Short, one-line summary for the activity row (the tool's title, e.g.\n * \"Read 42 lines\"). */\n summary?: string;\n /** The tool's full textual result \u2014 bash stdout/stderr, file-read\n * contents, edit confirmation, etc. Truncated by the helper to keep the\n * SSE + persistence bounded. Optional: older helpers omit it, and the UI\n * renders the row fine without it. */\n output?: string;\n}\n\nexport interface AgentDiffProposed {\n type: \"diff_proposed\";\n /** Full unified diff. */\n unified: string;\n /** Parsed file summary \u2014 UI uses this to render the changed-files list\n * in the apply dialog. */\n files: Array<{\n path: string;\n additions: number;\n deletions: number;\n status: \"added\" | \"modified\" | \"deleted\" | \"renamed\";\n }>;\n}\n\n/** opencode paused a tool call (bash/webfetch) and is waiting for the user\n * to approve or deny it. The turn stays open until the browser POSTs a\n * decision to /agent/permission. Only emitted in \"confirm\" mode \u2014 in \"auto\"\n * the helper auto-approves and the browser never sees this. */\nexport interface AgentPermissionRequest {\n type: \"permission_request\";\n /** opencode's permission id \u2014 echoed back to approve/deny. */\n permissionId: string;\n /** Permission category from opencode, e.g. \"bash\", \"webfetch\". */\n tool: string;\n /** Human-readable title opencode attached (e.g. \"Run command\"). */\n title?: string;\n /** The concrete action awaiting approval \u2014 the shell command, the URL \u2014\n * extracted from the permission metadata when present. */\n command?: string;\n}\n\n/** How much the user wants to gate tool execution this turn.\n * - auto: run everything without prompting (edits are reversible via\n * Keep/Revert; bash/webfetch auto-approved).\n * - confirm: edits still run freely, but bash + webfetch pause for an\n * explicit Allow/Deny. */\nexport type CodingPermissionMode = \"auto\" | \"confirm\";\n\n/** Maps to opencode's permission reply vocabulary:\n * once = allow this one call, always = allow for the rest of the session,\n * reject = deny. */\nexport type CodingPermissionResponse = \"once\" | \"always\" | \"reject\";\n\n/** POST /agent/permission \u2014 the browser approves/denies a paused tool call. */\nexport interface AgentPermissionDecisionRequest {\n sessionId: string;\n permissionId: string;\n response: CodingPermissionResponse;\n}\n\n/** Helper has hit a point where it needs an LLM call. The browser is\n * expected to forward this to the Diologue backend's coding LLM proxy\n * (so the call is metered + auth'd), then POST the completed response\n * back to POST /agent/llm-response/:requestId.\n *\n * Step 8 wires this end-to-end. The payload shape is intentionally\n * generic so it can carry tools, system prompts, etc. without\n * requiring a wire-format change for every adapter capability. */\nexport interface AgentLlmRequest {\n type: \"llm_request\";\n requestId: string;\n provider?: string;\n model?: string;\n payload: {\n messages: Array<{ role: CodingMessageRole; content: string }>;\n systemPrompt?: string;\n /** Sampling temperature. Adapters typically leave this null and let\n * the model default apply. */\n temperature?: number;\n /** Optional tool definitions for tool-use-capable models. Shape is\n * whatever provider expects \u2014 opaque to the broker. */\n tools?: unknown[];\n };\n}\n\n/** Roles accepted in brokered LLM messages and in the coding-agent\n * message persistence layer. Wider than chat's user|assistant because\n * coding agents commonly thread tool results back in. */\nexport type CodingMessageRole = \"user\" | \"assistant\" | \"system\" | \"tool\";\n\n/** What the adapter hands to the broker. Same shape as\n * AgentLlmRequest.payload + the model/provider hints that ride on the\n * envelope. Kept as its own type so the adapter signature is readable. */\nexport interface LlmBrokerRequestPayload {\n provider?: string;\n model?: string;\n systemPrompt?: string;\n temperature?: number;\n messages: Array<{ role: CodingMessageRole; content: string }>;\n tools?: unknown[];\n}\n\n/** What the browser POSTs back to /agent/llm-response/:requestId. The\n * broker resolves the adapter's pending Promise with this object. */\nexport interface LlmBrokerResponsePayload {\n text: string;\n provider: string;\n model: string;\n tokenUsage?: { input: number; output: number };\n /** Set when the cloud-side flow refuses or fails. Adapter sees an\n * Error with `message`. */\n error?: string;\n /** Tool calls emitted by the model. Shape mirrors OpenAI Chat\n * Completions so the helper's OpenAI-compat shim can pass them\n * through verbatim. Cloud-side, an Anthropic tool_use block gets\n * translated into this shape before the response is returned. */\n toolCalls?: Array<LlmBrokerToolCall>;\n /** OpenAI-style finish reason. Defaults to \"stop\". The shim uses\n * this to populate `choices[0].finish_reason` in its response. */\n finishReason?: \"stop\" | \"tool_calls\" | \"length\" | \"content_filter\";\n /** Raw provider API cost in USD. Set by the cloud after the\n * usage_ledger row is written; passed through unchanged by the\n * helper. UI uses this to show \"$0.012\" alongside the token count. */\n rawApiCostUsd?: number;\n /** Diologue-internal tokens charged for this call (after markup +\n * safety factor). The user's balance is debited by this many. */\n diologueTokens?: number;\n}\n\nexport interface LlmBrokerToolCall {\n id: string;\n type: \"function\";\n function: {\n name: string;\n /** JSON-encoded arguments, per OpenAI's wire format. */\n arguments: string;\n };\n}\n\n/** Incremental update emitted by the streaming brokered-LLM path.\n * Cloud emits these as SSE events on /api/coding/sessions/:id/llm-proxy-stream;\n * the browser forwards them to the helper at /agent/llm-chunk/:requestId. */\nexport type LlmStreamChunk =\n | { type: \"text_delta\"; text: string }\n | {\n type: \"tool_call_delta\";\n /** OpenAI streams tool-call args incrementally. We carry the\n * full id+name once, then partial arguments deltas. */\n index: number;\n id?: string;\n name?: string;\n argumentsDelta?: string;\n }\n | {\n type: \"complete\";\n /** Mirrors LlmBrokerResponsePayload \u2014 the full final payload. */\n payload: LlmBrokerResponsePayload;\n }\n | { type: \"error\"; message: string };\n\nexport interface AgentDone {\n type: \"done\";\n /** Persisted coding_messages.id for the assistant turn, if the helper\n * has reported it (browser may also create it client-side and patch). */\n messageId?: string;\n}\n\nexport interface AgentError {\n type: \"error\";\n message: string;\n /** Set when the error is one the UI can resolve by re-pairing or\n * re-selecting the repo. */\n recoverable?: boolean;\n}\n\n// ----------------------------------------------------------------------------\n// Apply changes\n// ----------------------------------------------------------------------------\n\nexport interface ApplyPatchRequest {\n /** Unified diff to apply. The helper validates with `git apply --check`\n * before writing anything. */\n unified: string;\n /** Reserved for a future \"the on-disk content hasn't drifted since the\n * diff was proposed\" check. Optional in v1 \u2014 git apply --check is\n * the only defense today. */\n baselineHash?: string;\n /** Target this session's worktree (where the agent's changes live). */\n sessionId?: string;\n}\n\nexport interface ApplyPatchResponse {\n ok: true;\n /** Number of files whose contents changed on disk. Parsed from the\n * helper's git apply output. */\n filesChanged: number;\n /** Repo-relative paths that were modified. Convenience for the UI so\n * it can render \"Updated 3 files: a.ts, b.ts, c.ts\". */\n paths: string[];\n}\n\nexport interface ApplyPatchErrorResponse {\n error:\n | \"no_repo_selected\"\n | \"invalid_body\"\n | \"diff_does_not_apply\"\n | \"git_command_failed\";\n message: string;\n /** When error is \"diff_does_not_apply\", stderr from `git apply --check`\n * is surfaced so the user can see why. */\n stderr?: string;\n}\n\n// ----------------------------------------------------------------------------\n// Revert changes\n// ----------------------------------------------------------------------------\n//\n// The real coding engine (opencode) edits files in the working tree DURING\n// the turn \u2014 by the time the user sees the diff, those changes are already\n// on disk. So the UI's primary \"undo\" action is a reverse-apply, not an\n// apply. The helper runs `git apply --reverse --check` before touching the\n// tree, mirroring the apply two-phase guard.\n\nexport interface RevertPatchRequest {\n /** The same unified diff the agent produced. Reverse-applying it against\n * the current working tree restores the pre-turn state (deleting added\n * files, recreating deleted ones, undoing modifications). */\n unified: string;\n /** Target this session's worktree (where the agent's changes live). */\n sessionId?: string;\n}\n\nexport interface RevertPatchResponse {\n ok: true;\n /** Number of files restored to their pre-turn state. */\n filesChanged: number;\n /** Repo-relative paths that were reverted. */\n paths: string[];\n}\n\nexport interface RevertPatchErrorResponse {\n error:\n | \"no_repo_selected\"\n | \"invalid_body\"\n | \"diff_does_not_revert\"\n | \"git_command_failed\";\n message: string;\n /** When error is \"diff_does_not_revert\", stderr from\n * `git apply --reverse --check` is surfaced so the user can see why. */\n stderr?: string;\n}\n", "// Single-origin CORS. We deliberately do NOT use the `cors` npm package \u2014\n// the policy is so narrow (one origin, one header) that hand-rolling it is\n// less ambiguous than reading docs to confirm flag combinations.\n//\n// The browser running on the user's machine is the only legitimate caller.\n// `Origin` mismatches are dropped silently at the preflight step (no CORS\n// headers returned \u2192 browser refuses the call), which is enough \u2014 we do NOT\n// rely on CORS for security, only as a defence-in-depth measure on top of\n// the token middleware.\n\nimport type { NextFunction, Request, Response } from \"express\";\n\nimport { LOCAL_AGENT_TOKEN_HEADER } from \"../../shared/coding-agent-types\";\n\nexport interface CorsOptions {\n allowedOrigin: string;\n}\n\nexport const createCorsMiddleware = (options: CorsOptions) => {\n const allowed = options.allowedOrigin;\n return (req: Request, res: Response, next: NextFunction): void => {\n const origin = req.header(\"origin\");\n if (origin && origin === allowed) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n res.setHeader(\"Access-Control-Allow-Credentials\", \"false\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET,POST,OPTIONS\");\n res.setHeader(\n \"Access-Control-Allow-Headers\",\n `Content-Type, ${LOCAL_AGENT_TOKEN_HEADER}`,\n );\n res.setHeader(\"Access-Control-Max-Age\", \"300\");\n // Private Network Access (Chrome): a page on a public/HTTPS origin\n // fetching a loopback address (this helper) triggers a PNA preflight\n // carrying `Access-Control-Request-Private-Network: true`. We must\n // echo the allow header or Chrome blocks the request outright.\n //\n // NOTE: Chrome's newer permission-based \"Local Network Access\" gate\n // ALSO requires a one-time user grant for the site \u2014 this header is\n // necessary but, on the latest Chrome, not by itself sufficient. See\n // the loopback-access note returned to the operator.\n if (\n req.header(\"access-control-request-private-network\") === \"true\"\n ) {\n res.setHeader(\"Access-Control-Allow-Private-Network\", \"true\");\n }\n }\n if (req.method === \"OPTIONS\") {\n // Preflight. If origin matched, headers above are set; if not, the\n // response carries no CORS headers and the browser rejects it.\n res.status(204).end();\n return;\n }\n next();\n };\n};\n", "// In-memory state for the helper process.\n//\n// MVP: a single selected repo per helper process. On restart the user must\n// re-select (which is consistent with the pairing token also being lost).\n//\n// We expose a small factory rather than module-level globals so tests can\n// spin up isolated state without leaking across cases.\n\nimport type { RepoStatus } from \"../../shared/coding-agent-types\";\n\nexport interface AgentState {\n getSelectedRepo(): RepoStatus | null;\n setSelectedRepo(repo: RepoStatus): void;\n clearSelectedRepo(): void;\n}\n\nexport const createState = (): AgentState => {\n let selected: RepoStatus | null = null;\n return {\n getSelectedRepo: () => selected,\n setSelectedRepo: (repo) => {\n selected = repo;\n },\n clearSelectedRepo: () => {\n selected = null;\n },\n };\n};\n", "// Per-session git worktrees.\n//\n// Each coding session works in its own isolated worktree on a `diologue/<id>`\n// branch, so the agent never touches the user's checkout (current branch or\n// uncommitted changes). The worktree is created lazily on the session's first\n// turn and lives under ~/.diologue/worktrees/.\n//\n// The worktree branches off the selected repo's current HEAD \u2014 so it's a clean\n// copy of HEAD, not the user's uncommitted working state. That's the isolation\n// trade-off (and usually what you want for \"let the agent go work on a task\").\n\nimport { existsSync } from \"node:fs\";\nimport { mkdir, rm } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\n\nimport {\n addWorktree,\n addWorktreeForBranch,\n branchExists,\n pruneWorktrees,\n removeWorktree,\n} from \"./lib/git\";\n\nexport interface WorktreeInfo {\n /** Absolute path of the isolated worktree the agent operates in. */\n path: string;\n /** The branch checked out in the worktree (diologue/<id>). */\n branch: string;\n /** The branch this worktree was created from \u2014 the PR base. */\n base: string;\n /** The main repo this worktree belongs to. */\n repoPath: string;\n}\n\nexport interface WorktreeManager {\n /** Get (or lazily create) the worktree for a session. Idempotent. */\n ensure(\n sessionId: string,\n repoPath: string,\n baseBranch: string,\n ): Promise<WorktreeInfo>;\n /** The session's worktree, or null if it has none yet. */\n get(sessionId: string): WorktreeInfo | null;\n /** Remove a session's worktree (keeps the branch \u2014 it holds the work). */\n remove(sessionId: string): Promise<void>;\n /** Remove every managed worktree. Called on helper shutdown. */\n cleanupAll(): Promise<void>;\n}\n\nconst sanitize = (s: string): string =>\n s.replace(/[^a-zA-Z0-9_-]/g, \"-\").replace(/^-+|-+$/g, \"\").slice(0, 50) ||\n \"session\";\n\nexport interface WorktreeManagerOptions {\n /** Base directory for worktrees. Defaults to ~/.diologue/worktrees. */\n baseDir?: string;\n}\n\nexport const createWorktreeManager = (\n options: WorktreeManagerOptions = {},\n): WorktreeManager => {\n const baseDir =\n options.baseDir ?? path.join(homedir(), \".diologue\", \"worktrees\");\n const map = new Map<string, WorktreeInfo>();\n\n const ensure = async (\n sessionId: string,\n repoPath: string,\n baseBranch: string,\n ): Promise<WorktreeInfo> => {\n const existing = map.get(sessionId);\n if (existing && existsSync(existing.path)) return existing;\n\n const safe = sanitize(sessionId);\n const branch = `diologue/${safe}`;\n const worktreePath = path.join(baseDir, safe);\n\n await mkdir(baseDir, { recursive: true });\n await pruneWorktrees(repoPath).catch(() => undefined);\n\n // Clear any stale dir/registration left by a previous run.\n if (existsSync(worktreePath)) {\n await removeWorktree(repoPath, worktreePath).catch(() => undefined);\n await rm(worktreePath, { recursive: true, force: true }).catch(\n () => undefined,\n );\n await pruneWorktrees(repoPath).catch(() => undefined);\n }\n\n // Reuse the branch across restarts (it carries the work); otherwise make it.\n if (await branchExists(repoPath, branch)) {\n await addWorktreeForBranch(repoPath, worktreePath, branch);\n } else {\n await addWorktree(repoPath, worktreePath, branch);\n }\n\n const info: WorktreeInfo = {\n path: worktreePath,\n branch,\n base: baseBranch,\n repoPath,\n };\n map.set(sessionId, info);\n return info;\n };\n\n const get = (sessionId: string): WorktreeInfo | null =>\n map.get(sessionId) ?? null;\n\n const remove = async (sessionId: string): Promise<void> => {\n const info = map.get(sessionId);\n if (!info) return;\n map.delete(sessionId);\n await removeWorktree(info.repoPath, info.path).catch(() => undefined);\n await rm(info.path, { recursive: true, force: true }).catch(\n () => undefined,\n );\n };\n\n const cleanupAll = async (): Promise<void> => {\n for (const id of [...map.keys()]) await remove(id);\n };\n\n return { ensure, get, remove, cleanupAll };\n};\n", "// Git command wrappers + status/diff parsing.\n//\n// Everything goes through execFile (NOT exec / shell). The cwd has already\n// been validated by validateRepoPath. We never accept a shell snippet, never\n// pass user input as a flag, and never interpolate user text into argv.\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport type { ChangedFile, GitStatusCode } from \"../../../shared/coding-agent-types\";\n\nconst execFileAsync = promisify(execFile);\n\nexport class GitCommandError extends Error {\n readonly stderr: string;\n readonly exitCode: number;\n constructor(args: readonly string[], exitCode: number, stderr: string) {\n super(`git ${args.join(\" \")} failed with exit ${exitCode}: ${stderr.trim()}`);\n this.name = \"GitCommandError\";\n this.stderr = stderr;\n this.exitCode = exitCode;\n }\n}\n\n/** Run a git subcommand. `args` are passed argv-style \u2014 no shell. */\nexport const runGit = async (cwd: string, args: string[]): Promise<string> => {\n try {\n const { stdout } = await execFileAsync(\"git\", args, {\n cwd,\n // Generous-ish buffer for diffs of medium-sized changes. We surface\n // a sizeBytes field so the UI can refuse to render extreme cases.\n maxBuffer: 16 * 1024 * 1024,\n // Inherit a stripped env \u2014 we don't want PAGER or GIT_* tracing.\n env: { ...process.env, GIT_PAGER: \"cat\", PAGER: \"cat\" },\n });\n return stdout;\n } catch (err: unknown) {\n if (err && typeof err === \"object\" && \"code\" in err) {\n const e = err as { code?: number; stderr?: string };\n throw new GitCommandError(args, e.code ?? -1, e.stderr ?? \"\");\n }\n throw err;\n }\n};\n\nexport const getBranch = async (cwd: string): Promise<string | null> => {\n try {\n const out = (await runGit(cwd, [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"])).trim();\n // Detached HEAD reports \"HEAD\" \u2014 surface as null so the UI can render\n // \"(detached)\" without us hard-coding that here.\n if (out === \"HEAD\") {\n return null;\n }\n return out;\n } catch {\n // Empty repo (no commits yet) \u2014 return null rather than blow up.\n return null;\n }\n};\n\nexport const getHeadShort = async (cwd: string): Promise<string | null> => {\n try {\n return (await runGit(cwd, [\"rev-parse\", \"--short\", \"HEAD\"])).trim();\n } catch {\n return null;\n }\n};\n\nexport const isDirty = async (cwd: string): Promise<boolean> => {\n const out = await runGit(cwd, [\"status\", \"--porcelain\"]);\n return out.trim().length > 0;\n};\n\n/** Parse `git status --short` output into structured rows.\n *\n * Format (per `git status` man page):\n * XY <path>\n * XY <orig_path> -> <path> (renames/copies)\n *\n * X = index status, Y = work-tree status.\n * Untracked files appear as \"?? <path>\".\n *\n * We DO NOT shell out from here \u2014 this function takes the already-captured\n * stdout so it's easy to unit-test with hand-crafted strings.\n */\nexport const parseShortStatus = (output: string): ChangedFile[] => {\n const lines = output.split(\"\\n\");\n const files: ChangedFile[] = [];\n for (const line of lines) {\n // Status lines are at least 4 chars: XY<space><path>\n if (line.length < 4) {\n continue;\n }\n const indexChar = line[0];\n const workChar = line[1];\n if (indexChar === undefined || workChar === undefined) {\n continue;\n }\n // Skip the separator at col 2 and the path follows. Handle rename\n // form by taking the post-arrow path.\n let pathPart = line.slice(3);\n const arrow = pathPart.indexOf(\" -> \");\n if (arrow !== -1) {\n pathPart = pathPart.slice(arrow + 4);\n }\n // Strip surrounding quotes git uses for paths containing special chars\n // (when `core.quotepath` is on). We don't try to fully unescape; the UI\n // will surface a sensible path either way and exact bytes aren't used\n // for any filesystem operation from here.\n if (\n pathPart.startsWith('\"') &&\n pathPart.endsWith('\"') &&\n pathPart.length >= 2\n ) {\n pathPart = pathPart.slice(1, -1);\n }\n files.push({\n path: pathPart,\n index: asGitStatusCode(indexChar),\n workTree: asGitStatusCode(workChar),\n untracked: indexChar === \"?\" && workChar === \"?\",\n });\n }\n return files;\n};\n\nconst VALID_STATUS_CODES = new Set([\" \", \"M\", \"A\", \"D\", \"R\", \"C\", \"U\", \"?\", \"!\"]);\n\nconst asGitStatusCode = (ch: string): GitStatusCode => {\n if (VALID_STATUS_CODES.has(ch)) {\n return ch as GitStatusCode;\n }\n // Unknown / typechange / etc. \u2192 treat as modified for display purposes.\n return \"M\";\n};\n\nexport const getStatusShort = async (cwd: string): Promise<ChangedFile[]> => {\n // -uall surfaces individual untracked files (otherwise dirs collapse).\n const out = await runGit(cwd, [\"status\", \"--short\", \"-uall\"]);\n return parseShortStatus(out);\n};\n\nconst runGitAllowingExit = (\n cwd: string,\n args: string[],\n allowedExitCodes: ReadonlySet<number>,\n): Promise<string> =>\n new Promise<string>((resolve, reject) => {\n execFile(\n \"git\",\n args,\n {\n cwd,\n maxBuffer: 16 * 1024 * 1024,\n env: { ...process.env, GIT_PAGER: \"cat\", PAGER: \"cat\" },\n },\n (err, stdout, stderr) => {\n if (!err) {\n resolve(stdout);\n return;\n }\n const e = err as NodeJS.ErrnoException & { code?: number };\n const exitCode = e.code ?? -1;\n if (allowedExitCodes.has(exitCode)) {\n resolve(stdout);\n return;\n }\n reject(new GitCommandError(args, exitCode, stderr));\n },\n );\n });\n\nconst getUntrackedFiles = async (cwd: string): Promise<string[]> => {\n const out = await runGit(cwd, [\n \"ls-files\",\n \"--others\",\n \"--exclude-standard\",\n \"-z\",\n ]);\n return out.split(\"\\0\").filter(Boolean);\n};\n\nconst getUntrackedFileDiff = async (\n cwd: string,\n file: string,\n): Promise<string> => {\n // `git diff --no-index` returns 1 when the two inputs differ, which is\n // success for our purpose of rendering a full-file addition.\n const diff = await runGitAllowingExit(\n cwd,\n [\"diff\", \"--no-index\", \"--\", \"/dev/null\", file],\n new Set([1]),\n );\n return diff\n .replace(/^diff --git a\\/dev\\/null b\\/(.+)$/m, \"diff --git a/$1 b/$1\")\n .replace(/^--- \\/dev\\/null$/m, \"--- /dev/null\");\n};\n\n/** Concatenated diff covering staged + unstaged tracked changes against\n * HEAD. Untracked files are appended as full-file additions so a newly\n * created page shows up in the review panel before it is staged. */\nexport const getDiff = async (cwd: string): Promise<string> => {\n const [trackedDiff, untrackedFiles] = await Promise.all([\n runGit(cwd, [\"diff\", \"HEAD\"]),\n getUntrackedFiles(cwd),\n ]);\n const untrackedDiffs = await Promise.all(\n untrackedFiles.map((file) => getUntrackedFileDiff(cwd, file)),\n );\n return [trackedDiff, ...untrackedDiffs]\n .filter((part) => part.trim().length > 0)\n .join(\"\\n\");\n};\n\n/** Run a git command, but write `input` to stdin. Used by `git apply` so\n * we can stream a unified diff in without writing it to a temp file. */\nconst runGitWithStdin = async (\n cwd: string,\n args: string[],\n input: string,\n): Promise<string> => {\n const { execFile } = await import(\"node:child_process\");\n return new Promise<string>((resolve, reject) => {\n const child = execFile(\n \"git\",\n args,\n {\n cwd,\n maxBuffer: 16 * 1024 * 1024,\n env: { ...process.env, GIT_PAGER: \"cat\", PAGER: \"cat\" },\n },\n (err, stdout, stderr) => {\n if (err) {\n const e = err as NodeJS.ErrnoException & { code?: number };\n reject(new GitCommandError(args, e.code ?? -1, stderr));\n return;\n }\n resolve(stdout);\n },\n );\n child.stdin?.write(input);\n child.stdin?.end();\n });\n};\n\n/** Returns true iff `git apply --check` accepts the diff (i.e. it would\n * apply cleanly against the current working tree). */\nexport const canApplyDiff = async (\n cwd: string,\n unified: string,\n): Promise<{ ok: true } | { ok: false; stderr: string }> => {\n try {\n await runGitWithStdin(cwd, [\"apply\", \"--check\"], unified);\n return { ok: true };\n } catch (err) {\n if (err instanceof GitCommandError) {\n return { ok: false, stderr: err.stderr };\n }\n throw err;\n }\n};\n\n/** Apply a unified diff to the working tree. Does NOT stage the changes \u2014\n * the user reviews the resulting working-tree state and commits via\n * their normal git workflow. */\nexport const applyDiff = async (\n cwd: string,\n unified: string,\n): Promise<void> => {\n await runGitWithStdin(cwd, [\"apply\"], unified);\n};\n\n/** Returns true iff `git apply --reverse --check` accepts the diff (i.e. the\n * working tree currently CONTAINS these changes and they can be cleanly\n * undone). Used by the revert flow: the coding engine already wrote the\n * changes to disk, so undoing them means reverse-applying the same diff. */\nexport const canRevertDiff = async (\n cwd: string,\n unified: string,\n): Promise<{ ok: true } | { ok: false; stderr: string }> => {\n try {\n await runGitWithStdin(cwd, [\"apply\", \"--reverse\", \"--check\"], unified);\n return { ok: true };\n } catch (err) {\n if (err instanceof GitCommandError) {\n return { ok: false, stderr: err.stderr };\n }\n throw err;\n }\n};\n\n/** Reverse-apply a unified diff, restoring the working tree to its state\n * before the diff's changes were written. Symmetric with applyDiff; does\n * NOT touch the index. */\nexport const revertDiff = async (\n cwd: string,\n unified: string,\n): Promise<void> => {\n await runGitWithStdin(cwd, [\"apply\", \"--reverse\"], unified);\n};\n\n/** Parse the per-file headers out of a unified diff. Returns repo-relative\n * paths in the order they appear. Used by the apply route to report\n * `paths` in the response without depending on git for it. */\nexport const parseDiffPaths = (unified: string): string[] => {\n const paths: string[] = [];\n for (const line of unified.split(\"\\n\")) {\n // `diff --git a/foo b/foo` \u2014 prefer the post-image path.\n const match = /^diff --git a\\/(.+) b\\/(.+)$/.exec(line);\n if (match) {\n paths.push(match[2]);\n }\n }\n return paths;\n};\n\n// ----------------------------------------------------------------------------\n// Branch / commit / push \u2014 used by the \"Create PR\" flow.\n// ----------------------------------------------------------------------------\n\n/** `git remote get-url origin`, or null when there's no `origin` remote. */\nexport const getRemoteUrl = async (cwd: string): Promise<string | null> => {\n try {\n return (await runGit(cwd, [\"remote\", \"get-url\", \"origin\"])).trim() || null;\n } catch (err) {\n if (err instanceof GitCommandError) return null;\n throw err;\n }\n};\n\n/** Best-effort default branch name (what a PR should target) \u2014 reads\n * `origin/HEAD`, falling back to \"main\" when it isn't configured. */\nexport const getDefaultBranch = async (cwd: string): Promise<string> => {\n try {\n const ref = (\n await runGit(cwd, [\"rev-parse\", \"--abbrev-ref\", \"origin/HEAD\"])\n ).trim();\n // \"origin/main\" \u2192 \"main\"\n const name = ref.replace(/^origin\\//, \"\");\n return name || \"main\";\n } catch (err) {\n if (err instanceof GitCommandError) return \"main\";\n throw err;\n }\n};\n\n/** Create + switch to a new branch from the current HEAD, carrying any\n * uncommitted working-tree changes onto it. */\nexport const createBranch = async (\n cwd: string,\n name: string,\n): Promise<void> => {\n await runGit(cwd, [\"checkout\", \"-b\", name]);\n};\n\n/** Stage everything and commit. Returns the new commit's short hash. */\nexport const commitAll = async (\n cwd: string,\n message: string,\n): Promise<string> => {\n await runGit(cwd, [\"add\", \"-A\"]);\n // -m twice would split into subject/body; we keep it to a single message.\n await runGit(cwd, [\"commit\", \"-m\", message]);\n return (await runGit(cwd, [\"rev-parse\", \"--short\", \"HEAD\"])).trim();\n};\n\n/** Push a branch to origin, setting upstream. */\nexport const pushBranch = async (\n cwd: string,\n branch: string,\n): Promise<void> => {\n await runGit(cwd, [\"push\", \"-u\", \"origin\", branch]);\n};\n\n/** Slugify a PR title into a safe `diologue/<slug>-<rand>` branch name. */\nexport const branchNameForTitle = (title: string): string => {\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 40)\n .replace(/-+$/g, \"\");\n const rand = Math.random().toString(36).slice(2, 7);\n return `diologue/${slug || \"change\"}-${rand}`;\n};\n\n// ----------------------------------------------------------------------------\n// Worktrees \u2014 per-session isolation. All commands run from the MAIN repo; the\n// worktree dir can live anywhere (git stores a link file pointing back).\n// ----------------------------------------------------------------------------\n\n/** True when a local branch already exists. */\nexport const branchExists = async (\n repoPath: string,\n branch: string,\n): Promise<boolean> => {\n try {\n await runGit(repoPath, [\n \"rev-parse\",\n \"--verify\",\n \"--quiet\",\n `refs/heads/${branch}`,\n ]);\n return true;\n } catch (err) {\n if (err instanceof GitCommandError) return false;\n throw err;\n }\n};\n\n/** Create a worktree at `worktreePath` on a NEW branch off the main repo's\n * current HEAD. */\nexport const addWorktree = async (\n repoPath: string,\n worktreePath: string,\n branch: string,\n): Promise<void> => {\n await runGit(repoPath, [\"worktree\", \"add\", \"-b\", branch, worktreePath, \"HEAD\"]);\n};\n\n/** Create a worktree at `worktreePath` checking out an EXISTING branch (used\n * when the helper restarted but the branch survives). */\nexport const addWorktreeForBranch = async (\n repoPath: string,\n worktreePath: string,\n branch: string,\n): Promise<void> => {\n await runGit(repoPath, [\"worktree\", \"add\", worktreePath, branch]);\n};\n\n/** Remove a worktree (force, since it may hold uncommitted agent work). */\nexport const removeWorktree = async (\n repoPath: string,\n worktreePath: string,\n): Promise<void> => {\n await runGit(repoPath, [\"worktree\", \"remove\", \"--force\", worktreePath]);\n};\n\n/** Tidy worktree bookkeeping for dirs that no longer exist. */\nexport const pruneWorktrees = async (repoPath: string): Promise<void> => {\n await runGit(repoPath, [\"worktree\", \"prune\"]);\n};\n", "// GET /health \u2014 unauthenticated, intentionally minimal. The UI uses this to\n// prove the helper is reachable BEFORE the user pastes a pairing token.\n// It echoes no secrets and the same payload is safe to log.\n\nimport type { Request, Response } from \"express\";\n\nimport type { LocalAgentConfig } from \"../config\";\nimport type { LocalAgentHealth } from \"../../../shared/coding-agent-types\";\n\nexport const createHealthHandler = (config: LocalAgentConfig) => {\n return (_req: Request, res: Response): void => {\n const body: LocalAgentHealth = {\n ok: true,\n helperVersion: config.helperVersion,\n boundHost: config.host,\n port: config.port,\n startedAt: config.startedAt,\n };\n res.json(body);\n };\n};\n", "// /repo/* \u2014 repo selection, status, diff, changed files.\n//\n// Every handler requires a selected repo (with the obvious exception of\n// /repo/select itself). When none has been selected the response is a 409\n// with a stable `error: \"no_repo_selected\"` code so the UI can render a\n// friendly empty state without parsing error messages.\n\nimport type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nimport type { AgentState } from \"../state\";\nimport type { WorktreeManager } from \"../worktree\";\nimport {\n applyDiff,\n branchNameForTitle,\n canApplyDiff,\n canRevertDiff,\n commitAll,\n createBranch,\n getBranch,\n getDefaultBranch,\n getDiff,\n getHeadShort,\n getRemoteUrl,\n getStatusShort,\n isDirty,\n parseDiffPaths,\n pushBranch,\n revertDiff,\n GitCommandError,\n} from \"../lib/git\";\nimport { validateRepoPath, InvalidRepoPathError } from \"../lib/paths\";\nimport { pickDirectory } from \"../lib/pick-directory\";\nimport { createPullRequest, ghReady, GhError } from \"../lib/github\";\nimport type {\n ApplyPatchResponse,\n BrowseDirectoryResponse,\n ChangedFilesResponse,\n CreatePrResponse,\n RepoDiffResponse,\n RepoStatus,\n RepoStatusResponse,\n RevertPatchResponse,\n SelectRepoResponse,\n} from \"../../../shared/coding-agent-types\";\n\nconst selectRepoSchema = z.object({\n path: z.string().min(1),\n});\n\nconst applyPatchSchema = z.object({\n unified: z.string().min(1).max(8 * 1024 * 1024),\n baselineHash: z.string().optional(),\n sessionId: z.string().min(1).optional(),\n});\n\nconst revertPatchSchema = z.object({\n unified: z.string().min(1).max(8 * 1024 * 1024),\n sessionId: z.string().min(1).optional(),\n});\n\nconst createPrSchema = z.object({\n title: z.string().min(1).max(200),\n body: z.string().max(20_000).optional(),\n /** When set, the PR is created from this session's worktree branch. */\n sessionId: z.string().min(1).optional(),\n});\n\nconst buildRepoStatus = async (resolvedPath: string): Promise<RepoStatus> => {\n const [branch, head, dirty] = await Promise.all([\n getBranch(resolvedPath),\n getHeadShort(resolvedPath),\n isDirty(resolvedPath),\n ]);\n return {\n path: resolvedPath,\n name: path.basename(resolvedPath),\n branch,\n head,\n isDirty: dirty,\n };\n};\n\nconst sendInvalidRepoPath = (\n res: Response,\n err: InvalidRepoPathError,\n): void => {\n res.status(400).json({ error: err.code, message: err.message });\n};\n\nconst sendGitError = (res: Response, err: GitCommandError): void => {\n res.status(500).json({\n error: \"git_command_failed\",\n message: err.message,\n exitCode: err.exitCode,\n });\n};\n\nconst requireSelectedRepo = (\n state: AgentState,\n res: Response,\n): RepoStatus | null => {\n const repo = state.getSelectedRepo();\n if (!repo) {\n res.status(409).json({ error: \"no_repo_selected\" });\n return null;\n }\n return repo;\n};\n\n/** Read the optional `sessionId` query/body param. */\nconst readSessionId = (value: unknown): string | undefined =>\n typeof value === \"string\" && value ? value : undefined;\n\nexport const createRepoRouter = (\n state: AgentState,\n worktrees: WorktreeManager,\n): Router => {\n const router = createRouter();\n\n router.post(\"/select\", async (req: Request, res: Response) => {\n const parsed = selectRepoSchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n try {\n const resolved = await validateRepoPath(parsed.data.path);\n const status = await buildRepoStatus(resolved);\n state.setSelectedRepo(status);\n const body: SelectRepoResponse = { ok: true, repo: status };\n res.json(body);\n } catch (err) {\n if (err instanceof InvalidRepoPathError) {\n sendInvalidRepoPath(res, err);\n return;\n }\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n // Pop the OS folder dialog on the user's machine and return the chosen path\n // (the browser can't read absolute paths itself). Does NOT select the repo \u2014\n // the UI fills the path input and the user confirms with /repo/select.\n router.post(\"/browse\", async (_req: Request, res: Response) => {\n const result = await pickDirectory();\n if (result.ok) {\n const body: BrowseDirectoryResponse = { path: result.path };\n res.json(body);\n return;\n }\n if (result.reason === \"cancelled\") {\n const body: BrowseDirectoryResponse = { cancelled: true };\n res.json(body);\n return;\n }\n // no_gui / unsupported / failed \u2014 the UI falls back to manual typing.\n res.status(501).json({\n error: result.reason,\n message:\n \"No desktop folder dialog is available on this machine. Type the repo path instead.\",\n });\n });\n\n // Branch the working-tree changes, commit, push, and open a PR via local gh.\n // This is the foundation the worktree-isolation + cloud-GitHub phases build\n // on; for now it works in place (creating a diologue/<slug> branch off the\n // current HEAD) and relies on the user's existing gh auth + git remote.\n router.post(\"/create-pr\", async (req: Request, res: Response) => {\n const repo = requireSelectedRepo(state, res);\n if (!repo) return;\n\n const parsed = createPrSchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n const { title } = parsed.data;\n const body =\n parsed.data.body ?? \"Opened from the Diologue coding agent.\";\n\n // Prefer the session's isolated worktree (already on a diologue/* branch).\n // Falls back to in-place branching for callers without a worktree.\n const worktree = parsed.data.sessionId\n ? worktrees.get(parsed.data.sessionId)\n : null;\n const cwd = worktree ? worktree.path : repo.path;\n\n try {\n if (!(await ghReady())) {\n res.status(501).json({\n error: \"gh_unavailable\",\n message:\n \"GitHub CLI (gh) isn't installed or authenticated on this machine. Run `gh auth login`, or push the branch and open the PR manually.\",\n });\n return;\n }\n if (!(await getRemoteUrl(cwd))) {\n res.status(400).json({\n error: \"no_remote\",\n message:\n \"This repo has no `origin` remote. Add one (git remote add origin \u2026) before creating a PR.\",\n });\n return;\n }\n if (!(await isDirty(cwd))) {\n res.status(409).json({\n error: \"nothing_to_commit\",\n message: \"There are no changes in the working tree to open a PR for.\",\n });\n return;\n }\n\n let branch: string;\n let base: string;\n if (worktree) {\n // The worktree is already on its branch \u2014 just commit + push + PR it.\n branch = worktree.branch;\n base = worktree.base;\n } else {\n // In-place: branch off the current HEAD. Target the current branch\n // unless we're already on a diologue/* branch (repeat click).\n const current = (await getBranch(cwd)) ?? \"\";\n const onAgentBranch = current.startsWith(\"diologue/\");\n base = onAgentBranch ? await getDefaultBranch(cwd) : current || \"main\";\n branch = onAgentBranch ? current : branchNameForTitle(title);\n if (!onAgentBranch) await createBranch(cwd, branch);\n }\n await commitAll(cwd, title);\n await pushBranch(cwd, branch);\n const url = await createPullRequest({ cwd, base, head: branch, title, body });\n\n const payload: CreatePrResponse = { url, branch, base };\n res.json(payload);\n } catch (err) {\n if (err instanceof GhError) {\n res.status(502).json({ error: err.code, message: err.message });\n return;\n }\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n router.get(\"/status\", async (_req: Request, res: Response) => {\n const repo = state.getSelectedRepo();\n if (!repo) {\n const body: RepoStatusResponse = { repo: null };\n res.json(body);\n return;\n }\n // Re-derive fresh status so the UI reflects current branch/dirty state\n // without making the user re-select.\n try {\n const refreshed = await buildRepoStatus(repo.path);\n state.setSelectedRepo(refreshed);\n const body: RepoStatusResponse = { repo: refreshed };\n res.json(body);\n } catch (err) {\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n // The diff/changed-files reflect the SESSION'S worktree (the agent's work),\n // not the user's checkout. With a sessionId that has no worktree yet (no\n // turns), they're empty; without a sessionId they fall back to the selected\n // repo for the legacy/no-session view.\n const resolveWorkdir = (\n repo: RepoStatus,\n sessionId: string | undefined,\n ): string | null => {\n if (!sessionId) return repo.path;\n const wt = worktrees.get(sessionId);\n return wt ? wt.path : null; // null = no work yet \u2192 empty result\n };\n\n router.get(\"/diff\", async (req: Request, res: Response) => {\n const repo = requireSelectedRepo(state, res);\n if (!repo) {\n return;\n }\n const workdir = resolveWorkdir(repo, readSessionId(req.query.sessionId));\n if (!workdir) {\n res.json({ unified: \"\", sizeBytes: 0 } satisfies RepoDiffResponse);\n return;\n }\n try {\n const unified = await getDiff(workdir);\n const body: RepoDiffResponse = {\n unified,\n sizeBytes: Buffer.byteLength(unified, \"utf8\"),\n };\n res.json(body);\n } catch (err) {\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n router.get(\"/changed-files\", async (req: Request, res: Response) => {\n const repo = requireSelectedRepo(state, res);\n if (!repo) {\n return;\n }\n const workdir = resolveWorkdir(repo, readSessionId(req.query.sessionId));\n if (!workdir) {\n res.json({ files: [] } satisfies ChangedFilesResponse);\n return;\n }\n try {\n const files = await getStatusShort(workdir);\n const body: ChangedFilesResponse = { files };\n res.json(body);\n } catch (err) {\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n // POST /repo/apply \u2014 apply a unified diff to the selected repo.\n //\n // Two-phase: `git apply --check` first; only if that succeeds do we run\n // the real `git apply`. The check phase is a no-op on the filesystem,\n // so a failing check leaves the working tree unchanged. We surface\n // stderr from the check so the user can see why a diff didn't apply.\n router.post(\"/apply\", async (req: Request, res: Response) => {\n const repo = requireSelectedRepo(state, res);\n if (!repo) return;\n\n const parsed = applyPatchSchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n\n // Target the session's worktree when there is one (that's where the\n // agent's changes live); else the selected repo.\n const applyWorkdir =\n (parsed.data.sessionId &&\n worktrees.get(parsed.data.sessionId)?.path) ||\n repo.path;\n\n try {\n const check = await canApplyDiff(applyWorkdir, parsed.data.unified);\n if (!check.ok) {\n res.status(409).json({\n error: \"diff_does_not_apply\",\n message:\n \"Diff doesn't apply against the current working tree. The repo \" +\n \"may have changed since the diff was proposed.\",\n stderr: check.stderr,\n });\n return;\n }\n await applyDiff(applyWorkdir, parsed.data.unified);\n const paths = parseDiffPaths(parsed.data.unified);\n const body: ApplyPatchResponse = {\n ok: true,\n filesChanged: paths.length,\n paths,\n };\n res.json(body);\n } catch (err) {\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n // POST /repo/revert \u2014 undo changes the coding engine already wrote.\n //\n // opencode edits the working tree directly during a turn, so \"undo\" is a\n // reverse-apply of the same diff, not an apply. Same two-phase guard as\n // /apply: `git apply --reverse --check` first; only on success do we run\n // the real reverse-apply. A failing check (e.g. the tree drifted, or the\n // changes were already reverted) leaves the working tree untouched and\n // returns 409 with stderr.\n router.post(\"/revert\", async (req: Request, res: Response) => {\n const repo = requireSelectedRepo(state, res);\n if (!repo) return;\n\n const parsed = revertPatchSchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n\n const revertWorkdir =\n (parsed.data.sessionId &&\n worktrees.get(parsed.data.sessionId)?.path) ||\n repo.path;\n\n try {\n const check = await canRevertDiff(revertWorkdir, parsed.data.unified);\n if (!check.ok) {\n res.status(409).json({\n error: \"diff_does_not_revert\",\n message:\n \"These changes can't be reverted cleanly. The working tree may \" +\n \"have changed since the agent wrote them, or they were already \" +\n \"reverted.\",\n stderr: check.stderr,\n });\n return;\n }\n await revertDiff(revertWorkdir, parsed.data.unified);\n const paths = parseDiffPaths(parsed.data.unified);\n const body: RevertPatchResponse = {\n ok: true,\n filesChanged: paths.length,\n paths,\n };\n res.json(body);\n } catch (err) {\n if (err instanceof GitCommandError) {\n sendGitError(res, err);\n return;\n }\n throw err;\n }\n });\n\n return router;\n};\n", "// Repo-path validation.\n//\n// The helper is the sole authority on what disk locations get read or\n// written. Every path that crosses the HTTP boundary funnels through\n// validateRepoPath() before any git or filesystem operation runs against it.\n//\n// Rules:\n// 1. Must be an absolute path (no relative, no ~).\n// 2. Must exist on disk.\n// 3. Must be a directory (not a file).\n// 4. Must contain a `.git` entry (file for worktrees, dir otherwise).\n// 5. The realpath-resolved form is what gets stored & operated on, so\n// a symlinked path is transparently followed and we never end up\n// operating on a different location than the user can see.\n\nimport { access, lstat, realpath, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport class InvalidRepoPathError extends Error {\n readonly code: InvalidRepoPathCode;\n constructor(code: InvalidRepoPathCode, message: string) {\n super(message);\n this.name = \"InvalidRepoPathError\";\n this.code = code;\n }\n}\n\nexport type InvalidRepoPathCode =\n | \"not_a_string\"\n | \"empty\"\n | \"not_absolute\"\n | \"does_not_exist\"\n | \"not_a_directory\"\n | \"not_a_git_repo\";\n\nconst exists = async (p: string): Promise<boolean> => {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n};\n\nexport const validateRepoPath = async (raw: unknown): Promise<string> => {\n if (typeof raw !== \"string\") {\n throw new InvalidRepoPathError(\"not_a_string\", \"path must be a string\");\n }\n const trimmed = raw.trim();\n if (trimmed.length === 0) {\n throw new InvalidRepoPathError(\"empty\", \"path must not be empty\");\n }\n if (!path.isAbsolute(trimmed)) {\n throw new InvalidRepoPathError(\n \"not_absolute\",\n `path must be absolute (got: ${trimmed})`,\n );\n }\n\n // Resolve symlinks. If the symlink target is missing, realpath throws,\n // which we surface as does_not_exist for a tidy UI message.\n let resolved: string;\n try {\n resolved = await realpath(trimmed);\n } catch {\n throw new InvalidRepoPathError(\n \"does_not_exist\",\n `path does not exist: ${trimmed}`,\n );\n }\n\n const dirStat = await stat(resolved);\n if (!dirStat.isDirectory()) {\n throw new InvalidRepoPathError(\n \"not_a_directory\",\n `path is not a directory: ${resolved}`,\n );\n }\n\n // `.git` is a directory in normal clones, a regular file for git\n // worktrees (containing `gitdir: ...`). Either is fine \u2014 we just need\n // *something* there.\n const gitMarker = path.join(resolved, \".git\");\n if (!(await exists(gitMarker))) {\n throw new InvalidRepoPathError(\n \"not_a_git_repo\",\n `path does not contain a .git entry: ${resolved}`,\n );\n }\n // Defence in depth: confirm it's not a broken symlink pointing nowhere.\n await lstat(gitMarker);\n\n return resolved;\n};\n", "// Native \"choose folder\" dialog, run on the user's machine by the helper.\n//\n// The browser can't get an absolute filesystem path (the File System Access\n// API hides it), so when the user clicks \"Browse\u2026\" the cloud UI asks the\n// helper \u2014 which IS on their machine \u2014 to pop the OS folder picker and return\n// the chosen path. We then feed that into /repo/select.\n//\n// Everything goes through execFile (NOT a shell) to avoid quoting/injection.\n// The prompt string is a fixed constant, never user input.\n\nimport { execFile } from \"node:child_process\";\nimport { homedir } from \"node:os\";\n\nexport type PickDirectoryResult =\n | { ok: true; path: string }\n | { ok: false; reason: \"cancelled\" | \"no_gui\" | \"unsupported\" | \"failed\" };\n\ninterface DialogSpec {\n cmd: string;\n args: string[];\n}\n\nconst PROMPT = \"Select a git repository\";\n\n/** Build the picker command for a platform. Pure + exported for tests.\n * Returns null for platforms we don't have a dialog for. */\nexport const dialogSpec = (platform: NodeJS.Platform): DialogSpec | null => {\n switch (platform) {\n case \"darwin\":\n // AppleScript: choose folder \u2192 POSIX path. Cancel exits non-zero.\n return {\n cmd: \"osascript\",\n args: [\n \"-e\",\n `set theFolder to choose folder with prompt \"${PROMPT}\"`,\n \"-e\",\n \"POSIX path of theFolder\",\n ],\n };\n case \"linux\":\n // zenity is the common GTK picker; the runner falls back to kdialog.\n return {\n cmd: \"zenity\",\n args: [\"--file-selection\", \"--directory\", `--title=${PROMPT}`],\n };\n case \"win32\":\n // PowerShell's WinForms folder browser. Exits 1 (no output) on cancel.\n return {\n cmd: \"powershell\",\n args: [\n \"-NoProfile\",\n \"-Command\",\n \"Add-Type -AssemblyName System.Windows.Forms;\" +\n \"$d = New-Object System.Windows.Forms.FolderBrowserDialog;\" +\n `$d.Description = '${PROMPT}';` +\n \"if ($d.ShowDialog() -eq 'OK') { Write-Output $d.SelectedPath }\",\n ],\n };\n default:\n return null;\n }\n};\n\nconst run = (cmd: string, args: string[]): Promise<string> =>\n new Promise((resolve, reject) => {\n execFile(\n cmd,\n args,\n // Folder dialogs can sit open a while; cap it so the request can't hang\n // forever if the user wanders off. Killing it reads as \"cancelled\".\n { timeout: 120_000, windowsHide: true },\n (err, stdout) => {\n if (err) reject(err);\n else resolve(stdout);\n },\n );\n });\n\n/** Pop the OS folder dialog and return the chosen absolute path.\n * Distinguishes user-cancel from \"no GUI / tool missing\" so the UI can\n * fall back to manual typing with the right message. */\nexport const pickDirectory = async (): Promise<PickDirectoryResult> => {\n const spec = dialogSpec(process.platform);\n if (!spec) return { ok: false, reason: \"unsupported\" };\n\n const attempt = async (cmd: string, args: string[]): Promise<PickDirectoryResult> => {\n try {\n const out = (await run(cmd, args)).trim();\n // Empty output = the dialog was dismissed without a selection.\n return out ? { ok: true, path: out } : { ok: false, reason: \"cancelled\" };\n } catch (err) {\n const code = (err as { code?: string | number }).code;\n // The launcher binary isn't installed \u2192 no usable GUI on this box.\n if (code === \"ENOENT\") return { ok: false, reason: \"no_gui\" };\n // Any other non-zero exit from a present dialog is, in practice, the\n // user cancelling (osascript -128, zenity cancel, PowerShell no-OK).\n return { ok: false, reason: \"cancelled\" };\n }\n };\n\n const result = await attempt(spec.cmd, spec.args);\n // Linux fallback: if zenity isn't installed, try kdialog before giving up.\n if (\n !result.ok &&\n result.reason === \"no_gui\" &&\n process.platform === \"linux\"\n ) {\n return attempt(\"kdialog\", [\"--getexistingdirectory\", homedir()]);\n }\n return result;\n};\n", "// Thin wrapper over the GitHub CLI (`gh`), used by the \"Create PR\" flow.\n//\n// We lean on the user's existing local `gh` auth rather than building a cloud\n// OAuth integration (that's a later phase). Everything goes through execFile \u2014\n// no shell \u2014 and the title/body are passed as argv, never interpolated.\n\nimport { execFile } from \"node:child_process\";\n\nexport class GhError extends Error {\n constructor(\n message: string,\n readonly code: \"gh_unavailable\" | \"gh_failed\",\n readonly stderr?: string,\n ) {\n super(message);\n this.name = \"GhError\";\n }\n}\n\nconst run = (\n args: string[],\n cwd?: string,\n): Promise<{ stdout: string; stderr: string }> =>\n new Promise((resolve, reject) => {\n execFile(\n \"gh\",\n args,\n { cwd, timeout: 60_000, windowsHide: true },\n (err, stdout, stderr) => {\n if (err) {\n const code = (err as { code?: string | number }).code;\n if (code === \"ENOENT\") {\n reject(\n new GhError(\n \"The GitHub CLI (gh) isn't installed on this machine.\",\n \"gh_unavailable\",\n ),\n );\n return;\n }\n reject(new GhError(stderr || err.message, \"gh_failed\", stderr));\n return;\n }\n resolve({ stdout, stderr });\n },\n );\n });\n\n/** True when `gh` is installed AND authenticated. */\nexport const ghReady = async (): Promise<boolean> => {\n try {\n await run([\"auth\", \"status\"]);\n return true;\n } catch {\n return false;\n }\n};\n\nexport interface CreatePrOptions {\n cwd: string;\n base: string;\n head: string;\n title: string;\n body: string;\n}\n\n/** Open a pull request via `gh pr create` and return its URL (gh prints the\n * URL to stdout on success). */\nexport const createPullRequest = async (\n opts: CreatePrOptions,\n): Promise<string> => {\n const { stdout } = await run(\n [\n \"pr\",\n \"create\",\n \"--base\",\n opts.base,\n \"--head\",\n opts.head,\n \"--title\",\n opts.title,\n \"--body\",\n opts.body,\n ],\n opts.cwd,\n );\n // gh prints the PR URL as the last non-empty line.\n const url = stdout\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter(Boolean)\n .reverse()\n .find((l) => l.startsWith(\"http\"));\n if (!url) {\n throw new GhError(\"gh pr create didn't return a PR URL.\", \"gh_failed\", stdout);\n }\n return url;\n};\n", "// POST /agent/message \u2014 SSE stream of AgentStreamEvent envelopes from the\n// configured adapter (mock today; real opencode in Step 9).\n//\n// Wire format on the response:\n// Content-Type: text/event-stream\n// Each event: data: <json>\\n\\n\n// Terminal: data: [DONE]\\n\\n\n//\n// Why SSE rather than chunked JSON: it's what the existing app uses for\n// chat streaming (see server/routes.ts), and the browser's EventSource +\n// fetch+reader-loop patterns are already familiar to the codebase.\n\nimport type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport { z } from \"zod\";\n\nimport type {\n AgentStreamEvent,\n LlmBrokerResponsePayload,\n LlmStreamChunk,\n} from \"../../../shared/coding-agent-types\";\nimport type { AgentState } from \"../state\";\nimport type { WorktreeManager } from \"../worktree\";\nimport type { AdapterHistoryMessage, OpenCodeAdapter } from \"../adapters\";\nimport { LlmBroker, type BrokerRegistry } from \"../broker\";\n\nconst historyRoleSchema = z.enum([\"user\", \"assistant\", \"system\", \"tool\"]);\n\nconst agentMessageBodySchema = z.object({\n sessionId: z.string().min(1),\n prompt: z.string().min(1).max(200_000),\n history: z\n .array(\n z.object({\n role: historyRoleSchema,\n content: z.string(),\n }),\n )\n .max(200)\n .optional(),\n /** Optional user-chosen routing for this turn. When absent the cloud\n * picks the default (CODING_AGENT_DEFAULT_PROVIDER/MODEL). */\n preferredProvider: z.string().min(1).max(100).optional(),\n preferredModel: z.string().min(1).max(200).optional(),\n /** Tool-gating policy. Absent \u2192 \"auto\" (run everything) so older clients\n * keep their current behaviour. */\n permissionMode: z.enum([\"auto\", \"confirm\"]).optional(),\n});\n\nconst writeEvent = (res: Response, event: AgentStreamEvent): void => {\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n};\n\nconst writeDone = (res: Response): void => {\n res.write(\"data: [DONE]\\n\\n\");\n};\n\nconst logAgentRoute = (message: string): void => {\n console.error(`[agent-route] ${message}`);\n};\n\nexport interface AgentRouterDeps {\n state: AgentState;\n adapter: OpenCodeAdapter;\n /** Registry that lets the sibling /agent/llm-response route find the\n * broker for a given session. */\n brokerRegistry: BrokerRegistry;\n /** Per-session worktrees \u2014 the agent runs in the session's isolated\n * worktree, not the user's checkout. */\n worktrees: WorktreeManager;\n}\n\nexport const createAgentRouter = (deps: AgentRouterDeps): Router => {\n const router = createRouter();\n\n router.post(\"/message\", async (req: Request, res: Response) => {\n const parsed = agentMessageBodySchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n\n const repo = deps.state.getSelectedRepo();\n if (!repo) {\n res.status(409).json({ error: \"no_repo_selected\" });\n return;\n }\n\n // Each session works in its own worktree off the selected repo's HEAD, so\n // the agent never touches the user's checkout. Created lazily here on the\n // first turn; reused on subsequent turns. If creation fails (non-git path,\n // permissions, ancient git), we fall back to working IN PLACE rather than\n // blocking the turn \u2014 degraded isolation beats a dead agent.\n let workdir = repo.path;\n try {\n const worktree = await deps.worktrees.ensure(\n parsed.data.sessionId,\n repo.path,\n repo.branch ?? \"main\",\n );\n workdir = worktree.path;\n } catch (err) {\n logAgentRoute(\n `worktree create failed (falling back to in-place) session=${parsed.data.sessionId.slice(0, 8)} error=${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n logAgentRoute(\n `message start session=${parsed.data.sessionId.slice(0, 8)} repo=${repo.path} workdir=${workdir} promptChars=${parsed.data.prompt.length} provider=${parsed.data.preferredProvider ?? \"(default)\"} model=${parsed.data.preferredModel ?? \"(default)\"}`,\n );\n\n // SSE preamble. We set headers BEFORE any res.write so the browser\n // begins streaming immediately rather than buffering the response.\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache, no-transform\");\n res.setHeader(\"Connection\", \"keep-alive\");\n // Disable proxy buffering for nginx/Cloudflare-style frontends. This\n // matters less for localhost but costs nothing.\n res.setHeader(\"X-Accel-Buffering\", \"no\");\n res.flushHeaders?.();\n\n const controller = new AbortController();\n // We listen on `res.on(\"close\")` (NOT `req.on(\"close\")`). Express's\n // body parser consumes `req` and the request stream's 'close' fires\n // early \u2014 using it here aborts the adapter before its first yield.\n // `res.on(\"close\")` only fires once the response is torn down by the\n // client or the server, which is exactly when we want to stop.\n res.on(\"close\", () => {\n if (!res.writableEnded) {\n controller.abort();\n }\n });\n\n // Build the per-stream LLM broker and register it under this session\n // so /agent/llm-response can find it. The user-chosen provider/model\n // ride on the broker as overrides so every LLM call this turn lands\n // on the same backend.\n const broker = new LlmBroker({\n emitToStream: (event) => writeEvent(res, event),\n overrideProvider: parsed.data.preferredProvider,\n overrideModel: parsed.data.preferredModel,\n });\n deps.brokerRegistry.register(parsed.data.sessionId, broker);\n\n try {\n const history: AdapterHistoryMessage[] | undefined = parsed.data.history\n ? parsed.data.history.map((m) => ({ role: m.role, content: m.content }))\n : undefined;\n\n for await (const event of deps.adapter.streamMessage({\n sessionId: parsed.data.sessionId,\n repoPath: workdir,\n prompt: parsed.data.prompt,\n history,\n signal: controller.signal,\n broker: (payload, observer) => broker.request(payload, observer),\n preferredProvider: parsed.data.preferredProvider,\n preferredModel: parsed.data.preferredModel,\n permissionMode: parsed.data.permissionMode,\n })) {\n if (controller.signal.aborted) {\n logAgentRoute(\n `message aborted session=${parsed.data.sessionId.slice(0, 8)}`,\n );\n break;\n }\n logAgentRoute(\n `stream event session=${parsed.data.sessionId.slice(0, 8)} type=${event.type}`,\n );\n writeEvent(res, event);\n }\n logAgentRoute(\n `message done session=${parsed.data.sessionId.slice(0, 8)}`,\n );\n writeDone(res);\n } catch (err) {\n // Abort is the expected exit when req.on('close') fired \u2014 don't\n // emit an error event for it.\n if (\n err &&\n typeof err === \"object\" &&\n \"name\" in err &&\n (err as { name?: string }).name === \"AbortError\"\n ) {\n writeDone(res);\n } else {\n const message =\n err instanceof Error ? err.message : \"Adapter stream failed\";\n logAgentRoute(\n `message error session=${parsed.data.sessionId.slice(0, 8)} error=${message}`,\n );\n writeEvent(res, { type: \"error\", message });\n writeDone(res);\n }\n } finally {\n // Tear down the broker so any in-flight requests reject rather than\n // dangle. Idempotent \u2014 safe even if the adapter already finished.\n broker.close(\"stream_closed\");\n deps.brokerRegistry.unregister(parsed.data.sessionId, broker);\n res.end();\n }\n });\n\n return router;\n};\n\n// ----------------------------------------------------------------------------\n// /agent/llm-response/:requestId\n// ----------------------------------------------------------------------------\n//\n// The browser POSTs the completed LLM response here. The route looks up\n// the broker bound to the session, fulfils the requestId, and returns\n// ok/no-match. Lives in this module rather than its own file because it\n// shares the broker dependency with the SSE route.\n\nconst llmResponseBodySchema = z.object({\n sessionId: z.string().min(1),\n text: z.string(),\n provider: z.string().min(1),\n model: z.string().min(1),\n tokenUsage: z\n .object({\n input: z.number().nonnegative(),\n output: z.number().nonnegative(),\n })\n .optional(),\n error: z.string().optional(),\n toolCalls: z\n .array(\n z.object({\n id: z.string().min(1),\n type: z.literal(\"function\"),\n function: z.object({\n name: z.string().min(1),\n arguments: z.string(),\n }),\n }),\n )\n .optional(),\n finishReason: z\n .enum([\"stop\", \"tool_calls\", \"length\", \"content_filter\"])\n .optional(),\n});\n\nexport interface LlmResponseRouterDeps {\n brokerRegistry: BrokerRegistry;\n}\n\nconst llmChunkBodySchema = z.object({\n sessionId: z.string().min(1),\n chunk: z.union([\n z.object({\n type: z.literal(\"text_delta\"),\n text: z.string(),\n }),\n z.object({\n type: z.literal(\"tool_call_delta\"),\n index: z.number().int().nonnegative(),\n id: z.string().optional(),\n name: z.string().optional(),\n argumentsDelta: z.string().optional(),\n }),\n z.object({\n type: z.literal(\"complete\"),\n payload: z.object({\n text: z.string(),\n provider: z.string(),\n model: z.string(),\n tokenUsage: z\n .object({\n input: z.number().nonnegative(),\n output: z.number().nonnegative(),\n })\n .optional(),\n finishReason: z\n .enum([\"stop\", \"tool_calls\", \"length\", \"content_filter\"])\n .optional(),\n toolCalls: z\n .array(\n z.object({\n id: z.string(),\n type: z.literal(\"function\"),\n function: z.object({\n name: z.string(),\n arguments: z.string(),\n }),\n }),\n )\n .optional(),\n }),\n }),\n z.object({\n type: z.literal(\"error\"),\n message: z.string(),\n }),\n ]),\n});\n\nexport const createLlmResponseRouter = (\n deps: LlmResponseRouterDeps,\n): Router => {\n const router = createRouter();\n\n router.post(\"/:requestId\", (req: Request, res: Response) => {\n const requestId = req.params.requestId;\n if (!requestId) {\n res.status(400).json({ error: \"missing_request_id\" });\n return;\n }\n const parsed = llmResponseBodySchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n const broker = deps.brokerRegistry.get(parsed.data.sessionId);\n if (!broker) {\n res.status(404).json({ error: \"no_active_stream_for_session\" });\n return;\n }\n if (!broker.has(requestId)) {\n res.status(404).json({ error: \"unknown_request_id\" });\n return;\n }\n if (parsed.data.error) {\n broker.fail(requestId, parsed.data.error);\n res.json({ ok: true });\n return;\n }\n const payload: LlmBrokerResponsePayload = {\n text: parsed.data.text,\n provider: parsed.data.provider,\n model: parsed.data.model,\n tokenUsage: parsed.data.tokenUsage,\n ...(parsed.data.toolCalls ? { toolCalls: parsed.data.toolCalls } : {}),\n ...(parsed.data.finishReason\n ? { finishReason: parsed.data.finishReason }\n : {}),\n };\n broker.fulfill(requestId, payload);\n res.json({ ok: true });\n });\n\n return router;\n};\n\n/** /agent/llm-chunk/:requestId \u2014 sibling of /agent/llm-response for the\n * streaming brokered-LLM path. The browser POSTs one chunk per Anthropic\n * delta as the cloud SSE emits them, and a final `complete` chunk that\n * resolves the broker's pending Promise. */\nexport const createLlmChunkRouter = (deps: LlmResponseRouterDeps): Router => {\n const router = createRouter();\n\n router.post(\"/:requestId\", (req: Request, res: Response) => {\n const requestId = req.params.requestId;\n if (!requestId) {\n res.status(400).json({ error: \"missing_request_id\" });\n return;\n }\n const parsed = llmChunkBodySchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n const broker = deps.brokerRegistry.get(parsed.data.sessionId);\n if (!broker) {\n res.status(404).json({ error: \"no_active_stream_for_session\" });\n return;\n }\n const accepted = broker.pushChunk(\n requestId,\n parsed.data.chunk as LlmStreamChunk,\n );\n if (!accepted) {\n res.status(404).json({ error: \"unknown_request_id\" });\n return;\n }\n res.json({ ok: true });\n });\n\n return router;\n};\n\n// ----------------------------------------------------------------------------\n// /agent/permission\n// ----------------------------------------------------------------------------\n//\n// In confirm mode the adapter pauses bash/webfetch and emits a\n// permission_request over the SSE stream. The browser shows Allow/Deny and\n// POSTs the decision here \u2014 on a SEPARATE request from the stream, mirroring\n// /agent/llm-response. We hand it to the adapter, which replies to opencode\n// and lets the paused turn resume.\n\nconst permissionDecisionBodySchema = z.object({\n sessionId: z.string().min(1),\n permissionId: z.string().min(1),\n response: z.enum([\"once\", \"always\", \"reject\"]),\n});\n\nexport interface PermissionRouterDeps {\n adapter: OpenCodeAdapter;\n}\n\nexport const createPermissionRouter = (deps: PermissionRouterDeps): Router => {\n const router = createRouter();\n\n router.post(\"/\", async (req: Request, res: Response) => {\n const parsed = permissionDecisionBodySchema.safeParse(req.body);\n if (!parsed.success) {\n res\n .status(400)\n .json({ error: \"invalid_body\", issues: parsed.error.issues });\n return;\n }\n if (!deps.adapter.replyPermission) {\n res.status(501).json({ error: \"permissions_unsupported\" });\n return;\n }\n const matched = await deps.adapter.replyPermission(\n parsed.data.sessionId,\n parsed.data.permissionId,\n parsed.data.response,\n );\n if (!matched) {\n // No in-flight turn for this session, or the permission already\n // resolved/expired. Non-fatal \u2014 the browser just drops the prompt.\n res.status(404).json({ error: \"no_active_permission\" });\n return;\n }\n res.json({ ok: true });\n });\n\n return router;\n};\n", "// LLM broker for a single in-flight /agent/message stream.\n//\n// Lifecycle:\n// 1. The SSE route in agent.ts builds a Broker per stream.\n// 2. The route hands `broker.request(...)` to the adapter as\n// `request.broker`.\n// 3. When the adapter calls `broker.request(payload)`:\n// a. Broker generates a fresh requestId.\n// b. Broker stores `{ resolve, reject }` in a pending map.\n// c. Broker calls the SSE-emit hook (provided by the route) with an\n// `llm_request` envelope carrying the requestId + payload.\n// d. The browser receives the envelope, makes a cloud LLM call,\n// and POSTs the response to `/agent/llm-response/:requestId`\n// (with sessionId in the body for registry lookup).\n// e. That route asks the registry for the broker bound to this\n// sessionId and calls `broker.fulfill(requestId, response)`,\n// which resolves the pending promise.\n// 4. The adapter receives the response and continues iteration.\n//\n// Why this design:\n// - The helper holds no cloud credentials. The browser carries them.\n// - One broker per stream keeps requestIds unique within a stream and\n// guarantees no cross-talk between concurrent /agent/message calls.\n// - The pending map is local to each broker (and so to each stream),\n// so we cannot accidentally fulfill a request from a different stream.\n\nimport { randomUUID } from \"node:crypto\";\n\nimport type {\n AgentLlmRequest,\n LlmBrokerRequestPayload,\n LlmBrokerResponsePayload,\n LlmStreamChunk,\n} from \"../../shared/coding-agent-types\";\n\nconst logBroker = (message: string): void => {\n console.error(`[llm-broker] ${message}`);\n};\n\n/** Hook adapters supply to observe incremental tokens as they stream\n * from the cloud. When set, the broker invokes it for every chunk\n * posted to /agent/llm-chunk/:requestId; the final `complete` chunk\n * also resolves the request's Promise. */\nexport type StreamObserver = (chunk: LlmStreamChunk) => void;\n\nexport interface BrokerOptions {\n /** Emits an SSE envelope on the parent /agent/message response.\n * Implemented by the route; injected so the broker stays decoupled\n * from Express. */\n emitToStream(event: AgentLlmRequest): void;\n /** Hard ceiling on how long a single LLM brokered call may take. The\n * browser is a network hop + the cloud is a model call; 90s is\n * comfortable headroom for non-streaming completions. */\n timeoutMs?: number;\n /** Override the provider on every request that flows through this\n * broker. The user-chosen provider from the chat UI rides on the\n * AdapterRequest and lands here at broker-construction time. */\n overrideProvider?: string;\n /** Override the model. Same rationale as overrideProvider \u2014 the\n * adapter doesn't always know what the user wants (e.g. opencode\n * hard-codes our placeholder model id in its outbound HTTP). */\n overrideModel?: string;\n}\n\ninterface PendingRequest {\n resolve(value: LlmBrokerResponsePayload): void;\n reject(reason: Error): void;\n timer: NodeJS.Timeout;\n observer?: StreamObserver;\n}\n\nexport class LlmBroker {\n private readonly pending = new Map<string, PendingRequest>();\n private readonly emit: BrokerOptions[\"emitToStream\"];\n private readonly timeoutMs: number;\n private readonly overrideProvider?: string;\n private readonly overrideModel?: string;\n private closed = false;\n\n constructor(options: BrokerOptions) {\n this.emit = options.emitToStream;\n this.timeoutMs = options.timeoutMs ?? 90_000;\n this.overrideProvider = options.overrideProvider;\n this.overrideModel = options.overrideModel;\n }\n\n /** Called by the adapter when it needs an LLM call. Resolves with the\n * full LlmBrokerResponsePayload once the browser fulfils it. Rejects\n * on timeout, broker close, or explicit failure from the browser.\n *\n * Pass `observer` to receive incremental stream chunks if the browser\n * fulfils via the streaming chunk endpoint. Observer is called once\n * per chunk; the Promise still resolves with the final aggregate\n * payload when the stream completes. */\n request(\n payload: LlmBrokerRequestPayload,\n observer?: StreamObserver,\n ): Promise<LlmBrokerResponsePayload> {\n if (this.closed) {\n return Promise.reject(new Error(\"broker is closed\"));\n }\n const requestId = randomUUID();\n return new Promise<LlmBrokerResponsePayload>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(requestId);\n logBroker(\n `timeout request=${requestId.slice(0, 8)} pending=${this.pending.size} after=${this.timeoutMs}ms`,\n );\n reject(\n new Error(\n `llm_broker_timeout: no response within ${this.timeoutMs}ms`,\n ),\n );\n }, this.timeoutMs);\n // Unref long-lived production timers so a stranded broker does not\n // keep the helper alive on shutdown. Keep short test/demo timeouts\n // ref'ed so their callbacks can fire before node:test exits.\n if (this.timeoutMs >= 1_000) {\n timer.unref?.();\n }\n this.pending.set(requestId, { resolve, reject, timer, observer });\n\n // Provider/model precedence: explicit broker override > payload's\n // declared provider/model. Opencode's outbound HTTP carries our\n // placeholder model id; we override it here with the user's\n // selection so the cloud routes to the right backend.\n const effectiveProvider = this.overrideProvider ?? payload.provider;\n const effectiveModel = this.overrideModel ?? payload.model;\n const envelope: AgentLlmRequest = {\n type: \"llm_request\",\n requestId,\n provider: effectiveProvider,\n model: effectiveModel,\n payload: {\n messages: payload.messages,\n tools: payload.tools,\n temperature: payload.temperature,\n systemPrompt: payload.systemPrompt,\n },\n };\n try {\n logBroker(\n `emit request=${requestId.slice(0, 8)} provider=${effectiveProvider ?? \"(default)\"} model=${effectiveModel ?? \"(default)\"} messages=${payload.messages.length} tools=${payload.tools?.length ?? 0}`,\n );\n this.emit(envelope);\n } catch (err) {\n clearTimeout(timer);\n this.pending.delete(requestId);\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n }\n\n /** True iff this broker has a pending request with the given id. Used\n * by the registry to route /agent/llm-response/:requestId without\n * needing the caller to supply sessionId. */\n has(requestId: string): boolean {\n return this.pending.has(requestId);\n }\n\n /** Called by the /agent/llm-response route once the browser has POSTed\n * the completion back. Returns true if a pending request matched.\n *\n * If the request was registered with an observer (streaming flow),\n * the observer is also invoked with a synthesised text_delta + a\n * `complete` chunk \u2014 this makes the streaming path work even when\n * the browser fulfils via the non-streaming /agent/llm-response\n * route (e.g. for providers that don't support streaming). */\n fulfill(requestId: string, response: LlmBrokerResponsePayload): boolean {\n const entry = this.pending.get(requestId);\n if (!entry) {\n logBroker(`fulfill_miss request=${requestId.slice(0, 8)}`);\n return false;\n }\n const toolNames = (response.toolCalls ?? [])\n .map((tc) => tc.function?.name ?? \"?\")\n .join(\",\");\n logBroker(\n `fulfill request=${requestId.slice(0, 8)} text=${response.text.length} toolCalls=${response.toolCalls?.length ?? 0}${toolNames ? ` tools=[${toolNames}]` : \"\"} finish=${response.finishReason ?? \"(none)\"}`,\n );\n clearTimeout(entry.timer);\n this.pending.delete(requestId);\n if (entry.observer) {\n try {\n if (response.text) {\n entry.observer({ type: \"text_delta\", text: response.text });\n }\n // Synthesise a tool_call_delta per tool call so the observer\n // (and the shim translators downstream) see them. The\n // non-streaming fulfil path previously emitted only text, so a\n // tool-only turn \u2014 which is what every coding task produces\n // (write/edit/bash) \u2014 reached opencode as an empty message with\n // no tool calls. The agent then did nothing: no file, no diff,\n // just a metered LLM round-trip. Because we already hold the\n // complete payload, each tool call is emitted as a single delta\n // carrying id + name + full arguments (no need to chunk args).\n for (const [index, tc] of (response.toolCalls ?? []).entries()) {\n entry.observer({\n type: \"tool_call_delta\",\n index,\n id: tc.id,\n name: tc.function.name,\n argumentsDelta: tc.function.arguments,\n });\n }\n entry.observer({ type: \"complete\", payload: response });\n } catch {\n // Observer errors are isolated from broker state.\n }\n }\n entry.resolve(response);\n return true;\n }\n\n /** Called by the /agent/llm-chunk/:requestId route when the browser\n * forwards an incremental stream chunk. The observer (if any) is\n * invoked; a `complete` chunk also resolves the pending Promise. */\n pushChunk(requestId: string, chunk: LlmStreamChunk): boolean {\n const entry = this.pending.get(requestId);\n if (!entry) {\n logBroker(\n `chunk_miss request=${requestId.slice(0, 8)} type=${chunk.type}`,\n );\n return false;\n }\n logBroker(\n `chunk request=${requestId.slice(0, 8)} type=${chunk.type}${chunk.type === \"text_delta\" ? ` chars=${chunk.text.length}` : \"\"}${chunk.type === \"tool_call_delta\" ? ` index=${chunk.index} name=${chunk.name ?? \"(delta)\"} argsDelta=${chunk.argumentsDelta?.length ?? 0}` : \"\"}`,\n );\n if (entry.observer) {\n try {\n entry.observer(chunk);\n } catch {\n // Observer errors must not break the broker \u2014 adapter's loop\n // is what cares about them.\n }\n }\n if (chunk.type === \"complete\") {\n clearTimeout(entry.timer);\n this.pending.delete(requestId);\n entry.resolve(chunk.payload);\n } else if (chunk.type === \"error\") {\n clearTimeout(entry.timer);\n this.pending.delete(requestId);\n entry.reject(new Error(chunk.message));\n }\n return true;\n }\n\n /** Called by the /agent/llm-response route when the browser reports a\n * failure rather than a completion. */\n fail(requestId: string, message: string): boolean {\n const entry = this.pending.get(requestId);\n if (!entry) {\n logBroker(`fail_miss request=${requestId.slice(0, 8)}`);\n return false;\n }\n logBroker(`fail request=${requestId.slice(0, 8)} message=${message}`);\n clearTimeout(entry.timer);\n this.pending.delete(requestId);\n entry.reject(new Error(message));\n return true;\n }\n\n /** Reject all in-flight requests. Called when the parent SSE stream\n * closes so a stranded request doesn't sit forever. */\n close(reason = \"broker_closed\"): void {\n if (this.pending.size > 0) {\n logBroker(`close reason=${reason} pending=${this.pending.size}`);\n }\n this.closed = true;\n for (const [, entry] of this.pending.entries()) {\n clearTimeout(entry.timer);\n entry.reject(new Error(reason));\n }\n this.pending.clear();\n }\n\n get pendingCount(): number {\n return this.pending.size;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Registry \u2014 one broker per active stream, indexed by sessionId.\n// ----------------------------------------------------------------------------\n//\n// /agent/llm-response/:requestId arrives on a SEPARATE HTTP request from\n// the browser. It needs to find the broker that emitted the originating\n// llm_request. We index by sessionId because that's what the chat panel\n// already tracks; the response route requires sessionId in its body so\n// the lookup is O(1).\n\nexport interface BrokerRegistry {\n /** Bind a broker to a session. If a prior broker is bound for the same\n * session, it is closed (its pending requests reject) \u2014 this handles\n * the case of the browser reconnecting without a clean prior close. */\n register(sessionId: string, broker: LlmBroker): void;\n /** Remove the binding for a session. Idempotent. */\n unregister(sessionId: string, broker: LlmBroker): void;\n /** Look up the broker for a session. Returns null if nothing is bound. */\n get(sessionId: string): LlmBroker | null;\n}\n\nexport const createBrokerRegistry = (): BrokerRegistry => {\n const bySession = new Map<string, LlmBroker>();\n return {\n register(sessionId, broker) {\n const existing = bySession.get(sessionId);\n if (existing && existing !== broker) {\n existing.close(\"superseded_by_new_stream\");\n }\n bySession.set(sessionId, broker);\n },\n unregister(sessionId, broker) {\n // Only unbind if it's still THIS broker (a concurrent register\n // may have replaced it).\n if (bySession.get(sessionId) === broker) {\n bySession.delete(sessionId);\n }\n },\n get(sessionId) {\n return bySession.get(sessionId) ?? null;\n },\n };\n};\n", "// POST /llm-shim/v1/chat/completions \u2014 OpenAI-compatible endpoint that\n// opencode targets via its custom-provider config.\n//\n// Wire-level expectations from opencode (via AI SDK 6's openai provider):\n// - Accepts a Chat Completions request with messages, tools, stream.\n// - Returns either a single JSON body or SSE chunks (depending on\n// `stream`).\n// - Authentication via `Authorization: Bearer <apiKey>`.\n\nimport type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\n\nimport {\n type OpenAIChatCompletionRequest,\n translateRequest,\n translateResponse,\n} from \"../shim/openai-translator\";\nimport {\n type ResponsesApiRequest,\n ResponsesStreamState,\n responsesToChatCompletions,\n} from \"../shim/responses-translator\";\nimport {\n _activeBrokerCount,\n getActiveBrokerByToken,\n getOnlyActiveBroker,\n} from \"../shim/active-broker\";\nimport type { LlmBroker } from \"../broker\";\nimport type { LlmStreamChunk } from \"../../../shared/coding-agent-types\";\n\nconst extractBearer = (req: Request): string | null => {\n const header = req.header(\"authorization\");\n if (!header) return null;\n if (!header.toLowerCase().startsWith(\"bearer \")) return null;\n return header.slice(7).trim() || null;\n};\n\nconst sendOpenAIError = (\n res: Response,\n status: number,\n message: string,\n type = \"invalid_request_error\",\n): void => {\n // OpenAI's error envelope shape \u2014 opencode parses these.\n res.status(status).json({ error: { message, type } });\n};\n\n/** Resolve the LlmBroker for an inbound shim request, or send the 401\n * and return null. Shared by BOTH shim routes (/v1/chat/completions\n * and /v1/responses) so the auth logic can never drift between them\n * again \u2014 a previous bug shipped the placeholder fallback on only one\n * route, and opencode (which uses /v1/responses) 401'd forever.\n *\n * Resolution order:\n * 1. Exact token match (the per-stream token, if opencode could\n * carry it \u2014 which it currently can't).\n * 2. Single-active-broker fallback: opencode's @ai-sdk/openai\n * provider sends a fixed placeholder bearer for every call, so\n * when exactly one stream is live we use it. See active-broker.ts\n * getOnlyActiveBroker for the multi-stream caveat. */\nconst resolveBrokerOr401 = (\n req: Request,\n res: Response,\n): LlmBroker | null => {\n const token = extractBearer(req);\n if (!token) {\n sendOpenAIError(res, 401, \"Missing Authorization bearer\", \"auth_error\");\n return null;\n }\n const broker = getActiveBrokerByToken(token) ?? getOnlyActiveBroker();\n if (!broker) {\n const tokenPrefix =\n token.length > 12 ? `${token.slice(0, 12)}\u2026` : token;\n console.error(\n `[llm-shim] 401 on ${req.path}: bearer=\"${tokenPrefix}\" active_brokers=${_activeBrokerCount()}`,\n );\n sendOpenAIError(\n res,\n 401,\n \"No active coding-agent stream is currently authorised on this helper.\",\n \"auth_error\",\n );\n return null;\n }\n return broker;\n};\n\nexport const createLlmShimRouter = (): Router => {\n const router = createRouter();\n\n router.post(\n \"/v1/chat/completions\",\n async (req: Request, res: Response) => {\n const broker = resolveBrokerOr401(req, res);\n if (!broker) return;\n\n // Minimal request validation \u2014 leave the heavy lifting to the\n // translator. We accept extra fields gracefully (`[key]: unknown`\n // catch-all in the request type).\n const body = req.body as OpenAIChatCompletionRequest | undefined;\n if (\n !body ||\n typeof body !== \"object\" ||\n !Array.isArray(body.messages) ||\n body.messages.length === 0\n ) {\n sendOpenAIError(res, 400, \"messages[] is required and must be non-empty\");\n return;\n }\n\n const brokerRequest = translateRequest(body);\n\n const completionId = `chatcmpl-${Math.random().toString(36).slice(2, 10)}`;\n const stream = body.stream === true;\n\n // True streaming path: open the SSE response NOW, attach a\n // stream observer to the broker, forward every Anthropic delta\n // as an OpenAI chunk in real time. The final `complete` chunk\n // resolves the broker's promise \u2014 we then send the finish chunk\n // and [DONE].\n if (stream) {\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache, no-transform\");\n res.setHeader(\"Connection\", \"keep-alive\");\n res.flushHeaders?.();\n const writeOpenAi = (delta: unknown): void => {\n res.write(`data: ${JSON.stringify(delta)}\\n\\n`);\n };\n // Emit the role chunk straight away so AI-SDK consumers see\n // the assistant turn start.\n writeOpenAi({\n id: completionId,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model: body.model,\n choices: [\n { index: 0, delta: { role: \"assistant\" }, finish_reason: null },\n ],\n });\n // Forward incremental chunks. Index tracking maps Anthropic's\n // content-block index \u2192 OpenAI's tool_call index (we collapse\n // text deltas; tool_call deltas keep their index).\n // Held by the observer closure; the outer scope reads it after\n // broker.request() resolves. We type it via a single-element\n // array to dodge the closure-narrowing-to-null issue.\n const finalPayloadRef: {\n current: ReturnType<typeof translateResponse> | null;\n } = { current: null };\n const observer = (chunk: LlmStreamChunk): void => {\n switch (chunk.type) {\n case \"text_delta\":\n writeOpenAi({\n id: completionId,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model: body.model,\n choices: [\n {\n index: 0,\n delta: { content: chunk.text },\n finish_reason: null,\n },\n ],\n });\n break;\n case \"tool_call_delta\":\n writeOpenAi({\n id: completionId,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model: body.model,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: chunk.index,\n ...(chunk.id ? { id: chunk.id } : {}),\n ...(chunk.id || chunk.name\n ? { type: \"function\" as const }\n : {}),\n ...(chunk.name || chunk.argumentsDelta !== undefined\n ? {\n function: {\n ...(chunk.name ? { name: chunk.name } : {}),\n ...(chunk.argumentsDelta !== undefined\n ? { arguments: chunk.argumentsDelta }\n : {}),\n },\n }\n : {}),\n },\n ],\n },\n finish_reason: null,\n },\n ],\n });\n break;\n case \"complete\":\n finalPayloadRef.current = translateResponse(\n chunk.payload,\n completionId,\n body.model,\n );\n break;\n case \"error\":\n // Errors are surfaced by the request() Promise rejection\n // catch below; nothing to write here.\n break;\n }\n };\n try {\n await broker.request(brokerRequest, observer);\n } catch (err) {\n writeOpenAi({\n id: completionId,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model: body.model,\n choices: [\n {\n index: 0,\n delta: {},\n finish_reason: \"stop\" as const,\n },\n ],\n });\n res.write(\"data: [DONE]\\n\\n\");\n res.end();\n void err;\n return;\n }\n // Final finish chunk + [DONE].\n const finishReason =\n finalPayloadRef.current?.choices[0]?.finish_reason ?? \"stop\";\n writeOpenAi({\n id: completionId,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model: body.model,\n choices: [\n {\n index: 0,\n delta: {},\n finish_reason: finishReason,\n },\n ],\n });\n res.write(\"data: [DONE]\\n\\n\");\n res.end();\n return;\n }\n\n let brokerResponse;\n try {\n brokerResponse = await broker.request(brokerRequest);\n } catch (err) {\n sendOpenAIError(\n res,\n 502,\n err instanceof Error ? err.message : \"Broker call failed\",\n \"api_error\",\n );\n return;\n }\n\n if (brokerResponse.error) {\n sendOpenAIError(res, 502, brokerResponse.error, \"api_error\");\n return;\n }\n\n // Non-streaming response.\n const out = translateResponse(brokerResponse, completionId, body.model);\n res.json(out);\n },\n );\n\n // ----------------------------------------------------------------------\n // POST /v1/responses \u2014 OpenAI Responses API endpoint\n //\n // opencode 1.x (via @ai-sdk/openai v3+) defaults to the Responses API\n // shape rather than the older Chat Completions one. We translate the\n // request into Chat Completions, reuse the broker layer, and translate\n // streaming chunks back into Responses-API SSE events.\n // ----------------------------------------------------------------------\n router.post(\"/v1/responses\", async (req: Request, res: Response) => {\n const broker = resolveBrokerOr401(req, res);\n if (!broker) return;\n\n const body = req.body as ResponsesApiRequest | undefined;\n if (\n !body ||\n typeof body !== \"object\" ||\n !Array.isArray(body.input) ||\n body.input.length === 0\n ) {\n sendOpenAIError(res, 400, \"input[] is required and must be non-empty\");\n return;\n }\n\n const chatRequest = responsesToChatCompletions(body);\n const brokerRequest = translateRequest(chatRequest);\n const stream = body.stream === true;\n\n if (stream) {\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache, no-transform\");\n res.setHeader(\"Connection\", \"keep-alive\");\n res.flushHeaders?.();\n\n const state = new ResponsesStreamState(body.model);\n const writeEvent = (ev: { event: string; data: unknown }): void => {\n // Responses API SSE wire: an `event:` line + a `data:` line with\n // the JSON payload, separated by a blank line. The AI SDK uses\n // the event name as the type discriminator.\n res.write(`event: ${ev.event}\\n`);\n res.write(`data: ${JSON.stringify(ev.data)}\\n\\n`);\n };\n\n for (const ev of state.start()) writeEvent(ev);\n\n const observer = (chunk: LlmStreamChunk): void => {\n for (const ev of state.chunk(chunk)) writeEvent(ev);\n };\n\n try {\n await broker.request(brokerRequest, observer);\n } catch (err) {\n // Emit response.failed so opencode surfaces a clean error.\n writeEvent({\n event: \"response.failed\",\n data: {\n type: \"response.failed\",\n response: {\n id: `resp_${Math.random().toString(36).slice(2, 12)}`,\n object: \"response\",\n status: \"failed\",\n model: body.model,\n error: {\n code: \"broker_error\",\n message:\n err instanceof Error ? err.message : \"Broker call failed\",\n },\n },\n },\n });\n res.write(\"data: [DONE]\\n\\n\");\n res.end();\n return;\n }\n\n for (const ev of state.finish()) writeEvent(ev);\n res.write(\"data: [DONE]\\n\\n\");\n res.end();\n return;\n }\n\n // Non-streaming path: collect deltas through the broker, then\n // assemble a single response object. Use the existing Chat\n // Completions translator to consolidate, then re-shape into the\n // Responses API top-level response object.\n let brokerResponse;\n try {\n brokerResponse = await broker.request(brokerRequest);\n } catch (err) {\n sendOpenAIError(\n res,\n 502,\n err instanceof Error ? err.message : \"Broker call failed\",\n \"api_error\",\n );\n return;\n }\n if (brokerResponse.error) {\n sendOpenAIError(res, 502, brokerResponse.error, \"api_error\");\n return;\n }\n const completionId = `resp_${Math.random().toString(36).slice(2, 12)}`;\n const chatResponse = translateResponse(\n brokerResponse,\n completionId,\n body.model,\n );\n const text =\n chatResponse.choices[0]?.message?.content ?? \"\";\n const toolCalls = chatResponse.choices[0]?.message?.tool_calls ?? [];\n const output: Array<Record<string, unknown>> = [];\n if (text) {\n output.push({\n id: `msg_${Math.random().toString(36).slice(2, 12)}`,\n type: \"message\",\n status: \"completed\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text }],\n });\n }\n for (const tc of toolCalls) {\n output.push({\n id: `fc_${Math.random().toString(36).slice(2, 12)}`,\n type: \"function_call\",\n status: \"completed\",\n name: tc.function.name,\n call_id: tc.id,\n arguments: tc.function.arguments,\n });\n }\n res.json({\n id: completionId,\n object: \"response\",\n created_at: Math.floor(Date.now() / 1000),\n status: \"completed\",\n model: body.model,\n output,\n usage: chatResponse.usage,\n });\n });\n\n return router;\n};\n", "// Translate OpenAI Chat Completions wire format \u2194 our broker payloads.\n//\n// This is pure-function on purpose so it's covered exhaustively by unit\n// tests without spinning up Express or opencode. The shim route in\n// routes/llm-shim.ts is then ~30 lines of HTTP plumbing on top.\n//\n// Why OpenAI Chat Completions specifically: opencode's provider config\n// supports any AI-SDK provider with a custom `baseURL` + `apiKey`, and\n// every other provider speaks this wire format too via OpenAI-compatible\n// shims. Building one translator covers the common path.\n\nimport type {\n CodingMessageRole,\n LlmBrokerRequestPayload,\n LlmBrokerResponsePayload,\n} from \"../../../shared/coding-agent-types\";\n\n// ----------------------------------------------------------------------------\n// OpenAI request shapes (subset we accept)\n// ----------------------------------------------------------------------------\n\nexport interface OpenAIChatCompletionRequest {\n model: string;\n messages: OpenAIChatMessage[];\n tools?: OpenAITool[];\n tool_choice?: \"auto\" | \"none\" | \"required\" | { type: \"function\"; function: { name: string } };\n temperature?: number;\n stream?: boolean;\n /** Many clients send these; we ignore them (top_p, n, max_tokens,\n * presence_penalty, frequency_penalty, etc.). They flow through to\n * the cloud provider via `payload.tools`'s catch-all if needed. */\n [key: string]: unknown;\n}\n\nexport interface OpenAIChatMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | null;\n name?: string;\n tool_call_id?: string;\n tool_calls?: Array<{\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }>;\n}\n\nexport interface OpenAITool {\n type: \"function\";\n function: {\n name: string;\n description?: string;\n parameters?: Record<string, unknown>;\n };\n}\n\n// ----------------------------------------------------------------------------\n// Request: OpenAI \u2192 broker\n// ----------------------------------------------------------------------------\n\n/** Pulls the system message out of the OpenAI message list and returns\n * the remaining messages flattened to the broker's role union. */\nexport const translateRequest = (\n req: OpenAIChatCompletionRequest,\n): LlmBrokerRequestPayload => {\n let systemPrompt: string | undefined;\n const messages: Array<{ role: CodingMessageRole; content: string }> = [];\n\n for (const msg of req.messages) {\n if (msg.role === \"system\") {\n const text = typeof msg.content === \"string\" ? msg.content : \"\";\n systemPrompt =\n systemPrompt === undefined ? text : `${systemPrompt}\\n\\n${text}`;\n continue;\n }\n // Tool result messages and assistant messages may carry structured\n // content; we flatten to text. tool_calls on an assistant message\n // are serialised into the content so the model has them in context.\n let content = typeof msg.content === \"string\" ? msg.content : \"\";\n if (msg.role === \"assistant\" && msg.tool_calls && msg.tool_calls.length > 0) {\n const callsLine = msg.tool_calls\n .map(\n (c) =>\n `[tool_call ${c.function.name} args=${c.function.arguments}]`,\n )\n .join(\"\\n\");\n content = content ? `${content}\\n${callsLine}` : callsLine;\n }\n if (msg.role === \"tool\" && msg.tool_call_id) {\n content = `[tool_result id=${msg.tool_call_id}] ${content}`;\n }\n messages.push({ role: msg.role, content });\n }\n\n return {\n provider: undefined, // cloud picks the default \u2014 see CODING_AGENT_DEFAULT_PROVIDER\n model: req.model, // pass through for telemetry; cloud may override\n systemPrompt,\n temperature: req.temperature,\n messages,\n tools: req.tools,\n };\n};\n\n// ----------------------------------------------------------------------------\n// Response: broker \u2192 OpenAI\n// ----------------------------------------------------------------------------\n\nexport interface OpenAIChatCompletionResponse {\n id: string;\n object: \"chat.completion\";\n created: number;\n model: string;\n choices: Array<{\n index: 0;\n message: {\n role: \"assistant\";\n content: string | null;\n tool_calls?: Array<{\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }>;\n };\n finish_reason: \"stop\" | \"tool_calls\" | \"length\" | \"content_filter\";\n }>;\n usage?: {\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n };\n}\n\n/** Non-streaming response. Used when the request didn't ask for stream. */\nexport const translateResponse = (\n response: LlmBrokerResponsePayload,\n id: string,\n modelEcho: string,\n): OpenAIChatCompletionResponse => {\n const hasToolCalls = response.toolCalls && response.toolCalls.length > 0;\n const finish_reason: OpenAIChatCompletionResponse[\"choices\"][number][\"finish_reason\"] =\n response.finishReason ?? (hasToolCalls ? \"tool_calls\" : \"stop\");\n return {\n id,\n object: \"chat.completion\",\n created: Math.floor(Date.now() / 1000),\n // Echo the model the client requested. The cloud may have routed to\n // a different actual model; we surface that in `response.model` but\n // OpenAI clients expect the echoed model id to match their request.\n model: modelEcho,\n choices: [\n {\n index: 0,\n message: {\n role: \"assistant\",\n content: response.text || null,\n ...(hasToolCalls ? { tool_calls: response.toolCalls } : {}),\n },\n finish_reason,\n },\n ],\n ...(response.tokenUsage\n ? {\n usage: {\n prompt_tokens: response.tokenUsage.input,\n completion_tokens: response.tokenUsage.output,\n total_tokens:\n response.tokenUsage.input + response.tokenUsage.output,\n },\n }\n : {}),\n };\n};\n\n// ----------------------------------------------------------------------------\n// Streaming response \u2014 buffered v1\n// ----------------------------------------------------------------------------\n//\n// True end-to-end streaming would require the broker chain to be\n// streaming-aware. For v1 we BUFFER the broker result and emit it as a\n// small set of SSE chunks (role+content, optional tool_calls, [DONE]).\n// Opencode's AI SDK consumer is fine with this \u2014 it just sees the\n// stream finish quickly. Real streaming is a follow-up.\n\nexport interface OpenAIChatCompletionChunk {\n id: string;\n object: \"chat.completion.chunk\";\n created: number;\n model: string;\n choices: Array<{\n index: 0;\n delta: {\n role?: \"assistant\";\n content?: string;\n tool_calls?: Array<{\n index: number;\n id?: string;\n type?: \"function\";\n function?: { name?: string; arguments?: string };\n }>;\n };\n finish_reason: null | \"stop\" | \"tool_calls\" | \"length\" | \"content_filter\";\n }>;\n}\n\n/** Returns the sequence of SSE-event JSON payloads to send for one\n * buffered response. The caller wraps each in `data: <json>\\n\\n` and\n * appends a final `data: [DONE]\\n\\n`. */\nexport const translateResponseToChunks = (\n response: LlmBrokerResponsePayload,\n id: string,\n modelEcho: string,\n): OpenAIChatCompletionChunk[] => {\n const created = Math.floor(Date.now() / 1000);\n const baseChunk = (\n delta: OpenAIChatCompletionChunk[\"choices\"][number][\"delta\"],\n finish_reason: OpenAIChatCompletionChunk[\"choices\"][number][\"finish_reason\"] = null,\n ): OpenAIChatCompletionChunk => ({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: modelEcho,\n choices: [{ index: 0, delta, finish_reason }],\n });\n\n const chunks: OpenAIChatCompletionChunk[] = [];\n\n // 1. Role chunk (OpenAI convention).\n chunks.push(baseChunk({ role: \"assistant\" }));\n\n // 2. Content chunk(s). For now one chunk containing the whole text.\n if (response.text) {\n chunks.push(baseChunk({ content: response.text }));\n }\n\n // 3. Tool call chunks. OpenAI streams these as incremental deltas\n // per call. Since we have the full result, emit one chunk per\n // tool call containing everything at once.\n if (response.toolCalls) {\n for (let i = 0; i < response.toolCalls.length; i++) {\n const tc = response.toolCalls[i]!;\n chunks.push(\n baseChunk({\n tool_calls: [\n {\n index: i,\n id: tc.id,\n type: \"function\",\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n },\n ],\n }),\n );\n }\n }\n\n // 4. Finish-reason chunk.\n const hasToolCalls = response.toolCalls && response.toolCalls.length > 0;\n chunks.push(baseChunk({}, response.finishReason ?? (hasToolCalls ? \"tool_calls\" : \"stop\")));\n\n return chunks;\n};\n", "// Translator between OpenAI Responses API and OpenAI Chat Completions.\n//\n// Why: opencode 1.x targets the AI SDK's `@ai-sdk/openai` provider,\n// which moved to the Responses API as its default wire format. Our\n// existing shim only speaks Chat Completions. Rather than ripping out\n// the existing shim, we translate at the edge:\n//\n// Inbound: Responses request body \u2192 Chat Completions request body\n// (then reuse openai-translator.translateRequest to get a\n// broker payload \u2014 the broker layer never sees Responses\n// API at all)\n//\n// Outbound: broker stream chunks \u2192 Responses API SSE events\n// (we don't go via Chat Completions chunks here because\n// the Responses API event vocabulary is rich enough that\n// double-translation would lose context, e.g. item IDs)\n//\n// Wire references:\n// Responses API docs: https://platform.openai.com/docs/api-reference/responses\n// AI SDK v3+ openai provider: defaults to Responses API for stream\n\nimport type { OpenAIChatCompletionRequest, OpenAIChatMessage, OpenAITool } from \"./openai-translator\";\nimport type { LlmStreamChunk } from \"../../../shared/coding-agent-types\";\n\n// ---- Request shapes (what we receive on POST /v1/responses) --------\n\nexport interface ResponsesApiRequest {\n model: string;\n input: ResponsesInputItem[];\n /** Responses API uses max_output_tokens; Chat Completions used max_tokens. */\n max_output_tokens?: number;\n temperature?: number;\n /** Responses tools use a FLAT shape \u2014 name/description/parameters live\n * at the top level of the tool, not under a `function` key. */\n tools?: ResponsesTool[];\n tool_choice?: \"auto\" | \"none\" | \"required\" | { type: \"function\"; name: string };\n stream?: boolean;\n /** Whether OpenAI should persist the response server-side. We always\n * ignore this; we're not OpenAI. */\n store?: boolean;\n /** Tolerate other fields the SDK adds (parallel_tool_calls, etc.). */\n [key: string]: unknown;\n}\n\nexport type ResponsesInputItem =\n | {\n role: \"system\" | \"user\" | \"assistant\";\n content: string | ResponsesContentPart[];\n }\n | {\n type: \"function_call\";\n call_id: string;\n name: string;\n arguments: string;\n }\n | {\n type: \"function_call_output\";\n call_id: string;\n output: string;\n };\n\nexport type ResponsesContentPart =\n | { type: \"input_text\"; text: string }\n | { type: \"output_text\"; text: string }\n // Tolerate other content types (input_image, refusal, etc.) \u2014 we\n // flatten unsupported parts to empty text rather than throwing.\n | { type: string; [key: string]: unknown };\n\nexport interface ResponsesTool {\n type: \"function\";\n name: string;\n description?: string;\n parameters?: Record<string, unknown>;\n}\n\n// ---- Request: Responses \u2192 Chat Completions -------------------------\n\nconst flattenContent = (\n content: string | ResponsesContentPart[],\n): string => {\n if (typeof content === \"string\") return content;\n return content\n .map((part) => {\n if (\n part.type === \"input_text\" ||\n part.type === \"output_text\"\n ) {\n return (part as { text?: string }).text ?? \"\";\n }\n return \"\";\n })\n .join(\"\");\n};\n\n/** Translate a Responses API request into the older Chat Completions\n * shape our existing shim understands. The broker layer is downstream\n * of openai-translator.translateRequest and stays untouched. */\nexport const responsesToChatCompletions = (\n req: ResponsesApiRequest,\n): OpenAIChatCompletionRequest => {\n const messages: OpenAIChatMessage[] = [];\n\n for (const item of req.input) {\n if (\"type\" in item && item.type === \"function_call\") {\n // Assistant tool-call turn in the rolling transcript: re-emit as\n // an assistant message with a tool_calls entry.\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: [\n {\n id: item.call_id,\n type: \"function\",\n function: { name: item.name, arguments: item.arguments },\n },\n ],\n });\n continue;\n }\n if (\"type\" in item && item.type === \"function_call_output\") {\n messages.push({\n role: \"tool\",\n content: item.output,\n tool_call_id: item.call_id,\n });\n continue;\n }\n // Plain conversation turn (system/user/assistant).\n const role = item.role;\n const content = flattenContent(item.content);\n messages.push({ role, content });\n }\n\n const tools: OpenAITool[] | undefined = req.tools?.map((t) => ({\n type: \"function\",\n function: {\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n },\n }));\n\n // tool_choice: Responses' { type: \"function\", name: \"x\" } \u2192 Chat's\n // { type: \"function\", function: { name: \"x\" } }\n let toolChoice: OpenAIChatCompletionRequest[\"tool_choice\"] | undefined;\n if (typeof req.tool_choice === \"string\") {\n toolChoice = req.tool_choice;\n } else if (req.tool_choice && typeof req.tool_choice === \"object\") {\n toolChoice = {\n type: \"function\",\n function: { name: req.tool_choice.name },\n };\n }\n\n return {\n model: req.model,\n messages,\n tools,\n tool_choice: toolChoice,\n temperature: req.temperature,\n stream: req.stream,\n // Pass max_output_tokens through as max_tokens for downstream\n // providers that honour it. Chat Completions naming.\n max_tokens: req.max_output_tokens,\n };\n};\n\n// ---- Streaming response: broker chunks \u2192 Responses API events ------\n\n/** Minimal subset of the Responses API streaming event shape we emit.\n * Every event has a top-level `type` (used by the AI SDK) and an\n * optional payload. We also write the `event:` SSE field, matching\n * OpenAI's wire. */\nexport interface ResponsesStreamEvent {\n event: string;\n data: Record<string, unknown>;\n}\n\nconst randomId = (prefix: string): string =>\n `${prefix}_${Math.random().toString(36).slice(2, 12)}`;\n\n/** Stateful translator. One instance per turn. Call methods in the\n * order the broker emits chunks; `start()` first, `chunk()` per\n * delta, `finish()` last. Each method returns the SSE events to\n * write \u2014 never returns null. */\nexport class ResponsesStreamState {\n private readonly responseId: string;\n private readonly model: string;\n private readonly createdAt: number;\n private outputIndex = 0;\n\n /** Open message item carrying assistant text, if any. */\n private text:\n | { itemId: string; outputIndex: number; contentIndex: number; buffer: string }\n | null = null;\n\n /** Open function_call items, keyed by the broker's tool_call index\n * (the model's call position, not our output_index). */\n private readonly tools = new Map<\n number,\n { itemId: string; outputIndex: number; callId: string; name: string; arguments: string; emittedAdded: boolean }\n >();\n\n private finished = false;\n /** Set when broker emits `complete`. We carry usage + finish_reason\n * into the final response.completed payload. */\n private finalUsage:\n | { input_tokens?: number; output_tokens?: number; total_tokens?: number }\n | undefined;\n private finalStatus: \"completed\" | \"incomplete\" | \"failed\" = \"completed\";\n\n constructor(model: string, id?: string) {\n this.responseId = id ?? randomId(\"resp\");\n this.model = model;\n this.createdAt = Math.floor(Date.now() / 1000);\n }\n\n /** Emit the response.created + response.in_progress pair. Call once\n * before any chunks. */\n start(): ResponsesStreamEvent[] {\n const responseObj = {\n id: this.responseId,\n object: \"response\",\n created_at: this.createdAt,\n status: \"in_progress\",\n model: this.model,\n output: [] as unknown[],\n usage: null,\n };\n return [\n { event: \"response.created\", data: { type: \"response.created\", response: responseObj } },\n { event: \"response.in_progress\", data: { type: \"response.in_progress\", response: responseObj } },\n ];\n }\n\n /** Translate a single broker chunk. Some chunks emit multiple events\n * (opening a new item requires output_item.added + content_part.added\n * before the delta itself). */\n chunk(c: LlmStreamChunk): ResponsesStreamEvent[] {\n if (this.finished) return [];\n switch (c.type) {\n case \"text_delta\":\n return this.handleTextDelta(c.text);\n case \"tool_call_delta\":\n return this.handleToolCallDelta(c);\n case \"complete\":\n // The broker's complete payload carries final usage + finish\n // reason. We don't emit response.completed here yet \u2014 that\n // happens in finish(). Just capture the data so finish() has it.\n // The broker's tokenUsage is {input,output}; rename to the\n // Responses API shape on the way out.\n {\n const tu = (c.payload as { tokenUsage?: { input?: number; output?: number } })\n ?.tokenUsage;\n if (tu) {\n const input = tu.input ?? 0;\n const output = tu.output ?? 0;\n this.finalUsage = {\n input_tokens: input,\n output_tokens: output,\n total_tokens: input + output,\n };\n }\n }\n // finish_reason may be on the payload's choices[0]; we don't\n // currently distinguish \"stop\" vs \"tool_calls\" in the\n // completed event so just default to \"completed\" status.\n return [];\n case \"error\":\n // The route handler is expected to catch broker errors and\n // emit response.failed itself; nothing for us to translate.\n this.finalStatus = \"failed\";\n return [];\n }\n }\n\n /** Close any open items + emit the terminal response.completed event. */\n finish(): ResponsesStreamEvent[] {\n if (this.finished) return [];\n this.finished = true;\n const out: ResponsesStreamEvent[] = [];\n out.push(...this.closeTextItem());\n out.push(...this.closeAllToolItems());\n\n const responseObj = {\n id: this.responseId,\n object: \"response\",\n created_at: this.createdAt,\n status: this.finalStatus,\n model: this.model,\n output: [], // For brevity \u2014 the AI SDK only needs status + usage at completion.\n usage: this.finalUsage ?? null,\n };\n out.push({\n event: \"response.completed\",\n data: { type: \"response.completed\", response: responseObj },\n });\n return out;\n }\n\n // ---- internals ----\n\n private handleTextDelta(text: string): ResponsesStreamEvent[] {\n if (!this.text) {\n // Open a new message item + content part before the first delta.\n const itemId = randomId(\"msg\");\n const outputIndex = this.outputIndex++;\n const contentIndex = 0;\n this.text = { itemId, outputIndex, contentIndex, buffer: text };\n return [\n {\n event: \"response.output_item.added\",\n data: {\n type: \"response.output_item.added\",\n output_index: outputIndex,\n item: {\n id: itemId,\n type: \"message\",\n status: \"in_progress\",\n role: \"assistant\",\n content: [],\n },\n },\n },\n {\n event: \"response.content_part.added\",\n data: {\n type: \"response.content_part.added\",\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: \"\" },\n },\n },\n {\n event: \"response.output_text.delta\",\n data: {\n type: \"response.output_text.delta\",\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n delta: text,\n },\n },\n ];\n }\n this.text.buffer += text;\n return [\n {\n event: \"response.output_text.delta\",\n data: {\n type: \"response.output_text.delta\",\n item_id: this.text.itemId,\n output_index: this.text.outputIndex,\n content_index: this.text.contentIndex,\n delta: text,\n },\n },\n ];\n }\n\n private handleToolCallDelta(c: {\n type: \"tool_call_delta\";\n index: number;\n id?: string;\n name?: string;\n argumentsDelta?: string;\n }): ResponsesStreamEvent[] {\n const events: ResponsesStreamEvent[] = [];\n\n // Text and tool calls are separate output items. If text is open,\n // we have to close it before adding the tool-call item.\n events.push(...this.closeTextItem());\n\n let entry = this.tools.get(c.index);\n if (!entry) {\n entry = {\n itemId: randomId(\"fc\"),\n outputIndex: this.outputIndex++,\n callId: c.id ?? randomId(\"call\"),\n name: c.name ?? \"\",\n arguments: \"\",\n emittedAdded: false,\n };\n this.tools.set(c.index, entry);\n } else {\n // Later deltas may carry id/name we missed on the first one.\n if (c.id) entry.callId = c.id;\n if (c.name) entry.name = c.name;\n }\n\n if (!entry.emittedAdded && entry.name) {\n // We have enough to emit the item header. Delay until we have a\n // name so the event payload is well-formed.\n entry.emittedAdded = true;\n events.push({\n event: \"response.output_item.added\",\n data: {\n type: \"response.output_item.added\",\n output_index: entry.outputIndex,\n item: {\n id: entry.itemId,\n type: \"function_call\",\n status: \"in_progress\",\n name: entry.name,\n call_id: entry.callId,\n arguments: \"\",\n },\n },\n });\n }\n\n if (c.argumentsDelta !== undefined && c.argumentsDelta.length > 0) {\n entry.arguments += c.argumentsDelta;\n if (entry.emittedAdded) {\n events.push({\n event: \"response.function_call_arguments.delta\",\n data: {\n type: \"response.function_call_arguments.delta\",\n item_id: entry.itemId,\n output_index: entry.outputIndex,\n delta: c.argumentsDelta,\n },\n });\n }\n // If we haven't emitted the added event yet (name still missing),\n // we buffer the arguments and flush when we get the name. The\n // common case is name arrives in the first chunk so this is rare.\n }\n\n return events;\n }\n\n private closeTextItem(): ResponsesStreamEvent[] {\n if (!this.text) return [];\n const { itemId, outputIndex, contentIndex, buffer } = this.text;\n this.text = null;\n return [\n {\n event: \"response.output_text.done\",\n data: {\n type: \"response.output_text.done\",\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n text: buffer,\n },\n },\n {\n event: \"response.content_part.done\",\n data: {\n type: \"response.content_part.done\",\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: buffer },\n },\n },\n {\n event: \"response.output_item.done\",\n data: {\n type: \"response.output_item.done\",\n output_index: outputIndex,\n item: {\n id: itemId,\n type: \"message\",\n status: \"completed\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: buffer }],\n },\n },\n },\n ];\n }\n\n private closeAllToolItems(): ResponsesStreamEvent[] {\n const out: ResponsesStreamEvent[] = [];\n for (const entry of this.tools.values()) {\n if (!entry.emittedAdded) {\n // Edge case: we never had a name, so we couldn't open the item.\n // Open and immediately close so the SDK sees a valid sequence.\n entry.emittedAdded = true;\n out.push({\n event: \"response.output_item.added\",\n data: {\n type: \"response.output_item.added\",\n output_index: entry.outputIndex,\n item: {\n id: entry.itemId,\n type: \"function_call\",\n status: \"in_progress\",\n name: entry.name || \"unknown\",\n call_id: entry.callId,\n arguments: \"\",\n },\n },\n });\n }\n out.push({\n event: \"response.function_call_arguments.done\",\n data: {\n type: \"response.function_call_arguments.done\",\n item_id: entry.itemId,\n output_index: entry.outputIndex,\n arguments: entry.arguments,\n },\n });\n out.push({\n event: \"response.output_item.done\",\n data: {\n type: \"response.output_item.done\",\n output_index: entry.outputIndex,\n item: {\n id: entry.itemId,\n type: \"function_call\",\n status: \"completed\",\n name: entry.name || \"unknown\",\n call_id: entry.callId,\n arguments: entry.arguments,\n },\n },\n });\n }\n this.tools.clear();\n return out;\n }\n}\n", "// Active-broker tracking for the OpenAI-compat shim.\n//\n// The shim endpoint receives outbound HTTP from opencode. Opencode's\n// AI-SDK provider doesn't expose which coding session is making the\n// call \u2014 it's a stateless provider call. We need a way to route the\n// shim's request to the right LlmBroker.\n//\n// Approach:\n// Per-stream tokens. Each /agent/message stream binds its broker with\n// `bindActiveBroker(broker)`, getting back a unique token. That token\n// becomes the `apiKey` opencode supplies via the provider config (or\n// via the placeholder mechanism described in opencode-adapter.ts).\n// The shim looks up the broker by token \u2014 multiple concurrent streams\n// coexist cleanly because each has its own token.\n\nimport { randomUUID } from \"node:crypto\";\n\nimport type { LlmBroker } from \"../broker\";\n\ninterface ActiveBrokerEntry {\n token: string;\n broker: LlmBroker;\n}\n\nconst byToken = new Map<string, ActiveBrokerEntry>();\n\nexport interface ActiveBrokerHandle {\n token: string;\n release(): void;\n}\n\n/** Bind a broker as active. Returns a unique token the adapter passes\n * to opencode as the apiKey, plus a release() to call when done. */\nexport const bindActiveBroker = (broker: LlmBroker): ActiveBrokerHandle => {\n const token = `dlg-${randomUUID()}`;\n byToken.set(token, { token, broker });\n return {\n token,\n release() {\n // Only delete if it's still THIS entry. A racing rebind under the\n // same token (which would only happen if randomUUID collided \u2014\n // 1-in-2^122) is the only way someone else could hold it.\n const current = byToken.get(token);\n if (current && current.broker === broker) {\n byToken.delete(token);\n }\n },\n };\n};\n\n/** Look up the broker by token. Returns null when the token doesn't\n * match any active stream \u2014 the shim treats that as 401. */\nexport const getActiveBrokerByToken = (token: string): LlmBroker | null => {\n return byToken.get(token)?.broker ?? null;\n};\n\n/** Fallback used by the shim when the bearer token doesn't match\n * exactly. opencode's @ai-sdk/openai provider doesn't support\n * per-call apiKey overrides; the adapter sets a placeholder at\n * server-spawn time and opencode echoes it as the bearer forever.\n *\n * When exactly ONE broker is bound, we use it \u2014 that's the MVP\n * single-stream case. When two or more brokers are concurrently\n * bound, we return null so the request 401s rather than randomly\n * cross-wiring streams. Multi-stream support requires a deeper\n * refactor (per-stream opencode servers, or an SDK that accepts\n * per-call apiKey) that's outside this fix's scope. */\nexport const getOnlyActiveBroker = (): LlmBroker | null => {\n if (byToken.size !== 1) return null;\n const first = byToken.values().next().value;\n return first?.broker ?? null;\n};\n\n/** Test-only: number of currently-bound brokers. */\nexport const _activeBrokerCount = (): number => byToken.size;\n\n/** Test-only: clear all bindings. */\nexport const _resetActiveBroker = (): void => {\n byToken.clear();\n};\n", "// Deterministic mock adapter.\n//\n// Purpose:\n// 1. Lets the rest of the helper (SSE route, chat UI) be built and tested\n// before we wire the real opencode subprocess in Step 9.\n// 2. Serves as the documented fallback when the real adapter can't\n// satisfy a call (we'd rather show the user *something* than fail).\n//\n// Behaviour for one turn:\n// - Emits ~6 text_delta events that together read as a plausible\n// assistant reply referencing the user's prompt.\n// - Emits one tool_call_start + tool_call_end pair representing a\n// synthetic \"fs.read README.md\" call.\n// - Emits one diff_proposed with a small unified diff against a file\n// called `MOCK_NOTES.md` (so the apply flow can be exercised in a real\n// repo without touching tracked files).\n// - Emits done with a synthetic messageId.\n//\n// Timing is configurable so tests can run at zero delay while interactive\n// dev runs have a small pause between tokens for a more realistic feel.\n\nimport { setTimeout as delay } from \"node:timers/promises\";\n\nimport type {\n AgentStreamEvent,\n AgentDiffProposed,\n} from \"../../../shared/coding-agent-types\";\nimport type { AdapterRequest, OpenCodeAdapter } from \"./types\";\n\nexport interface MockAdapterOptions {\n /** Milliseconds between successive text_delta events. 0 makes it\n * effectively synchronous, which is what tests want. */\n tokenDelayMs?: number;\n /** Skip emitting the synthetic tool-call pair. Tests that only care\n * about text streaming use this to cut noise. */\n skipToolCall?: boolean;\n /** Skip emitting the diff_proposed event. Tests for the \"no diff yet\"\n * branch use this. */\n skipDiff?: boolean;\n /** Override the synthetic messageId in the final `done` event. Useful\n * for tests that want a stable value. */\n fixedMessageId?: string;\n /** When set and a broker is available on the AdapterRequest, the mock\n * performs ONE round-trip through the broker before emitting its\n * canned text. This exercises the Step 8 brokered-LLM bridge end to\n * end without needing the real opencode binary. */\n useBroker?: boolean;\n /** Provider hint sent to the broker. Only used when useBroker is true. */\n brokerProvider?: string;\n /** Model hint sent to the broker. Only used when useBroker is true. */\n brokerModel?: string;\n}\n\nconst RESPONSE_FRAGMENTS = [\n \"Got it \u2014 looking at\",\n \" the repo at\",\n \" {repoPath}.\",\n \"\\n\\nThis is a mock response from MockOpenCodeAdapter,\",\n \" so the actual analysis hasn't run.\",\n \" The real opencode integration lands in Step 9; until then\",\n \" every prompt produces this same canned reply plus a small synthetic\",\n \" diff so you can exercise the apply flow safely.\",\n];\n\nconst SYNTHETIC_DIFF: AgentDiffProposed = {\n type: \"diff_proposed\",\n unified: [\n \"diff --git a/MOCK_NOTES.md b/MOCK_NOTES.md\",\n \"new file mode 100644\",\n \"index 0000000..1111111\",\n \"--- /dev/null\",\n \"+++ b/MOCK_NOTES.md\",\n \"@@ -0,0 +1,3 @@\",\n \"+# Mock notes\",\n \"+\",\n \"+Created by MockOpenCodeAdapter to demonstrate the diff flow.\",\n \"\",\n ].join(\"\\n\"),\n files: [\n {\n path: \"MOCK_NOTES.md\",\n additions: 3,\n deletions: 0,\n status: \"added\",\n },\n ],\n};\n\nexport class MockOpenCodeAdapter implements OpenCodeAdapter {\n readonly name = \"mock\";\n\n constructor(private readonly options: MockAdapterOptions = {}) {}\n\n async *streamMessage(\n request: AdapterRequest,\n ): AsyncIterable<AgentStreamEvent> {\n const tokenDelay = this.options.tokenDelayMs ?? 0;\n\n // If broker-mode is on and a broker is wired, do ONE round-trip first.\n // The response text isn't streamed to the user verbatim \u2014 instead we\n // prepend a single text_delta that proves the bridge worked. The\n // rest of the canned events follow as normal.\n let brokerPrefix: string | null = null;\n if (this.options.useBroker && request.broker) {\n this.throwIfAborted(request.signal);\n try {\n const response = await request.broker({\n provider: this.options.brokerProvider,\n model: this.options.brokerModel,\n messages: [\n { role: \"user\", content: request.prompt },\n ],\n });\n brokerPrefix =\n `[brokered via ${response.provider}:${response.model}] ` +\n response.text.slice(0, 200);\n } catch (err) {\n // Don't tear down the whole turn \u2014 emit an error envelope and\n // continue with canned text so the user still sees something.\n yield {\n type: \"error\",\n message: `Brokered LLM call failed: ${(err as Error).message}`,\n recoverable: true,\n };\n }\n }\n\n if (brokerPrefix) {\n yield { type: \"text_delta\", text: brokerPrefix + \"\\n\\n\" };\n }\n\n // Text deltas. We treat the prompt itself opaquely; the only\n // substitution is {repoPath} so the response feels grounded.\n for (const fragment of RESPONSE_FRAGMENTS) {\n this.throwIfAborted(request.signal);\n const text = fragment.replace(\"{repoPath}\", request.repoPath);\n yield { type: \"text_delta\", text };\n if (tokenDelay > 0) {\n await delay(tokenDelay);\n }\n }\n\n if (!this.options.skipToolCall) {\n this.throwIfAborted(request.signal);\n const toolCallId = `mock-tool-${Date.now()}`;\n yield {\n type: \"tool_call_start\",\n toolCallId,\n tool: \"fs.read\",\n args: { path: \"README.md\" },\n };\n if (tokenDelay > 0) {\n await delay(tokenDelay);\n }\n yield {\n type: \"tool_call_end\",\n toolCallId,\n ok: true,\n summary: \"Read README.md (mock \u2014 no actual file IO performed)\",\n };\n }\n\n if (!this.options.skipDiff) {\n this.throwIfAborted(request.signal);\n yield SYNTHETIC_DIFF;\n }\n\n this.throwIfAborted(request.signal);\n yield {\n type: \"done\",\n messageId:\n this.options.fixedMessageId ??\n `mock-msg-${request.sessionId}-${Date.now()}`,\n };\n }\n\n private throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) {\n // We deliberately throw rather than emit AgentError so the SSE route\n // can distinguish \"cancelled by caller\" from \"adapter blew up.\"\n const err = new Error(\"aborted\");\n (err as Error & { name: string }).name = \"AbortError\";\n throw err;\n }\n }\n}\n", "// Translate opencode's SDK event union \u2192 our AgentStreamEvent envelope.\n//\n// Pulled out as a pure function so it's trivially unit-testable without\n// spawning opencode. The adapter feeds events into mapEvent() and forwards\n// whatever non-null AgentStreamEvent(s) come back.\n//\n// References (sdk node_modules paths):\n// - dist/gen/types.gen.d.ts \u2192 Event, Part, ToolState, EventMessagePart*\n//\n// Mapping rules:\n// - EventMessagePartUpdated with a text Part \u2192 text_delta carrying the\n// `delta` field (the chunk just added) or the whole `text` on first\n// emission if no delta is present.\n// - EventMessagePartUpdated with a tool Part transitions:\n// pending/running \u2192 tool_call_start (idempotent: we de-dup by callID\n// in the adapter so multiple updates don't spam).\n// completed \u2192 tool_call_end (ok: true) with the tool's title as\n// the summary.\n// error \u2192 tool_call_end (ok: false) with the error message.\n// - EventMessagePartUpdated with a patch Part \u2192 diff_proposed. The\n// SDK's PatchPart only carries a hash + file list; we DO NOT have\n// the unified diff text here. The adapter pairs this with a\n// subsequent client.session.diff() call to fetch the actual diff.\n// - EventSessionIdle for our session \u2192 signals end-of-turn. The\n// adapter emits a `done` event when it sees this.\n// - Everything else \u2192 ignored at v1 (reasoning parts, step-start/finish,\n// compaction, session.status, file.edited, etc.). Some of these will\n// become useful in later phases (e.g. reasoning panel) but ignoring\n// them keeps the wire shape stable.\n\nimport type {\n AgentStreamEvent,\n AgentToolCallEnd,\n AgentToolCallStart,\n} from \"../../../shared/coding-agent-types\";\n\n/** What the adapter needs to know about an event stream item to translate\n * it. We declare our own minimal interface here so the mapper isn't\n * coupled to the SDK's `Event` union (which would force a runtime import\n * in tests). The opencode SDK types are structurally compatible. */\nexport interface OpencodeStreamItem {\n type: string;\n properties?: {\n sessionID?: string;\n part?: OpencodePart;\n delta?: string;\n // For `permission.updated`, the SDK's Permission rides directly on\n // `properties` (id/type/title/metadata), not under `part`.\n id?: string;\n /** Permission category for `permission.updated` \u2014 \"bash\"/\"webfetch\"/etc. */\n type?: string;\n title?: string;\n metadata?: Record<string, unknown>;\n };\n}\n\nexport interface OpencodePart {\n id?: string;\n type: string;\n text?: string;\n callID?: string;\n tool?: string;\n state?: {\n status: \"pending\" | \"running\" | \"completed\" | \"error\";\n input?: Record<string, unknown>;\n title?: string;\n error?: string;\n /** Full textual result the model sees \u2014 bash stdout, file contents,\n * edit confirmation, etc. opencode's ToolStateCompleted carries this. */\n output?: string;\n };\n files?: string[];\n}\n\n/** Cap forwarded tool output so a `cat` of a huge file or a chatty build\n * can't bloat the SSE stream or the persisted transcript row. */\nconst MAX_TOOL_OUTPUT_CHARS = 4000;\n\nconst truncateOutput = (raw: string | undefined): string | undefined => {\n if (!raw) return undefined;\n if (raw.length <= MAX_TOOL_OUTPUT_CHARS) return raw;\n const omitted = raw.length - MAX_TOOL_OUTPUT_CHARS;\n return `${raw.slice(0, MAX_TOOL_OUTPUT_CHARS)}\\n\u2026 [${omitted} more characters truncated]`;\n};\n\n/** Per-stream state the mapper threads between calls so we can de-dup\n * repeated tool_call_start emissions for the same callID, and so the\n * caller can know when a patch was seen (and should fetch the unified\n * diff out-of-band). */\nexport interface MapState {\n ourSessionId: string;\n openCodeSessionId: string;\n /** Tool callIDs we've already announced as started. */\n startedTools: Set<string>;\n /** Tool callIDs we've already announced as ended (so a duplicate\n * completed/error event doesn't double-fire). */\n endedTools: Set<string>;\n /** Hashes of patches we've already announced. */\n announcedPatches: Set<string>;\n /** Permission ids we've already surfaced (opencode re-emits the same\n * permission as its status changes; we only announce it once). */\n announcedPermissions: Set<string>;\n}\n\nexport const createMapState = (\n ourSessionId: string,\n openCodeSessionId: string,\n): MapState => ({\n ourSessionId,\n openCodeSessionId,\n startedTools: new Set(),\n endedTools: new Set(),\n announcedPatches: new Set(),\n announcedPermissions: new Set(),\n});\n\n/** Pull the concrete action out of a permission's metadata so the UI can\n * show \"rm -rf dist\" / \"https://\u2026\" rather than an opaque id. opencode keys\n * vary by tool; we probe the common ones and fall back to nothing (the\n * title then carries the context). */\nconst extractPermissionCommand = (\n metadata: Record<string, unknown> | undefined,\n): string | undefined => {\n if (!metadata) return undefined;\n for (const key of [\"command\", \"cmd\", \"url\", \"pattern\", \"filePath\"]) {\n const v = metadata[key];\n if (typeof v === \"string\" && v) return v;\n }\n return undefined;\n};\n\nexport interface MappedEvent {\n events: AgentStreamEvent[];\n /** True when this event signals the end of the current turn. */\n done: boolean;\n /** Set when the event is a PatchPart so the adapter knows to fetch the\n * unified diff via client.session.diff(). The hash + files list ride\n * on the diff_proposed event we emit; the adapter is responsible for\n * swapping in the actual unified diff after fetching. */\n patchToFetch?: { hash: string; files: string[] };\n}\n\nconst EMPTY: MappedEvent = { events: [], done: false };\n\nexport const mapEvent = (\n item: OpencodeStreamItem,\n state: MapState,\n): MappedEvent => {\n // Filter to events for our session. The SDK's event stream is GLOBAL\n // across the opencode instance, so the helper sees events from every\n // session it has open. Sessions other than ours are ignored.\n const sid = item.properties?.sessionID;\n if (sid && sid !== state.openCodeSessionId) {\n return EMPTY;\n }\n\n if (item.type === \"session.idle\" && sid === state.openCodeSessionId) {\n return { events: [{ type: \"done\" }], done: true };\n }\n\n if (item.type === \"permission.updated\") {\n // opencode paused a tool (bash/webfetch with permission \"ask\"). Surface\n // it once; the adapter decides whether to auto-approve (auto mode) or\n // forward to the browser (confirm mode).\n const permissionId = item.properties?.id;\n if (!permissionId || state.announcedPermissions.has(permissionId)) {\n return EMPTY;\n }\n state.announcedPermissions.add(permissionId);\n return {\n events: [\n {\n type: \"permission_request\",\n permissionId,\n tool: item.properties?.type ?? \"tool\",\n title: item.properties?.title,\n command: extractPermissionCommand(item.properties?.metadata),\n },\n ],\n done: false,\n };\n }\n\n if (item.type !== \"message.part.updated\") {\n return EMPTY;\n }\n const part = item.properties?.part;\n if (!part) return EMPTY;\n\n switch (part.type) {\n case \"text\": {\n // Prefer the delta if the SDK supplies it (incremental update);\n // fall back to the full text on the first emission.\n const delta = item.properties?.delta ?? part.text ?? \"\";\n if (!delta) return EMPTY;\n return { events: [{ type: \"text_delta\", text: delta }], done: false };\n }\n case \"tool\": {\n const callId = part.callID ?? part.id ?? \"\";\n const tool = part.tool ?? \"tool\";\n const status = part.state?.status;\n if (!callId || !status) return EMPTY;\n\n if (status === \"pending\" || status === \"running\") {\n if (state.startedTools.has(callId)) {\n return EMPTY;\n }\n state.startedTools.add(callId);\n const event: AgentToolCallStart = {\n type: \"tool_call_start\",\n toolCallId: callId,\n tool,\n args: part.state?.input,\n };\n return { events: [event], done: false };\n }\n\n if (status === \"completed\" || status === \"error\") {\n if (state.endedTools.has(callId)) {\n return EMPTY;\n }\n state.endedTools.add(callId);\n const event: AgentToolCallEnd = {\n type: \"tool_call_end\",\n toolCallId: callId,\n ok: status === \"completed\",\n summary:\n status === \"error\"\n ? part.state?.error ?? \"Tool failed\"\n : part.state?.title,\n output: truncateOutput(\n status === \"error\" ? part.state?.error : part.state?.output,\n ),\n };\n return { events: [event], done: false };\n }\n return EMPTY;\n }\n case \"patch\": {\n const hash = (part as { hash?: string }).hash ?? \"\";\n if (!hash || state.announcedPatches.has(hash)) {\n return EMPTY;\n }\n state.announcedPatches.add(hash);\n const files = part.files ?? [];\n // We can't classify add/modify/delete from the patch part alone \u2014\n // it just lists changed paths. The adapter swaps in the unified\n // diff after fetching it; until then we emit a placeholder so the\n // chat panel at least shows \"Diff proposed: N files\".\n return {\n events: [\n {\n type: \"diff_proposed\",\n unified: \"\",\n files: files.map((path) => ({\n path,\n additions: 0,\n deletions: 0,\n status: \"modified\" as const,\n })),\n },\n ],\n done: false,\n patchToFetch: { hash, files },\n };\n }\n default:\n return EMPTY;\n }\n};\n", "// EngineLocator \u2014 the seam that lets us swap how the helper acquires a\n// running opencode/diologue-engine server.\n//\n// Why this exists:\n//\n// In v1 (the MVP), OpenCodeProcessAdapter loaded @opencode-ai/sdk from\n// npm and called `sdk.createOpencodeServer()` to spawn an in-process\n// server. That works, but ties us to (a) the user having `opencode` on\n// PATH and (b) shipping an npm SDK version we don't control.\n//\n// The V1-V6 vendoring track introduces a rebranded engine built from\n// vendor/opencode/ (via scripts/rebrand-engine.ts, V2). To swap the\n// adapter cleanly from \"npm SDK\" \u2192 \"local bundled engine\" we need a\n// stable abstraction the adapter can depend on. That's EngineLocator.\n//\n// Two implementations live next to this file:\n//\n// - NpmSdkLocator (engine-locator-npm.ts)\n// Current behavior. Lazily imports @opencode-ai/sdk and uses its\n// createOpencodeServer/createOpencodeClient. Still requires the\n// `opencode` binary on PATH. This is the DEFAULT for now so the\n// MVP keeps working unchanged.\n//\n// - LocalEngineLocator (engine-locator-local.ts)\n// Scaffolding for the V4 path. Validates that build/diologue-engine/\n// exists and that the runtime (bun) is available, then \u2014 until V4\n// ships the bundled engine \u2014 throws a clear \"not yet wired\" error.\n// Opt-in via LOCAL_AGENT_ENGINE=local.\n//\n// Selection:\n//\n// createEngineLocator() reads LOCAL_AGENT_ENGINE (defaults to \"npm\").\n// Tests construct locators directly via the class constructors.\n\nimport type { OpencodeStreamItem } from \"./opencode-event-mapper\";\n\n// ---- Protocol shapes (the slice of opencode's API we depend on) -----\n\n/** Subset of opencode's session/global API surface. Declared narrowly so\n * the adapter file doesn't carry the entire generated SDK in its type\n * surface. */\nexport interface OpencodeClient {\n session: {\n create(options: {\n body?: { title?: string };\n query?: { directory?: string };\n }): Promise<{ data?: { id: string } }>;\n prompt(options: {\n path: { id: string };\n query?: { directory?: string };\n body: {\n parts: Array<{ type: \"text\"; text: string }>;\n system?: string;\n model?: { providerID: string; modelID: string };\n };\n }): Promise<unknown>;\n diff(options: {\n path: { id: string };\n query?: { directory?: string };\n }): Promise<{ data?: { unified?: string } }>;\n };\n /** Reply to a pending permission request. opencode pauses bash/webfetch\n * when configured with `permission: \"ask\"`; this resumes (or rejects) it.\n * `response`: once = allow this call, always = allow for the session,\n * reject = deny. */\n postSessionIdPermissionsPermissionId(options: {\n path: { id: string; permissionID: string };\n query?: { directory?: string };\n body?: { response: \"once\" | \"always\" | \"reject\" };\n }): Promise<unknown>;\n global: {\n event(options?: {\n signal?: AbortSignal;\n }): Promise<{ stream: AsyncIterable<OpencodeStreamItem> }>;\n };\n}\n\n/** Subset of opencode's Config we set. Mostly for registering the\n * Diologue-brokered provider. */\nexport interface OpencodeConfig {\n provider?: {\n [key: string]: {\n name?: string;\n npm?: string;\n models?: {\n [modelId: string]: { id?: string; name?: string };\n };\n options?: {\n apiKey?: string;\n baseURL?: string;\n };\n };\n };\n model?: string;\n /** Tool-permission policy. opencode gates edit/bash/webfetch behind a\n * permission prompt (`permission.updated` \u2192 `permission.replied`). In\n * this headless helper there is no interactive approver, so we must set\n * these to \"allow\" or the agent can never write to the repo. */\n permission?: {\n edit?: \"ask\" | \"allow\" | \"deny\";\n bash?: \"ask\" | \"allow\" | \"deny\";\n webfetch?: \"ask\" | \"allow\" | \"deny\";\n };\n}\n\n// ---- The locator interface itself -----------------------------------\n\nexport interface EngineHandle {\n /** Base URL of the running engine HTTP server. */\n readonly url: string;\n /** Client bound to that URL. */\n readonly client: OpencodeClient;\n /** Shut the engine down. Idempotent. */\n close(): Promise<void> | void;\n}\n\nexport interface EngineStartOptions {\n /** Provider/model config to register with the engine on startup. */\n config?: OpencodeConfig;\n /** Cancellation signal honoured by the locator. */\n signal?: AbortSignal;\n}\n\nexport interface EngineLocator {\n /** Human-readable name for logs + error messages. */\n readonly name: string;\n /** Bring an engine up and return a handle. The caller is responsible\n * for caching the handle for reuse across requests. */\n start(options: EngineStartOptions): Promise<EngineHandle>;\n}\n\n// ---- Factory --------------------------------------------------------\n\nexport type EngineLocatorName = \"npm\" | \"local\";\n\nexport const ENGINE_LOCATOR_ENV = \"LOCAL_AGENT_ENGINE\" as const;\n// V5 flip: the local (vendored, postinstall-fetched) engine is now the\n// default. Users can still opt into the legacy npm path with\n// LOCAL_AGENT_ENGINE=npm for debugging while the V5 release stabilises.\nexport const DEFAULT_LOCATOR_NAME: EngineLocatorName = \"local\";\n\nconst isLocatorName = (value: string): value is EngineLocatorName =>\n value === \"npm\" || value === \"local\";\n\n/** Build an EngineLocator from a name (or from process.env.LOCAL_AGENT_ENGINE).\n * Throws on an unknown name so a typo doesn't silently fall back to npm. */\nexport const createEngineLocator = async (\n name?: string,\n): Promise<EngineLocator> => {\n const resolved = name ?? process.env[ENGINE_LOCATOR_ENV] ?? DEFAULT_LOCATOR_NAME;\n if (!isLocatorName(resolved)) {\n throw new Error(\n `[engine-locator] Unknown engine \"${resolved}\". ` +\n `Set ${ENGINE_LOCATOR_ENV} to \"npm\" or \"local\".`,\n );\n }\n // Late-imported so this module stays free of locator-specific deps.\n if (resolved === \"npm\") {\n const { NpmSdkLocator } = await import(\"./engine-locator-npm\");\n return new NpmSdkLocator();\n }\n const { LocalEngineLocator } = await import(\"./engine-locator-local\");\n return new LocalEngineLocator();\n};\n", "// OpenCodeProcessAdapter \u2014 adapter wired through the EngineLocator seam.\n//\n// Lifecycle:\n// - Asks its EngineLocator for a running engine the first time\n// streamMessage is called. Handle is cached for reuse.\n// - For each AdapterRequest, looks up (or creates) an opencode session\n// bound to the request.repoPath. This gives the engine its native\n// conversation continuity within a coding-agent session.\n// - Subscribes to the global event stream once per turn, filters to\n// our opencode session, translates events via opencode-event-mapper,\n// and yields AgentStreamEvent envelopes.\n// - On `session.idle` we stop iterating, fetch the latest unified diff\n// if any patch was proposed, and emit a final `done` event.\n// - On `close()` we shut the engine down and clear caches.\n//\n// Engine sourcing \u2014 the EngineLocator seam:\n// The adapter no longer cares HOW the engine starts; it only depends\n// on EngineHandle ({ url, client, close }). Two locators ship today:\n//\n// - NpmSdkLocator (default) \u2014 wraps @opencode-ai/sdk + the user's\n// `opencode` binary on PATH. This is what the MVP used.\n// - LocalEngineLocator (opt-in via LOCAL_AGENT_ENGINE=local) \u2014\n// targets build/diologue-engine/ from the V2 rebrand. Scaffolding\n// only in V3; V4 wires the actual spawn.\n//\n// Tests inject locators directly. The legacy `sdkLoader` option is\n// still accepted (mapped to a NpmSdkLocator under the hood) so older\n// call sites keep working.\n\nimport type {\n AgentStreamEvent,\n AgentTextDelta,\n CodingPermissionResponse,\n} from \"../../../shared/coding-agent-types\";\nimport type { AdapterRequest, OpenCodeAdapter } from \"./types\";\nimport {\n createMapState,\n mapEvent,\n type MapState,\n type OpencodeStreamItem,\n} from \"./opencode-event-mapper\";\nimport { LlmBroker } from \"../broker\";\nimport { bindActiveBroker } from \"../shim/active-broker\";\n\nimport {\n createEngineLocator,\n type EngineHandle,\n type EngineLocator,\n type OpencodeClient,\n type OpencodeConfig,\n} from \"./engine-locator\";\nimport { NpmSdkLocator } from \"./engine-locator-npm\";\n\n/** The opencode model id we register against our shim provider. Echoed\n * back to the model layer; can be anything as long as it stays stable\n * across the provider config and the session.prompt model field. */\nconst DIOLOGUE_PROVIDER_ID = \"diologue\";\nconst DIOLOGUE_MODEL_ID = \"diologue-routed\";\n\n/** After `session.prompt()` resolves (the turn finished server-side) we wait\n * this long for any trailing `patch`/`session.idle` events before ending the\n * event loop ourselves. Keeps the happy path (idle arrives) intact while\n * guaranteeing a turn can't hang forever when idle never lands. */\nconst END_OF_TURN_GRACE_MS = 600;\n\n/** In confirm mode, how long a paused bash/webfetch waits for the user before\n * the helper auto-rejects it. Without this a closed tab / walked-away user\n * would hang the turn forever (prompt() never resolves \u2192 broker leaks). */\nconst PERMISSION_DECISION_TIMEOUT_MS = 180_000;\n\n/** Tracks an in-flight turn so the sibling /agent/permission route can reach\n * the engine client + opencode session to reply to a paused tool call. */\ninterface ActiveTurn {\n client: OpencodeClient;\n openCodeSessionId: string;\n repoPath: string;\n /** permissionId \u2192 auto-reject timer, for permissions awaiting the user. */\n pending: Map<string, ReturnType<typeof setTimeout>>;\n}\n\n/** Prepended to each turn's prompt before it reaches the engine. opencode's\n * models \u2014 gpt-4.1 especially \u2014 otherwise sometimes *describe* a change\n * instead of making it, ending the turn with a text answer and no file\n * written. This pushes the agent to actually use its edit tools. It is only\n * added to the engine-bound prompt; the UI transcript and the cloud-persisted\n * message keep the user's original text. */\nconst EDIT_DIRECTIVE =\n \"You are an autonomous coding agent operating on a real git repository. \" +\n \"Carry out the request by editing files directly with your tools \" +\n \"(write / edit / patch / bash) \u2014 do not just describe the change, outline \" +\n \"steps, or print code for the user to copy. Read only what you need, make \" +\n \"the edits on disk, and finish once the change has actually been written.\";\n\nconst logAdapter = (message: string): void => {\n console.error(`[opencode-adapter] ${message}`);\n};\n\n// ----------------------------------------------------------------------------\n// Adapter\n// ----------------------------------------------------------------------------\n\nexport interface OpenCodeProcessAdapterOptions {\n /** Engine locator. Defaults to one resolved from LOCAL_AGENT_ENGINE\n * (or `npm` when unset). Tests inject directly. */\n engineLocator?: EngineLocator;\n /** Base URL of the helper itself (so the engine can reach the shim).\n * Defaults to http://127.0.0.1:LOCAL_AGENT_PORT. Tests inject. */\n helperBaseUrl?: string;\n /** @deprecated Prefer `engineLocator`. Kept for tests written before\n * the V3 refactor \u2014 silently constructs a NpmSdkLocator under the hood. */\n sdkLoader?: () => Promise<unknown>;\n}\n\nexport class OpenCodeProcessAdapter implements OpenCodeAdapter {\n readonly name = \"opencode\";\n\n private serverPromise: Promise<EngineHandle> | null = null;\n /** Maps our sessionId \u2192 opencode session id so successive turns within\n * one coding-agent session reuse the same opencode conversation. */\n private readonly sessionMap = new Map<string, string>();\n /** In-flight turns by our sessionId, so replyPermission() can reach the\n * engine client to resolve a paused tool call. */\n private readonly activeTurns = new Map<string, ActiveTurn>();\n\n constructor(private readonly options: OpenCodeProcessAdapterOptions = {}) {}\n\n private async resolveLocator(): Promise<EngineLocator> {\n if (this.options.engineLocator) {\n return this.options.engineLocator;\n }\n if (this.options.sdkLoader) {\n // Backward compatibility for tests that used the pre-V3 surface.\n return new NpmSdkLocator({\n sdkLoader: this.options.sdkLoader as never,\n });\n }\n return createEngineLocator();\n }\n\n private resolveHelperBaseUrl(): string {\n if (this.options.helperBaseUrl) return this.options.helperBaseUrl;\n const port = process.env.LOCAL_AGENT_PORT ?? \"4099\";\n return `http://127.0.0.1:${port}`;\n }\n\n private buildShimConfig(): OpencodeConfig {\n const baseURL = `${this.resolveHelperBaseUrl()}/llm-shim/v1`;\n return {\n provider: {\n [DIOLOGUE_PROVIDER_ID]: {\n name: \"Diologue (brokered)\",\n // openai-compatible AI SDK provider \u2014 present in opencode's\n // bundled providers. Any other custom-base-URL provider would\n // work too; openai is the most permissive.\n npm: \"@ai-sdk/openai\",\n models: {\n [DIOLOGUE_MODEL_ID]: {\n id: DIOLOGUE_MODEL_ID,\n name: \"Diologue brokered model\",\n },\n },\n options: {\n baseURL,\n // The real apiKey gets injected per-stream via the\n // active-broker handle. This placeholder is what opencode\n // sees at config time; we'll override via env at runtime.\n apiKey: \"placeholder-will-be-replaced\",\n },\n },\n },\n model: `${DIOLOGUE_PROVIDER_ID}/${DIOLOGUE_MODEL_ID}`,\n // Permission policy. The ADAPTER is the policy engine, not this config:\n // - edit stays \"allow\" \u2014 edits land in the working tree and are\n // reversible via the chat's Keep/Revert, so we never prompt on them.\n // - bash + webfetch are \"ask\" so opencode pauses them. In \"auto\" mode\n // the adapter auto-approves instantly (behaves like allow); in\n // \"confirm\" mode it forwards an AgentPermissionRequest to the browser\n // and resumes only once the user replies. See streamMessage().\n permission: {\n edit: \"allow\",\n bash: \"ask\",\n webfetch: \"ask\",\n },\n };\n }\n\n private async ensureServer(): Promise<EngineHandle> {\n if (this.serverPromise) {\n return this.serverPromise;\n }\n this.serverPromise = (async () => {\n const locator = await this.resolveLocator();\n logAdapter(`starting engine helperBase=${this.resolveHelperBaseUrl()}`);\n const handle = await locator.start({ config: this.buildShimConfig() });\n logAdapter(`engine ready url=${handle.url}`);\n return handle;\n })();\n try {\n return await this.serverPromise;\n } catch (err) {\n // Reset so a transient failure can be retried on the next call.\n this.serverPromise = null;\n throw err;\n }\n }\n\n private async ensureOpencodeSession(\n handle: EngineHandle,\n ourSessionId: string,\n repoPath: string,\n title: string | undefined,\n ): Promise<string> {\n const existing = this.sessionMap.get(ourSessionId);\n if (existing) {\n logAdapter(\n `reuse opencodeSession=${existing.slice(0, 8)} session=${ourSessionId.slice(0, 8)} repo=${repoPath}`,\n );\n return existing;\n }\n logAdapter(`create session=${ourSessionId.slice(0, 8)} repo=${repoPath}`);\n const created = await handle.client.session.create({\n body: { title },\n query: { directory: repoPath },\n });\n const id = created.data?.id;\n if (!id) {\n throw new Error(\"opencode session.create returned no id\");\n }\n this.sessionMap.set(ourSessionId, id);\n logAdapter(\n `created opencodeSession=${id.slice(0, 8)} session=${ourSessionId.slice(0, 8)}`,\n );\n return id;\n }\n\n /** Send a decision for a paused permission to opencode. Best-effort: a\n * failure is logged but not thrown \u2014 the worst case is the turn hangs\n * until the grace/abort path tears it down. */\n private async replyToOpencode(\n client: OpencodeClient,\n openCodeSessionId: string,\n repoPath: string,\n permissionId: string,\n response: CodingPermissionResponse,\n ): Promise<void> {\n try {\n await client.postSessionIdPermissionsPermissionId({\n path: { id: openCodeSessionId, permissionID: permissionId },\n query: { directory: repoPath },\n body: { response },\n });\n logAdapter(\n `permission reply id=${permissionId.slice(0, 8)} response=${response}`,\n );\n } catch (err) {\n logAdapter(\n `permission reply FAILED id=${permissionId.slice(0, 8)} response=${response} error=${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n /** Apply a browser decision to a paused permission. Invoked by the\n * /agent/permission route on a separate HTTP request from the SSE stream. */\n async replyPermission(\n sessionId: string,\n permissionId: string,\n response: CodingPermissionResponse,\n ): Promise<boolean> {\n const turn = this.activeTurns.get(sessionId);\n if (!turn) return false;\n const timer = turn.pending.get(permissionId);\n if (timer) {\n clearTimeout(timer);\n turn.pending.delete(permissionId);\n }\n await this.replyToOpencode(\n turn.client,\n turn.openCodeSessionId,\n turn.repoPath,\n permissionId,\n response,\n );\n return true;\n }\n\n async *streamMessage(\n request: AdapterRequest,\n ): AsyncIterable<AgentStreamEvent> {\n let handle: EngineHandle;\n try {\n handle = await this.ensureServer();\n } catch (err) {\n yield {\n type: \"error\",\n message:\n err instanceof Error\n ? err.message\n : \"Failed to start opencode server\",\n recoverable: false,\n };\n return;\n }\n\n let openCodeSessionId: string;\n try {\n openCodeSessionId = await this.ensureOpencodeSession(\n handle,\n request.sessionId,\n request.repoPath,\n request.prompt.slice(0, 80),\n );\n } catch (err) {\n yield {\n type: \"error\",\n message:\n err instanceof Error\n ? err.message\n : \"Failed to start opencode session\",\n recoverable: true,\n };\n return;\n }\n\n const mapState: MapState = createMapState(\n request.sessionId,\n openCodeSessionId,\n );\n let sawAnyPatch = false;\n let lastPatchHash = \"\";\n\n // Permission policy for this turn. opencode is configured to \"ask\" for\n // bash/webfetch; in \"auto\" we approve instantly, in \"confirm\" we forward\n // to the browser and wait. Register the turn so /agent/permission can\n // reach the client to reply.\n const permissionMode = request.permissionMode ?? \"auto\";\n const activeTurn: ActiveTurn = {\n client: handle.client,\n openCodeSessionId,\n repoPath: request.repoPath,\n pending: new Map(),\n };\n this.activeTurns.set(request.sessionId, activeTurn);\n\n // Build a stream-scoped broker that forwards to the request.broker\n // callback when opencode (via the shim) needs an LLM call. We bind\n // it as the active shim broker for the duration of this turn so the\n // /llm-shim route can find it.\n //\n // If the AdapterRequest has no broker, the shim returns 401 and\n // opencode will surface that as an error \u2014 which is the correct\n // behaviour (we can't do anything without a brokered LLM path).\n let activeBrokerHandle: { token: string; release(): void } | null = null;\n let streamScopedBroker: LlmBroker | null = null;\n if (request.broker) {\n const callback = request.broker;\n streamScopedBroker = new LlmBroker({\n emitToStream: () => {\n // Unused: we go directly through the callback rather than\n // emit-then-fulfill. The broker abstraction's emit/fulfill\n // pair is for the SSE route in agent.ts; here we just want\n // the awaitable round-trip without the SSE plumbing.\n },\n });\n // Override request() with a direct callback invocation. Preserve\n // the optional stream observer: opencode uses streaming Responses\n // API calls, and tool-only coding turns depend on observer-delivered\n // tool_call_delta chunks to execute writes/edits.\n const realCallback = callback;\n streamScopedBroker.request = async (payload, observer) => {\n return realCallback(payload, observer);\n };\n activeBrokerHandle = bindActiveBroker(streamScopedBroker);\n // Diagnostic: confirm bind fired and at what token (first 12 chars)\n // so we can correlate with the shim's 401 log on the other side.\n logAdapter(\n `bound active broker token=\"${activeBrokerHandle.token.slice(0, 12)}\u2026\" session=${request.sessionId.slice(0, 8)}`,\n );\n } else {\n logAdapter(\n `WARNING request.broker missing session=${request.sessionId.slice(0, 8)}; shim will 401`,\n );\n }\n\n // Subscribe to events FIRST, then send the prompt \u2014 otherwise we\n // could miss early deltas.\n const eventController = new AbortController();\n const stopOnAbort = () => eventController.abort();\n if (request.signal) {\n if (request.signal.aborted) {\n return;\n }\n request.signal.addEventListener(\"abort\", stopOnAbort);\n }\n\n let eventStream: AsyncIterable<OpencodeStreamItem>;\n try {\n const res = await handle.client.global.event({\n signal: eventController.signal,\n });\n eventStream = res.stream;\n logAdapter(\n `subscribed events session=${request.sessionId.slice(0, 8)} opencodeSession=${openCodeSessionId.slice(0, 8)}`,\n );\n } catch (err) {\n yield {\n type: \"error\",\n message:\n err instanceof Error\n ? err.message\n : \"Failed to subscribe to opencode events\",\n recoverable: true,\n };\n request.signal?.removeEventListener(\"abort\", stopOnAbort);\n return;\n }\n\n // Kick off the prompt. Don't await \u2014 we want to start draining events\n // immediately. Errors land in the catch below.\n //\n // The `model` field tells opencode to route this turn through the\n // diologue/diologue-routed provider we registered at server-spawn\n // time. Outbound LLM calls from opencode then hit our /llm-shim\n // endpoint, where the active-broker mapping routes them back through\n // the AdapterRequest.broker callback to the cloud (with the cloud\n // billing ledger entry).\n //\n // Note: opencode also needs the apiKey at request time. Because we\n // baked a placeholder into Config (the SDK doesn't support per-call\n // apiKey override), the shim accepts the placeholder + cross-checks\n // against the active broker token instead. See active-broker.ts.\n const promptBody: {\n parts: Array<{ type: \"text\"; text: string }>;\n model?: { providerID: string; modelID: string };\n } = {\n parts: [\n { type: \"text\", text: `${EDIT_DIRECTIVE}\\n\\n${request.prompt}` },\n ],\n };\n if (streamScopedBroker && activeBrokerHandle) {\n promptBody.model = {\n providerID: DIOLOGUE_PROVIDER_ID,\n modelID: DIOLOGUE_MODEL_ID,\n };\n }\n logAdapter(\n `prompt start session=${request.sessionId.slice(0, 8)} opencodeSession=${openCodeSessionId.slice(0, 8)} promptChars=${request.prompt.length} provider=${request.preferredProvider ?? \"(default)\"} model=${request.preferredModel ?? \"(default)\"}`,\n );\n const promptPromise = handle.client.session\n .prompt({\n path: { id: openCodeSessionId },\n query: { directory: request.repoPath },\n body: promptBody,\n })\n .then((value) => {\n logAdapter(\n `prompt accepted session=${request.sessionId.slice(0, 8)} opencodeSession=${openCodeSessionId.slice(0, 8)}`,\n );\n return value;\n })\n .catch((err: unknown) => {\n const error = err instanceof Error ? err : new Error(String(err));\n logAdapter(\n `prompt failed session=${request.sessionId.slice(0, 8)} opencodeSession=${openCodeSessionId.slice(0, 8)} error=${error.message}`,\n );\n // Promote into the iteration so the consumer sees one AgentError.\n return error;\n });\n\n // End-of-turn detection. opencode can signal completion two ways and we\n // honour whichever lands first:\n // - a `session.idle` event on the stream (mapped.done) \u2014 the happy path;\n // - `session.prompt()` resolving \u2014 the turn finished server-side.\n // Some engine builds never deliver a matching `session.idle`, so relying\n // on it alone hangs the turn forever: the UI spins, and the `finally`\n // below (which releases the active broker) never runs \u2014 that leak is what\n // made a later turn 401 with active_brokers=2. We add a stop signal driven\n // by the prompt settling, with a short grace for any trailing patch/idle.\n let stopConsuming = false;\n const stopSignal: Promise<\"stop\"> = new Promise((resolve) => {\n void promptPromise.finally(() => {\n setTimeout(() => {\n stopConsuming = true;\n resolve(\"stop\");\n }, END_OF_TURN_GRACE_MS);\n });\n });\n\n let emittedDone = false;\n const iterator = eventStream[Symbol.asyncIterator]();\n try {\n while (!stopConsuming) {\n if (request.signal?.aborted) break;\n const nextP = iterator.next();\n const next = await Promise.race([\n nextP,\n stopSignal.then(() => null),\n ]);\n if (next === null) {\n // Prompt settled + grace elapsed. The in-flight next() is now\n // abandoned; swallow its eventual (abort) rejection so it doesn't\n // surface as an unhandled rejection.\n void nextP.catch(() => undefined);\n break;\n }\n if (next.done) break; // engine event stream ended\n // The engine's global event stream wraps every opencode event as\n // `{ directory, payload: Event }`. The session events the mapper\n // understands (message.part.updated, session.idle, tool/patch parts)\n // live under `payload`. Subscribing without unwrapping was the bug\n // behind the blank chat transcript, the missing in-chat diff, and the\n // turn that never saw `session.idle` (the original hang) \u2014 every raw\n // item had no top-level `type`, so the mapper dropped all of them.\n // Unwrap defensively: fall back to the item itself if it's already\n // an un-wrapped Event (older engines / event.subscribe shape).\n const rawItem = next.value as\n | OpencodeStreamItem\n | { payload?: OpencodeStreamItem };\n const item =\n \"payload\" in rawItem && rawItem.payload\n ? rawItem.payload\n : (rawItem as OpencodeStreamItem);\n const mapped = mapEvent(item, mapState);\n if (mapped.events.length > 0 || mapped.done) {\n logAdapter(\n `event ${item.type} mapped=${mapped.events.map((event) => event.type).join(\",\") || \"(none)\"} done=${mapped.done} session=${request.sessionId.slice(0, 8)}`,\n );\n }\n for (const event of mapped.events) {\n if (event.type === \"permission_request\") {\n if (permissionMode === \"confirm\") {\n // Pause for the user. Hold the turn open until they reply via\n // /agent/permission; auto-reject if they never do.\n const timer = setTimeout(() => {\n activeTurn.pending.delete(event.permissionId);\n void this.replyToOpencode(\n handle.client,\n openCodeSessionId,\n request.repoPath,\n event.permissionId,\n \"reject\",\n );\n logAdapter(\n `permission auto-rejected (timeout) id=${event.permissionId.slice(0, 8)} session=${request.sessionId.slice(0, 8)}`,\n );\n }, PERMISSION_DECISION_TIMEOUT_MS);\n activeTurn.pending.set(event.permissionId, timer);\n yield event;\n } else {\n // Auto mode: approve immediately without bothering the browser.\n void this.replyToOpencode(\n handle.client,\n openCodeSessionId,\n request.repoPath,\n event.permissionId,\n \"once\",\n );\n }\n continue;\n }\n if (event.type === \"diff_proposed\" && mapped.patchToFetch) {\n sawAnyPatch = true;\n lastPatchHash = mapped.patchToFetch.hash;\n }\n if (event.type === \"done\") emittedDone = true;\n yield event;\n }\n if (mapped.done) {\n break;\n }\n }\n } finally {\n eventController.abort();\n try {\n await iterator.return?.();\n } catch {\n // Best-effort stream teardown.\n }\n request.signal?.removeEventListener(\"abort\", stopOnAbort);\n // Reject any permission still awaiting a decision so opencode doesn't\n // hang, then drop the turn from the active registry.\n for (const [permId, timer] of activeTurn.pending) {\n clearTimeout(timer);\n void this.replyToOpencode(\n handle.client,\n openCodeSessionId,\n request.repoPath,\n permId,\n \"reject\",\n );\n }\n activeTurn.pending.clear();\n this.activeTurns.delete(request.sessionId);\n // Always release the active broker so the next turn (or a\n // different process state) can rebind cleanly.\n activeBrokerHandle?.release();\n streamScopedBroker?.close(\"turn_ended\");\n }\n\n // Fetch the final unified diff unconditionally \u2014 some engine builds apply\n // edits without ever emitting a `patch` event, and the chat's Keep/Revert\n // card is driven entirely by this diff_proposed envelope.\n try {\n const diffRes = await handle.client.session.diff({\n path: { id: openCodeSessionId },\n query: { directory: request.repoPath },\n });\n const unified = diffRes.data?.unified ?? \"\";\n if (unified) {\n yield {\n type: \"diff_proposed\",\n unified,\n files: parseUnifiedDiffFiles(unified),\n };\n }\n } catch (err) {\n logAdapter(\n `diff fetch failed session=${request.sessionId.slice(0, 8)} error=${err instanceof Error ? err.message : String(err)}`,\n );\n // Diff fetch is best-effort. Any in-loop diff_proposed already gave the\n // UI a file list to show.\n }\n\n // Promote prompt errors (if any) into the stream as an AgentError.\n const promptResult = await promptPromise;\n if (promptResult instanceof Error) {\n yield { type: \"error\", message: promptResult.message, recoverable: true };\n } else {\n logAdapter(`turn complete session=${request.sessionId.slice(0, 8)}`);\n }\n\n // Guarantee a terminal `done` even when no matching `session.idle` ever\n // arrived, so the UI always stops streaming.\n if (!emittedDone) {\n yield { type: \"done\" };\n }\n\n void sawAnyPatch; // tracked for logging/correlation only\n void lastPatchHash; // reserved for future apply flow correlation\n }\n\n async close(): Promise<void> {\n if (!this.serverPromise) return;\n try {\n const handle = await this.serverPromise;\n await handle.close();\n } catch {\n // Best-effort.\n } finally {\n this.serverPromise = null;\n this.sessionMap.clear();\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n// Diff helpers\n// ----------------------------------------------------------------------------\n\n/** Lightweight unified-diff parser \u2014 just enough to extract the per-file\n * summary we need on `AgentDiffProposed.files`. Counts +/- lines per file\n * and infers status (added/deleted/modified) from the `diff --git` header\n * pair and the presence of /dev/null. */\nconst parseUnifiedDiffFiles = (\n unified: string,\n): Array<{\n path: string;\n additions: number;\n deletions: number;\n status: \"added\" | \"modified\" | \"deleted\" | \"renamed\";\n}> => {\n const files: Array<{\n path: string;\n additions: number;\n deletions: number;\n status: \"added\" | \"modified\" | \"deleted\" | \"renamed\";\n }> = [];\n let current: {\n path: string;\n additions: number;\n deletions: number;\n status: \"added\" | \"modified\" | \"deleted\" | \"renamed\";\n } | null = null;\n let oldPath = \"\";\n let newPath = \"\";\n\n for (const line of unified.split(\"\\n\")) {\n if (line.startsWith(\"diff --git\")) {\n if (current) files.push(current);\n // diff --git a/foo b/foo\n const match = /^diff --git a\\/(.+) b\\/(.+)$/.exec(line);\n if (match) {\n oldPath = match[1];\n newPath = match[2];\n } else {\n oldPath = \"\";\n newPath = \"\";\n }\n current = {\n path: newPath || oldPath,\n additions: 0,\n deletions: 0,\n status: oldPath === newPath ? \"modified\" : \"renamed\",\n };\n continue;\n }\n if (!current) continue;\n if (line.startsWith(\"--- /dev/null\")) {\n current.status = \"added\";\n } else if (line.startsWith(\"+++ /dev/null\")) {\n current.status = \"deleted\";\n } else if (line.startsWith(\"+\") && !line.startsWith(\"+++\")) {\n current.additions++;\n } else if (line.startsWith(\"-\") && !line.startsWith(\"---\")) {\n current.deletions++;\n }\n }\n if (current) files.push(current);\n\n // Suppress AgentDeltaText helper from being unused.\n void (null as unknown as AgentTextDelta);\n return files;\n};\n", "// Barrel \u2014 keeps `import { OpenCodeAdapter, MockOpenCodeAdapter } from\n// \"./adapters\"` short. The default adapter resolves to opencode in prod,\n// with the mock used in two scenarios:\n// 1. Explicit override via LOCAL_AGENT_ADAPTER=mock for offline demos.\n// 2. Inside tests that don't spawn the binary.\n\nexport type {\n AdapterRequest,\n AdapterHistoryMessage,\n LlmBrokerCallback,\n OpenCodeAdapter,\n} from \"./types\";\nexport { MockOpenCodeAdapter, type MockAdapterOptions } from \"./mock-adapter\";\nexport { OpenCodeProcessAdapter } from \"./opencode-adapter\";\n\nimport { MockOpenCodeAdapter } from \"./mock-adapter\";\nimport { OpenCodeProcessAdapter } from \"./opencode-adapter\";\nimport type { OpenCodeAdapter } from \"./types\";\n\n/** Returns the adapter the helper will use for new sessions. Default is\n * the real OpenCodeProcessAdapter; set `LOCAL_AGENT_ADAPTER=mock` to\n * force the canned-response mock (useful for demos with no opencode\n * binary or no provider keys configured). */\nexport const buildDefaultAdapter = (): OpenCodeAdapter => {\n const forced = (process.env.LOCAL_AGENT_ADAPTER ?? \"\").trim().toLowerCase();\n if (forced === \"mock\") {\n return new MockOpenCodeAdapter({ tokenDelayMs: 40 });\n }\n return new OpenCodeProcessAdapter();\n};\n", "// `diologue-local-agent quickstart` \u2014 the zero-friction install path.\n//\n// Flow (browser-first; cloud's /init endpoint requires auth):\n// 1. User opens <cloud>/coding-agent/quickstart in their browser.\n// 2. Page POSTs /api/coding/quickstart/init \u2192 gets a setupToken.\n// 3. Page shows a copy-paste command:\n// npx @diologue/local-agent --cloud=<cloud> --token=<setupToken>\n// 4. User pastes it in a terminal.\n// 5. Helper starts with that token (as LOCAL_AGENT_TOKEN) and the\n// cloud as LOCAL_AGENT_ALLOWED_ORIGIN.\n// 6. Helper POSTs /api/coding/quickstart/checked-in with the token.\n// 7. Browser polls /quickstart/:token until status flips to \"ready\",\n// then redirects to /coding-agent?helper=\u2026&token=\u2026 with the\n// connection auto-tested.\n//\n// On a fresh machine this is \"one command, no copy-paste of tokens\n// into the UI.\"\n\nimport { loadConfig } from \"../config\";\nimport { createApp } from \"../server\";\n\nexport interface QuickstartOptions {\n cloudUrl: string;\n port: number;\n autoOpenBrowser: boolean;\n helperVersion: string;\n /** Setup token from the browser's /coding-agent/quickstart page. When\n * absent the helper directs the user to the browser flow first. */\n setupToken?: string;\n}\n\nconst checkInQuickstart = async (\n cloudUrl: string,\n setupToken: string,\n helperVersion: string,\n port: number,\n): Promise<void> => {\n const res = await fetch(`${cloudUrl}/api/coding/quickstart/checked-in`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n setupToken,\n helperVersion,\n port,\n }),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `Cloud quickstart/checked-in returned ${res.status}: ${text || \"no body\"}`,\n );\n }\n};\n\nconst tryOpenBrowser = async (url: string): Promise<boolean> => {\n try {\n // `open` is a thin cross-platform wrapper around `open`/`xdg-open`/`start`.\n // Skips the spawn (gracefully returns false) on headless boxes.\n const open = (await import(\"open\")).default;\n await open(url);\n return true;\n } catch {\n return false;\n }\n};\n\nexport const runQuickstart = async (\n options: QuickstartOptions,\n): Promise<void> => {\n const cloudUrl = options.cloudUrl.replace(/\\/$/, \"\");\n\n // When no setup token was supplied, the user ran `npx @diologue/local-agent`\n // without going through the browser quickstart page first. Bounce them\n // there and exit \u2014 the page is the source of truth for the token because\n // its /init call is authenticated.\n if (!options.setupToken) {\n const browserUrl = `${cloudUrl}/coding-agent/quickstart`;\n console.log(\"[quickstart] No --token supplied.\");\n console.log(`[quickstart] Open the quickstart page first:`);\n console.log(`[quickstart] ${browserUrl}`);\n console.log(\"\");\n console.log(\n \"[quickstart] It'll give you the exact `npx` command to paste here.\",\n );\n if (options.autoOpenBrowser) {\n await tryOpenBrowser(browserUrl);\n }\n return;\n }\n\n console.log(`[quickstart] Cloud: ${cloudUrl}`);\n console.log(`[quickstart] Setup token: ${options.setupToken.slice(0, 12)}\u2026`);\n\n // Configure the helper to share the setup token with the browser and\n // accept requests from the cloud's origin only.\n process.env.LOCAL_AGENT_TOKEN = options.setupToken;\n process.env.LOCAL_AGENT_ALLOWED_ORIGIN = cloudUrl;\n process.env.LOCAL_AGENT_PORT = String(options.port);\n\n const config = loadConfig();\n const { app, adapter } = createApp({ config });\n\n const server = app.listen(config.port, config.host, async () => {\n const url = `http://${config.host}:${config.port}`;\n console.log(`[quickstart] Helper listening on ${url}`);\n console.log(`[quickstart] Adapter: ${adapter.name}`);\n console.log(\"[quickstart] Telling the cloud the helper is ready\u2026\");\n try {\n await checkInQuickstart(\n cloudUrl,\n options.setupToken!,\n options.helperVersion,\n config.port,\n );\n console.log(\n \"[quickstart] \u2714 Ready. The browser tab should redirect you to /coding-agent in a moment.\",\n );\n } catch (err) {\n console.error(\n `[quickstart] Couldn't notify the cloud \u2014 the browser will keep polling but won't auto-connect.`,\n );\n console.error(`[quickstart] Underlying error: ${(err as Error).message}`);\n console.error(\"\");\n console.error(\n `[quickstart] Workaround: in the browser, paste these into the connection panel:`,\n );\n console.error(`[quickstart] Helper URL: ${url}`);\n console.error(`[quickstart] Pairing token: ${options.setupToken}`);\n }\n console.log(\"\");\n console.log(\"[quickstart] Press Ctrl+C to stop the helper.\");\n });\n\n const shutdown = (signal: NodeJS.Signals) => {\n console.log(`\\n[quickstart] Received ${signal}, shutting down\u2026`);\n server.close(() => process.exit(0));\n setTimeout(() => process.exit(0), 3_000).unref();\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n};\n", "// Credential storage for the paired-device JWT.\n//\n// Primary: OS keychain via `keytar` \u2014 macOS Keychain / Linux\n// Secret Service / Windows Credential Manager. Encrypted at rest,\n// access-controlled by the OS, gold standard for desktop CLI tools.\n//\n// Fallback: `~/.diologue/credentials` file with 0600 mode. Used when\n// keytar can't load (native module missing, headless Linux with no\n// dbus, etc.). Still survives reboots; readable only by the user.\n//\n// We deliberately try-and-fallback rather than fail hard \u2014 keytar's\n// native build is fiddly enough that bricking the helper over it\n// would be hostile. The store records which backend it ended up\n// using so the UI can surface \"your credential is in a plaintext\n// file\" as a soft warning.\n\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n\nconst KEYTAR_SERVICE = \"diologue.local-agent\";\nconst KEYTAR_ACCOUNT = \"device-credential\";\n\nexport type CredentialBackend = \"keychain\" | \"file\";\n\nexport interface StoredCredential {\n jwt: string;\n deviceId: string;\n expiresAt: string; // ISO\n pairedAt: string;\n label: string;\n /** Cloud URL the helper was paired against. Helpers can talk to\n * more than one cloud over their lifetime; we keep the URL with\n * the credential so it's clear which one this JWT is valid for. */\n cloudUrl: string;\n}\n\nexport interface CredentialStore {\n load(): Promise<{ credential: StoredCredential; backend: CredentialBackend } | null>;\n save(credential: StoredCredential): Promise<{ backend: CredentialBackend }>;\n clear(): Promise<void>;\n}\n\n// ----------------------------------------------------------------------------\n// Internal: lazy keytar loader\n// ----------------------------------------------------------------------------\n\ninterface KeytarLike {\n getPassword(service: string, account: string): Promise<string | null>;\n setPassword(service: string, account: string, password: string): Promise<void>;\n deletePassword(service: string, account: string): Promise<boolean>;\n}\n\nlet keytarPromise: Promise<KeytarLike | null> | null = null;\n\nconst loadKeytar = async (): Promise<KeytarLike | null> => {\n if (keytarPromise) return keytarPromise;\n keytarPromise = (async () => {\n try {\n // Dynamic import via an indirection so TS doesn't try to resolve\n // the module at compile time (keytar is `optionalDependencies`\n // and may not be present at all). The cast through `unknown`\n // narrows the runtime shape \u2014 checked structurally below.\n const moduleName = \"keytar\";\n const mod = (await import(/* @vite-ignore */ moduleName)) as unknown as {\n default?: KeytarLike;\n } & KeytarLike;\n return (mod.default ?? mod) as KeytarLike;\n } catch {\n return null;\n }\n })();\n return keytarPromise;\n};\n\n// ----------------------------------------------------------------------------\n// Internal: file fallback location\n// ----------------------------------------------------------------------------\n\nconst fileFallbackPath = (): string => {\n // ~/.diologue/credentials \u2014 same layout as gh, hub, fly etc. use.\n const home = os.homedir();\n return path.join(home, \".diologue\", \"credentials\");\n};\n\n// ----------------------------------------------------------------------------\n// CredentialStore implementation\n// ----------------------------------------------------------------------------\n\nexport interface CredentialStoreOptions {\n /** Force a backend (for tests). Production code never sets this. */\n forceBackend?: CredentialBackend;\n /** Override the file path (for tests). */\n filePathOverride?: string;\n}\n\nexport const createCredentialStore = (\n options: CredentialStoreOptions = {},\n): CredentialStore => {\n const filePath = options.filePathOverride ?? fileFallbackPath();\n const forceBackend = options.forceBackend;\n\n const resolveBackend = async (): Promise<CredentialBackend> => {\n if (forceBackend) return forceBackend;\n const keytar = await loadKeytar();\n return keytar ? \"keychain\" : \"file\";\n };\n\n return {\n async load() {\n const backend = await resolveBackend();\n if (backend === \"keychain\") {\n const keytar = await loadKeytar();\n if (!keytar) return null;\n const raw = await keytar.getPassword(KEYTAR_SERVICE, KEYTAR_ACCOUNT);\n if (!raw) return null;\n try {\n const credential = JSON.parse(raw) as StoredCredential;\n return { credential, backend: \"keychain\" as const };\n } catch {\n return null;\n }\n }\n // File backend.\n try {\n const raw = await fs.readFile(filePath, \"utf8\");\n const credential = JSON.parse(raw) as StoredCredential;\n return { credential, backend: \"file\" as const };\n } catch {\n return null;\n }\n },\n\n async save(credential) {\n const backend = await resolveBackend();\n if (backend === \"keychain\") {\n const keytar = await loadKeytar();\n if (!keytar) {\n // Race: backend resolved to keychain but keytar disappeared.\n // Fall through to file.\n } else {\n await keytar.setPassword(\n KEYTAR_SERVICE,\n KEYTAR_ACCOUNT,\n JSON.stringify(credential),\n );\n return { backend: \"keychain\" };\n }\n }\n // File backend.\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(filePath, JSON.stringify(credential, null, 2), {\n mode: 0o600,\n });\n return { backend: \"file\" };\n },\n\n async clear() {\n const backend = await resolveBackend();\n if (backend === \"keychain\") {\n const keytar = await loadKeytar();\n if (keytar) {\n await keytar\n .deletePassword(KEYTAR_SERVICE, KEYTAR_ACCOUNT)\n .catch(() => undefined);\n }\n }\n // Always also clear the file backup, in case credentials migrated\n // between backends.\n await fs.rm(filePath, { force: true }).catch(() => undefined);\n },\n };\n};\n", "// `diologue-local-agent start` \u2014 raw helper start.\n//\n// This is the existing behaviour from index.ts, lifted into a mode\n// function so the CLI can dispatch it alongside `quickstart` and `pair`.\n// Manual flags override env vars; env vars override defaults.\n\nimport { loadConfig } from \"../config\";\nimport { createCredentialStore } from \"../lib/keychain\";\nimport { createApp } from \"../server\";\n\nexport interface StartOptions {\n port?: number;\n token?: string;\n allowedOrigin?: string;\n adapter?: \"mock\" | \"opencode\";\n}\n\nconst printTunnelStatus = async (): Promise<void> => {\n const store = createCredentialStore();\n const loaded = await store.load().catch(() => null);\n if (!loaded) {\n console.log(\"[local-agent] Tunnel: not paired.\");\n console.log(\n \"[local-agent] Run `diologue-local-agent pair <code>` to enable remote-machine use.\",\n );\n return;\n }\n const expires = new Date(loaded.credential.expiresAt);\n const daysLeft = Math.max(\n 0,\n Math.round((expires.getTime() - Date.now()) / 86_400_000),\n );\n console.log(\n `[local-agent] Tunnel: paired as \"${loaded.credential.label}\" (${loaded.credential.deviceId})`,\n );\n console.log(\n `[local-agent] Cloud: ${loaded.credential.cloudUrl} Expires in ${daysLeft}d Storage: ${loaded.backend}`,\n );\n};\n\nexport const runStart = async (options: StartOptions): Promise<void> => {\n // Apply CLI overrides via env before loadConfig reads them. Keeping\n // env as the indirection means config.ts stays the single source of\n // truth for how the helper resolves its settings.\n if (options.port) process.env.LOCAL_AGENT_PORT = String(options.port);\n if (options.token) process.env.LOCAL_AGENT_TOKEN = options.token;\n if (options.allowedOrigin)\n process.env.LOCAL_AGENT_ALLOWED_ORIGIN = options.allowedOrigin;\n if (options.adapter) process.env.LOCAL_AGENT_ADAPTER = options.adapter;\n\n const config = loadConfig();\n const { app, adapter, worktrees } = createApp({ config });\n\n const server = app.listen(config.port, config.host, async () => {\n const url = `http://${config.host}:${config.port}`;\n console.log(`[local-agent] Listening on ${url}`);\n console.log(`[local-agent] Adapter: ${adapter.name}`);\n console.log(\"[local-agent] Pairing token (paste into the web UI):\");\n console.log(`[local-agent] ${config.token}`);\n console.log(`[local-agent] Allowed browser origin: ${config.allowedOrigin}`);\n await printTunnelStatus();\n console.log(\n \"[local-agent] Press Ctrl+C to stop. The token is regenerated on each restart.\",\n );\n });\n\n const shutdown = (signal: NodeJS.Signals) => {\n console.log(`\\n[local-agent] Received ${signal}, shutting down...`);\n // Best-effort: tear down per-session worktrees so they don't accumulate\n // under ~/.diologue/worktrees across restarts. The branches survive.\n void worktrees.cleanupAll().finally(() => {\n server.close(() => process.exit(0));\n });\n setTimeout(() => process.exit(0), 3_000).unref();\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n};\n", "// `diologue-local-agent pair <code>` \u2014 device-pairing CLI.\n//\n// Lifted from scripts/pair.ts into a mode function so the unified CLI\n// can dispatch it. Behaviour is identical: hit /api/devices/pair-complete,\n// store the resulting JWT in the credential store.\n\nimport os from \"node:os\";\n\nimport { createCredentialStore } from \"../lib/keychain\";\n\nconst detectOs = (): \"darwin\" | \"linux\" | \"win32\" | undefined => {\n const p = process.platform;\n if (p === \"darwin\" || p === \"linux\" || p === \"win32\") return p;\n return undefined;\n};\n\nconst detectLabel = (): string => {\n const hostname = os.hostname() || \"unknown-host\";\n return hostname.replace(/\\.local$/i, \"\");\n};\n\ninterface PairCompleteResponse {\n device: { id: string; label: string };\n jwt: string;\n expiresAt: string;\n}\n\nexport interface PairOptions {\n code: string;\n cloudUrl: string;\n helperVersion: string;\n}\n\nexport const runPair = async (options: PairOptions): Promise<void> => {\n const cloudUrl = options.cloudUrl.replace(/\\/$/, \"\");\n const label = process.env.DIOLOGUE_DEVICE_LABEL?.trim() || detectLabel();\n const osId = detectOs();\n\n console.log(`[pair] Cloud: ${cloudUrl}`);\n console.log(`[pair] Label: ${label}`);\n console.log(`[pair] OS: ${osId ?? \"(unknown)\"}`);\n console.log(`[pair] Code: ${options.code}`);\n console.log(\"[pair] Exchanging code for a device credential\u2026\");\n\n let response: Response;\n try {\n response = await fetch(`${cloudUrl}/api/devices/pair-complete`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n code: options.code,\n label,\n ...(osId ? { os: osId } : {}),\n helperVersion: options.helperVersion,\n }),\n });\n } catch (err) {\n console.error(\n \"[pair] Could not reach the cloud. Check --cloud / DIOLOGUE_CLOUD_URL and your network.\",\n );\n console.error(`[pair] Underlying error: ${(err as Error).message}`);\n process.exit(1);\n }\n\n if (response.status === 404) {\n console.error(\n \"[pair] Code not found. Generate a fresh one from Settings \u2192 Devices.\",\n );\n process.exit(1);\n }\n if (response.status === 410) {\n console.error(\"[pair] Code already used or expired. Generate a fresh one.\");\n process.exit(1);\n }\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n console.error(`[pair] Cloud returned ${response.status}: ${text}`);\n process.exit(1);\n }\n\n const data = (await response.json()) as PairCompleteResponse;\n if (!data.jwt || !data.device?.id) {\n console.error(\"[pair] Cloud response missing fields:\", data);\n process.exit(1);\n }\n\n const store = createCredentialStore();\n const { backend } = await store.save({\n jwt: data.jwt,\n deviceId: data.device.id,\n expiresAt: data.expiresAt,\n pairedAt: new Date().toISOString(),\n label: data.device.label,\n cloudUrl,\n });\n\n console.log(\"\");\n console.log(`[pair] \u2714 Paired as \"${data.device.label}\" (${data.device.id})`);\n console.log(`[pair] Credential expires: ${data.expiresAt}`);\n console.log(\n `[pair] Stored in: ${backend === \"keychain\" ? \"OS keychain\" : \"~/.diologue/credentials (file, 0600)\"}`,\n );\n if (backend === \"file\") {\n console.log(\n \"[pair] Note: keytar (OS keychain) couldn't load; using file fallback.\",\n );\n }\n console.log(\"\");\n console.log(\"[pair] The helper will pick this credential up on next start.\");\n console.log(\"[pair] Start it with: diologue-local-agent\");\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["access", "path", "exists", "require", "access", "constants", "spawn", "path", "fileURLToPath", "__filename", "__dirname", "REPO_ROOT", "exists", "process", "execFile", "path", "path", "execFile", "homedir", "execFile", "run", "path", "createRouter", "z", "z", "createRouter", "createRouter", "randomUUID", "createRouter", "writeEvent", "path", "NpmSdkLocator", "LocalEngineLocator", "path", "os", "os", "process"]
|
|
7
7
|
}
|