billion-context 0.1.111 → 0.1.112-pr.792.636
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -2
- package/dist/agent/dsh-acp.js.map +1 -1
- package/dist/agent/omp.js +9 -2
- package/dist/agent/omp.js.map +1 -1
- package/dist/agent/opencode.js +26535 -1
- package/dist/agent/opencode.js.map +1 -1
- package/dist/agent/pi.js +9 -2
- package/dist/agent/pi.js.map +1 -1
- package/dist/index.js +720 -506
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -150,7 +150,8 @@ Pick by your client:
|
|
|
150
150
|
| Client | Use |
|
|
151
151
|
|---|---|
|
|
152
152
|
| **pi** | [`billion-context-pi`](https://github.com/ranxianglei/billion-context-pi) (in-process extension) |
|
|
153
|
-
| **opencode** | [`opencode-acp`](https://github.com/ranxianglei/opencode-acp) (in-process extension) |
|
|
153
|
+
| **opencode 1.x** | [`opencode-acp`](https://github.com/ranxianglei/opencode-acp) (in-process extension, V1 plugin API) or `bili opencode` |
|
|
154
|
+
| **opencode 2.0+** | `bili opencode` (built-in V2 plugin — native tools, no separate package) |
|
|
154
155
|
| **omp** | [`billion-context`](https://github.com/ranxianglei/billion-context) via `bili omp` (built-in plugin) |
|
|
155
156
|
| **everything else** (no context hook) | [`billion-context`](https://github.com/ranxianglei/billion-context) — `bili <client>` (launcher, preferred) or `/bili/` prefix |
|
|
156
157
|
|
|
@@ -202,7 +203,7 @@ bili pi # launch pi through the proxy — file-fre
|
|
|
202
203
|
bili codex # launch codex through the proxy
|
|
203
204
|
bili claude # launch claude through the proxy
|
|
204
205
|
bili omp # pi-style, file-free (#535): env + extension registerProvider + compaction cancel, real ~/.omp untouched
|
|
205
|
-
bili opencode # MITM for HTTPS + temp opencode.json (/bili/ for HTTP) + thin /acp plugin
|
|
206
|
+
bili opencode # MITM for HTTPS + temp opencode.json (/bili/ for HTTP) + thin /acp plugin; OpenCode 2.0+: built-in V2 plugin with native bili tools, native compaction auto-disabled
|
|
206
207
|
bili hermes # file-free (#535): hermes proxy env (HTTPS_PROXY + HERMES_CA_BUNDLE) — https via CONNECT MITM, http via absolute-form forward proxy; real ~/.hermes untouched
|
|
207
208
|
bili dsh # deepseek-harness: non-loopback upstreams ride proxy envs (https MITM, http absolute-form), loopback keeps the overlay DSH_HOME (~/.dsh-bili) rewrite (#535), built-in deepseek route via DEEPSEEK_BASE_URL, native /acp command injected via --patch
|
|
208
209
|
bili codebuddy # Tencent CodeBuddy Code CLI: CODEBUDDY_BASE_URL /bili/ rewrite (OpenAI chat completions wire), budget aligned via CODEBUDDY_AUTO_COMPACT_WINDOW; real ~/.codebuddy untouched
|
|
@@ -238,6 +239,68 @@ automatically.
|
|
|
238
239
|
For per-client configuration examples (OpenCode, Codex, Pi, login-client
|
|
239
240
|
MITM, …) see the web UI guide at [http://localhost:8787](http://localhost:8787).
|
|
240
241
|
|
|
242
|
+
### OpenCode 2.0
|
|
243
|
+
|
|
244
|
+
OpenCode 2.0 ships a new plugin API (`@opencode/plugin`); the standalone
|
|
245
|
+
`opencode-acp` extension is V1-only and does not load under 2.0. Both bili
|
|
246
|
+
modes work on 2.0. The 2.x plugin API surface is still moving between builds
|
|
247
|
+
(adjacent npm `dev`-channel builds expose different `ctx` shapes), so the
|
|
248
|
+
hook/tool details below are version-specific observations, not a stable
|
|
249
|
+
contract:
|
|
250
|
+
|
|
251
|
+
- **Launcher:** `bili opencode` works unchanged. On a 2.x host it injects the
|
|
252
|
+
built-in V2 plugin (`dist/agent/opencode.js`) into the temp config as a temp
|
|
253
|
+
wrapper directory whose `index.js` re-exports the plugin file — 2.x rejects
|
|
254
|
+
bare file paths in the config `plugin` array (directory entries use
|
|
255
|
+
`index.js` as entrypoint); 1.x hosts get the bare file path. Host generation
|
|
256
|
+
is detected with a `--version` probe (a failed probe defaults to the 1.x
|
|
257
|
+
shape). The V2 plugin registers the bili tools natively in-host — compress /
|
|
258
|
+
decompress / search_context / acp_status (+ absorb), JSON-Schema inputs — and
|
|
259
|
+
stamps the proxy headers on every outgoing provider request, so compression
|
|
260
|
+
runs in plugin mode with no wire-level tool injection. Native auto-compaction
|
|
261
|
+
is disabled automatically (`compaction.auto: false`). Every registration is
|
|
262
|
+
defensive (optional chaining): on any 2.x build where a seam is missing or
|
|
263
|
+
never fires, the plugin stays inert and the session transparently runs in
|
|
264
|
+
plain proxy mode (wire-level tool injection) instead of breaking — observed
|
|
265
|
+
on two adjacent `dev` builds (2026-09-13 / 2026-09-14) whose API surfaces
|
|
266
|
+
differ from each other (#754 review probes); conversely verified end-to-end
|
|
267
|
+
on `@opencode/cli` 2.0.3 (native `acp_status` executed through the plugin
|
|
268
|
+
endpoint, zero wire-level injection).
|
|
269
|
+
- **Pure proxy:** point the provider baseURL at the proxy like any other
|
|
270
|
+
client:
|
|
271
|
+
|
|
272
|
+
```json
|
|
273
|
+
{
|
|
274
|
+
"provider": {
|
|
275
|
+
"myprovider": {
|
|
276
|
+
"npm": "@ai-sdk/openai-compatible",
|
|
277
|
+
"options": {
|
|
278
|
+
"baseURL": "http://localhost:8787/bili/http://upstream.example/v1",
|
|
279
|
+
"apiKey": "sk-any"
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Note: 2.0 AI-SDK providers require an `apiKey` field even for local
|
|
287
|
+
endpoints that never check it — set any non-empty value.
|
|
288
|
+
|
|
289
|
+
Caveats: the 2.x line publishes as npm package `@opencode/cli`. Command
|
|
290
|
+
support is build-dependent: one pre-release exposed only
|
|
291
|
+
list/get/update/remove, while 2.0.x stable lets plugins ADD commands via
|
|
292
|
+
`ctx.command.transform((editor) => editor.add(...))` — invocable in the TUI by
|
|
293
|
+
accepting the slash-menu completion (Tab + Enter); note `opencode run` mode
|
|
294
|
+
dispatches no slash commands at all (they pass through to the model). The
|
|
295
|
+
bundled plugin deliberately registers no commands on either shape — call the
|
|
296
|
+
`acp_status` tool instead of an `/acp` command. The bundled agent file keeps
|
|
297
|
+
the V1 `server()` export alongside the V2 `setup()`, so the same artifact also
|
|
298
|
+
loads on hosts ≥ 1.18.29 that support dual-shape plugins. Design note: the V2 plugin is a thin protocol client (no
|
|
299
|
+
acp-kernel inside) because the proxy stays the single compression authority,
|
|
300
|
+
which eliminates kernel-version drift between agent and proxy — it does not
|
|
301
|
+
rely on the plugin API being unable to mutate context (that capability varies
|
|
302
|
+
by 2.x build).
|
|
303
|
+
|
|
241
304
|
### Verify
|
|
242
305
|
|
|
243
306
|
With the proxy running and your config saved, check it answers and that your
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/agent/shared.ts","../../src/agent/dsh-acp.ts"],"sourcesContent":["// Shared thin-plugin core for agent-side extensions (\"内外呼应\", issue #1).\n// The agent plugin is a PURE PROTOCOL CLIENT: no acp-kernel import, no\n// compression logic. The proxy stays the single compression authority; the\n// plugin only (1) detects the proxy, (2) fetches the tool manifest (single\n// source of truth), (3) forwards tool executes, (4) reads status. Same\n// package as the proxy ⇒ same version ⇒ no kernel-skew bug class.\n\nexport type ManifestTool = {\n name: string;\n description?: string;\n inputSchema: unknown;\n};\n\nconst MANIFEST_TIMEOUT_MS = 5000;\nconst TOOL_TIMEOUT_MS = 60000;\nconst STATUS_TIMEOUT_MS = 5000;\n\n/** Detect the proxy from a provider baseUrl's `/bili/` zero-config prefix.\n * The real prefix embeds the full upstream URL (`/bili/https://…`), so the\n * check requires `bili` as the first path segment followed by an http(s)\n * URL — a plain `/foo/bili/` path segment is NOT a bili proxy.\n * Returns the proxy origin (scheme//host) the request will actually hit. */\nexport function proxyBaseFromUrl(baseUrl: string | undefined): string | undefined {\n if (!baseUrl) return undefined;\n try {\n const url = new URL(baseUrl);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n const segments = url.pathname.split(\"/\").filter((s) => s.length > 0);\n if (segments[0] !== \"bili\") return undefined;\n const rest = url.pathname.slice(url.pathname.indexOf(\"bili\") + \"bili\".length);\n if (!/^\\/https?:\\/\\//.test(rest)) return undefined;\n return `${url.protocol}//${url.host}`;\n } catch {\n return undefined;\n }\n}\n\n/** MITM transparent mode has no `/bili/` prefix; the proxy's launcher exports\n * BILLION_CONTEXT_PROXY. A stale value surfaces as a tool-forward error. */\nexport function proxyBaseFromEnv(): string | undefined {\n const raw = process.env.BILLION_CONTEXT_PROXY?.trim();\n if (!raw) return undefined;\n try {\n const url = new URL(raw);\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? `${url.protocol}//${url.host}` : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function detectProxyBase(baseUrl: string | undefined): string | undefined {\n if (process.env.BILLION_CONTEXT_PLUGIN === \"0\") return undefined;\n return proxyBaseFromUrl(baseUrl) ?? proxyBaseFromEnv();\n}\n\nasync function fetchJson(url: string, init: RequestInit | undefined, timeoutMs: number, externalSignal?: AbortSignal): Promise<{ ok: boolean; status: number; json: unknown }> {\n const ac = new AbortController();\n // An already-aborted external signal never fires its \"abort\" event, so\n // forward the state directly — otherwise only the timeout could stop\n // the request, turning an instant cancel into a timeout wait.\n if (externalSignal?.aborted) ac.abort();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n const onExternalAbort = () => ac.abort();\n externalSignal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n try {\n let res: Response;\n try {\n res = await fetch(url, { ...init, signal: ac.signal });\n } catch (err) {\n if (ac.signal.aborted && !externalSignal?.aborted) throw new Error(`timeout after ${timeoutMs}ms: ${url}`);\n throw err;\n }\n const text = await res.text();\n let json: unknown = undefined;\n try {\n json = JSON.parse(text);\n } catch {\n json = undefined;\n }\n return { ok: res.ok, status: res.status, json };\n } finally {\n clearTimeout(timer);\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n }\n}\n\nexport async function fetchManifest(proxyBase: string): Promise<ManifestTool[]> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, MANIFEST_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") throw new Error(`manifest fetch failed: ${status}`);\n const data = json as { tools?: { anthropic?: { name?: string; description?: string; input_schema?: unknown }[] } };\n const tools = (data.tools?.anthropic ?? []).filter((t): t is { name: string; description?: string; input_schema?: unknown } => typeof t.name === \"string\");\n if (tools.length === 0) throw new Error(\"manifest served no anthropic tools\");\n return tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.input_schema ?? { type: \"object\", properties: {} } }));\n}\n\nexport async function forwardTool(proxyBase: string, conversationId: string, tool: string, args: unknown, signal?: AbortSignal): Promise<string> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/tool`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId, tool, args: args ?? {} }),\n }, TOOL_TIMEOUT_MS, signal);\n const data = json as { ok?: boolean; result?: string; error?: string } | undefined;\n if (!ok || !data?.ok) {\n throw new Error(`bili proxy tool ${tool} failed (${status}): ${data?.error ?? \"unknown error\"}`);\n }\n return data.result ?? \"\";\n}\n\n/** Soft-fail by design: the status read is best-effort UI data; undefined\n * means \"no data\" whether the proxy is down or the session is unknown. */\nexport async function fetchStatus(proxyBase: string, conversationId: string): Promise<Record<string, unknown> | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=${encodeURIComponent(conversationId)}`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Wire-mode status read (dsh): those clients carry no per-conversation id\n * the proxy could bind, so ask for the most recently active session instead\n * (fallback=latest). Same soft-fail contract as fetchStatus. */\nexport async function fetchStatusLatest(proxyBase: string): Promise<Record<string, unknown> | undefined> {\n // conversationId must be non-empty (server rejects the empty string), but\n // any unknown id is fine: fallback=latest then resolves the most recently\n // active session.\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=dsh&fallback=latest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Liveness + version probe for status UIs: same loopback origin as the\n * status endpoint, so a 404 status + a live manifest means \"proxy up,\n * conversation not seen yet\" — an armed-but-idle state, not an error. */\nexport async function fetchProxyVersion(proxyBase: string): Promise<string | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n const version = (json as { version?: unknown }).version;\n return typeof version === \"string\" && version.length > 0 ? version : undefined;\n}\n","// Native dsh (deepseek-harness) cordis plugin: registers the `/acp` command.\n// Injected by the `bili dsh` launcher through a `--patch` overlay that\n// inserts this module (as a file:// URL) into the loader entry tree — the\n// same plugin shape as dsh-command-compact. Pure protocol client, same\n// discipline as the other agent plugins: no acp-kernel import, every byte of\n// displayed data comes from the proxy's HTTP endpoints.\n\nimport { proxyBaseFromEnv, fetchProxyVersion, fetchStatusLatest } from \"./shared.js\";\n\nexport const name = \"bili-acp\";\nexport const inject = [\"commands\"];\n\ntype CommandOutcome = { kind: \"success\" | \"error\"; text: string };\n\ntype CommandsService = {\n register: (command: { name: string; description: string; handler: () => Promise<CommandOutcome> }) => unknown;\n};\n\ntype PluginContext = { commands: CommandsService };\n\n/** One `/acp` invocation: latest session panel, else armed-but-idle, else a\n * reachable-proxy failure. dsh conversations carry no client-side id we can\n * bind to, so the read asks for the most recently active session. */\nasync function statusOutcome(): Promise<CommandOutcome> {\n const base = proxyBaseFromEnv();\n if (!base) {\n return {\n kind: \"error\",\n text: \"bili: no proxy detected — launch dsh through `bili dsh` so /acp can read context status.\",\n };\n }\n const status = await fetchStatusLatest(base);\n const panel = status?.panel;\n if (status && typeof panel === \"string\" && panel.length > 0) {\n return { kind: \"success\", text: panel };\n }\n const version = await fetchProxyVersion(base);\n if (version) {\n return {\n kind: \"success\",\n text: `billion-context@${version} — proxy connected, compression armed. No model request seen yet; send one, then run /acp again.`,\n };\n }\n return {\n kind: \"error\",\n text: `bili: proxy not reachable at ${base} — is the bili proxy still running?`,\n };\n}\n\nexport function apply(ctx: PluginContext): void {\n ctx.commands.register({\n name: \"acp\",\n description: \"Show bili context-compression status\",\n handler: statusOutcome,\n });\n}\n"],"mappings":";;;;AAeA,IAAM,oBAAoB;AAwBnB,SAAS,mBAAuC;AACnD,QAAM,MAAM,QAAQ,IAAI,uBAAuB,KAAK;AACpD,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AACA,UAAM,MAAM,IAAI,IAAI,GAAG;AACvB,WAAO,IAAI,aAAa,WAAW,IAAI,aAAa,WAAW,GAAG,IAAI,QAAQ,KAAK,IAAI,IAAI,KAAK;AAAA,EACpG,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAOA,eAAe,UAAU,KAAa,MAA+B,WAAmB,gBAAuF;AAC3K,QAAM,KAAK,IAAI,gBAAgB;AAI/B,MAAI,gBAAgB,QAAS,IAAG,MAAM;AACtC,QAAM,QAAQ,WAAW,MAAM,GAAG,MAAM,GAAG,SAAS;AACpD,QAAM,kBAAkB,MAAM,GAAG,MAAM;AACvC,kBAAgB,iBAAiB,SAAS,iBAAiB,EAAE,MAAM,KAAK,CAAC;AACzE,MAAI;AACA,QAAI;AACJ,QAAI;AACA,YAAM,MAAM,MAAM,KAAK,EAAE,GAAG,MAAM,QAAQ,GAAG,OAAO,CAAC;AAAA,IACzD,SAAS,KAAK;AACV,UAAI,GAAG,OAAO,WAAW,CAAC,gBAAgB,QAAS,OAAM,IAAI,MAAM,iBAAiB,SAAS,OAAO,GAAG,EAAE;AACzG,YAAM;AAAA,IACV;AACA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,OAAgB;AACpB,QAAI;AACA,aAAO,KAAK,MAAM,IAAI;AAAA,IAC1B,QAAQ;AACJ,aAAO;AAAA,IACX;AACA,WAAO,EAAE,IAAI,IAAI,IAAI,QAAQ,IAAI,QAAQ,KAAK;AAAA,EAClD,UAAE;AACE,iBAAa,KAAK;AAClB,oBAAgB,oBAAoB,SAAS,eAAe;AAAA,EAChE;AACJ;AAmCA,eAAsB,kBAAkB,WAAiE;AAIrG,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,UAAU,GAAG,SAAS,4DAA4D,QAAW,iBAAiB;AACzI,MAAI,CAAC,MAAM,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AACrD,SAAO;AACX;AAKA,eAAsB,kBAAkB,WAAgD;AACpF,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,UAAU,GAAG,SAAS,2BAA2B,QAAW,iBAAiB;AACxG,MAAI,CAAC,MAAM,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AACrD,QAAM,UAAW,KAA+B;AAChD,SAAO,OAAO,YAAY,YAAY,QAAQ,SAAS,IAAI,UAAU;AACzE;;;AC/HO,IAAM,OAAO;AACb,IAAM,SAAS,CAAC,UAAU;AAajC,eAAe,gBAAyC;AACpD,QAAM,OAAO,iBAAiB;AAC9B,MAAI,CAAC,MAAM;AACP,WAAO;AAAA,MACH,MAAM;AAAA,MACN,MAAM;AAAA,IACV;AAAA,EACJ;AACA,QAAM,SAAS,MAAM,kBAAkB,IAAI;AAC3C,QAAM,QAAQ,QAAQ;AACtB,MAAI,UAAU,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACzD,WAAO,EAAE,MAAM,WAAW,MAAM,MAAM;AAAA,EAC1C;AACA,QAAM,UAAU,MAAM,kBAAkB,IAAI;AAC5C,MAAI,SAAS;AACT,WAAO;AAAA,MACH,MAAM;AAAA,MACN,MAAM,mBAAmB,OAAO;AAAA,IACpC;AAAA,EACJ;AACA,SAAO;AAAA,IACH,MAAM;AAAA,IACN,MAAM,gCAAgC,IAAI;AAAA,EAC9C;AACJ;AAEO,SAAS,MAAM,KAA0B;AAC5C,MAAI,SAAS,SAAS;AAAA,IAClB,MAAM;AAAA,IACN,aAAa;AAAA,IACb,SAAS;AAAA,EACb,CAAC;AACL;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/agent/shared.ts","../../src/agent/dsh-acp.ts"],"sourcesContent":["// Shared thin-plugin core for agent-side extensions (\"内外呼应\", issue #1).\n// The agent plugin is a PURE PROTOCOL CLIENT: no acp-kernel import, no\n// compression logic. The proxy stays the single compression authority; the\n// plugin only (1) detects the proxy, (2) fetches the tool manifest (single\n// source of truth), (3) forwards tool executes, (4) reads status. Same\n// package as the proxy ⇒ same version ⇒ no kernel-skew bug class.\n\nexport type ManifestTool = {\n name: string;\n description?: string;\n inputSchema: unknown;\n};\n\nconst MANIFEST_TIMEOUT_MS = 5000;\nconst TOOL_TIMEOUT_MS = 60000;\nconst STATUS_TIMEOUT_MS = 5000;\n\n/** Detect the proxy from a provider baseUrl's `/bili/` zero-config prefix.\n * The real prefix embeds the full upstream URL (`/bili/https://…`), so the\n * check requires `bili` as the first path segment followed by an http(s)\n * URL — a plain `/foo/bili/` path segment is NOT a bili proxy.\n * Returns the proxy origin (scheme//host) the request will actually hit. */\nexport function proxyBaseFromUrl(baseUrl: string | undefined): string | undefined {\n if (!baseUrl) return undefined;\n try {\n const url = new URL(baseUrl);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n const segments = url.pathname.split(\"/\").filter((s) => s.length > 0);\n if (segments[0] !== \"bili\") return undefined;\n const rest = url.pathname.slice(url.pathname.indexOf(\"bili\") + \"bili\".length);\n if (!/^\\/https?:\\/\\//.test(rest)) return undefined;\n return `${url.protocol}//${url.host}`;\n } catch {\n return undefined;\n }\n}\n\n/** MITM transparent mode has no `/bili/` prefix; the proxy's launcher exports\n * BILLION_CONTEXT_PROXY. A stale value surfaces as a tool-forward error. */\nexport function proxyBaseFromEnv(): string | undefined {\n const raw = process.env.BILLION_CONTEXT_PROXY?.trim();\n if (!raw) return undefined;\n try {\n const url = new URL(raw);\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? `${url.protocol}//${url.host}` : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function detectProxyBase(baseUrl: string | undefined): string | undefined {\n if (process.env.BILLION_CONTEXT_PLUGIN === \"0\") return undefined;\n return proxyBaseFromUrl(baseUrl) ?? proxyBaseFromEnv();\n}\n\nasync function fetchJson(url: string, init: RequestInit | undefined, timeoutMs: number, externalSignal?: AbortSignal): Promise<{ ok: boolean; status: number; json: unknown }> {\n const ac = new AbortController();\n // An already-aborted external signal never fires its \"abort\" event, so\n // forward the state directly — otherwise only the timeout could stop\n // the request, turning an instant cancel into a timeout wait.\n if (externalSignal?.aborted) ac.abort();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n const onExternalAbort = () => ac.abort();\n externalSignal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n try {\n let res: Response;\n try {\n res = await fetch(url, { ...init, signal: ac.signal });\n } catch (err) {\n if (ac.signal.aborted && !externalSignal?.aborted) throw new Error(`timeout after ${timeoutMs}ms: ${url}`);\n throw err;\n }\n const text = await res.text();\n let json: unknown = undefined;\n try {\n json = JSON.parse(text);\n } catch {\n json = undefined;\n }\n return { ok: res.ok, status: res.status, json };\n } finally {\n clearTimeout(timer);\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n }\n}\n\nexport async function fetchManifest(proxyBase: string, format: \"anthropic\" | \"openai\" = \"anthropic\"): Promise<ManifestTool[]> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, MANIFEST_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") throw new Error(`manifest fetch failed: ${status}`);\n if (format === \"openai\") {\n // OpenAI function style: {name, description, parameters} (plain JSON Schema).\n const data = json as { tools?: { openai?: { name?: string; description?: string; parameters?: unknown }[] } };\n const tools = (data.tools?.openai ?? []).filter((t): t is { name: string; description?: string; parameters?: unknown } => typeof t.name === \"string\");\n if (tools.length === 0) throw new Error(\"manifest served no openai tools\");\n return tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.parameters ?? { type: \"object\", properties: {} } }));\n }\n const data = json as { tools?: { anthropic?: { name?: string; description?: string; input_schema?: unknown }[] } };\n const tools = (data.tools?.anthropic ?? []).filter((t): t is { name: string; description?: string; input_schema?: unknown } => typeof t.name === \"string\");\n if (tools.length === 0) throw new Error(\"manifest served no anthropic tools\");\n return tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.input_schema ?? { type: \"object\", properties: {} } }));\n}\n\nconst COMPACT_TIMEOUT_MS = 5000;\n\n/** Report a host-native compaction boundary to the proxy archive (#395):\n * hosts that compact natively (opencode) cannot cancel it, so the proxy\n * marks the boundary and archives unreachable blocks. Fire-and-forget at\n * call sites — a missed report degrades to stale blocks, not broken turns. */\nexport async function reportCompactionBoundary(proxyBase: string, conversationId: string): Promise<void> {\n await fetchJson(`${proxyBase}/__bili/plugin/compact`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId }),\n }, COMPACT_TIMEOUT_MS);\n}\n\nexport async function forwardTool(proxyBase: string, conversationId: string, tool: string, args: unknown, signal?: AbortSignal): Promise<string> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/tool`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId, tool, args: args ?? {} }),\n }, TOOL_TIMEOUT_MS, signal);\n const data = json as { ok?: boolean; result?: string; error?: string } | undefined;\n if (!ok || !data?.ok) {\n throw new Error(`bili proxy tool ${tool} failed (${status}): ${data?.error ?? \"unknown error\"}`);\n }\n return data.result ?? \"\";\n}\n\n/** Soft-fail by design: the status read is best-effort UI data; undefined\n * means \"no data\" whether the proxy is down or the session is unknown. */\nexport async function fetchStatus(proxyBase: string, conversationId: string): Promise<Record<string, unknown> | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=${encodeURIComponent(conversationId)}`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Wire-mode status read (dsh): those clients carry no per-conversation id\n * the proxy could bind, so ask for the most recently active session instead\n * (fallback=latest). Same soft-fail contract as fetchStatus. */\nexport async function fetchStatusLatest(proxyBase: string): Promise<Record<string, unknown> | undefined> {\n // conversationId must be non-empty (server rejects the empty string), but\n // any unknown id is fine: fallback=latest then resolves the most recently\n // active session.\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=dsh&fallback=latest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Liveness + version probe for status UIs: same loopback origin as the\n * status endpoint, so a 404 status + a live manifest means \"proxy up,\n * conversation not seen yet\" — an armed-but-idle state, not an error. */\nexport async function fetchProxyVersion(proxyBase: string): Promise<string | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n const version = (json as { version?: unknown }).version;\n return typeof version === \"string\" && version.length > 0 ? version : undefined;\n}\n","// Native dsh (deepseek-harness) cordis plugin: registers the `/acp` command.\n// Injected by the `bili dsh` launcher through a `--patch` overlay that\n// inserts this module (as a file:// URL) into the loader entry tree — the\n// same plugin shape as dsh-command-compact. Pure protocol client, same\n// discipline as the other agent plugins: no acp-kernel import, every byte of\n// displayed data comes from the proxy's HTTP endpoints.\n\nimport { proxyBaseFromEnv, fetchProxyVersion, fetchStatusLatest } from \"./shared.js\";\n\nexport const name = \"bili-acp\";\nexport const inject = [\"commands\"];\n\ntype CommandOutcome = { kind: \"success\" | \"error\"; text: string };\n\ntype CommandsService = {\n register: (command: { name: string; description: string; handler: () => Promise<CommandOutcome> }) => unknown;\n};\n\ntype PluginContext = { commands: CommandsService };\n\n/** One `/acp` invocation: latest session panel, else armed-but-idle, else a\n * reachable-proxy failure. dsh conversations carry no client-side id we can\n * bind to, so the read asks for the most recently active session. */\nasync function statusOutcome(): Promise<CommandOutcome> {\n const base = proxyBaseFromEnv();\n if (!base) {\n return {\n kind: \"error\",\n text: \"bili: no proxy detected — launch dsh through `bili dsh` so /acp can read context status.\",\n };\n }\n const status = await fetchStatusLatest(base);\n const panel = status?.panel;\n if (status && typeof panel === \"string\" && panel.length > 0) {\n return { kind: \"success\", text: panel };\n }\n const version = await fetchProxyVersion(base);\n if (version) {\n return {\n kind: \"success\",\n text: `billion-context@${version} — proxy connected, compression armed. No model request seen yet; send one, then run /acp again.`,\n };\n }\n return {\n kind: \"error\",\n text: `bili: proxy not reachable at ${base} — is the bili proxy still running?`,\n };\n}\n\nexport function apply(ctx: PluginContext): void {\n ctx.commands.register({\n name: \"acp\",\n description: \"Show bili context-compression status\",\n handler: statusOutcome,\n });\n}\n"],"mappings":";;;;AAeA,IAAM,oBAAoB;AAwBnB,SAAS,mBAAuC;AACnD,QAAM,MAAM,QAAQ,IAAI,uBAAuB,KAAK;AACpD,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AACA,UAAM,MAAM,IAAI,IAAI,GAAG;AACvB,WAAO,IAAI,aAAa,WAAW,IAAI,aAAa,WAAW,GAAG,IAAI,QAAQ,KAAK,IAAI,IAAI,KAAK;AAAA,EACpG,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAOA,eAAe,UAAU,KAAa,MAA+B,WAAmB,gBAAuF;AAC3K,QAAM,KAAK,IAAI,gBAAgB;AAI/B,MAAI,gBAAgB,QAAS,IAAG,MAAM;AACtC,QAAM,QAAQ,WAAW,MAAM,GAAG,MAAM,GAAG,SAAS;AACpD,QAAM,kBAAkB,MAAM,GAAG,MAAM;AACvC,kBAAgB,iBAAiB,SAAS,iBAAiB,EAAE,MAAM,KAAK,CAAC;AACzE,MAAI;AACA,QAAI;AACJ,QAAI;AACA,YAAM,MAAM,MAAM,KAAK,EAAE,GAAG,MAAM,QAAQ,GAAG,OAAO,CAAC;AAAA,IACzD,SAAS,KAAK;AACV,UAAI,GAAG,OAAO,WAAW,CAAC,gBAAgB,QAAS,OAAM,IAAI,MAAM,iBAAiB,SAAS,OAAO,GAAG,EAAE;AACzG,YAAM;AAAA,IACV;AACA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,OAAgB;AACpB,QAAI;AACA,aAAO,KAAK,MAAM,IAAI;AAAA,IAC1B,QAAQ;AACJ,aAAO;AAAA,IACX;AACA,WAAO,EAAE,IAAI,IAAI,IAAI,QAAQ,IAAI,QAAQ,KAAK;AAAA,EAClD,UAAE;AACE,iBAAa,KAAK;AAClB,oBAAgB,oBAAoB,SAAS,eAAe;AAAA,EAChE;AACJ;AAwDA,eAAsB,kBAAkB,WAAiE;AAIrG,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,UAAU,GAAG,SAAS,4DAA4D,QAAW,iBAAiB;AACzI,MAAI,CAAC,MAAM,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AACrD,SAAO;AACX;AAKA,eAAsB,kBAAkB,WAAgD;AACpF,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,UAAU,GAAG,SAAS,2BAA2B,QAAW,iBAAiB;AACxG,MAAI,CAAC,MAAM,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AACrD,QAAM,UAAW,KAA+B;AAChD,SAAO,OAAO,YAAY,YAAY,QAAQ,SAAS,IAAI,UAAU;AACzE;;;ACpJO,IAAM,OAAO;AACb,IAAM,SAAS,CAAC,UAAU;AAajC,eAAe,gBAAyC;AACpD,QAAM,OAAO,iBAAiB;AAC9B,MAAI,CAAC,MAAM;AACP,WAAO;AAAA,MACH,MAAM;AAAA,MACN,MAAM;AAAA,IACV;AAAA,EACJ;AACA,QAAM,SAAS,MAAM,kBAAkB,IAAI;AAC3C,QAAM,QAAQ,QAAQ;AACtB,MAAI,UAAU,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACzD,WAAO,EAAE,MAAM,WAAW,MAAM,MAAM;AAAA,EAC1C;AACA,QAAM,UAAU,MAAM,kBAAkB,IAAI;AAC5C,MAAI,SAAS;AACT,WAAO;AAAA,MACH,MAAM;AAAA,MACN,MAAM,mBAAmB,OAAO;AAAA,IACpC;AAAA,EACJ;AACA,SAAO;AAAA,IACH,MAAM;AAAA,IACN,MAAM,gCAAgC,IAAI;AAAA,EAC9C;AACJ;AAEO,SAAS,MAAM,KAA0B;AAC5C,MAAI,SAAS,SAAS;AAAA,IAClB,MAAM;AAAA,IACN,aAAa;AAAA,IACb,SAAS;AAAA,EACb,CAAC;AACL;","names":[]}
|
package/dist/agent/omp.js
CHANGED
|
@@ -60,9 +60,15 @@ async function fetchJson(url, init, timeoutMs, externalSignal) {
|
|
|
60
60
|
externalSignal?.removeEventListener("abort", onExternalAbort);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
async function fetchManifest(proxyBase) {
|
|
63
|
+
async function fetchManifest(proxyBase, format = "anthropic") {
|
|
64
64
|
const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, void 0, MANIFEST_TIMEOUT_MS);
|
|
65
65
|
if (!ok || !json || typeof json !== "object") throw new Error(`manifest fetch failed: ${status}`);
|
|
66
|
+
if (format === "openai") {
|
|
67
|
+
const data2 = json;
|
|
68
|
+
const tools2 = (data2.tools?.openai ?? []).filter((t) => typeof t.name === "string");
|
|
69
|
+
if (tools2.length === 0) throw new Error("manifest served no openai tools");
|
|
70
|
+
return tools2.map((t) => ({ name: t.name, description: t.description, inputSchema: t.parameters ?? { type: "object", properties: {} } }));
|
|
71
|
+
}
|
|
66
72
|
const data = json;
|
|
67
73
|
const tools = (data.tools?.anthropic ?? []).filter((t) => typeof t.name === "string");
|
|
68
74
|
if (tools.length === 0) throw new Error("manifest served no anthropic tools");
|
|
@@ -305,7 +311,8 @@ function createBiliPlugin(agentOverride, opts) {
|
|
|
305
311
|
};
|
|
306
312
|
const proxyBase = detectProxyBase(ctx.model?.baseUrl);
|
|
307
313
|
if (proxyBase === void 0) {
|
|
308
|
-
|
|
314
|
+
const removeHint = agent === "pi" ? ", or remove this plugin (`bili plugin remove pi`) if you use billion-context-pi or don't want a proxy" : agent === "omp" ? ", or remove this plugin (`bili plugin remove omp`) if you don't want a proxy" : "";
|
|
315
|
+
notify(`bili: no proxy detected \u2014 run via \`bili ${agent}\` (or set a /bili/ baseURL) to use proxy mode${removeHint}`, "warning");
|
|
309
316
|
return;
|
|
310
317
|
}
|
|
311
318
|
const conversationId = sessionIdOf(ctx) ?? "unknown";
|
package/dist/agent/omp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/agent/shared.ts","../../src/agent/pi.ts","../../src/agent/omp.ts"],"sourcesContent":["// Shared thin-plugin core for agent-side extensions (\"内外呼应\", issue #1).\n// The agent plugin is a PURE PROTOCOL CLIENT: no acp-kernel import, no\n// compression logic. The proxy stays the single compression authority; the\n// plugin only (1) detects the proxy, (2) fetches the tool manifest (single\n// source of truth), (3) forwards tool executes, (4) reads status. Same\n// package as the proxy ⇒ same version ⇒ no kernel-skew bug class.\n\nexport type ManifestTool = {\n name: string;\n description?: string;\n inputSchema: unknown;\n};\n\nconst MANIFEST_TIMEOUT_MS = 5000;\nconst TOOL_TIMEOUT_MS = 60000;\nconst STATUS_TIMEOUT_MS = 5000;\n\n/** Detect the proxy from a provider baseUrl's `/bili/` zero-config prefix.\n * The real prefix embeds the full upstream URL (`/bili/https://…`), so the\n * check requires `bili` as the first path segment followed by an http(s)\n * URL — a plain `/foo/bili/` path segment is NOT a bili proxy.\n * Returns the proxy origin (scheme//host) the request will actually hit. */\nexport function proxyBaseFromUrl(baseUrl: string | undefined): string | undefined {\n if (!baseUrl) return undefined;\n try {\n const url = new URL(baseUrl);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n const segments = url.pathname.split(\"/\").filter((s) => s.length > 0);\n if (segments[0] !== \"bili\") return undefined;\n const rest = url.pathname.slice(url.pathname.indexOf(\"bili\") + \"bili\".length);\n if (!/^\\/https?:\\/\\//.test(rest)) return undefined;\n return `${url.protocol}//${url.host}`;\n } catch {\n return undefined;\n }\n}\n\n/** MITM transparent mode has no `/bili/` prefix; the proxy's launcher exports\n * BILLION_CONTEXT_PROXY. A stale value surfaces as a tool-forward error. */\nexport function proxyBaseFromEnv(): string | undefined {\n const raw = process.env.BILLION_CONTEXT_PROXY?.trim();\n if (!raw) return undefined;\n try {\n const url = new URL(raw);\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? `${url.protocol}//${url.host}` : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function detectProxyBase(baseUrl: string | undefined): string | undefined {\n if (process.env.BILLION_CONTEXT_PLUGIN === \"0\") return undefined;\n return proxyBaseFromUrl(baseUrl) ?? proxyBaseFromEnv();\n}\n\nasync function fetchJson(url: string, init: RequestInit | undefined, timeoutMs: number, externalSignal?: AbortSignal): Promise<{ ok: boolean; status: number; json: unknown }> {\n const ac = new AbortController();\n // An already-aborted external signal never fires its \"abort\" event, so\n // forward the state directly — otherwise only the timeout could stop\n // the request, turning an instant cancel into a timeout wait.\n if (externalSignal?.aborted) ac.abort();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n const onExternalAbort = () => ac.abort();\n externalSignal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n try {\n let res: Response;\n try {\n res = await fetch(url, { ...init, signal: ac.signal });\n } catch (err) {\n if (ac.signal.aborted && !externalSignal?.aborted) throw new Error(`timeout after ${timeoutMs}ms: ${url}`);\n throw err;\n }\n const text = await res.text();\n let json: unknown = undefined;\n try {\n json = JSON.parse(text);\n } catch {\n json = undefined;\n }\n return { ok: res.ok, status: res.status, json };\n } finally {\n clearTimeout(timer);\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n }\n}\n\nexport async function fetchManifest(proxyBase: string): Promise<ManifestTool[]> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, MANIFEST_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") throw new Error(`manifest fetch failed: ${status}`);\n const data = json as { tools?: { anthropic?: { name?: string; description?: string; input_schema?: unknown }[] } };\n const tools = (data.tools?.anthropic ?? []).filter((t): t is { name: string; description?: string; input_schema?: unknown } => typeof t.name === \"string\");\n if (tools.length === 0) throw new Error(\"manifest served no anthropic tools\");\n return tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.input_schema ?? { type: \"object\", properties: {} } }));\n}\n\nexport async function forwardTool(proxyBase: string, conversationId: string, tool: string, args: unknown, signal?: AbortSignal): Promise<string> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/tool`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId, tool, args: args ?? {} }),\n }, TOOL_TIMEOUT_MS, signal);\n const data = json as { ok?: boolean; result?: string; error?: string } | undefined;\n if (!ok || !data?.ok) {\n throw new Error(`bili proxy tool ${tool} failed (${status}): ${data?.error ?? \"unknown error\"}`);\n }\n return data.result ?? \"\";\n}\n\n/** Soft-fail by design: the status read is best-effort UI data; undefined\n * means \"no data\" whether the proxy is down or the session is unknown. */\nexport async function fetchStatus(proxyBase: string, conversationId: string): Promise<Record<string, unknown> | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=${encodeURIComponent(conversationId)}`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Wire-mode status read (dsh): those clients carry no per-conversation id\n * the proxy could bind, so ask for the most recently active session instead\n * (fallback=latest). Same soft-fail contract as fetchStatus. */\nexport async function fetchStatusLatest(proxyBase: string): Promise<Record<string, unknown> | undefined> {\n // conversationId must be non-empty (server rejects the empty string), but\n // any unknown id is fine: fallback=latest then resolves the most recently\n // active session.\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=dsh&fallback=latest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Liveness + version probe for status UIs: same loopback origin as the\n * status endpoint, so a 404 status + a live manifest means \"proxy up,\n * conversation not seen yet\" — an armed-but-idle state, not an error. */\nexport async function fetchProxyVersion(proxyBase: string): Promise<string | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n const version = (json as { version?: unknown }).version;\n return typeof version === \"string\" && version.length > 0 ? version : undefined;\n}\n","// Thin agent extension for pi and omp (\"内外呼应\", issue #1). Loaded by pi\n// via the package.json `pi` manifest (dist/agent/pi.js) or by omp via the\n// config.yml `extensions:` list (dist/agent/omp.js). pi and omp share the\n// ExtensionFactory API shape, so one factory serves both; types below are\n// minimal structural declarations — the bundled artifact imports NOTHING\n// from the host at runtime (the host duck-types us in).\n\nimport { detectProxyBase, fetchManifest, forwardTool, fetchStatus, fetchProxyVersion, type ManifestTool } from \"./shared.js\";\n\ntype Ctx = {\n sessionManager?: { getSessionId?: () => string } | undefined;\n model?: { contextWindow?: number; baseUrl?: string; provider?: string; id?: string; [key: string]: unknown } | undefined;\n cwd?: string;\n};\n\ntype TextBlock = { type: \"text\"; text: string };\ntype ToolResult = { content: TextBlock[]; isError?: boolean };\n\ntype ToolDefinition = {\n name: string;\n description?: string;\n parameters: unknown;\n // omp 17.x mounts extension tools that omit loadMode under xd:// devices\n // (invisible to the main turn's tools array — only title requests see\n // them). Declaring \"essential\" keeps ACP tools top-level; pi upstream\n // ignores the field.\n loadMode?: string;\n execute: (toolCallId: string, params: Record<string, unknown>, signal: AbortSignal | undefined, onUpdate: ((u: unknown) => void) | undefined, ctx: Ctx) => Promise<ToolResult>;\n};\n\ntype CommandCtx = {\n sessionManager?: { getSessionId?: () => string } | undefined;\n model?: { contextWindow?: number; baseUrl?: string } | undefined;\n ui?: { notify?: (message: string, type?: string) => void } | undefined;\n};\n\ntype ExtensionAPI = {\n on: (event: string, handler: (event: never, ctx: Ctx) => unknown) => void;\n registerTool: (tool: ToolDefinition) => void;\n registerCommand?: (name: string, options: { description?: string; handler: (args: string, ctx: CommandCtx) => void | Promise<void> }) => void;\n // #535: launcher passes provider URL rewrites via env; the extension\n // overrides each provider's baseUrl at load (file-free routing — no\n // models.json overlay). Optional because older hosts may lack it.\n registerProvider?: (name: string, config: { baseUrl: string }) => void;\n // #535 omp-only: omp pins the session's Model object from the static\n // catalog BEFORE extensions load, and its registerProvider — unlike\n // pi's _refreshCurrentModelFromRegistry — never re-resolves the live\n // session model, so the extension must re-pin it via setModel (see the\n // session_start handler below). Optional because pi hosts lack it.\n setModel?: (model: Record<string, unknown> & { baseUrl?: string }) => Promise<boolean | void> | boolean | void;\n // Persistent transcript output (rendered by TUI and web hosts like\n // pi-web); notify() is a transient toast — only the fallback for hosts\n // without sendMessage (issue #359).\n sendMessage?: (message: { customType: string; content: string; display: boolean }) => void;\n};\n\nfunction agentName(override: string | undefined): string {\n if (override) return override;\n return process.env.BILLION_CONTEXT_PLUGIN_AGENT === \"omp\" ? \"omp\" : \"pi\";\n}\n\nfunction proxyBaseForCtx(ctx: Ctx): string | undefined {\n return detectProxyBase(ctx.model?.baseUrl);\n}\n\nfunction sessionIdOf(ctx: Ctx): string | undefined {\n try {\n const sid = ctx.sessionManager?.getSessionId?.();\n return typeof sid === \"string\" ? sid : undefined;\n } catch {\n return undefined;\n }\n}\n\n// omp's chat-completions payloads carry NO conversation signal (no\n// prompt_cache_key / session / user, and no session header — verified by dump),\n// so the proxy's openai identity falls to a content fingerprint that never\n// matches the session id this plugin registered (the identity register is keyed\n// by the omp session uuid). The before_provider_request return value REPLACES\n// the whole outgoing payload (omp onPayload chain, verified in the omp 17.3.8\n// dist), so stamp prompt_cache_key with the omp session id: the proxy binds\n// pluginMode by that identity and /acp finds the session by it.\n// Chat shape = messages array, no responses `input`, no native prompt_cache_key.\n// max_tokens is NOT a discriminator: omp's openai-compat providers send it in\n// every chat-completions body (maxTokensField:\"max_tokens\") exactly like the\n// anthropic wire — excluding it meant the target shape was never stamped\n// (#268). The anthropic wire gets stamped too: the proxy records the mapping\n// from the body pck there as well and strips the field before forwarding to\n// the real Anthropic. pi is untouched (it stamps x-bili-plugin-conversation in\n// before_provider_headers, which outranks the body field).\nfunction stampPromptCacheKey(event: unknown, ctx: Ctx, agent: string): Record<string, unknown> | undefined {\n if (agent !== \"omp\") return undefined;\n const payload = (event as { payload?: unknown } | undefined)?.payload;\n if (payload === null || typeof payload !== \"object\" || Array.isArray(payload)) return undefined;\n const p = payload as Record<string, unknown>;\n if (!Array.isArray(p.messages)) return undefined;\n if (p.input !== undefined) return undefined;\n if (typeof p.prompt_cache_key === \"string\" && p.prompt_cache_key.trim().length > 0) return undefined;\n const sid = sessionIdOf(ctx);\n if (sid === undefined || sid.length === 0) return undefined;\n return { ...p, prompt_cache_key: sid };\n}\n\nfunction fmtTok(n: number): string {\n if (n < 1000) return String(n);\n if (n < 1_000_000) return `${(n / 1000).toFixed(1)}K`;\n return `${(n / 1_000_000).toFixed(2)}M`;\n}\n\nfunction renderAcpStatus(s: Record<string, unknown>): string {\n const num = (v: unknown): number | null => (typeof v === \"number\" && Number.isFinite(v) ? v : null);\n const contextTokens = num(s.contextTokens);\n const contextLimit = num(s.contextLimit);\n const inputTokens = num(s.inputTokens);\n const outputTokens = num(s.outputTokens);\n const cachedTokens = num(s.cachedTokens);\n const requests = num(s.requests);\n const blocks = Array.isArray(s.blocks) ? (s.blocks as Array<{ tier?: number; active?: boolean }>) : [];\n const activeBlocks = blocks.filter((b) => b.active === true).length;\n const lines: string[] = [\"📊 ACP status\"];\n if (contextTokens !== null) {\n const pct = contextLimit !== null && contextLimit > 0 ? ` (${((contextTokens / contextLimit) * 100).toFixed(1)}%)` : \"\";\n lines.push(` context: ${fmtTok(contextTokens)}${contextLimit !== null ? ` / ${fmtTok(contextLimit)}` : \"\"}${pct}`);\n }\n const hostCredit = num(s.hostCredit);\n if (hostCredit !== null && hostCredit > 0) {\n lines.push(` host baseline: uncompressed (proxy backfilled +${fmtTok(hostCredit)} tok)`);\n }\n if (inputTokens !== null || outputTokens !== null || cachedTokens !== null) {\n lines.push(` in/out/cached: ${fmtTok(inputTokens ?? 0)} / ${fmtTok(outputTokens ?? 0)} / ${fmtTok(cachedTokens ?? 0)}`);\n }\n if (requests !== null) lines.push(` requests: ${requests}`);\n if (blocks.length > 0) lines.push(` blocks: ${blocks.length} (${activeBlocks} active)`);\n return lines.join(\"\\n\");\n}\n\nfunction manifestToTool(proxyBase: string, tool: ManifestTool, agent: string): ToolDefinition {\n return {\n name: tool.name,\n description: tool.description,\n parameters: tool.inputSchema,\n loadMode: \"essential\",\n execute: async (_toolCallId, params, signal, _onUpdate, ctx) => {\n const conversationId = sessionIdOf(ctx) ?? \"unknown\";\n try {\n const output = await forwardTool(proxyBase, conversationId, tool.name, params, signal);\n return { content: [{ type: \"text\", text: output }] };\n } catch (err) {\n return { content: [{ type: \"text\", text: `bili tool error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };\n }\n },\n };\n}\n\nfunction parseProviderRewrites(env: NodeJS.ProcessEnv): Record<string, string> | undefined {\n const raw = env.BILI_PROVIDER_REWRITES;\n if (raw === undefined || raw.trim().length === 0) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n console.error(\"bili-plugin: BILI_PROVIDER_REWRITES is not valid JSON — provider URLs left untouched\");\n return undefined;\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof value !== \"string\" || !/^https?:\\/\\//i.test(value)) continue;\n out[key] = value;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nconst RETRY_INTERVAL_MS = 10000;\n\ntype RegisterState = { sid?: string; toolsFor?: string; toolsReady?: boolean; pending?: Promise<void>; retryAt?: number; identityAt?: string; retryIntervalMs: number };\n\n// omp never emits before_provider_headers, so the x-bili-plugin marker cannot\n// be stamped per request. Register the conversation id once (after tools are\n// ready): the proxy binds any request carrying that id into plugin mode —\n// same launcher path claude/codex use (#162).\nasync function postIdentityRegister(proxyBase: string, conversationId: string, agent: string): Promise<void> {\n const res = await fetch(`${proxyBase}/__bili/plugin/register`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId, agent, identity: true }),\n signal: AbortSignal.timeout(5000),\n });\n if (!res.ok) throw new Error(`register HTTP ${res.status}`);\n}\n\nasync function registerTools(pi: ExtensionAPI, ctx: Ctx, state: RegisterState, agent: string): Promise<void> {\n const proxyBase = proxyBaseForCtx(ctx);\n if (proxyBase === undefined) return;\n // Cache on the session id; \"\" (host has no sessionManager) still caches,\n // so a successful registration is not re-fetched on every provider\n // request — the manifest is session-independent anyway.\n const sid = sessionIdOf(ctx) ?? \"\";\n if (sid === state.sid) return;\n if (state.pending !== undefined) return state.pending;\n if (state.retryAt !== undefined && Date.now() < state.retryAt) return;\n const wait = state.retryIntervalMs;\n state.pending = (async () => {\n let tools: ManifestTool[];\n try {\n tools = await fetchManifest(proxyBase);\n } catch (err) {\n state.retryAt = Date.now() + wait;\n console.error(`bili-plugin(${agent}): manifest fetch failed: ${err instanceof Error ? err.message : String(err)} — retrying in ${wait / 1000}s`);\n return;\n }\n try {\n // toolsFor (not sid) guards the register loop: a retry after a\n // failed identity register re-fetches the manifest but must NOT\n // re-register the tools (the host may not dedupe by name).\n if (state.toolsFor !== sid) {\n for (const t of tools) pi.registerTool(manifestToTool(proxyBase, t, agent));\n state.toolsFor = sid;\n }\n state.toolsReady = true;\n state.retryAt = undefined;\n if (agent === \"omp\" && sid !== \"\" && state.identityAt !== sid) {\n try {\n await postIdentityRegister(proxyBase, sid, agent);\n state.identityAt = sid;\n } catch (err) {\n // Leave state.sid UNSET so the next per-request event\n // re-enters (throttled by retryAt) and retries ONLY the\n // register — setting sid here would wedge the session in\n // wire mode forever (the early return above blocks every\n // retry).\n state.retryAt = Date.now() + wait;\n console.error(`bili-plugin(${agent}): identity register failed (${err instanceof Error ? err.message : String(err)}) — retrying in ${wait / 1000}s`);\n return;\n }\n }\n state.sid = sid;\n } catch (err) {\n state.sid = undefined;\n state.toolsFor = undefined;\n state.retryAt = Date.now() + wait;\n console.error(`bili-plugin(${agent}): tool registration deferred (${err instanceof Error ? err.message : String(err)}) — retrying in ${wait / 1000}s`);\n }\n })();\n try {\n await state.pending;\n } finally {\n state.pending = undefined;\n }\n}\n\nexport function createBiliPlugin(agentOverride?: string, opts?: { retryIntervalMs?: number }): (pi: ExtensionAPI) => void {\n return function biliPlugin(pi: ExtensionAPI): void {\n const agent = agentName(agentOverride);\n const state: RegisterState = { retryIntervalMs: opts?.retryIntervalMs ?? RETRY_INTERVAL_MS };\n // #535: file-free routing — override provider baseUrls at load from\n // the launcher-passed manifest (see buildPiEnv). registerProvider is\n // queued during initial extension load and applied before any model\n // traffic, so every request (including round 1) rides the proxy.\n const rewrites = parseProviderRewrites(process.env);\n if (rewrites !== undefined && typeof pi.registerProvider !== \"function\") {\n console.error(\n \"bili-plugin: BILI_PROVIDER_REWRITES is set but this pi build has no registerProvider API — \" +\n \"provider traffic goes DIRECT (uncompressed). Update pi, or reinstall the bili plugin: `bili plugin install pi`.\",\n );\n }\n if (rewrites !== undefined && typeof pi.registerProvider === \"function\") {\n for (const [key, url] of Object.entries(rewrites)) {\n try {\n pi.registerProvider(key, { baseUrl: url });\n } catch (err) {\n console.error(`bili-plugin: registerProvider(${key}) failed: ${err instanceof Error ? err.message : String(err)} — traffic for this provider goes direct`);\n }\n }\n }\n // #535: cancel the host's NATIVE compaction so its summarizer never\n // fires alongside bili's ACP compression — the in-extension\n // replacement for the old compaction-off config injection. pi's event\n // carries `reason`: cancel only threshold + overflow so manual\n // /compact stays user-owned. omp's event has no reason field, so omp\n // cancels ALL compaction — under bili, manual native /compact is\n // equally harmful (the native summarizer would destroy the\n // ACP-tagged context), the host shows \"Compaction cancelled\", and\n // the user should reach for /acp instead. Only armed under `bili`\n // launch: plain pi/omp with the plugin installed stays fully native.\n if ((agent === \"pi\" || agent === \"omp\") && process.env.BILLION_CONTEXT_PROXY !== undefined) {\n pi.on(\"session_before_compact\", (event) => {\n if (agent === \"pi\") {\n const reason = (event as unknown as { reason?: unknown }).reason;\n if (reason === \"threshold\" || reason === \"overflow\") return { cancel: true };\n return undefined;\n }\n return { cancel: true };\n });\n }\n // #535 omp-only: omp resolves modelRoles.default into options.model\n // from the PRE-extension static catalog (main.ts: \"scope is resolved\n // before extensions register their providers\"), and omp's fork lacks\n // pi's registerProvider → _refreshCurrentModelFromRegistry hop — the\n // registry gets the rewritten baseUrl but the live session keeps the\n // direct one, so every request bypasses the proxy (fetch trace →\n // http://127.0.0.1:8197/v1/responses with zero proxy forwards). Re-pin\n // the session model at load + on every session switch: spread the\n // current model with the rewritten baseUrl through the host setModel\n // (keyed-provider-gated; local providers carry dummy keys). Mid-session\n // /model picks resolve from the already-overridden registry, so only\n // session start/restore need this.\n if (agent === \"omp\" && rewrites !== undefined && typeof pi.setModel === \"function\") {\n const repin = async (ctx: Ctx): Promise<void> => {\n const model = ctx?.model;\n if (model === null || typeof model !== \"object\") return;\n const provider = model.provider;\n if (typeof provider !== \"string\" || provider === \"\") return;\n const rewritten = rewrites[provider];\n if (rewritten === undefined || model.baseUrl === rewritten) return;\n try {\n const switched = await pi.setModel?.({ ...model, baseUrl: rewritten });\n if (switched === false) {\n console.error(`bili-plugin: omp setModel(${provider}/${String(model.id)}) rejected (no API key) — traffic for this provider goes direct`);\n }\n } catch (err) {\n console.error(`bili-plugin: omp setModel failed: ${err instanceof Error ? err.message : String(err)} — traffic goes direct`);\n }\n };\n pi.on(\"session_start\", (_event, ctx) => repin(ctx));\n pi.on(\"session_switch\", (_event, ctx) => repin(ctx));\n }\n if (typeof pi.registerCommand === \"function\") {\n pi.registerCommand(\"acp\", {\n description: \"Show ACP context-compression status for this session\",\n handler: async (_args, ctx) => {\n const notify = (message: string, type?: string): void => {\n try {\n ctx.ui?.notify?.(message, type);\n } catch {\n // host UI unavailable — the command is best-effort\n }\n };\n const proxyBase = detectProxyBase(ctx.model?.baseUrl);\n if (proxyBase === undefined) {\n notify(\"bili: no proxy detected (run via `bili <client>` or set a /bili/ baseURL)\", \"warning\");\n return;\n }\n const conversationId = sessionIdOf(ctx) ?? \"unknown\";\n let status: Record<string, unknown> | undefined;\n try {\n status = await fetchStatus(proxyBase, conversationId);\n } catch (err) {\n notify(`bili: status fetch failed: ${err instanceof Error ? err.message : String(err)}`, \"error\");\n return;\n }\n if (status === undefined) {\n // 404 from a live proxy = this conversation has sent no\n // model request yet (e.g. /acp right after startup).\n // Probe the manifest to confirm liveness + version and\n // show an armed/idle notice instead of a scary warning.\n let version: string | undefined;\n try {\n version = await fetchProxyVersion(proxyBase);\n } catch {\n version = undefined;\n }\n if (version !== undefined) {\n notify(\n `billion-context@${version} — proxy connected, compression armed. No model request in this conversation yet; send one, then run /acp again.`,\n \"info\",\n );\n } else {\n notify(\"bili: no ACP session yet (send a model request first, then run /acp)\", \"warning\");\n }\n return;\n }\n const panel = typeof status.panel === \"string\" ? status.panel : undefined;\n const text = panel ?? renderAcpStatus(status);\n // Persistent transcript output (TUI + web hosts like pi-web).\n // The proxy strips this message from the model context by\n // content signature (src/acp-panel.ts), so it never reaches\n // the LLM; notify() is the fallback for hosts without\n // sendMessage (older pi).\n if (typeof pi.sendMessage === \"function\") {\n try {\n pi.sendMessage({ customType: \"bili-acp-status\", content: text, display: true });\n return;\n } catch (err) {\n console.error(`bili-plugin(${agent}): sendMessage failed (${err instanceof Error ? err.message : String(err)}) — falling back to notify`);\n }\n }\n notify(text, \"info\");\n },\n });\n }\n pi.on(\"before_provider_headers\", (event, ctx) => {\n try {\n if (proxyBaseForCtx(ctx) === undefined) return;\n const headers = (event as unknown as { headers?: Record<string, string> }).headers;\n if (headers === undefined || typeof headers !== \"object\" || Array.isArray(headers)) return;\n // The x-bili-plugin marker tells the proxy \"the client owns the\n // ACP tools natively — skip wire-level injection\". Stamping it\n // before registerTools() finishes would send round 1 out with\n // NO ACP tools (the first provider request races the manifest\n // fetch). Claim ownership only once tools are registered;\n // until then the request rides the proxy's wire mode. A\n // permanently failing manifest fetch keeps us in wire mode —\n // a graceful fallback rather than a tool-less session.\n if (state.toolsReady === true) {\n const sid = sessionIdOf(ctx);\n if (sid !== undefined) headers[\"x-bili-plugin-conversation\"] = sid;\n headers[\"x-bili-plugin\"] = agent;\n const window = ctx.model?.contextWindow;\n if (typeof window === \"number\" && Number.isFinite(window) && window > 0) {\n headers[\"x-bili-plugin-context-window\"] = String(Math.floor(window));\n }\n }\n } catch (err) {\n console.error(`bili-plugin(${agent}): header stamp skipped (${err instanceof Error ? err.message : String(err)})`);\n }\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n });\n pi.on(\"before_provider_request\", (event, ctx) => {\n // omp emits this per model request (but never before_provider_headers);\n // it doubles as the retry driver when the session_start manifest\n // fetch raced the proxy startup. Cached by sid, throttled by retryAt.\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n return stampPromptCacheKey(event, ctx, agent);\n });\n pi.on(\"session_start\", (_event, ctx) => {\n state.sid = undefined;\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n });\n // omp fires session_compact on in-session native compaction (sid does\n // not rotate), so the proxy reuses stale state — notify it to archive\n // the now-unreachable blocks (#395). Fire-and-forget: a failed\n // notification must never break the agent's compaction.\n pi.on(\"session_compact\", (_event, ctx) => {\n const proxyBase = proxyBaseForCtx(ctx);\n if (proxyBase === undefined) return;\n const sid = sessionIdOf(ctx);\n if (sid === undefined || sid.length === 0) return;\n fetch(`${proxyBase}/__bili/plugin/compact`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId: sid }),\n signal: AbortSignal.timeout(5000),\n }).catch(() => {});\n });\n };\n}\n\nexport default createBiliPlugin();\n\nexport { fetchStatus };\n","import { createBiliPlugin } from \"./pi.js\";\n\nexport default createBiliPlugin(\"omp\");\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/agent/shared.ts","../../src/agent/pi.ts","../../src/agent/omp.ts"],"sourcesContent":["// Shared thin-plugin core for agent-side extensions (\"内外呼应\", issue #1).\n// The agent plugin is a PURE PROTOCOL CLIENT: no acp-kernel import, no\n// compression logic. The proxy stays the single compression authority; the\n// plugin only (1) detects the proxy, (2) fetches the tool manifest (single\n// source of truth), (3) forwards tool executes, (4) reads status. Same\n// package as the proxy ⇒ same version ⇒ no kernel-skew bug class.\n\nexport type ManifestTool = {\n name: string;\n description?: string;\n inputSchema: unknown;\n};\n\nconst MANIFEST_TIMEOUT_MS = 5000;\nconst TOOL_TIMEOUT_MS = 60000;\nconst STATUS_TIMEOUT_MS = 5000;\n\n/** Detect the proxy from a provider baseUrl's `/bili/` zero-config prefix.\n * The real prefix embeds the full upstream URL (`/bili/https://…`), so the\n * check requires `bili` as the first path segment followed by an http(s)\n * URL — a plain `/foo/bili/` path segment is NOT a bili proxy.\n * Returns the proxy origin (scheme//host) the request will actually hit. */\nexport function proxyBaseFromUrl(baseUrl: string | undefined): string | undefined {\n if (!baseUrl) return undefined;\n try {\n const url = new URL(baseUrl);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n const segments = url.pathname.split(\"/\").filter((s) => s.length > 0);\n if (segments[0] !== \"bili\") return undefined;\n const rest = url.pathname.slice(url.pathname.indexOf(\"bili\") + \"bili\".length);\n if (!/^\\/https?:\\/\\//.test(rest)) return undefined;\n return `${url.protocol}//${url.host}`;\n } catch {\n return undefined;\n }\n}\n\n/** MITM transparent mode has no `/bili/` prefix; the proxy's launcher exports\n * BILLION_CONTEXT_PROXY. A stale value surfaces as a tool-forward error. */\nexport function proxyBaseFromEnv(): string | undefined {\n const raw = process.env.BILLION_CONTEXT_PROXY?.trim();\n if (!raw) return undefined;\n try {\n const url = new URL(raw);\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? `${url.protocol}//${url.host}` : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function detectProxyBase(baseUrl: string | undefined): string | undefined {\n if (process.env.BILLION_CONTEXT_PLUGIN === \"0\") return undefined;\n return proxyBaseFromUrl(baseUrl) ?? proxyBaseFromEnv();\n}\n\nasync function fetchJson(url: string, init: RequestInit | undefined, timeoutMs: number, externalSignal?: AbortSignal): Promise<{ ok: boolean; status: number; json: unknown }> {\n const ac = new AbortController();\n // An already-aborted external signal never fires its \"abort\" event, so\n // forward the state directly — otherwise only the timeout could stop\n // the request, turning an instant cancel into a timeout wait.\n if (externalSignal?.aborted) ac.abort();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n const onExternalAbort = () => ac.abort();\n externalSignal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n try {\n let res: Response;\n try {\n res = await fetch(url, { ...init, signal: ac.signal });\n } catch (err) {\n if (ac.signal.aborted && !externalSignal?.aborted) throw new Error(`timeout after ${timeoutMs}ms: ${url}`);\n throw err;\n }\n const text = await res.text();\n let json: unknown = undefined;\n try {\n json = JSON.parse(text);\n } catch {\n json = undefined;\n }\n return { ok: res.ok, status: res.status, json };\n } finally {\n clearTimeout(timer);\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n }\n}\n\nexport async function fetchManifest(proxyBase: string, format: \"anthropic\" | \"openai\" = \"anthropic\"): Promise<ManifestTool[]> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, MANIFEST_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") throw new Error(`manifest fetch failed: ${status}`);\n if (format === \"openai\") {\n // OpenAI function style: {name, description, parameters} (plain JSON Schema).\n const data = json as { tools?: { openai?: { name?: string; description?: string; parameters?: unknown }[] } };\n const tools = (data.tools?.openai ?? []).filter((t): t is { name: string; description?: string; parameters?: unknown } => typeof t.name === \"string\");\n if (tools.length === 0) throw new Error(\"manifest served no openai tools\");\n return tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.parameters ?? { type: \"object\", properties: {} } }));\n }\n const data = json as { tools?: { anthropic?: { name?: string; description?: string; input_schema?: unknown }[] } };\n const tools = (data.tools?.anthropic ?? []).filter((t): t is { name: string; description?: string; input_schema?: unknown } => typeof t.name === \"string\");\n if (tools.length === 0) throw new Error(\"manifest served no anthropic tools\");\n return tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.input_schema ?? { type: \"object\", properties: {} } }));\n}\n\nconst COMPACT_TIMEOUT_MS = 5000;\n\n/** Report a host-native compaction boundary to the proxy archive (#395):\n * hosts that compact natively (opencode) cannot cancel it, so the proxy\n * marks the boundary and archives unreachable blocks. Fire-and-forget at\n * call sites — a missed report degrades to stale blocks, not broken turns. */\nexport async function reportCompactionBoundary(proxyBase: string, conversationId: string): Promise<void> {\n await fetchJson(`${proxyBase}/__bili/plugin/compact`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId }),\n }, COMPACT_TIMEOUT_MS);\n}\n\nexport async function forwardTool(proxyBase: string, conversationId: string, tool: string, args: unknown, signal?: AbortSignal): Promise<string> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/tool`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId, tool, args: args ?? {} }),\n }, TOOL_TIMEOUT_MS, signal);\n const data = json as { ok?: boolean; result?: string; error?: string } | undefined;\n if (!ok || !data?.ok) {\n throw new Error(`bili proxy tool ${tool} failed (${status}): ${data?.error ?? \"unknown error\"}`);\n }\n return data.result ?? \"\";\n}\n\n/** Soft-fail by design: the status read is best-effort UI data; undefined\n * means \"no data\" whether the proxy is down or the session is unknown. */\nexport async function fetchStatus(proxyBase: string, conversationId: string): Promise<Record<string, unknown> | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=${encodeURIComponent(conversationId)}`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Wire-mode status read (dsh): those clients carry no per-conversation id\n * the proxy could bind, so ask for the most recently active session instead\n * (fallback=latest). Same soft-fail contract as fetchStatus. */\nexport async function fetchStatusLatest(proxyBase: string): Promise<Record<string, unknown> | undefined> {\n // conversationId must be non-empty (server rejects the empty string), but\n // any unknown id is fine: fallback=latest then resolves the most recently\n // active session.\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=dsh&fallback=latest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Liveness + version probe for status UIs: same loopback origin as the\n * status endpoint, so a 404 status + a live manifest means \"proxy up,\n * conversation not seen yet\" — an armed-but-idle state, not an error. */\nexport async function fetchProxyVersion(proxyBase: string): Promise<string | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n const version = (json as { version?: unknown }).version;\n return typeof version === \"string\" && version.length > 0 ? version : undefined;\n}\n","// Thin agent extension for pi and omp (\"内外呼应\", issue #1). Loaded by pi\n// via the package.json `pi` manifest (dist/agent/pi.js) or by omp via the\n// config.yml `extensions:` list (dist/agent/omp.js). pi and omp share the\n// ExtensionFactory API shape, so one factory serves both; types below are\n// minimal structural declarations — the bundled artifact imports NOTHING\n// from the host at runtime (the host duck-types us in).\n\nimport { detectProxyBase, fetchManifest, forwardTool, fetchStatus, fetchProxyVersion, type ManifestTool } from \"./shared.js\";\n\ntype Ctx = {\n sessionManager?: { getSessionId?: () => string } | undefined;\n model?: { contextWindow?: number; baseUrl?: string; provider?: string; id?: string; [key: string]: unknown } | undefined;\n cwd?: string;\n};\n\ntype TextBlock = { type: \"text\"; text: string };\ntype ToolResult = { content: TextBlock[]; isError?: boolean };\n\ntype ToolDefinition = {\n name: string;\n description?: string;\n parameters: unknown;\n // omp 17.x mounts extension tools that omit loadMode under xd:// devices\n // (invisible to the main turn's tools array — only title requests see\n // them). Declaring \"essential\" keeps ACP tools top-level; pi upstream\n // ignores the field.\n loadMode?: string;\n execute: (toolCallId: string, params: Record<string, unknown>, signal: AbortSignal | undefined, onUpdate: ((u: unknown) => void) | undefined, ctx: Ctx) => Promise<ToolResult>;\n};\n\ntype CommandCtx = {\n sessionManager?: { getSessionId?: () => string } | undefined;\n model?: { contextWindow?: number; baseUrl?: string } | undefined;\n ui?: { notify?: (message: string, type?: string) => void } | undefined;\n};\n\ntype ExtensionAPI = {\n on: (event: string, handler: (event: never, ctx: Ctx) => unknown) => void;\n registerTool: (tool: ToolDefinition) => void;\n registerCommand?: (name: string, options: { description?: string; handler: (args: string, ctx: CommandCtx) => void | Promise<void> }) => void;\n // #535: launcher passes provider URL rewrites via env; the extension\n // overrides each provider's baseUrl at load (file-free routing — no\n // models.json overlay). Optional because older hosts may lack it.\n registerProvider?: (name: string, config: { baseUrl: string }) => void;\n // #535 omp-only: omp pins the session's Model object from the static\n // catalog BEFORE extensions load, and its registerProvider — unlike\n // pi's _refreshCurrentModelFromRegistry — never re-resolves the live\n // session model, so the extension must re-pin it via setModel (see the\n // session_start handler below). Optional because pi hosts lack it.\n setModel?: (model: Record<string, unknown> & { baseUrl?: string }) => Promise<boolean | void> | boolean | void;\n // Persistent transcript output (rendered by TUI and web hosts like\n // pi-web); notify() is a transient toast — only the fallback for hosts\n // without sendMessage (issue #359).\n sendMessage?: (message: { customType: string; content: string; display: boolean }) => void;\n};\n\nfunction agentName(override: string | undefined): string {\n if (override) return override;\n return process.env.BILLION_CONTEXT_PLUGIN_AGENT === \"omp\" ? \"omp\" : \"pi\";\n}\n\nfunction proxyBaseForCtx(ctx: Ctx): string | undefined {\n return detectProxyBase(ctx.model?.baseUrl);\n}\n\nfunction sessionIdOf(ctx: Ctx): string | undefined {\n try {\n const sid = ctx.sessionManager?.getSessionId?.();\n return typeof sid === \"string\" ? sid : undefined;\n } catch {\n return undefined;\n }\n}\n\n// omp's chat-completions payloads carry NO conversation signal (no\n// prompt_cache_key / session / user, and no session header — verified by dump),\n// so the proxy's openai identity falls to a content fingerprint that never\n// matches the session id this plugin registered (the identity register is keyed\n// by the omp session uuid). The before_provider_request return value REPLACES\n// the whole outgoing payload (omp onPayload chain, verified in the omp 17.3.8\n// dist), so stamp prompt_cache_key with the omp session id: the proxy binds\n// pluginMode by that identity and /acp finds the session by it.\n// Chat shape = messages array, no responses `input`, no native prompt_cache_key.\n// max_tokens is NOT a discriminator: omp's openai-compat providers send it in\n// every chat-completions body (maxTokensField:\"max_tokens\") exactly like the\n// anthropic wire — excluding it meant the target shape was never stamped\n// (#268). The anthropic wire gets stamped too: the proxy records the mapping\n// from the body pck there as well and strips the field before forwarding to\n// the real Anthropic. pi is untouched (it stamps x-bili-plugin-conversation in\n// before_provider_headers, which outranks the body field).\nfunction stampPromptCacheKey(event: unknown, ctx: Ctx, agent: string): Record<string, unknown> | undefined {\n if (agent !== \"omp\") return undefined;\n const payload = (event as { payload?: unknown } | undefined)?.payload;\n if (payload === null || typeof payload !== \"object\" || Array.isArray(payload)) return undefined;\n const p = payload as Record<string, unknown>;\n if (!Array.isArray(p.messages)) return undefined;\n if (p.input !== undefined) return undefined;\n if (typeof p.prompt_cache_key === \"string\" && p.prompt_cache_key.trim().length > 0) return undefined;\n const sid = sessionIdOf(ctx);\n if (sid === undefined || sid.length === 0) return undefined;\n return { ...p, prompt_cache_key: sid };\n}\n\nfunction fmtTok(n: number): string {\n if (n < 1000) return String(n);\n if (n < 1_000_000) return `${(n / 1000).toFixed(1)}K`;\n return `${(n / 1_000_000).toFixed(2)}M`;\n}\n\nfunction renderAcpStatus(s: Record<string, unknown>): string {\n const num = (v: unknown): number | null => (typeof v === \"number\" && Number.isFinite(v) ? v : null);\n const contextTokens = num(s.contextTokens);\n const contextLimit = num(s.contextLimit);\n const inputTokens = num(s.inputTokens);\n const outputTokens = num(s.outputTokens);\n const cachedTokens = num(s.cachedTokens);\n const requests = num(s.requests);\n const blocks = Array.isArray(s.blocks) ? (s.blocks as Array<{ tier?: number; active?: boolean }>) : [];\n const activeBlocks = blocks.filter((b) => b.active === true).length;\n const lines: string[] = [\"📊 ACP status\"];\n if (contextTokens !== null) {\n const pct = contextLimit !== null && contextLimit > 0 ? ` (${((contextTokens / contextLimit) * 100).toFixed(1)}%)` : \"\";\n lines.push(` context: ${fmtTok(contextTokens)}${contextLimit !== null ? ` / ${fmtTok(contextLimit)}` : \"\"}${pct}`);\n }\n const hostCredit = num(s.hostCredit);\n if (hostCredit !== null && hostCredit > 0) {\n lines.push(` host baseline: uncompressed (proxy backfilled +${fmtTok(hostCredit)} tok)`);\n }\n if (inputTokens !== null || outputTokens !== null || cachedTokens !== null) {\n lines.push(` in/out/cached: ${fmtTok(inputTokens ?? 0)} / ${fmtTok(outputTokens ?? 0)} / ${fmtTok(cachedTokens ?? 0)}`);\n }\n if (requests !== null) lines.push(` requests: ${requests}`);\n if (blocks.length > 0) lines.push(` blocks: ${blocks.length} (${activeBlocks} active)`);\n return lines.join(\"\\n\");\n}\n\nfunction manifestToTool(proxyBase: string, tool: ManifestTool, agent: string): ToolDefinition {\n return {\n name: tool.name,\n description: tool.description,\n parameters: tool.inputSchema,\n loadMode: \"essential\",\n execute: async (_toolCallId, params, signal, _onUpdate, ctx) => {\n const conversationId = sessionIdOf(ctx) ?? \"unknown\";\n try {\n const output = await forwardTool(proxyBase, conversationId, tool.name, params, signal);\n return { content: [{ type: \"text\", text: output }] };\n } catch (err) {\n return { content: [{ type: \"text\", text: `bili tool error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };\n }\n },\n };\n}\n\nfunction parseProviderRewrites(env: NodeJS.ProcessEnv): Record<string, string> | undefined {\n const raw = env.BILI_PROVIDER_REWRITES;\n if (raw === undefined || raw.trim().length === 0) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n console.error(\"bili-plugin: BILI_PROVIDER_REWRITES is not valid JSON — provider URLs left untouched\");\n return undefined;\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof value !== \"string\" || !/^https?:\\/\\//i.test(value)) continue;\n out[key] = value;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nconst RETRY_INTERVAL_MS = 10000;\n\ntype RegisterState = { sid?: string; toolsFor?: string; toolsReady?: boolean; pending?: Promise<void>; retryAt?: number; identityAt?: string; retryIntervalMs: number };\n\n// omp never emits before_provider_headers, so the x-bili-plugin marker cannot\n// be stamped per request. Register the conversation id once (after tools are\n// ready): the proxy binds any request carrying that id into plugin mode —\n// same launcher path claude/codex use (#162).\nasync function postIdentityRegister(proxyBase: string, conversationId: string, agent: string): Promise<void> {\n const res = await fetch(`${proxyBase}/__bili/plugin/register`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId, agent, identity: true }),\n signal: AbortSignal.timeout(5000),\n });\n if (!res.ok) throw new Error(`register HTTP ${res.status}`);\n}\n\nasync function registerTools(pi: ExtensionAPI, ctx: Ctx, state: RegisterState, agent: string): Promise<void> {\n const proxyBase = proxyBaseForCtx(ctx);\n if (proxyBase === undefined) return;\n // Cache on the session id; \"\" (host has no sessionManager) still caches,\n // so a successful registration is not re-fetched on every provider\n // request — the manifest is session-independent anyway.\n const sid = sessionIdOf(ctx) ?? \"\";\n if (sid === state.sid) return;\n if (state.pending !== undefined) return state.pending;\n if (state.retryAt !== undefined && Date.now() < state.retryAt) return;\n const wait = state.retryIntervalMs;\n state.pending = (async () => {\n let tools: ManifestTool[];\n try {\n tools = await fetchManifest(proxyBase);\n } catch (err) {\n state.retryAt = Date.now() + wait;\n console.error(`bili-plugin(${agent}): manifest fetch failed: ${err instanceof Error ? err.message : String(err)} — retrying in ${wait / 1000}s`);\n return;\n }\n try {\n // toolsFor (not sid) guards the register loop: a retry after a\n // failed identity register re-fetches the manifest but must NOT\n // re-register the tools (the host may not dedupe by name).\n if (state.toolsFor !== sid) {\n for (const t of tools) pi.registerTool(manifestToTool(proxyBase, t, agent));\n state.toolsFor = sid;\n }\n state.toolsReady = true;\n state.retryAt = undefined;\n if (agent === \"omp\" && sid !== \"\" && state.identityAt !== sid) {\n try {\n await postIdentityRegister(proxyBase, sid, agent);\n state.identityAt = sid;\n } catch (err) {\n // Leave state.sid UNSET so the next per-request event\n // re-enters (throttled by retryAt) and retries ONLY the\n // register — setting sid here would wedge the session in\n // wire mode forever (the early return above blocks every\n // retry).\n state.retryAt = Date.now() + wait;\n console.error(`bili-plugin(${agent}): identity register failed (${err instanceof Error ? err.message : String(err)}) — retrying in ${wait / 1000}s`);\n return;\n }\n }\n state.sid = sid;\n } catch (err) {\n state.sid = undefined;\n state.toolsFor = undefined;\n state.retryAt = Date.now() + wait;\n console.error(`bili-plugin(${agent}): tool registration deferred (${err instanceof Error ? err.message : String(err)}) — retrying in ${wait / 1000}s`);\n }\n })();\n try {\n await state.pending;\n } finally {\n state.pending = undefined;\n }\n}\n\nexport function createBiliPlugin(agentOverride?: string, opts?: { retryIntervalMs?: number }): (pi: ExtensionAPI) => void {\n return function biliPlugin(pi: ExtensionAPI): void {\n const agent = agentName(agentOverride);\n const state: RegisterState = { retryIntervalMs: opts?.retryIntervalMs ?? RETRY_INTERVAL_MS };\n // #535: file-free routing — override provider baseUrls at load from\n // the launcher-passed manifest (see buildPiEnv). registerProvider is\n // queued during initial extension load and applied before any model\n // traffic, so every request (including round 1) rides the proxy.\n const rewrites = parseProviderRewrites(process.env);\n if (rewrites !== undefined && typeof pi.registerProvider !== \"function\") {\n console.error(\n \"bili-plugin: BILI_PROVIDER_REWRITES is set but this pi build has no registerProvider API — \" +\n \"provider traffic goes DIRECT (uncompressed). Update pi, or reinstall the bili plugin: `bili plugin install pi`.\",\n );\n }\n if (rewrites !== undefined && typeof pi.registerProvider === \"function\") {\n for (const [key, url] of Object.entries(rewrites)) {\n try {\n pi.registerProvider(key, { baseUrl: url });\n } catch (err) {\n console.error(`bili-plugin: registerProvider(${key}) failed: ${err instanceof Error ? err.message : String(err)} — traffic for this provider goes direct`);\n }\n }\n }\n // #535: cancel the host's NATIVE compaction so its summarizer never\n // fires alongside bili's ACP compression — the in-extension\n // replacement for the old compaction-off config injection. pi's event\n // carries `reason`: cancel only threshold + overflow so manual\n // /compact stays user-owned. omp's event has no reason field, so omp\n // cancels ALL compaction — under bili, manual native /compact is\n // equally harmful (the native summarizer would destroy the\n // ACP-tagged context), the host shows \"Compaction cancelled\", and\n // the user should reach for /acp instead. Only armed under `bili`\n // launch: plain pi/omp with the plugin installed stays fully native.\n if ((agent === \"pi\" || agent === \"omp\") && process.env.BILLION_CONTEXT_PROXY !== undefined) {\n pi.on(\"session_before_compact\", (event) => {\n if (agent === \"pi\") {\n const reason = (event as unknown as { reason?: unknown }).reason;\n if (reason === \"threshold\" || reason === \"overflow\") return { cancel: true };\n return undefined;\n }\n return { cancel: true };\n });\n }\n // #535 omp-only: omp resolves modelRoles.default into options.model\n // from the PRE-extension static catalog (main.ts: \"scope is resolved\n // before extensions register their providers\"), and omp's fork lacks\n // pi's registerProvider → _refreshCurrentModelFromRegistry hop — the\n // registry gets the rewritten baseUrl but the live session keeps the\n // direct one, so every request bypasses the proxy (fetch trace →\n // http://127.0.0.1:8197/v1/responses with zero proxy forwards). Re-pin\n // the session model at load + on every session switch: spread the\n // current model with the rewritten baseUrl through the host setModel\n // (keyed-provider-gated; local providers carry dummy keys). Mid-session\n // /model picks resolve from the already-overridden registry, so only\n // session start/restore need this.\n if (agent === \"omp\" && rewrites !== undefined && typeof pi.setModel === \"function\") {\n const repin = async (ctx: Ctx): Promise<void> => {\n const model = ctx?.model;\n if (model === null || typeof model !== \"object\") return;\n const provider = model.provider;\n if (typeof provider !== \"string\" || provider === \"\") return;\n const rewritten = rewrites[provider];\n if (rewritten === undefined || model.baseUrl === rewritten) return;\n try {\n const switched = await pi.setModel?.({ ...model, baseUrl: rewritten });\n if (switched === false) {\n console.error(`bili-plugin: omp setModel(${provider}/${String(model.id)}) rejected (no API key) — traffic for this provider goes direct`);\n }\n } catch (err) {\n console.error(`bili-plugin: omp setModel failed: ${err instanceof Error ? err.message : String(err)} — traffic goes direct`);\n }\n };\n pi.on(\"session_start\", (_event, ctx) => repin(ctx));\n pi.on(\"session_switch\", (_event, ctx) => repin(ctx));\n }\n if (typeof pi.registerCommand === \"function\") {\n pi.registerCommand(\"acp\", {\n description: \"Show ACP context-compression status for this session\",\n handler: async (_args, ctx) => {\n const notify = (message: string, type?: string): void => {\n try {\n ctx.ui?.notify?.(message, type);\n } catch {\n // host UI unavailable — the command is best-effort\n }\n };\n const proxyBase = detectProxyBase(ctx.model?.baseUrl);\n if (proxyBase === undefined) {\n // #788: neutral wording — the plugin also loads under plain\n // pi/omp launches where the user never intended proxy mode\n // (e.g. they use billion-context-pi in-process instead), so\n // offer both exits instead of assuming proxy intent.\n const removeHint = agent === \"pi\"\n ? \", or remove this plugin (`bili plugin remove pi`) if you use billion-context-pi or don't want a proxy\"\n : agent === \"omp\"\n ? \", or remove this plugin (`bili plugin remove omp`) if you don't want a proxy\"\n : \"\";\n notify(`bili: no proxy detected — run via \\`bili ${agent}\\` (or set a /bili/ baseURL) to use proxy mode${removeHint}`, \"warning\");\n return;\n }\n const conversationId = sessionIdOf(ctx) ?? \"unknown\";\n let status: Record<string, unknown> | undefined;\n try {\n status = await fetchStatus(proxyBase, conversationId);\n } catch (err) {\n notify(`bili: status fetch failed: ${err instanceof Error ? err.message : String(err)}`, \"error\");\n return;\n }\n if (status === undefined) {\n // 404 from a live proxy = this conversation has sent no\n // model request yet (e.g. /acp right after startup).\n // Probe the manifest to confirm liveness + version and\n // show an armed/idle notice instead of a scary warning.\n let version: string | undefined;\n try {\n version = await fetchProxyVersion(proxyBase);\n } catch {\n version = undefined;\n }\n if (version !== undefined) {\n notify(\n `billion-context@${version} — proxy connected, compression armed. No model request in this conversation yet; send one, then run /acp again.`,\n \"info\",\n );\n } else {\n notify(\"bili: no ACP session yet (send a model request first, then run /acp)\", \"warning\");\n }\n return;\n }\n const panel = typeof status.panel === \"string\" ? status.panel : undefined;\n const text = panel ?? renderAcpStatus(status);\n // Persistent transcript output (TUI + web hosts like pi-web).\n // The proxy strips this message from the model context by\n // content signature (src/acp-panel.ts), so it never reaches\n // the LLM; notify() is the fallback for hosts without\n // sendMessage (older pi).\n if (typeof pi.sendMessage === \"function\") {\n try {\n pi.sendMessage({ customType: \"bili-acp-status\", content: text, display: true });\n return;\n } catch (err) {\n console.error(`bili-plugin(${agent}): sendMessage failed (${err instanceof Error ? err.message : String(err)}) — falling back to notify`);\n }\n }\n notify(text, \"info\");\n },\n });\n }\n pi.on(\"before_provider_headers\", (event, ctx) => {\n try {\n if (proxyBaseForCtx(ctx) === undefined) return;\n const headers = (event as unknown as { headers?: Record<string, string> }).headers;\n if (headers === undefined || typeof headers !== \"object\" || Array.isArray(headers)) return;\n // The x-bili-plugin marker tells the proxy \"the client owns the\n // ACP tools natively — skip wire-level injection\". Stamping it\n // before registerTools() finishes would send round 1 out with\n // NO ACP tools (the first provider request races the manifest\n // fetch). Claim ownership only once tools are registered;\n // until then the request rides the proxy's wire mode. A\n // permanently failing manifest fetch keeps us in wire mode —\n // a graceful fallback rather than a tool-less session.\n if (state.toolsReady === true) {\n const sid = sessionIdOf(ctx);\n if (sid !== undefined) headers[\"x-bili-plugin-conversation\"] = sid;\n headers[\"x-bili-plugin\"] = agent;\n const window = ctx.model?.contextWindow;\n if (typeof window === \"number\" && Number.isFinite(window) && window > 0) {\n headers[\"x-bili-plugin-context-window\"] = String(Math.floor(window));\n }\n }\n } catch (err) {\n console.error(`bili-plugin(${agent}): header stamp skipped (${err instanceof Error ? err.message : String(err)})`);\n }\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n });\n pi.on(\"before_provider_request\", (event, ctx) => {\n // omp emits this per model request (but never before_provider_headers);\n // it doubles as the retry driver when the session_start manifest\n // fetch raced the proxy startup. Cached by sid, throttled by retryAt.\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n return stampPromptCacheKey(event, ctx, agent);\n });\n pi.on(\"session_start\", (_event, ctx) => {\n state.sid = undefined;\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n });\n // omp fires session_compact on in-session native compaction (sid does\n // not rotate), so the proxy reuses stale state — notify it to archive\n // the now-unreachable blocks (#395). Fire-and-forget: a failed\n // notification must never break the agent's compaction.\n pi.on(\"session_compact\", (_event, ctx) => {\n const proxyBase = proxyBaseForCtx(ctx);\n if (proxyBase === undefined) return;\n const sid = sessionIdOf(ctx);\n if (sid === undefined || sid.length === 0) return;\n fetch(`${proxyBase}/__bili/plugin/compact`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId: sid }),\n signal: AbortSignal.timeout(5000),\n }).catch(() => {});\n });\n };\n}\n\nexport default createBiliPlugin();\n\nexport { fetchStatus };\n","import { createBiliPlugin } from \"./pi.js\";\n\nexport default createBiliPlugin(\"omp\");\n"],"mappings":";;;;AAaA,IAAM,sBAAsB;AAC5B,IAAM,kBAAkB;AACxB,IAAM,oBAAoB;AAOnB,SAAS,iBAAiB,SAAiD;AAC9E,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI;AACA,UAAM,MAAM,IAAI,IAAI,OAAO;AAC3B,QAAI,IAAI,aAAa,WAAW,IAAI,aAAa,SAAU,QAAO;AAClE,UAAM,WAAW,IAAI,SAAS,MAAM,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AACnE,QAAI,SAAS,CAAC,MAAM,OAAQ,QAAO;AACnC,UAAM,OAAO,IAAI,SAAS,MAAM,IAAI,SAAS,QAAQ,MAAM,IAAI,OAAO,MAAM;AAC5E,QAAI,CAAC,iBAAiB,KAAK,IAAI,EAAG,QAAO;AACzC,WAAO,GAAG,IAAI,QAAQ,KAAK,IAAI,IAAI;AAAA,EACvC,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAIO,SAAS,mBAAuC;AACnD,QAAM,MAAM,QAAQ,IAAI,uBAAuB,KAAK;AACpD,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AACA,UAAM,MAAM,IAAI,IAAI,GAAG;AACvB,WAAO,IAAI,aAAa,WAAW,IAAI,aAAa,WAAW,GAAG,IAAI,QAAQ,KAAK,IAAI,IAAI,KAAK;AAAA,EACpG,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEO,SAAS,gBAAgB,SAAiD;AAC7E,MAAI,QAAQ,IAAI,2BAA2B,IAAK,QAAO;AACvD,SAAO,iBAAiB,OAAO,KAAK,iBAAiB;AACzD;AAEA,eAAe,UAAU,KAAa,MAA+B,WAAmB,gBAAuF;AAC3K,QAAM,KAAK,IAAI,gBAAgB;AAI/B,MAAI,gBAAgB,QAAS,IAAG,MAAM;AACtC,QAAM,QAAQ,WAAW,MAAM,GAAG,MAAM,GAAG,SAAS;AACpD,QAAM,kBAAkB,MAAM,GAAG,MAAM;AACvC,kBAAgB,iBAAiB,SAAS,iBAAiB,EAAE,MAAM,KAAK,CAAC;AACzE,MAAI;AACA,QAAI;AACJ,QAAI;AACA,YAAM,MAAM,MAAM,KAAK,EAAE,GAAG,MAAM,QAAQ,GAAG,OAAO,CAAC;AAAA,IACzD,SAAS,KAAK;AACV,UAAI,GAAG,OAAO,WAAW,CAAC,gBAAgB,QAAS,OAAM,IAAI,MAAM,iBAAiB,SAAS,OAAO,GAAG,EAAE;AACzG,YAAM;AAAA,IACV;AACA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,OAAgB;AACpB,QAAI;AACA,aAAO,KAAK,MAAM,IAAI;AAAA,IAC1B,QAAQ;AACJ,aAAO;AAAA,IACX;AACA,WAAO,EAAE,IAAI,IAAI,IAAI,QAAQ,IAAI,QAAQ,KAAK;AAAA,EAClD,UAAE;AACE,iBAAa,KAAK;AAClB,oBAAgB,oBAAoB,SAAS,eAAe;AAAA,EAChE;AACJ;AAEA,eAAsB,cAAc,WAAmB,SAAiC,aAAsC;AAC1H,QAAM,EAAE,IAAI,QAAQ,KAAK,IAAI,MAAM,UAAU,GAAG,SAAS,2BAA2B,QAAW,mBAAmB;AAClH,MAAI,CAAC,MAAM,CAAC,QAAQ,OAAO,SAAS,SAAU,OAAM,IAAI,MAAM,0BAA0B,MAAM,EAAE;AAChG,MAAI,WAAW,UAAU;AAErB,UAAMA,QAAO;AACb,UAAMC,UAASD,MAAK,OAAO,UAAU,CAAC,GAAG,OAAO,CAAC,MAAyE,OAAO,EAAE,SAAS,QAAQ;AACpJ,QAAIC,OAAM,WAAW,EAAG,OAAM,IAAI,MAAM,iCAAiC;AACzE,WAAOA,OAAM,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,EAAE,aAAa,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,YAAY,CAAC,EAAE,EAAE,EAAE;AAAA,EAC3I;AACA,QAAM,OAAO;AACb,QAAM,SAAS,KAAK,OAAO,aAAa,CAAC,GAAG,OAAO,CAAC,MAA2E,OAAO,EAAE,SAAS,QAAQ;AACzJ,MAAI,MAAM,WAAW,EAAG,OAAM,IAAI,MAAM,oCAAoC;AAC5E,SAAO,MAAM,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,EAAE,aAAa,aAAa,EAAE,gBAAgB,EAAE,MAAM,UAAU,YAAY,CAAC,EAAE,EAAE,EAAE;AAC7I;AAgBA,eAAsB,YAAY,WAAmB,gBAAwB,MAAc,MAAe,QAAuC;AAC7I,QAAM,EAAE,IAAI,QAAQ,KAAK,IAAI,MAAM,UAAU,GAAG,SAAS,uBAAuB;AAAA,IAC5E,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,EAAE,gBAAgB,MAAM,MAAM,QAAQ,CAAC,EAAE,CAAC;AAAA,EACnE,GAAG,iBAAiB,MAAM;AAC1B,QAAM,OAAO;AACb,MAAI,CAAC,MAAM,CAAC,MAAM,IAAI;AAClB,UAAM,IAAI,MAAM,mBAAmB,IAAI,YAAY,MAAM,MAAM,MAAM,SAAS,eAAe,EAAE;AAAA,EACnG;AACA,SAAO,KAAK,UAAU;AAC1B;AAIA,eAAsB,YAAY,WAAmB,gBAAsE;AACvH,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,UAAU,GAAG,SAAS,wCAAwC,mBAAmB,cAAc,CAAC,IAAI,QAAW,iBAAiB;AAC3J,MAAI,CAAC,MAAM,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AACrD,SAAO;AACX;AAiBA,eAAsB,kBAAkB,WAAgD;AACpF,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,UAAU,GAAG,SAAS,2BAA2B,QAAW,iBAAiB;AACxG,MAAI,CAAC,MAAM,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AACrD,QAAM,UAAW,KAA+B;AAChD,SAAO,OAAO,YAAY,YAAY,QAAQ,SAAS,IAAI,UAAU;AACzE;;;ACrGA,SAAS,UAAU,UAAsC;AACrD,MAAI,SAAU,QAAO;AACrB,SAAO,QAAQ,IAAI,iCAAiC,QAAQ,QAAQ;AACxE;AAEA,SAAS,gBAAgB,KAA8B;AACnD,SAAO,gBAAgB,IAAI,OAAO,OAAO;AAC7C;AAEA,SAAS,YAAY,KAA8B;AAC/C,MAAI;AACA,UAAM,MAAM,IAAI,gBAAgB,eAAe;AAC/C,WAAO,OAAO,QAAQ,WAAW,MAAM;AAAA,EAC3C,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAkBA,SAAS,oBAAoB,OAAgB,KAAU,OAAoD;AACvG,MAAI,UAAU,MAAO,QAAO;AAC5B,QAAM,UAAW,OAA6C;AAC9D,MAAI,YAAY,QAAQ,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,EAAG,QAAO;AACtF,QAAM,IAAI;AACV,MAAI,CAAC,MAAM,QAAQ,EAAE,QAAQ,EAAG,QAAO;AACvC,MAAI,EAAE,UAAU,OAAW,QAAO;AAClC,MAAI,OAAO,EAAE,qBAAqB,YAAY,EAAE,iBAAiB,KAAK,EAAE,SAAS,EAAG,QAAO;AAC3F,QAAM,MAAM,YAAY,GAAG;AAC3B,MAAI,QAAQ,UAAa,IAAI,WAAW,EAAG,QAAO;AAClD,SAAO,EAAE,GAAG,GAAG,kBAAkB,IAAI;AACzC;AAEA,SAAS,OAAO,GAAmB;AAC/B,MAAI,IAAI,IAAM,QAAO,OAAO,CAAC;AAC7B,MAAI,IAAI,IAAW,QAAO,IAAI,IAAI,KAAM,QAAQ,CAAC,CAAC;AAClD,SAAO,IAAI,IAAI,KAAW,QAAQ,CAAC,CAAC;AACxC;AAEA,SAAS,gBAAgB,GAAoC;AACzD,QAAM,MAAM,CAAC,MAA+B,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,IAAI,IAAI;AAC9F,QAAM,gBAAgB,IAAI,EAAE,aAAa;AACzC,QAAM,eAAe,IAAI,EAAE,YAAY;AACvC,QAAM,cAAc,IAAI,EAAE,WAAW;AACrC,QAAM,eAAe,IAAI,EAAE,YAAY;AACvC,QAAM,eAAe,IAAI,EAAE,YAAY;AACvC,QAAM,WAAW,IAAI,EAAE,QAAQ;AAC/B,QAAM,SAAS,MAAM,QAAQ,EAAE,MAAM,IAAK,EAAE,SAAwD,CAAC;AACrG,QAAM,eAAe,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,IAAI,EAAE;AAC7D,QAAM,QAAkB,CAAC,sBAAe;AACxC,MAAI,kBAAkB,MAAM;AACxB,UAAM,MAAM,iBAAiB,QAAQ,eAAe,IAAI,MAAO,gBAAgB,eAAgB,KAAK,QAAQ,CAAC,CAAC,OAAO;AACrH,UAAM,KAAK,cAAc,OAAO,aAAa,CAAC,GAAG,iBAAiB,OAAO,MAAM,OAAO,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,EAAE;AAAA,EACtH;AACA,QAAM,aAAa,IAAI,EAAE,UAAU;AACnC,MAAI,eAAe,QAAQ,aAAa,GAAG;AACvC,UAAM,KAAK,oDAAoD,OAAO,UAAU,CAAC,OAAO;AAAA,EAC5F;AACA,MAAI,gBAAgB,QAAQ,iBAAiB,QAAQ,iBAAiB,MAAM;AACxE,UAAM,KAAK,oBAAoB,OAAO,eAAe,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC,EAAE;AAAA,EAC3H;AACA,MAAI,aAAa,KAAM,OAAM,KAAK,eAAe,QAAQ,EAAE;AAC3D,MAAI,OAAO,SAAS,EAAG,OAAM,KAAK,aAAa,OAAO,MAAM,KAAK,YAAY,UAAU;AACvF,SAAO,MAAM,KAAK,IAAI;AAC1B;AAEA,SAAS,eAAe,WAAmB,MAAoB,OAA+B;AAC1F,SAAO;AAAA,IACH,MAAM,KAAK;AAAA,IACX,aAAa,KAAK;AAAA,IAClB,YAAY,KAAK;AAAA,IACjB,UAAU;AAAA,IACV,SAAS,OAAO,aAAa,QAAQ,QAAQ,WAAW,QAAQ;AAC5D,YAAM,iBAAiB,YAAY,GAAG,KAAK;AAC3C,UAAI;AACA,cAAM,SAAS,MAAM,YAAY,WAAW,gBAAgB,KAAK,MAAM,QAAQ,MAAM;AACrF,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,CAAC,EAAE;AAAA,MACvD,SAAS,KAAK;AACV,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,oBAAoB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,KAAK;AAAA,MACtI;AAAA,IACJ;AAAA,EACJ;AACJ;AAEA,SAAS,sBAAsB,KAA4D;AACvF,QAAM,MAAM,IAAI;AAChB,MAAI,QAAQ,UAAa,IAAI,KAAK,EAAE,WAAW,EAAG,QAAO;AACzD,MAAI;AACJ,MAAI;AACA,aAAS,KAAK,MAAM,GAAG;AAAA,EAC3B,QAAQ;AACJ,YAAQ,MAAM,2FAAsF;AACpG,WAAO;AAAA,EACX;AACA,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG,QAAO;AACnF,QAAM,MAA8B,CAAC;AACrC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAiC,GAAG;AAC1E,QAAI,OAAO,UAAU,YAAY,CAAC,gBAAgB,KAAK,KAAK,EAAG;AAC/D,QAAI,GAAG,IAAI;AAAA,EACf;AACA,SAAO,OAAO,KAAK,GAAG,EAAE,SAAS,IAAI,MAAM;AAC/C;AAEA,IAAM,oBAAoB;AAQ1B,eAAe,qBAAqB,WAAmB,gBAAwB,OAA8B;AACzG,QAAM,MAAM,MAAM,MAAM,GAAG,SAAS,2BAA2B;AAAA,IAC3D,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,EAAE,gBAAgB,OAAO,UAAU,KAAK,CAAC;AAAA,IAC9D,QAAQ,YAAY,QAAQ,GAAI;AAAA,EACpC,CAAC;AACD,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,iBAAiB,IAAI,MAAM,EAAE;AAC9D;AAEA,eAAe,cAAc,IAAkB,KAAU,OAAsB,OAA8B;AACzG,QAAM,YAAY,gBAAgB,GAAG;AACrC,MAAI,cAAc,OAAW;AAI7B,QAAM,MAAM,YAAY,GAAG,KAAK;AAChC,MAAI,QAAQ,MAAM,IAAK;AACvB,MAAI,MAAM,YAAY,OAAW,QAAO,MAAM;AAC9C,MAAI,MAAM,YAAY,UAAa,KAAK,IAAI,IAAI,MAAM,QAAS;AAC/D,QAAM,OAAO,MAAM;AACnB,QAAM,WAAW,YAAY;AACzB,QAAI;AACJ,QAAI;AACA,cAAQ,MAAM,cAAc,SAAS;AAAA,IACzC,SAAS,KAAK;AACV,YAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,cAAQ,MAAM,eAAe,KAAK,6BAA6B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,uBAAkB,OAAO,GAAI,GAAG;AAC/I;AAAA,IACJ;AACA,QAAI;AAIA,UAAI,MAAM,aAAa,KAAK;AACxB,mBAAW,KAAK,MAAO,IAAG,aAAa,eAAe,WAAW,GAAG,KAAK,CAAC;AAC1E,cAAM,WAAW;AAAA,MACrB;AACA,YAAM,aAAa;AACnB,YAAM,UAAU;AAChB,UAAI,UAAU,SAAS,QAAQ,MAAM,MAAM,eAAe,KAAK;AAC3D,YAAI;AACA,gBAAM,qBAAqB,WAAW,KAAK,KAAK;AAChD,gBAAM,aAAa;AAAA,QACvB,SAAS,KAAK;AAMV,gBAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,kBAAQ,MAAM,eAAe,KAAK,gCAAgC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,wBAAmB,OAAO,GAAI,GAAG;AACnJ;AAAA,QACJ;AAAA,MACJ;AACA,YAAM,MAAM;AAAA,IAChB,SAAS,KAAK;AACV,YAAM,MAAM;AACZ,YAAM,WAAW;AACjB,YAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,cAAQ,MAAM,eAAe,KAAK,kCAAkC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,wBAAmB,OAAO,GAAI,GAAG;AAAA,IACzJ;AAAA,EACJ,GAAG;AACH,MAAI;AACA,UAAM,MAAM;AAAA,EAChB,UAAE;AACE,UAAM,UAAU;AAAA,EACpB;AACJ;AAEO,SAAS,iBAAiB,eAAwB,MAAiE;AACtH,SAAO,SAAS,WAAW,IAAwB;AAC/C,UAAM,QAAQ,UAAU,aAAa;AACrC,UAAM,QAAuB,EAAE,iBAAiB,MAAM,mBAAmB,kBAAkB;AAK3F,UAAM,WAAW,sBAAsB,QAAQ,GAAG;AAClD,QAAI,aAAa,UAAa,OAAO,GAAG,qBAAqB,YAAY;AACrE,cAAQ;AAAA,QACJ;AAAA,MAEJ;AAAA,IACJ;AACA,QAAI,aAAa,UAAa,OAAO,GAAG,qBAAqB,YAAY;AACrE,iBAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAC/C,YAAI;AACA,aAAG,iBAAiB,KAAK,EAAE,SAAS,IAAI,CAAC;AAAA,QAC7C,SAAS,KAAK;AACV,kBAAQ,MAAM,iCAAiC,GAAG,aAAa,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,+CAA0C;AAAA,QAC7J;AAAA,MACJ;AAAA,IACJ;AAWA,SAAK,UAAU,QAAQ,UAAU,UAAU,QAAQ,IAAI,0BAA0B,QAAW;AACxF,SAAG,GAAG,0BAA0B,CAAC,UAAU;AACvC,YAAI,UAAU,MAAM;AAChB,gBAAM,SAAU,MAA0C;AAC1D,cAAI,WAAW,eAAe,WAAW,WAAY,QAAO,EAAE,QAAQ,KAAK;AAC3E,iBAAO;AAAA,QACX;AACA,eAAO,EAAE,QAAQ,KAAK;AAAA,MAC1B,CAAC;AAAA,IACL;AAaA,QAAI,UAAU,SAAS,aAAa,UAAa,OAAO,GAAG,aAAa,YAAY;AAChF,YAAM,QAAQ,OAAO,QAA4B;AAC7C,cAAM,QAAQ,KAAK;AACnB,YAAI,UAAU,QAAQ,OAAO,UAAU,SAAU;AACjD,cAAM,WAAW,MAAM;AACvB,YAAI,OAAO,aAAa,YAAY,aAAa,GAAI;AACrD,cAAM,YAAY,SAAS,QAAQ;AACnC,YAAI,cAAc,UAAa,MAAM,YAAY,UAAW;AAC5D,YAAI;AACA,gBAAM,WAAW,MAAM,GAAG,WAAW,EAAE,GAAG,OAAO,SAAS,UAAU,CAAC;AACrE,cAAI,aAAa,OAAO;AACpB,oBAAQ,MAAM,6BAA6B,QAAQ,IAAI,OAAO,MAAM,EAAE,CAAC,sEAAiE;AAAA,UAC5I;AAAA,QACJ,SAAS,KAAK;AACV,kBAAQ,MAAM,qCAAqC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,6BAAwB;AAAA,QAC/H;AAAA,MACJ;AACA,SAAG,GAAG,iBAAiB,CAAC,QAAQ,QAAQ,MAAM,GAAG,CAAC;AAClD,SAAG,GAAG,kBAAkB,CAAC,QAAQ,QAAQ,MAAM,GAAG,CAAC;AAAA,IACvD;AACA,QAAI,OAAO,GAAG,oBAAoB,YAAY;AAC1C,SAAG,gBAAgB,OAAO;AAAA,QACtB,aAAa;AAAA,QACb,SAAS,OAAO,OAAO,QAAQ;AAC3B,gBAAM,SAAS,CAAC,SAAiB,SAAwB;AACrD,gBAAI;AACA,kBAAI,IAAI,SAAS,SAAS,IAAI;AAAA,YAClC,QAAQ;AAAA,YAER;AAAA,UACJ;AACA,gBAAM,YAAY,gBAAgB,IAAI,OAAO,OAAO;AACpD,cAAI,cAAc,QAAW;AAKzB,kBAAM,aAAa,UAAU,OACvB,0GACA,UAAU,QACN,iFACA;AACV,mBAAO,iDAA4C,KAAK,iDAAiD,UAAU,IAAI,SAAS;AAChI;AAAA,UACJ;AACA,gBAAM,iBAAiB,YAAY,GAAG,KAAK;AAC3C,cAAI;AACJ,cAAI;AACA,qBAAS,MAAM,YAAY,WAAW,cAAc;AAAA,UACxD,SAAS,KAAK;AACV,mBAAO,8BAA8B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,IAAI,OAAO;AAChG;AAAA,UACJ;AACA,cAAI,WAAW,QAAW;AAKtB,gBAAI;AACJ,gBAAI;AACA,wBAAU,MAAM,kBAAkB,SAAS;AAAA,YAC/C,QAAQ;AACJ,wBAAU;AAAA,YACd;AACA,gBAAI,YAAY,QAAW;AACvB;AAAA,gBACI,mBAAmB,OAAO;AAAA,gBAC1B;AAAA,cACJ;AAAA,YACJ,OAAO;AACH,qBAAO,wEAAwE,SAAS;AAAA,YAC5F;AACA;AAAA,UACJ;AACA,gBAAM,QAAQ,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAChE,gBAAM,OAAO,SAAS,gBAAgB,MAAM;AAM5C,cAAI,OAAO,GAAG,gBAAgB,YAAY;AACtC,gBAAI;AACA,iBAAG,YAAY,EAAE,YAAY,mBAAmB,SAAS,MAAM,SAAS,KAAK,CAAC;AAC9E;AAAA,YACJ,SAAS,KAAK;AACV,sBAAQ,MAAM,eAAe,KAAK,0BAA0B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,iCAA4B;AAAA,YAC5I;AAAA,UACJ;AACA,iBAAO,MAAM,MAAM;AAAA,QACvB;AAAA,MACJ,CAAC;AAAA,IACL;AACA,OAAG,GAAG,2BAA2B,CAAC,OAAO,QAAQ;AAC7C,UAAI;AACA,YAAI,gBAAgB,GAAG,MAAM,OAAW;AACxC,cAAM,UAAW,MAA0D;AAC3E,YAAI,YAAY,UAAa,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,EAAG;AASpF,YAAI,MAAM,eAAe,MAAM;AAC3B,gBAAM,MAAM,YAAY,GAAG;AAC3B,cAAI,QAAQ,OAAW,SAAQ,4BAA4B,IAAI;AAC/D,kBAAQ,eAAe,IAAI;AAC3B,gBAAM,SAAS,IAAI,OAAO;AAC1B,cAAI,OAAO,WAAW,YAAY,OAAO,SAAS,MAAM,KAAK,SAAS,GAAG;AACrE,oBAAQ,8BAA8B,IAAI,OAAO,KAAK,MAAM,MAAM,CAAC;AAAA,UACvE;AAAA,QACJ;AAAA,MACJ,SAAS,KAAK;AACV,gBAAQ,MAAM,eAAe,KAAK,4BAA4B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,GAAG;AAAA,MACrH;AACA,WAAK,cAAc,IAAI,KAAK,OAAO,KAAK,EAAE,MAAM,CAAC,QAAiB,QAAQ,MAAM,eAAe,KAAK,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE,CAAC;AAAA,IACjK,CAAC;AACD,OAAG,GAAG,2BAA2B,CAAC,OAAO,QAAQ;AAI7C,WAAK,cAAc,IAAI,KAAK,OAAO,KAAK,EAAE,MAAM,CAAC,QAAiB,QAAQ,MAAM,eAAe,KAAK,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE,CAAC;AAC7J,aAAO,oBAAoB,OAAO,KAAK,KAAK;AAAA,IAChD,CAAC;AACD,OAAG,GAAG,iBAAiB,CAAC,QAAQ,QAAQ;AACpC,YAAM,MAAM;AACZ,WAAK,cAAc,IAAI,KAAK,OAAO,KAAK,EAAE,MAAM,CAAC,QAAiB,QAAQ,MAAM,eAAe,KAAK,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE,CAAC;AAAA,IACjK,CAAC;AAKD,OAAG,GAAG,mBAAmB,CAAC,QAAQ,QAAQ;AACtC,YAAM,YAAY,gBAAgB,GAAG;AACrC,UAAI,cAAc,OAAW;AAC7B,YAAM,MAAM,YAAY,GAAG;AAC3B,UAAI,QAAQ,UAAa,IAAI,WAAW,EAAG;AAC3C,YAAM,GAAG,SAAS,0BAA0B;AAAA,QACxC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,gBAAgB,IAAI,CAAC;AAAA,QAC5C,QAAQ,YAAY,QAAQ,GAAI;AAAA,MACpC,CAAC,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACrB,CAAC;AAAA,EACL;AACJ;AAEA,IAAO,aAAQ,iBAAiB;;;ACvchC,IAAO,cAAQ,iBAAiB,KAAK;","names":["data","tools"]}
|