billion-context 0.1.127 → 0.1.128
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/omp-native.js +27 -0
- package/dist/agent/omp-native.js.map +1 -1
- package/dist/agent/omp.js +27 -0
- package/dist/agent/omp.js.map +1 -1
- package/dist/agent/pi-native.js +27 -0
- package/dist/agent/pi-native.js.map +1 -1
- package/dist/agent/pi.js +27 -0
- package/dist/agent/pi.js.map +1 -1
- package/dist/index.js +122 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/agent/omp.js
CHANGED
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
import { createRequire as __biliCreateRequire } from 'node:module';
|
|
2
2
|
const require = __biliCreateRequire(import.meta.url);
|
|
3
3
|
|
|
4
|
+
// src/loop/tag-echo-filter.ts
|
|
5
|
+
function buildAcplikeName() {
|
|
6
|
+
const cores = ["acp", "apc", "cap", "cpa", "pac", "pca"];
|
|
7
|
+
const names = new Set(cores);
|
|
8
|
+
for (const c of cores) {
|
|
9
|
+
for (const ch of ["a", "c", "p", "i"]) {
|
|
10
|
+
for (let pos = 0; pos <= c.length; pos++) names.add(c.slice(0, pos) + ch + c.slice(pos));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return [...names].sort((a, b) => b.length - a.length).join("|");
|
|
14
|
+
}
|
|
15
|
+
var ACP_NAME_ALT = `(?:${buildAcplikeName()})`;
|
|
16
|
+
var NAME = ACP_NAME_ALT;
|
|
17
|
+
var PAIRED = new RegExp("<" + NAME + "\\s[^<>]{0,256}>([^<>]{0,64})<\\/" + NAME + ">");
|
|
18
|
+
var LONE_OPEN = new RegExp("<" + NAME + "(?:\\s[^<>]{0,256})?>");
|
|
19
|
+
var LONE_CLOSE = new RegExp("<\\/" + NAME + "(?=[\\s>])[^<>]{0,32}>");
|
|
20
|
+
var PARTIAL_TAIL = new RegExp("(<" + NAME + "\\s[^<>]*|<\\/" + NAME + "(?:\\s[^<>]{0,32})?|<\\/?[acip]*)$");
|
|
21
|
+
var TRUNC_OPEN = new RegExp("<" + NAME + "\\s[^<>]*$");
|
|
22
|
+
var TRUNC_CLOSE = new RegExp("<\\/" + NAME + "(?:\\s[^<>]{0,32})?$");
|
|
23
|
+
var BROKEN_ATTRS = new RegExp("<" + NAME + "\\s[^<>]{0,512}(?=<)");
|
|
24
|
+
var DEFINITE_TAIL = new RegExp("^<" + NAME + "\\s|^<\\/" + NAME);
|
|
25
|
+
var OPEN_WITH_ATTRS = new RegExp("^<" + NAME + "\\s");
|
|
26
|
+
var CLOSE_NAME_ANCHORED = new RegExp("^" + NAME);
|
|
27
|
+
var MARKER_HEAD = new RegExp("^\\p{So}(?:[ \\t])?\\[ACP\\]", "u");
|
|
28
|
+
var MARKER_LINE = new RegExp("^\\p{So}(?:[ \\t])?\\[ACP\\][^\\n]*\\n?", "gmu");
|
|
29
|
+
var RENDER_TAG_DETECT = new RegExp("<\\/?" + NAME + "(?=[\\s>])|\\\\u003c\\/?" + NAME + "(?=[\\s>\\\\])");
|
|
30
|
+
|
|
4
31
|
// src/acp-panel.ts
|
|
5
32
|
var CACHE_REPORT_OPEN = "[acp-cache]";
|
|
6
33
|
var CACHE_REPORT_CLOSE = "[/acp-cache]";
|
package/dist/agent/omp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/acp-panel.ts","../../src/agent/shared.ts","../../src/agent/pi.ts","../../src/agent/omp.ts"],"sourcesContent":["// ACP panel stripping for proxy mode (issue #359). The pi plugin's /acp emits\n// the status panel as a persistent custom_message; in proxy mode pi's\n// convertToLlm projects every custom message as an ordinary user message, so\n// the panel would ride the recent zone to the model as if it were real\n// conversation content. pi's projection drops the customType, so the proxy\n// (which generates the panel itself via buildStatusPanel) strips it by content\n// signature before it enters the compression state.\n//\n// The match must be WHOLE-MESSAGE, not a prefix: a user can copy a panel from\n// pi-web and append a follow-up question in the same message — a prefix match\n// would strip the user's question too (data loss, unrecoverable in-session).\n// Each renderer is therefore anchored on BOTH its start and its end:\n// - buildStatusPanel (acp-kernel): starts with the U+256D top border + title\n// \"ACP Context Analysis\", and always ends with the \"Tag visibility: ...\"\n// footer (unconditional, last line pushed).\n// - renderAcpStatus (plugin fallback): first line exactly \"📊 ACP status\",\n// every other line an indented field.\n// - wrapCacheReport (#800, /acp-cache): explicit plugin-owned markers around\n// the kernel's cache report — its shape is kernel-owned and variable-length\n// (LINE ITEMS table up to the sample cap), so we cannot anchor on the\n// renderer output the way we can for the two panels above.\nconst PANEL_BOX_TOP = \"\\u256d\";\nconst PANEL_BOX_TITLE = \"ACP Context Analysis\";\nconst PANEL_BOX_FOOTER = \"Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.\";\nconst PANEL_FALLBACK_HEADER = \"\\u{1f4ca} ACP status\";\n\nexport const CACHE_REPORT_OPEN = \"[acp-cache]\";\nexport const CACHE_REPORT_CLOSE = \"[/acp-cache]\";\n\n/** Wrap a cache report for persistent transcript display (see above). */\nexport function wrapCacheReport(report: string): string {\n return `${CACHE_REPORT_OPEN}\\n${report}\\n${CACHE_REPORT_CLOSE}`;\n}\n\nexport function isAcpPanelText(text: string): boolean {\n const t = text.trim();\n if (t.length === 0) return false;\n return isBoxPanel(t) || isFallbackPanel(t) || isCacheReport(t);\n}\n\nfunction isCacheReport(t: string): boolean {\n return t.startsWith(CACHE_REPORT_OPEN) && t.endsWith(CACHE_REPORT_CLOSE);\n}\n\nfunction isBoxPanel(t: string): boolean {\n return t.startsWith(PANEL_BOX_TOP) && t.includes(PANEL_BOX_TITLE) && t.endsWith(PANEL_BOX_FOOTER);\n}\n\nfunction isFallbackPanel(t: string): boolean {\n const lines = t.split(\"\\n\");\n if (lines[0] !== PANEL_FALLBACK_HEADER) return false;\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n if (line.length > 0 && !line.startsWith(\" \")) return false;\n }\n return true;\n}\n\n// Plain text of a message's content (string or text-block array). Returns\n// undefined for mixed/multimodal content — such a message can never be a\n// panel, so it is preserved.\nfunction messageText(content: unknown): string | undefined {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n let joined = \"\";\n for (const part of content) {\n const p = part as Record<string, unknown> | null;\n if (p === null || typeof p !== \"object\") return undefined;\n const pt = p.type;\n if (pt !== undefined && pt !== \"text\" && pt !== \"input_text\") return undefined;\n if (typeof p.text === \"string\") joined += p.text;\n }\n return joined;\n }\n return undefined;\n}\n\n// Strip ACP panel user messages from an anthropic/openai messages array (in\n// place); returns the count removed.\nexport function stripAcpPanelMessages(messages: unknown): number {\n if (!Array.isArray(messages)) return 0;\n let stripped = 0;\n for (let i = messages.length - 1; i >= 0; i--) {\n const rec = messages[i] as Record<string, unknown> | null;\n if (rec === null || typeof rec !== \"object\") continue;\n if (rec.role !== \"user\") continue;\n const text = messageText(rec.content);\n if (text !== undefined && isAcpPanelText(text)) {\n messages.splice(i, 1);\n stripped++;\n }\n }\n return stripped;\n}\n\n// Strip ACP panel user messages from a Responses input array (in place);\n// returns the count removed. Type-less user items (omp wire form) count as\n// messages, mirroring dropWhitespaceResponsesMessages.\nexport function stripAcpPanelResponsesInput(input: unknown): number {\n if (!Array.isArray(input)) return 0;\n let stripped = 0;\n for (let i = input.length - 1; i >= 0; i--) {\n const rec = input[i] as Record<string, unknown> | null;\n if (rec === null || typeof rec !== \"object\") continue;\n const type = rec.type;\n if (type !== \"message\" && type !== undefined) continue;\n if (rec.role !== \"user\") continue;\n const text = messageText(rec.content);\n if (text !== undefined && isAcpPanelText(text)) {\n input.splice(i, 1);\n stripped++;\n }\n }\n return stripped;\n}\n","// 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 type RuntimeInfoReport = {\n agent: string;\n model: string;\n contextWindow?: number;\n maxOutput?: number;\n baseURL?: string;\n source?: string;\n};\n\n/** Runtime-info protocol (#955): push the client's OWN model config (what\n * the plugin read from the host's config) to the proxy at bootstrap and on\n * model switch, before any model request. Fire-and-forget by design — the\n * proxy treats a missing report as \"guess like before\" (registry/table). */\nexport async function reportRuntimeInfo(proxyBase: string, info: RuntimeInfoReport): Promise<void> {\n const { ok, status } = await fetchJson(`${proxyBase}/__bili/plugin/runtime-info`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(info),\n }, STATUS_TIMEOUT_MS);\n if (!ok) throw new Error(`runtime-info report failed (${status})`);\n}\n\n// Per-agent last report (model id): a stamp fires at most one POST per model\n// switch, not per request. Failed reports roll back so the next stamp retries.\nconst lastRuntimeReport = new Map<string, string>();\n\n/** Stamp-time runtime-info report (#955): no-op unless the agent's reported\n * model changed (or this is the first stamp after proxy attach/spawn).\n * Soft-fail — an unreachable proxy keeps wire mode exactly as before. */\nexport function reportRuntimeInfoOnChange(proxyBase: string | undefined, info: RuntimeInfoReport): void {\n if (proxyBase === undefined || proxyBase.length === 0) return;\n if (lastRuntimeReport.get(info.agent) === info.model) return;\n lastRuntimeReport.set(info.agent, info.model);\n void reportRuntimeInfo(proxyBase, info).catch(() => {\n lastRuntimeReport.delete(info.agent);\n });\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\n// Model-facing body for /acp status renders: the visible panel goes to the synthetic message's\n// description (TUI Notice cap ~1KB); this inert one-liner keeps the model context clean.\nexport const V2_SYNTHETIC_TEXT = \"[bili-acp-status] ACP status panel was displayed in your terminal; this line is not an instruction.\";\n\n/** Armed-but-idle /acp notice (proxy live, no model request sent yet). One\n * source of truth for pi / dsh / opencode (#883). */\nexport function armedIdleNotice(version: string): string {\n return `billion-context@${version} — proxy connected, compression armed. No model request yet; send one, then run /acp again.`;\n}\n\n/** Fallback when the version probe also fails — a warning, not an info notice. */\nexport function noSessionWarning(): string {\n return \"bili: no ACP session yet (send a model request first, then run /acp)\";\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 { wrapCacheReport } from \"../acp-panel.js\";\nimport { detectProxyBase, fetchManifest, forwardTool, fetchStatus, fetchProxyVersion, reportRuntimeInfoOnChange, armedIdleNotice, noSessionWarning, 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 | undefined): 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\n// #788: neutral wording — the plugin also loads under plain pi/omp launches\n// where the user never intended proxy mode (e.g. they use billion-context-pi\n// in-process instead), so offer both exits instead of assuming proxy intent.\nfunction noProxyWarning(agent: string): string {\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 return `bili: no proxy detected — run via \\`bili ${agent}\\` (or set a /bili/ baseURL) to use proxy mode${removeHint}`;\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 (#851): session_before_compact\n // carries NO reason field, so at hook level manual compaction\n // (/compact, plan-mode \"Approve and compact context\") is\n // indistinguishable from auto — but every auto pass announces itself\n // first via auto_compaction_start (reason threshold|overflow|idle|\n // incomplete), which omp emits (awaited) before the hook fires;\n // manual paths never do. Track the announcement: announced passes\n // stay cancelled, unannounced ones are left user-owned. A surviving\n // native compaction is safe: the proxy archives the unreachable\n // blocks on session_compact (#395).\n // Whether we own compression is decided at EVENT time, not load time:\n // in native mode (#519) the proxy origin lands in\n // BILLION_CONTEXT_PROXY only after the async bootstrap finishes, so a\n // load-time check would leave the cancel disarmed for the whole\n // session. Plain pi/omp with the plugin installed but NO reachable\n // proxy (incl. a failed bootstrap) stays fully native.\n if (agent === \"pi\" || agent === \"omp\") {\n if (agent === \"pi\") {\n pi.on(\"session_before_compact\", (event, ctx) => {\n if (proxyBaseForCtx(ctx) === undefined) return undefined;\n const reason = (event as unknown as { reason?: unknown }).reason;\n if (reason === \"threshold\" || reason === \"overflow\") return { cancel: true };\n return undefined;\n });\n } else {\n let autoPending = false;\n pi.on(\"auto_compaction_start\", () => {\n autoPending = true;\n });\n pi.on(\"auto_compaction_end\", () => {\n autoPending = false;\n });\n pi.on(\"session_before_compact\", (event, ctx) => {\n if (proxyBaseForCtx(ctx) === undefined) return undefined;\n if (!autoPending) return undefined;\n autoPending = false;\n return { cancel: true };\n });\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(noProxyWarning(agent), \"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(armedIdleNotice(version), \"info\");\n } else {\n notify(noSessionWarning(), \"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 // #800: human entry point for the cache-reconciliation feature — the model side\n // already has the acp_cache tool; this command shows humans the identical report\n // (both paths hit handleAcpCache on the proxy). Launcher mode and native mode both\n // load this factory (see pi-native.ts), so one registration covers both.\n pi.registerCommand(\"acp-cache\", {\n description: \"Prompt-cache reconciliation for this session (same report as the acp_cache tool). Usage: /acp-cache [full]\",\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(noProxyWarning(agent), \"warning\");\n return;\n }\n const conversationId = sessionIdOf(ctx) ?? \"unknown\";\n const toolArgs = /(^|\\s)(--)?full(\\s|$)/.test(args ?? \"\") ? { detail: \"full\" as const } : {};\n let text: string;\n try {\n text = await forwardTool(proxyBase, conversationId, \"acp_cache\", toolArgs);\n } catch (err) {\n notify(`bili: cache report failed: ${err instanceof Error ? err.message : String(err)}`, \"error\");\n return;\n }\n // Persistent transcript output (TUI + web hosts like pi-web). The proxy strips\n // the wrapped message from the model context by content signature\n // (src/acp-panel.ts), so it never reaches the LLM; notify() is the fallback\n // for hosts without sendMessage (older pi).\n if (typeof pi.sendMessage === \"function\") {\n try {\n pi.sendMessage({ customType: \"bili-acp-cache\", content: wrapCacheReport(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 // Runtime-info (#955): model id + configured max output.\n // pi's model config exposes id / contextWindow / baseUrl;\n // maxTokens lives on the model object when configured.\n const modelId = ctx.model?.id;\n if (typeof modelId === \"string\" && modelId.length > 0) {\n headers[\"x-bili-plugin-model\"] = modelId;\n const maxOut = (ctx.model as { maxTokens?: unknown } | undefined)?.maxTokens;\n if (typeof maxOut === \"number\" && Number.isFinite(maxOut) && maxOut > 0) headers[\"x-bili-plugin-max-output\"] = String(Math.floor(maxOut));\n reportRuntimeInfoOnChange(proxyBaseForCtx(ctx), { agent, model: modelId, contextWindow: typeof window === \"number\" && window > 0 ? Math.floor(window) : undefined, maxOutput: typeof maxOut === \"number\" && maxOut > 0 ? Math.floor(maxOut) : undefined, baseURL: ctx.model?.baseUrl, source: \"client-config\" });\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 // omp never emits before_provider_headers — where pi stamps the\n // x-bili-plugin-* headers and reports runtime info (#955) — so omp's\n // report rides this per-request event instead: POST only, deduped per\n // model switch, gated on toolsReady like pi's header path (ownership\n // claim = ACP tools registered; round 1 rides wire mode).\n function reportOmpRuntimeInfo(ctx: Ctx): void {\n if (state.toolsReady !== true) return;\n const modelId = ctx.model?.id;\n if (typeof modelId !== \"string\" || modelId.length === 0) return;\n const window = ctx.model?.contextWindow;\n const maxOut = (ctx.model as { maxTokens?: unknown } | undefined)?.maxTokens;\n reportRuntimeInfoOnChange(proxyBaseForCtx(ctx), { agent, model: modelId, contextWindow: typeof window === \"number\" && window > 0 ? Math.floor(window) : undefined, maxOutput: typeof maxOut === \"number\" && maxOut > 0 ? Math.floor(maxOut) : undefined, baseURL: ctx.model?.baseUrl, source: \"client-config\" });\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 if (agent === \"omp\") reportOmpRuntimeInfo(ctx);\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":["data","tools"]}
|
|
1
|
+
{"version":3,"sources":["../../src/loop/tag-echo-filter.ts","../../src/acp-panel.ts","../../src/agent/shared.ts","../../src/agent/pi.ts","../../src/agent/omp.ts"],"sourcesContent":["// Streaming-safe stripper for model-emitted literal ACP render tags (#206).\n// Compressed history is rendered to the model as render tags; models sometimes\n// imitate them in visible output (\"tag echo\"), the client replays the echoed\n// tags on later turns, and the imitation amplifies into unbounded repetition.\n// Stripping render tags from outgoing text breaks the loop at the source.\n//\n// The imitation has two shapes. The plain one is a copy of the rendered form\n// (an opening with its attributes, a ref inside, a close); the wrapped one puts\n// the model's whole turn, its tool call included, where the opening's\n// attributes are still open, so no close is ever written and regex matching\n// alone cannot see the span (see BROKEN_ATTRS, SWALLOW_CAP). Both end the same\n// way: the span is swallowed, never handed to the client as orphan markup.\n// ONLY the render form (\\x3c<name> attrs…\\x3e, \\x3c<name …/\\x3e, \\x3c/<name>\\x3e) is stripped —\n// the underscore-namespaced text-protocol triggers (\\x3cacp_compress\\x3e etc.) and\n// ordinary prose containing \\x3c pass through untouched.\n// #673: models typo the 3-letter name when imitating (observed: acip/acpi,\n// including mixed correct-open + typo'd-close), so <name> matches a bounded\n// mutation set instead of the exact spelling: the three core letters in any\n// order, plus at most ONE extra letter drawn from that set or an inserted i.\n// Every match still requires the name to be followed by \\s or > (attrs or\n// close), so real words that merely contain the letters (acpi/acpi.h includes,\n// caption, app, uppercase ACPI) never match; a false positive costs at most\n// the same bounded caps as before (swallow ≤ SWALLOW_CAP, hold ≤ HOLD_LIMIT/TAG_OPEN_CAP).\nfunction buildAcplikeName(): string {\n const cores = [\"acp\", \"apc\", \"cap\", \"cpa\", \"pac\", \"pca\"];\n const names = new Set<string>(cores);\n for (const c of cores) {\n for (const ch of [\"a\", \"c\", \"p\", \"i\"]) {\n for (let pos = 0; pos <= c.length; pos++) names.add(c.slice(0, pos) + ch + c.slice(pos));\n }\n }\n return [...names].sort((a, b) => b.length - a.length).join(\"|\");\n}\n\n/** Longest-first alternation of every tolerated render-tag name (#673). */\nexport const ACP_NAME_ALT = `(?:${buildAcplikeName()})`;\nconst NAME = ACP_NAME_ALT;\n\n// Opening-tag attrs are bounded: a render tag opening is short (tokens + type,\n// \\x3c 50 chars). An unbounded \\x3c<name> …\\x3e match would swallow a long prose span\n// that merely starts with a tag head and contains a \\x3e somewhere later.\nconst PAIRED = new RegExp(\"\\x3c\" + NAME + \"\\\\s[^<>]{0,256}>([^<>]{0,64})\\x3c\\\\/\" + NAME + \">\");\nconst LONE_OPEN = new RegExp(\"\\x3c\" + NAME + \"(?:\\\\s[^<>]{0,256})?>\");\nconst LONE_CLOSE = new RegExp(\"\\x3c\\\\/\" + NAME + \"(?=[\\\\s>])[^<>]{0,32}>\");\n// A suffix of the buffer that could still grow into a render tag: either an\n// unterminated \\x3c<name> … opening (attrs so far, no \\x3e yet) or a short\n// ambiguous prefix like \\x3c, \\x3ca, \\x3c/ac, \\x3cacip, …\nconst PARTIAL_TAIL = new RegExp(\"(\\x3c\" + NAME + \"\\\\s[^<>]*|\\x3c\\\\/\" + NAME + \"(?:\\\\s[^<>]{0,32})?|\\x3c\\\\/?[acip]*)$\");\n// An unterminated render-tag opening at the end of a string: \\x3c<name> plus\n// attrs, no \\x3e — a truncated imitation, never prose (triggers use \\x3cacp_).\nconst TRUNC_OPEN = new RegExp(\"\\x3c\" + NAME + \"\\\\s[^<>]*$\");\n// A truncated render-tag CLOSE at the end of a string: \\x3c/<name> optionally\n// plus truncated attrs — a truncated imitation close, never prose. Mirrors\n// TRUNC_OPEN on the close side.\nconst TRUNC_CLOSE = new RegExp(\"\\x3c\\\\/\" + NAME + \"(?:\\\\s[^<>]{0,32})?$\");\n// The wrapped-turn imitation: the model opens a render tag and writes its\n// payload where the attributes are still open, so the attribute list runs into\n// a `<` instead of ending at its `>`. The recorded shape (architect session\n// 01a0a0cb, 2026-09-16T17:29:09) opens with `<acp tokens=\"1\" text=\"text`\n// immediately followed by the turn's own tool-call markup. No tag regex can\n// match it — every attribute class stops at `<` — so the span is recognised by\n// shape: a render-tag name, whitespace, then an attribute list bounded by the\n// next `<`. A properly terminated opening never matches: its attribute list\n// ends at a `>`, and no `<` can be reached from there within the class.\nconst BROKEN_ATTRS = new RegExp(\"\\x3c\" + NAME + \"\\\\s[^<>]{0,512}(?=\\x3c)\");\nconst DEFINITE_TAIL = new RegExp(\"^\\x3c\" + NAME + \"\\\\s|^\\x3c\\\\/\" + NAME);\nconst OPEN_WITH_ATTRS = new RegExp(\"^\\x3c\" + NAME + \"\\\\s\");\nconst CLOSE_HEAD = \"\\x3c/\";\nconst CLOSE_NAME_ANCHORED = new RegExp(\"^\" + NAME);\nconst HOLD_LIMIT = 128;\n// Hold cap for a definite unterminated opening tail — far beyond any real tag\n// opening; beyond this the tail is dropped instead of held or passed through.\nconst TAG_OPEN_CAP = 4096;\nconst SWALLOW_CAP = 80;\n// Budget for a wrapped-turn imitation (see BROKEN_ATTRS), which is attested by\n// shape rather than matched: the span is the imitation's payload, so passing\n// the budget discards it instead of releasing it as prose (SWALLOW_CAP's #644\n// rule, kept for the plain opening where an over-long tail is real content).\n// It has to clear a whole turn — the recorded one ran 186 chars and held the\n// turn's tool call.\nconst IMITATION_SWALLOW_CAP = 4096;\n\n/** Exclusive end index (past the terminating \\x3e) of the first loose close\n * tag in s, or -1. #673: the close name may be a typo variant; termination\n * still requires the strict \\x3e right after the name — malformed closes are\n * LONE_CLOSE's job, not the swallow terminator's. */\nfunction looseCloseEnd(s: string): number {\n let idx = s.indexOf(CLOSE_HEAD);\n while (idx >= 0) {\n const m = CLOSE_NAME_ANCHORED.exec(s.slice(idx + 2));\n if (m && s[idx + 2 + m[0].length] === \">\") return idx + 2 + m[0].length + 1;\n idx = s.indexOf(CLOSE_HEAD, idx + 1);\n }\n return -1;\n}\n\n/** The span of one wrapped-turn imitation in `s`: where it starts, and the span\n * that has to go — its head plus, when no loose close follows, the rest of the\n * text (the model's whole turn lives inside it, tool call included). Returns\n * null when no opening in `s` wraps the turn. An opening wraps the turn when\n * its attribute list carries an odd number of quotes — a value was opened and\n * never closed — or when the list runs into a `<` at all (BROKEN_ATTRS). Every\n * genuine opening is balanced, e.g. `tokens=\"1\" type=\"text\"`. */\nfunction wrappedSpan(s: string): { start: number; end: number } | null {\n const broken = BROKEN_ATTRS.exec(s);\n const open = LONE_OPEN.exec(s);\n const spans: { start: number; end: number }[] = [];\n if (broken) spans.push({ start: broken.index, end: broken.index + broken[0].length });\n if (open && OPEN_WITH_ATTRS.test(open[0]) && ((open[0].match(/\"/g) ?? []).length & 1) === 1) {\n spans.push({ start: open.index, end: open.index + open[0].length });\n }\n if (spans.length === 0) return null;\n const first = spans.reduce((a, b) => (b.start < a.start ? b : a));\n const rest = s.slice(first.end);\n const close = looseCloseEnd(rest);\n return { start: first.start, end: close >= 0 ? first.end + close : s.length };\n}\n\nexport interface TagEchoFilterStats {\n /** Raw chars pushed over the filter's lifetime (before stripping). */\n inputChars: number;\n /** Clean chars emitted (push outputs + flush output). */\n outputChars: number;\n /** Whether anything was dropped as an imitation. */\n dropped: boolean;\n}\n\nexport interface TagEchoFilter {\n push(delta: string): string;\n flush(): string;\n dropped(): boolean;\n /** True while the filter holds a partial-tag tail that a later push may complete. */\n pending(): boolean;\n /** Lifetime accounting — feeds degenerate-turn detection (#673). Deliberately not reset by intermediate flushes. */\n stats(): TagEchoFilterStats;\n}\n\n// #717: model-emitted ACP CONFIRMATION MARKERS. After executing a proxy tool\n// call bili emits a visibility marker (\"\\n📦 [ACP] Compressed m00120–m0300 →\n// 1 block(s), ~12K tokens saved.\") as a standalone text block; in client\n// history it looks like ordinary assistant text. Under sustained context\n// pressure a model was observed writing these markers itself — 17 fake\n// \"compressions\" that never reached the proxy (#717). Real markers never pass\n// through the model-output path (the proxy injects them itself), so any\n// marker-shaped line in upstream model output is by definition forged: strip\n// the line and warn, breaking the self-reinforcing loop. Shape: line start +\n// exactly one Unicode symbol char (\\p{So} — every real marker icon is a single\n// So code point; letters such as CJK hanzi are prose, not markers, so a line\n// like \"见[ACP]标记的含义\" must survive) + optional single space/tab + literal\n// \"[ACP]\". Strictly no leading whitespace: an indented occurrence is quoting\n// the format (code block, docs) and must pass through. The multi-line\n// acp_status variant only has its head line stripped — without the head the\n// body reads as unattributed prose, and no reliable terminator exists to\n// swallow it safely.\nconst MARKER_HEAD = /^\\p{So}(?:[ \\t])?\\[ACP\\]/u;\n// Deliberately BROADER than MARKER_HEAD: the streaming state machine must\n// hold ANY non-ASCII line-start prefix (CJK, accented letters, and lone\n// surrogates produced when a chunk boundary splits an astral icon) because\n// it cannot know whether the next chunk completes a forged head. Holding is\n// cheap and lossless (flush/content-preservation resolves it); the strict\n// \\p{So} decision happens only in MARKER_HEAD, so prose is never stripped.\nconst MARKER_HEAD_PREFIX = /^[^\\x00-\\x7F](?:[ \\t])?(?:\\[ACP\\]|\\[ACP|\\[AC|\\[A|\\[)?$/u;\nexport const MARKER_LINE = /^\\p{So}(?:[ \\t])?\\[ACP\\][^\\n]*\\n?/gmu;\n// Conservative tail probe for the streaming fast-path gate below: the chunk's\n// last line is still an undecidable marker-head prefix (icon alone, or icon +\n// partial \"[ACP\"), so the next chunk must flow through the filter. Any\n// non-ASCII lead is accepted here on purpose — over-pushing costs one no-op\n// filter pass, under-pushing leaks a forged marker.\nconst MARKER_TAIL = /(?:^|\\n)[^\\x00-\\x7F](?:[ \\t])?(?:\\[ACP|\\[AC|\\[A|\\[)?$/u;\n\n/** Fast-path gate for the streaming pipes (#717): could this chunk contain a\n * forged marker line, or leave a marker head undecidable across the chunk\n * boundary? Coarse by design — a false positive costs one no-op filter pass,\n * but skipping a chunk that carries or starts a forged line forwards it raw. */\nexport function mayStartMarkerLine(s: string): boolean {\n return s.includes(\"[ACP]\") || MARKER_TAIL.test(s);\n}\n\nexport function stripMarkerLines(text: string): string {\n return text.replace(MARKER_LINE, \"\");\n}\n\nexport function stripAcpTags(text: string): string {\n // A wrapped-turn imitation first, whole: it swallows the model's turn, and\n // leaving its payload behind hands the client the orphan markup that makes\n // the turn unusable. Each pass removes at least the head, so this ends.\n let out = text;\n for (;;) {\n const wrapped = wrappedSpan(out);\n if (wrapped === null) break;\n out = out.slice(0, wrapped.start) + out.slice(wrapped.end);\n }\n return out\n .replace(new RegExp(PAIRED.source, \"g\"), \"\")\n .replace(new RegExp(LONE_OPEN.source, \"g\"), \"\")\n .replace(new RegExp(LONE_CLOSE.source, \"g\"), \"\")\n .replace(new RegExp(TRUNC_OPEN.source), \"\")\n .replace(new RegExp(TRUNC_CLOSE.source), \"\")\n .replace(MARKER_LINE, \"\");\n}\n\n// Raw-wire pre-check for forged confirmation markers (#717): the literal\n// \"[ACP]\" survives JSON escaping unscathed (brackets are not escaped), so a\n// plain includes() on the raw SSE/JSON string is sound and cheap. The\n// false-positive cost is one no-op re-serialize; the strict line-anchored\n// match decides what actually gets removed.\nexport function containsMarkerLineText(s: string): boolean {\n return s.includes(\"[ACP]\");\n}\n\n// Cheap pre-check on a raw wire string (SSE event or JSON body): does it\n// contain anything that looks like a render tag (literal or JSON-escaped\n// \\u003c form)? Callers use this to skip re-serializing chunks that need\n// no stripping, preserving byte-identical passthrough.\nconst RENDER_TAG_DETECT = new RegExp(\"\\x3c\\\\/?\" + NAME + \"(?=[\\\\s>])|\\\\\\\\u003c\\\\/?\" + NAME + \"(?=[\\\\s>\\\\\\\\])\");\nexport function containsRenderTagText(s: string): boolean {\n return RENDER_TAG_DETECT.test(s);\n}\n\n// #468: some upstreams stream a model-imitated render tag in tokenizer-sized\n// fragments (\"\\x3cac\", \"p tokens\", ...) so no single chunk ever trips\n// RENDER_TAG_DETECT. Per-chunk gates must also engage when the chunk contains\n// or ends with the head of a render tag, so the streaming state machine can\n// stitch it back together. Pure-prose chunks still skip the machine\n// (byte-identical passthrough); only chunks with a tag-head tail (\"\\x3c\", \"\\x3c/\",\n// \"\\x3ca\", \"\\x3cac\", \"\\x3cacp ...attrs\", \"\\x3c/acp ...\") engage it.\nexport function mayStartRenderTag(s: string): boolean {\n return RENDER_TAG_DETECT.test(s) || PARTIAL_TAIL.test(s);\n}\n\n// #361: tool-call XML template fragments a model may echo from the context\n// (same source as acp tag echo — the model \"writes the tool call as text\").\n// Detected + warned for attribution, NOT stripped: a closing tool-XML tag\n// cannot be distinguished from legitimate prose discussing tool-call code,\n// so stripping would corrupt real content (see #295 review).\nconst TOOL_CALL_XML = /\\x3c\\/?(?:antml:)?(invoke|tool_calls|tool_call|parameter|parameters)\\b[^<>]*\\x3e|\\\\u003c\\/?(?:antml:)?(invoke|tool_calls|tool_call|parameter|parameters)\\b|\\x3c\\/?antml:[a-z_]+/i;\nexport function containsToolCallXmlFragment(s: string): boolean {\n return TOOL_CALL_XML.test(s);\n}\n\nexport function createTagEchoFilter(onDrop?: (snippet: string) => void): TagEchoFilter {\n let held = \"\";\n let swallowUntilClose = false;\n let swallowed = \"\";\n /** Which budget the current swallow answers to (SWALLOW_CAP or\n * IMITATION_SWALLOW_CAP). */\n let swallowLimit = SWALLOW_CAP;\n /** Whether passing that budget releases the span as prose — true for a plain\n * opening, where an over-long tail is content (#644); false for a\n * wrapped-turn imitation, which is attested by shape and whose payload must\n * never reach the client. */\n let swallowReleases = true;\n let droppedAny = false;\n let notified = false;\n let inputChars = 0;\n let outputChars = 0;\n const drop = (snippet: string) => {\n droppedAny = true;\n if (onDrop && !notified) {\n notified = true;\n onDrop(snippet);\n }\n };\n const process = (input: string): string => {\n let buf = input;\n let out = \"\";\n for (;;) {\n if (swallowUntilClose) {\n const combined = swallowed + buf;\n const end = looseCloseEnd(combined);\n if (end >= 0) {\n drop(combined.slice(0, end));\n swallowed = \"\";\n swallowUntilClose = false;\n buf = combined.slice(end);\n continue;\n }\n if (combined.length > swallowLimit) {\n swallowed = \"\";\n if (swallowReleases) {\n swallowUntilClose = false;\n buf = combined;\n continue;\n }\n // The span is an attested imitation's payload: discard it and\n // keep swallowing, so no part of it reaches the client.\n drop(combined);\n return out;\n }\n swallowed = combined;\n return out;\n }\n const p = PAIRED.exec(buf);\n const o = LONE_OPEN.exec(buf);\n const c = LONE_CLOSE.exec(buf);\n let m: RegExpExecArray | null = null;\n for (const cand of [p, o, c]) {\n if (cand && (m === null || cand.index < m.index)) m = cand;\n }\n if (!m) {\n // An opening whose attribute list never terminates (see\n // BROKEN_ATTRS): everything after it is the imitation's payload,\n // the turn's own tool call among it. Dropping the head alone\n // would hand the client the orphan markup that makes the turn\n // unusable, so the span is swallowed whole and the turn reaches\n // the client empty, where the degenerate-turn retry re-asks for\n // it (#732/#821). A loose close still ends the span, so genuine\n // prose after a closed imitation survives.\n const broken = BROKEN_ATTRS.exec(buf);\n if (broken) {\n drop(broken[0]);\n out += buf.slice(0, broken.index);\n buf = buf.slice(broken.index + broken[0].length);\n swallowUntilClose = true;\n swallowLimit = IMITATION_SWALLOW_CAP;\n swallowReleases = false;\n swallowed = \"\";\n continue;\n }\n const t = PARTIAL_TAIL.exec(buf);\n if (t) {\n // A definite \\x3c<name> opening is never prose — hold it far\n // past HOLD_LIMIT (drop it past TAG_OPEN_CAP); a short\n // ambiguous prefix stays on the small hold cap so prose\n // is never delayed or lost.\n const definite = DEFINITE_TAIL.test(t[0]);\n const cap = definite ? TAG_OPEN_CAP : HOLD_LIMIT;\n if (t[0].length <= cap) {\n held = t[0];\n out += buf.slice(0, buf.length - t[0].length);\n } else if (definite) {\n drop(t[0]);\n out += buf.slice(0, buf.length - t[0].length);\n } else {\n out += buf;\n }\n } else {\n out += buf;\n }\n break;\n }\n drop(m[0]);\n out += buf.slice(0, m.index);\n buf = buf.slice(m.index + m[0].length);\n // A PAIRED match is by definition a complete open+content+close\n // span — only an attrs-bearing LONE_OPEN leaves the stream\n // mid-tag and needs to swallow until its close arrives.\n if (m === o && OPEN_WITH_ATTRS.test(m[0])) {\n // An odd number of quotes means the opening's attribute list\n // never closed: the model wrapped its turn inside the value (the\n // sibling shape opens with such a value and then runs into a `<`,\n // which BROKEN_ATTRS catches). A wrapped span is the imitation's\n // payload — its tool call among it — so it is discarded rather\n // than released at the #644 budget. Every genuine opening has\n // balanced quotes: `tokens=\"1\" type=\"text\"`.\n const wrapped = ((m[0].match(/\"/g) ?? []).length & 1) === 1;\n swallowUntilClose = true;\n swallowLimit = wrapped ? IMITATION_SWALLOW_CAP : SWALLOW_CAP;\n swallowReleases = !wrapped;\n swallowed = \"\";\n }\n }\n return out;\n };\n return {\n push(delta: string): string {\n inputChars += delta.length;\n const chunk = held + delta;\n held = \"\";\n const r = process(chunk);\n outputChars += r.length;\n return r;\n },\n flush(): string {\n const rest = swallowed + held;\n const wasSwallowing = swallowUntilClose;\n swallowed = \"\";\n held = \"\";\n swallowUntilClose = false;\n let result: string;\n if (wasSwallowing) {\n // Stream ended inside an unclosed render tag: the held content\n // is tag content (a ref), not prose.\n if (rest.length > 0) drop(rest);\n result = \"\";\n } else {\n const t = new RegExp(TRUNC_OPEN.source).exec(rest);\n if (t) {\n drop(t[0]);\n result = rest.slice(0, t.index);\n } else {\n const tc = new RegExp(TRUNC_CLOSE.source).exec(rest);\n if (tc) {\n drop(tc[0]);\n result = rest.slice(0, tc.index);\n } else {\n result = rest;\n }\n }\n }\n outputChars += result.length;\n return result;\n },\n dropped(): boolean {\n return droppedAny;\n },\n pending(): boolean {\n return held.length > 0 || swallowUntilClose;\n },\n stats(): TagEchoFilterStats {\n return { inputChars, outputChars, dropped: droppedAny };\n },\n };\n}\n\nfunction stripParts(content: unknown): unknown {\n if (!Array.isArray(content)) return content;\n return content.map((part) => {\n if (part && typeof part === \"object\" && typeof (part as Record<string, unknown>).text === \"string\") {\n return { ...(part as Record<string, unknown>), text: stripAcpTags((part as Record<string, unknown>).text as string) };\n }\n return part;\n });\n}\n\nfunction stripItemContent(it: unknown): unknown {\n if (!it || typeof it !== \"object\") return it;\n const io = it as Record<string, unknown>;\n let out: Record<string, unknown> | undefined;\n const set = (k: string, v: unknown): void => {\n out ??= { ...io };\n out[k] = v;\n };\n if (Array.isArray(io.content)) set(\"content\", stripParts(io.content));\n if (typeof io.arguments === \"string\") set(\"arguments\", stripAcpTags(io.arguments));\n if (Array.isArray(io.summary)) {\n set(\n \"summary\",\n io.summary.map((s) =>\n s && typeof s === \"object\" && typeof (s as Record<string, unknown>).text === \"string\"\n ? { ...(s as Record<string, unknown>), text: stripAcpTags((s as Record<string, unknown>).text as string) }\n : s,\n ),\n );\n }\n return out ?? it;\n}\n\nfunction stripIfString(v: unknown): unknown {\n return typeof v === \"string\" ? stripAcpTags(v) : v;\n}\n\n// #933: whole-object invariant — field-enumerated stripping kept missing new\n// carriers (tool-call arguments); these walk every string leaf instead.\nexport function stripAcpTagsDeep(v: unknown): unknown {\n if (typeof v === \"string\") return stripAcpTags(v);\n if (Array.isArray(v)) {\n let changed = false;\n const out = v.map((x) => {\n const r = stripAcpTagsDeep(x);\n if (r !== x) changed = true;\n return r;\n });\n return changed ? out : v;\n }\n if (v && typeof v === \"object\") {\n let changed = false;\n const out: Record<string, unknown> = {};\n for (const [k, x] of Object.entries(v as Record<string, unknown>)) {\n const r = stripAcpTagsDeep(x);\n if (r !== x) changed = true;\n out[k] = r;\n }\n return changed ? out : v;\n }\n return v;\n}\n\nexport function findAcpLikeTagSnippetsDeep(v: unknown, path = \"$\"): string[] {\n const hits: string[] = [];\n const visit = (x: unknown, p: string): void => {\n if (typeof x === \"string\") {\n const pairedSpans: Array<[number, number]> = [];\n for (const m of x.matchAll(new RegExp(PAIRED.source, \"g\"))) {\n hits.push(`${p}: ${m[0].slice(0, 60)}`);\n pairedSpans.push([m.index, m.index + m[0].length]);\n }\n for (const re of [new RegExp(LONE_OPEN.source, \"g\"), new RegExp(LONE_CLOSE.source, \"g\")]) {\n for (const m of x.matchAll(re)) {\n if (pairedSpans.some(([s, e]) => m.index! >= s && m.index! < e)) continue;\n hits.push(`${p}: ${m[0].slice(0, 60)}`);\n }\n }\n for (const m of x.matchAll(MARKER_LINE)) hits.push(`${p}: ${m[0].slice(0, 60)}`);\n return;\n }\n if (Array.isArray(x)) {\n x.forEach((it, i) => visit(it, `${p}[${i}]`));\n return;\n }\n if (x && typeof x === \"object\") {\n for (const [k, it] of Object.entries(x as Record<string, unknown>)) visit(it, `${p}.${k}`);\n }\n };\n visit(v, path);\n return hits;\n}\n\nfunction stripToolCallArgs(tcs: unknown): unknown {\n if (!Array.isArray(tcs)) return tcs;\n return tcs.map((tc) => {\n if (!tc || typeof tc !== \"object\") return tc;\n const tco = tc as Record<string, unknown>;\n const fn = tco[\"function\"];\n if (fn && typeof fn === \"object\" && typeof (fn as Record<string, unknown>)[\"arguments\"] === \"string\") {\n return { ...tco, function: { ...(fn as Record<string, unknown>), arguments: stripAcpTags((fn as Record<string, unknown>)[\"arguments\"] as string) } };\n }\n return tc;\n });\n}\n\n// Plugin-passthrough parity for the OpenAI chat-completions wire (issue #14:\n// pi + qwen echoed render tags through the verbatim plugin stream): strip the\n// text fields and tool-call arguments a chat chunk / completion carries —\n// `choices[].delta.{content,reasoning_content,reasoning}` +\n// `tool_calls[].function.arguments` on streams, `choices[].message.*` on\n// non-streaming bodies. Mutates in place, mirroring stripResponsesText.\nexport function stripOpenaiChatText<T>(obj: T): T {\n if (!obj || typeof obj !== \"object\") return obj;\n const o = obj as Record<string, unknown>;\n if (!Array.isArray(o[\"choices\"])) return obj;\n o[\"choices\"] = (o[\"choices\"] as unknown[]).map((c) => {\n if (!c || typeof c !== \"object\") return c;\n const ch = c as Record<string, unknown>;\n for (const holder of [\"delta\", \"message\"]) {\n const h = ch[holder];\n if (h && typeof h === \"object\") {\n const hh = { ...(h as Record<string, unknown>) };\n hh[\"content\"] = stripIfString(hh[\"content\"]);\n hh[\"reasoning_content\"] = stripIfString(hh[\"reasoning_content\"]);\n hh[\"reasoning\"] = stripIfString(hh[\"reasoning\"]);\n hh[\"tool_calls\"] = stripToolCallArgs(hh[\"tool_calls\"]);\n ch[holder] = hh;\n }\n }\n return ch;\n });\n return obj;\n}\n\n// Plugin-passthrough parity for the Anthropic wire: strip `delta.{text,\n// thinking}` on content_block_delta streams and `content[].{text,thinking}`\n// on non-streaming message bodies. Mutates in place.\nexport function stripAnthropicText<T>(obj: T): T {\n if (!obj || typeof obj !== \"object\") return obj;\n const o = obj as Record<string, unknown>;\n const d = o[\"delta\"];\n if (d && typeof d === \"object\") {\n const dd = { ...(d as Record<string, unknown>) };\n dd[\"text\"] = stripIfString(dd[\"text\"]);\n dd[\"thinking\"] = stripIfString(dd[\"thinking\"]);\n dd[\"partial_json\"] = stripIfString(dd[\"partial_json\"]);\n o[\"delta\"] = dd;\n }\n if (Array.isArray(o[\"content\"])) {\n o[\"content\"] = (o[\"content\"] as unknown[]).map((c) => {\n if (!c || typeof c !== \"object\") return c;\n const cc = c as Record<string, unknown>;\n const hasText = typeof cc[\"text\"] === \"string\" || typeof cc[\"thinking\"] === \"string\";\n const input = cc[\"type\"] === \"tool_use\" ? cc[\"input\"] : undefined;\n const hasInput = input != null && typeof input === \"object\";\n if (!hasText && !hasInput) return c;\n return {\n ...cc,\n ...(hasText ? { text: stripIfString(cc[\"text\"]), thinking: stripIfString(cc[\"thinking\"]) } : {}),\n ...(hasInput ? { input: stripAcpTagsDeep(input) } : {}),\n };\n });\n }\n return obj;\n}\n\n// Strip render tags from the text fields of a Responses-API event/response\n// object (mutates in place). Handles the shapes that carry literal text:\n// output_text.done `.text`, content_part.done `.part.text`,\n// output_item.done `.item.content[].text`, and `.response.output[].content[]`\n// on response.completed.\nexport function stripResponsesText<T>(obj: T): T {\n if (!obj || typeof obj !== \"object\") return obj;\n const o = obj as Record<string, unknown>;\n if (typeof o.text === \"string\") o.text = stripAcpTags(o.text);\n if (typeof o.arguments === \"string\") o.arguments = stripAcpTags(o.arguments);\n if (typeof o.delta === \"string\") o.delta = stripAcpTags(o.delta);\n if (o.part && typeof o.part === \"object\" && typeof (o.part as Record<string, unknown>).text === \"string\") {\n o.part = { ...(o.part as Record<string, unknown>), text: stripAcpTags((o.part as Record<string, unknown>).text as string) };\n }\n if (o.item && typeof o.item === \"object\") {\n o.item = stripItemContent(o.item);\n }\n if (o.response && typeof o.response === \"object\") {\n const resp = { ...(o.response as Record<string, unknown>) };\n if (Array.isArray(resp.output)) {\n resp.output = resp.output.map(stripItemContent);\n }\n o.response = resp;\n }\n if (Array.isArray(o.output)) {\n o.output = o.output.map(stripItemContent);\n }\n return obj;\n}\n\n// #717 streaming counterpart of stripMarkerLines. Same contract as\n// createTagEchoFilter: push deltas, emit clean text, hold back at most the\n// undecidable line-start prefix (~8 chars) until a later push decides it,\n// flush resolves at block/stream end. A swallowed marker line is dropped\n// whole (its \\n included) so surrounding lines rejoin cleanly.\nexport function createMarkerLineFilter(onDrop?: (snippet: string) => void): TagEchoFilter {\n let buf = \"\";\n let atLineStart = true;\n let swallowing = false;\n let droppedAny = false;\n let notified = false;\n let inputChars = 0;\n let outputChars = 0;\n\n const noteDrop = (snippet: string) => {\n droppedAny = true;\n if (!notified) {\n notified = true;\n onDrop?.(snippet);\n }\n };\n\n const process = (chunk: string): string => {\n buf += chunk;\n inputChars += chunk.length;\n let out = \"\";\n while (buf.length > 0) {\n if (swallowing) {\n const nl = buf.indexOf(\"\\n\");\n if (nl < 0) return out;\n noteDrop(buf.slice(0, nl));\n buf = buf.slice(nl + 1);\n swallowing = false;\n atLineStart = true;\n continue;\n }\n if (atLineStart) {\n if (MARKER_HEAD.test(buf)) {\n swallowing = true;\n continue;\n }\n if (MARKER_HEAD_PREFIX.test(buf)) return out;\n out += buf[0];\n buf = buf.slice(1);\n atLineStart = false;\n continue;\n }\n const nl = buf.indexOf(\"\\n\");\n if (nl >= 0) {\n out += buf.slice(0, nl + 1);\n buf = buf.slice(nl + 1);\n atLineStart = true;\n } else {\n out += buf;\n buf = \"\";\n }\n }\n outputChars += out.length;\n return out;\n };\n\n return {\n push(delta: string): string {\n return process(delta);\n },\n flush(): string {\n let out = \"\";\n if (buf.length > 0) {\n if (swallowing || MARKER_HEAD.test(buf)) {\n noteDrop(buf);\n buf = \"\";\n } else {\n // Undecidable prefix or plain tail: content preservation.\n out = buf;\n buf = \"\";\n }\n }\n swallowing = false;\n atLineStart = true;\n outputChars += out.length;\n return out;\n },\n dropped: () => droppedAny,\n pending: () => buf.length > 0,\n stats: () => ({ inputChars, outputChars, dropped: droppedAny }),\n };\n}\n\n/** Run two streaming filters in sequence (input flows a→b), so the\n * marker-line stripper (#717) layers onto the render-tag echo filter\n * (#206/#673) without touching call sites. Stats merge: input from a,\n * output from b, dropped/pending OR'd. */\nexport function composeStreamFilters(a: TagEchoFilter, b: TagEchoFilter): TagEchoFilter {\n return {\n push: (delta: string) => b.push(a.push(delta)),\n flush: () => {\n const tail = a.flush();\n return tail === \"\" ? b.flush() : b.push(tail) + b.flush();\n },\n dropped: () => a.dropped() || b.dropped(),\n pending: () => a.pending() || b.pending(),\n stats: () => ({\n inputChars: a.stats().inputChars,\n outputChars: b.stats().outputChars,\n dropped: a.dropped() || b.dropped(),\n }),\n };\n}\n","// ACP panel stripping for proxy mode (issue #359). The pi plugin's /acp emits\n// the status panel as a persistent custom_message; in proxy mode pi's\n// convertToLlm projects every custom message as an ordinary user message, so\n// the panel would ride the recent zone to the model as if it were real\n// conversation content. pi's projection drops the customType, so the proxy\n// (which generates the panel itself via buildStatusPanel) strips it by content\n// signature before it enters the compression state.\n//\n// The match must be WHOLE-MESSAGE, not a prefix: a user can copy a panel from\n// pi-web and append a follow-up question in the same message — a prefix match\n// would strip the user's question too (data loss, unrecoverable in-session).\n// Each renderer is therefore anchored on BOTH its start and its end:\n// - buildStatusPanel (acp-kernel): starts with the U+256D top border + title\n// \"ACP Context Analysis\", and always ends with the \"Tag visibility: ...\"\n// footer (unconditional, last line pushed).\n// - renderAcpStatus (plugin fallback): first line exactly \"📊 ACP status\",\n// every other line an indented field.\n// - wrapCacheReport (#800, /acp-cache): explicit plugin-owned markers around\n// the kernel's cache report — its shape is kernel-owned and variable-length\n// (LINE ITEMS table up to the sample cap), so we cannot anchor on the\n// renderer output the way we can for the two panels above.\nimport { MARKER_LINE, stripMarkerLines } from \"./loop/tag-echo-filter.js\";\n\nconst PANEL_BOX_TOP = \"\\u256d\";\nconst PANEL_BOX_TITLE = \"ACP Context Analysis\";\nconst PANEL_BOX_FOOTER = \"Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.\";\nconst PANEL_FALLBACK_HEADER = \"\\u{1f4ca} ACP status\";\n\nexport const CACHE_REPORT_OPEN = \"[acp-cache]\";\nexport const CACHE_REPORT_CLOSE = \"[/acp-cache]\";\n\n/** Wrap a cache report for persistent transcript display (see above). */\nexport function wrapCacheReport(report: string): string {\n return `${CACHE_REPORT_OPEN}\\n${report}\\n${CACHE_REPORT_CLOSE}`;\n}\n\nexport function isAcpPanelText(text: string): boolean {\n const t = text.trim();\n if (t.length === 0) return false;\n return isBoxPanel(t) || isFallbackPanel(t) || isCacheReport(t);\n}\n\nfunction isCacheReport(t: string): boolean {\n return t.startsWith(CACHE_REPORT_OPEN) && t.endsWith(CACHE_REPORT_CLOSE);\n}\n\nfunction isBoxPanel(t: string): boolean {\n return t.startsWith(PANEL_BOX_TOP) && t.includes(PANEL_BOX_TITLE) && t.endsWith(PANEL_BOX_FOOTER);\n}\n\nfunction isFallbackPanel(t: string): boolean {\n const lines = t.split(\"\\n\");\n if (lines[0] !== PANEL_FALLBACK_HEADER) return false;\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n if (line.length > 0 && !line.startsWith(\" \")) return false;\n }\n return true;\n}\n\n// Plain text of a message's content (string or text-block array). Returns\n// undefined for mixed/multimodal content — such a message can never be a\n// panel, so it is preserved.\nfunction messageText(content: unknown): string | undefined {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n let joined = \"\";\n for (const part of content) {\n const p = part as Record<string, unknown> | null;\n if (p === null || typeof p !== \"object\") return undefined;\n const pt = p.type;\n if (pt !== undefined && pt !== \"text\" && pt !== \"input_text\") return undefined;\n if (typeof p.text === \"string\") joined += p.text;\n }\n return joined;\n }\n return undefined;\n}\n\n// Strip ACP panel user messages from an anthropic/openai messages array (in\n// place); returns the count removed.\nexport function stripAcpPanelMessages(messages: unknown): number {\n if (!Array.isArray(messages)) return 0;\n let stripped = 0;\n for (let i = messages.length - 1; i >= 0; i--) {\n const rec = messages[i] as Record<string, unknown> | null;\n if (rec === null || typeof rec !== \"object\") continue;\n if (rec.role !== \"user\") continue;\n const text = messageText(rec.content);\n if (text !== undefined && isAcpPanelText(text)) {\n messages.splice(i, 1);\n stripped++;\n }\n }\n return stripped;\n}\n\n// #1029: ACP status markers (\"❌ [ACP] …\", \"📦 [ACP] …\") are ephemeral proxy\n// status lines streamed into assistant text (#717). Clients that resend full\n// history (e.g. DSH Desktop) carry them back on every turn, where they\n// accumulate in the model context and the reading flow. Strip marker lines\n// from incoming user/assistant text before projection (mirrors\n// stripAcpPanelMessages above; line semantics shared with the outgoing\n// tag-echo filter). Messages are NEVER deleted — a marker-only assistant\n// message may still carry tool_calls whose tool results must not be orphaned;\n// such text degrades to a single space instead.\nexport function stripAcpStatusMarkers(messages: unknown): number {\n if (!Array.isArray(messages)) return 0;\n let stripped = 0;\n for (const rec of messages) {\n if (rec === null || typeof rec !== \"object\") continue;\n const m = rec as Record<string, unknown>;\n if (m.role !== \"user\" && m.role !== \"assistant\") continue;\n const content = m.content;\n if (typeof content === \"string\") {\n const out = stripMarkerLines(content);\n if (out !== content) {\n m.content = out.trim().length > 0 ? out : \" \";\n stripped += countMarkerLines(content);\n }\n } else if (Array.isArray(content)) {\n for (const part of content) {\n if (part === null || typeof part !== \"object\") continue;\n const p = part as Record<string, unknown>;\n const text = p.text;\n if (typeof text !== \"string\") continue;\n const out = stripMarkerLines(text);\n if (out !== text) {\n p.text = out.trim().length > 0 ? out : \" \";\n stripped += countMarkerLines(text);\n }\n }\n }\n }\n return stripped;\n}\n\nfunction countMarkerLines(text: string): number {\n let n = 0;\n for (const _ of text.matchAll(MARKER_LINE)) n++;\n return n;\n}\n\n// Strip ACP panel user messages from a Responses input array (in place);\n// returns the count removed. Type-less user items (omp wire form) count as\n// messages, mirroring dropWhitespaceResponsesMessages.\nexport function stripAcpPanelResponsesInput(input: unknown): number {\n if (!Array.isArray(input)) return 0;\n let stripped = 0;\n for (let i = input.length - 1; i >= 0; i--) {\n const rec = input[i] as Record<string, unknown> | null;\n if (rec === null || typeof rec !== \"object\") continue;\n const type = rec.type;\n if (type !== \"message\" && type !== undefined) continue;\n if (rec.role !== \"user\") continue;\n const text = messageText(rec.content);\n if (text !== undefined && isAcpPanelText(text)) {\n input.splice(i, 1);\n stripped++;\n }\n }\n return stripped;\n}\n","// 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 type RuntimeInfoReport = {\n agent: string;\n model: string;\n contextWindow?: number;\n maxOutput?: number;\n baseURL?: string;\n source?: string;\n};\n\n/** Runtime-info protocol (#955): push the client's OWN model config (what\n * the plugin read from the host's config) to the proxy at bootstrap and on\n * model switch, before any model request. Fire-and-forget by design — the\n * proxy treats a missing report as \"guess like before\" (registry/table). */\nexport async function reportRuntimeInfo(proxyBase: string, info: RuntimeInfoReport): Promise<void> {\n const { ok, status } = await fetchJson(`${proxyBase}/__bili/plugin/runtime-info`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(info),\n }, STATUS_TIMEOUT_MS);\n if (!ok) throw new Error(`runtime-info report failed (${status})`);\n}\n\n// Per-agent last report (model id): a stamp fires at most one POST per model\n// switch, not per request. Failed reports roll back so the next stamp retries.\nconst lastRuntimeReport = new Map<string, string>();\n\n/** Stamp-time runtime-info report (#955): no-op unless the agent's reported\n * model changed (or this is the first stamp after proxy attach/spawn).\n * Soft-fail — an unreachable proxy keeps wire mode exactly as before. */\nexport function reportRuntimeInfoOnChange(proxyBase: string | undefined, info: RuntimeInfoReport): void {\n if (proxyBase === undefined || proxyBase.length === 0) return;\n if (lastRuntimeReport.get(info.agent) === info.model) return;\n lastRuntimeReport.set(info.agent, info.model);\n void reportRuntimeInfo(proxyBase, info).catch(() => {\n lastRuntimeReport.delete(info.agent);\n });\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\n// Model-facing body for /acp status renders: the visible panel goes to the synthetic message's\n// description (TUI Notice cap ~1KB); this inert one-liner keeps the model context clean.\nexport const V2_SYNTHETIC_TEXT = \"[bili-acp-status] ACP status panel was displayed in your terminal; this line is not an instruction.\";\n\n/** Armed-but-idle /acp notice (proxy live, no model request sent yet). One\n * source of truth for pi / dsh / opencode (#883). */\nexport function armedIdleNotice(version: string): string {\n return `billion-context@${version} — proxy connected, compression armed. No model request yet; send one, then run /acp again.`;\n}\n\n/** Fallback when the version probe also fails — a warning, not an info notice. */\nexport function noSessionWarning(): string {\n return \"bili: no ACP session yet (send a model request first, then run /acp)\";\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 { wrapCacheReport } from \"../acp-panel.js\";\nimport { detectProxyBase, fetchManifest, forwardTool, fetchStatus, fetchProxyVersion, reportRuntimeInfoOnChange, armedIdleNotice, noSessionWarning, 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 | undefined): 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\n// #788: neutral wording — the plugin also loads under plain pi/omp launches\n// where the user never intended proxy mode (e.g. they use billion-context-pi\n// in-process instead), so offer both exits instead of assuming proxy intent.\nfunction noProxyWarning(agent: string): string {\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 return `bili: no proxy detected — run via \\`bili ${agent}\\` (or set a /bili/ baseURL) to use proxy mode${removeHint}`;\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 (#851): session_before_compact\n // carries NO reason field, so at hook level manual compaction\n // (/compact, plan-mode \"Approve and compact context\") is\n // indistinguishable from auto — but every auto pass announces itself\n // first via auto_compaction_start (reason threshold|overflow|idle|\n // incomplete), which omp emits (awaited) before the hook fires;\n // manual paths never do. Track the announcement: announced passes\n // stay cancelled, unannounced ones are left user-owned. A surviving\n // native compaction is safe: the proxy archives the unreachable\n // blocks on session_compact (#395).\n // Whether we own compression is decided at EVENT time, not load time:\n // in native mode (#519) the proxy origin lands in\n // BILLION_CONTEXT_PROXY only after the async bootstrap finishes, so a\n // load-time check would leave the cancel disarmed for the whole\n // session. Plain pi/omp with the plugin installed but NO reachable\n // proxy (incl. a failed bootstrap) stays fully native.\n if (agent === \"pi\" || agent === \"omp\") {\n if (agent === \"pi\") {\n pi.on(\"session_before_compact\", (event, ctx) => {\n if (proxyBaseForCtx(ctx) === undefined) return undefined;\n const reason = (event as unknown as { reason?: unknown }).reason;\n if (reason === \"threshold\" || reason === \"overflow\") return { cancel: true };\n return undefined;\n });\n } else {\n let autoPending = false;\n pi.on(\"auto_compaction_start\", () => {\n autoPending = true;\n });\n pi.on(\"auto_compaction_end\", () => {\n autoPending = false;\n });\n pi.on(\"session_before_compact\", (event, ctx) => {\n if (proxyBaseForCtx(ctx) === undefined) return undefined;\n if (!autoPending) return undefined;\n autoPending = false;\n return { cancel: true };\n });\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(noProxyWarning(agent), \"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(armedIdleNotice(version), \"info\");\n } else {\n notify(noSessionWarning(), \"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 // #800: human entry point for the cache-reconciliation feature — the model side\n // already has the acp_cache tool; this command shows humans the identical report\n // (both paths hit handleAcpCache on the proxy). Launcher mode and native mode both\n // load this factory (see pi-native.ts), so one registration covers both.\n pi.registerCommand(\"acp-cache\", {\n description: \"Prompt-cache reconciliation for this session (same report as the acp_cache tool). Usage: /acp-cache [full]\",\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(noProxyWarning(agent), \"warning\");\n return;\n }\n const conversationId = sessionIdOf(ctx) ?? \"unknown\";\n const toolArgs = /(^|\\s)(--)?full(\\s|$)/.test(args ?? \"\") ? { detail: \"full\" as const } : {};\n let text: string;\n try {\n text = await forwardTool(proxyBase, conversationId, \"acp_cache\", toolArgs);\n } catch (err) {\n notify(`bili: cache report failed: ${err instanceof Error ? err.message : String(err)}`, \"error\");\n return;\n }\n // Persistent transcript output (TUI + web hosts like pi-web). The proxy strips\n // the wrapped message from the model context by content signature\n // (src/acp-panel.ts), so it never reaches the LLM; notify() is the fallback\n // for hosts without sendMessage (older pi).\n if (typeof pi.sendMessage === \"function\") {\n try {\n pi.sendMessage({ customType: \"bili-acp-cache\", content: wrapCacheReport(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 // Runtime-info (#955): model id + configured max output.\n // pi's model config exposes id / contextWindow / baseUrl;\n // maxTokens lives on the model object when configured.\n const modelId = ctx.model?.id;\n if (typeof modelId === \"string\" && modelId.length > 0) {\n headers[\"x-bili-plugin-model\"] = modelId;\n const maxOut = (ctx.model as { maxTokens?: unknown } | undefined)?.maxTokens;\n if (typeof maxOut === \"number\" && Number.isFinite(maxOut) && maxOut > 0) headers[\"x-bili-plugin-max-output\"] = String(Math.floor(maxOut));\n reportRuntimeInfoOnChange(proxyBaseForCtx(ctx), { agent, model: modelId, contextWindow: typeof window === \"number\" && window > 0 ? Math.floor(window) : undefined, maxOutput: typeof maxOut === \"number\" && maxOut > 0 ? Math.floor(maxOut) : undefined, baseURL: ctx.model?.baseUrl, source: \"client-config\" });\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 // omp never emits before_provider_headers — where pi stamps the\n // x-bili-plugin-* headers and reports runtime info (#955) — so omp's\n // report rides this per-request event instead: POST only, deduped per\n // model switch, gated on toolsReady like pi's header path (ownership\n // claim = ACP tools registered; round 1 rides wire mode).\n function reportOmpRuntimeInfo(ctx: Ctx): void {\n if (state.toolsReady !== true) return;\n const modelId = ctx.model?.id;\n if (typeof modelId !== \"string\" || modelId.length === 0) return;\n const window = ctx.model?.contextWindow;\n const maxOut = (ctx.model as { maxTokens?: unknown } | undefined)?.maxTokens;\n reportRuntimeInfoOnChange(proxyBaseForCtx(ctx), { agent, model: modelId, contextWindow: typeof window === \"number\" && window > 0 ? Math.floor(window) : undefined, maxOutput: typeof maxOut === \"number\" && maxOut > 0 ? Math.floor(maxOut) : undefined, baseURL: ctx.model?.baseUrl, source: \"client-config\" });\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 if (agent === \"omp\") reportOmpRuntimeInfo(ctx);\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":["data","tools"]}
|
package/dist/agent/pi-native.js
CHANGED
|
@@ -47304,6 +47304,33 @@ async function ensureProxyRunning(opts, deps = {}) {
|
|
|
47304
47304
|
}
|
|
47305
47305
|
var PATH_EXTS = process.platform === "win32" ? [".cmd", ".bat", ".exe", ""] : [""];
|
|
47306
47306
|
|
|
47307
|
+
// src/loop/tag-echo-filter.ts
|
|
47308
|
+
function buildAcplikeName() {
|
|
47309
|
+
const cores = ["acp", "apc", "cap", "cpa", "pac", "pca"];
|
|
47310
|
+
const names = new Set(cores);
|
|
47311
|
+
for (const c of cores) {
|
|
47312
|
+
for (const ch of ["a", "c", "p", "i"]) {
|
|
47313
|
+
for (let pos = 0; pos <= c.length; pos++) names.add(c.slice(0, pos) + ch + c.slice(pos));
|
|
47314
|
+
}
|
|
47315
|
+
}
|
|
47316
|
+
return [...names].sort((a, b2) => b2.length - a.length).join("|");
|
|
47317
|
+
}
|
|
47318
|
+
var ACP_NAME_ALT = `(?:${buildAcplikeName()})`;
|
|
47319
|
+
var NAME = ACP_NAME_ALT;
|
|
47320
|
+
var PAIRED = new RegExp("<" + NAME + "\\s[^<>]{0,256}>([^<>]{0,64})<\\/" + NAME + ">");
|
|
47321
|
+
var LONE_OPEN = new RegExp("<" + NAME + "(?:\\s[^<>]{0,256})?>");
|
|
47322
|
+
var LONE_CLOSE = new RegExp("<\\/" + NAME + "(?=[\\s>])[^<>]{0,32}>");
|
|
47323
|
+
var PARTIAL_TAIL = new RegExp("(<" + NAME + "\\s[^<>]*|<\\/" + NAME + "(?:\\s[^<>]{0,32})?|<\\/?[acip]*)$");
|
|
47324
|
+
var TRUNC_OPEN = new RegExp("<" + NAME + "\\s[^<>]*$");
|
|
47325
|
+
var TRUNC_CLOSE = new RegExp("<\\/" + NAME + "(?:\\s[^<>]{0,32})?$");
|
|
47326
|
+
var BROKEN_ATTRS = new RegExp("<" + NAME + "\\s[^<>]{0,512}(?=<)");
|
|
47327
|
+
var DEFINITE_TAIL = new RegExp("^<" + NAME + "\\s|^<\\/" + NAME);
|
|
47328
|
+
var OPEN_WITH_ATTRS = new RegExp("^<" + NAME + "\\s");
|
|
47329
|
+
var CLOSE_NAME_ANCHORED = new RegExp("^" + NAME);
|
|
47330
|
+
var MARKER_HEAD = new RegExp("^\\p{So}(?:[ \\t])?\\[ACP\\]", "u");
|
|
47331
|
+
var MARKER_LINE = new RegExp("^\\p{So}(?:[ \\t])?\\[ACP\\][^\\n]*\\n?", "gmu");
|
|
47332
|
+
var RENDER_TAG_DETECT = new RegExp("<\\/?" + NAME + "(?=[\\s>])|\\\\u003c\\/?" + NAME + "(?=[\\s>\\\\])");
|
|
47333
|
+
|
|
47307
47334
|
// src/acp-panel.ts
|
|
47308
47335
|
var CACHE_REPORT_OPEN = "[acp-cache]";
|
|
47309
47336
|
var CACHE_REPORT_CLOSE = "[/acp-cache]";
|