billion-context 0.1.77 → 0.1.78-pr.360.325
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.js +10 -1
- package/dist/agent/omp.js.map +1 -1
- package/dist/agent/pi.js +10 -1
- package/dist/agent/pi.js.map +1 -1
- package/dist/index.js +94 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/agent/omp.js
CHANGED
|
@@ -267,7 +267,16 @@ function createBiliPlugin(agentOverride, opts) {
|
|
|
267
267
|
return;
|
|
268
268
|
}
|
|
269
269
|
const panel = typeof status.panel === "string" ? status.panel : void 0;
|
|
270
|
-
|
|
270
|
+
const text = panel ?? renderAcpStatus(status);
|
|
271
|
+
if (typeof pi.sendMessage === "function") {
|
|
272
|
+
try {
|
|
273
|
+
pi.sendMessage({ customType: "bili-acp-status", content: text, display: true });
|
|
274
|
+
return;
|
|
275
|
+
} catch (err) {
|
|
276
|
+
console.error(`bili-plugin(${agent}): sendMessage failed (${err instanceof Error ? err.message : String(err)}) \u2014 falling back to notify`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
notify(text, "info");
|
|
271
280
|
}
|
|
272
281
|
});
|
|
273
282
|
}
|
package/dist/agent/omp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/agent/shared.ts","../../src/agent/pi.ts","../../src/agent/omp.ts"],"sourcesContent":["// Shared thin-plugin core for agent-side extensions (\"内外呼应\", issue #1).\n// The agent plugin is a PURE PROTOCOL CLIENT: no acp-kernel import, no\n// compression logic. The proxy stays the single compression authority; the\n// plugin only (1) detects the proxy, (2) fetches the tool manifest (single\n// source of truth), (3) forwards tool executes, (4) reads status. Same\n// package as the proxy ⇒ same version ⇒ no kernel-skew bug class.\n\nexport type ManifestTool = {\n name: string;\n description?: string;\n inputSchema: unknown;\n};\n\nconst MANIFEST_TIMEOUT_MS = 5000;\nconst TOOL_TIMEOUT_MS = 60000;\nconst STATUS_TIMEOUT_MS = 5000;\n\n/** Detect the proxy from a provider baseUrl's `/bili/` zero-config prefix.\n * The real prefix embeds the full upstream URL (`/bili/https://…`), so the\n * check requires `bili` as the first path segment followed by an http(s)\n * URL — a plain `/foo/bili/` path segment is NOT a bili proxy.\n * Returns the proxy origin (scheme//host) the request will actually hit. */\nexport function proxyBaseFromUrl(baseUrl: string | undefined): string | undefined {\n if (!baseUrl) return undefined;\n try {\n const url = new URL(baseUrl);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n const segments = url.pathname.split(\"/\").filter((s) => s.length > 0);\n if (segments[0] !== \"bili\") return undefined;\n const rest = url.pathname.slice(url.pathname.indexOf(\"bili\") + \"bili\".length);\n if (!/^\\/https?:\\/\\//.test(rest)) return undefined;\n return `${url.protocol}//${url.host}`;\n } catch {\n return undefined;\n }\n}\n\n/** MITM transparent mode has no `/bili/` prefix; the proxy's launcher exports\n * BILLION_CONTEXT_PROXY. A stale value surfaces as a tool-forward error. */\nexport function proxyBaseFromEnv(): string | undefined {\n const raw = process.env.BILLION_CONTEXT_PROXY?.trim();\n if (!raw) return undefined;\n try {\n const url = new URL(raw);\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? `${url.protocol}//${url.host}` : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function detectProxyBase(baseUrl: string | undefined): string | undefined {\n if (process.env.BILLION_CONTEXT_PLUGIN === \"0\") return undefined;\n return proxyBaseFromUrl(baseUrl) ?? proxyBaseFromEnv();\n}\n\nasync function fetchJson(url: string, init: RequestInit | undefined, timeoutMs: number, externalSignal?: AbortSignal): Promise<{ ok: boolean; status: number; json: unknown }> {\n const ac = new AbortController();\n // An already-aborted external signal never fires its \"abort\" event, so\n // forward the state directly — otherwise only the timeout could stop\n // the request, turning an instant cancel into a timeout wait.\n if (externalSignal?.aborted) ac.abort();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n const onExternalAbort = () => ac.abort();\n externalSignal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n try {\n let res: Response;\n try {\n res = await fetch(url, { ...init, signal: ac.signal });\n } catch (err) {\n if (ac.signal.aborted && !externalSignal?.aborted) throw new Error(`timeout after ${timeoutMs}ms: ${url}`);\n throw err;\n }\n const text = await res.text();\n let json: unknown = undefined;\n try {\n json = JSON.parse(text);\n } catch {\n json = undefined;\n }\n return { ok: res.ok, status: res.status, json };\n } finally {\n clearTimeout(timer);\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n }\n}\n\nexport async function fetchManifest(proxyBase: string): Promise<ManifestTool[]> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, MANIFEST_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") throw new Error(`manifest fetch failed: ${status}`);\n const data = json as { tools?: { anthropic?: { name?: string; description?: string; input_schema?: unknown }[] } };\n const tools = (data.tools?.anthropic ?? []).filter((t): t is { name: string; description?: string; input_schema?: unknown } => typeof t.name === \"string\");\n if (tools.length === 0) throw new Error(\"manifest served no anthropic tools\");\n return tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.input_schema ?? { type: \"object\", properties: {} } }));\n}\n\nexport async function forwardTool(proxyBase: string, conversationId: string, tool: string, args: unknown, signal?: AbortSignal): Promise<string> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/tool`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId, tool, args: args ?? {} }),\n }, TOOL_TIMEOUT_MS, signal);\n const data = json as { ok?: boolean; result?: string; error?: string } | undefined;\n if (!ok || !data?.ok) {\n throw new Error(`bili proxy tool ${tool} failed (${status}): ${data?.error ?? \"unknown error\"}`);\n }\n return data.result ?? \"\";\n}\n\n/** Soft-fail by design: the status read is best-effort UI data; undefined\n * means \"no data\" whether the proxy is down or the session is unknown. */\nexport async function fetchStatus(proxyBase: string, conversationId: string): Promise<Record<string, unknown> | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=${encodeURIComponent(conversationId)}`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Wire-mode status read (dsh): those clients carry no per-conversation id\n * the proxy could bind, so ask for the most recently active session instead\n * (fallback=latest). Same soft-fail contract as fetchStatus. */\nexport async function fetchStatusLatest(proxyBase: string): Promise<Record<string, unknown> | undefined> {\n // conversationId must be non-empty (server rejects the empty string), but\n // any unknown id is fine: fallback=latest then resolves the most recently\n // active session.\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=dsh&fallback=latest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Liveness + version probe for status UIs: same loopback origin as the\n * status endpoint, so a 404 status + a live manifest means \"proxy up,\n * conversation not seen yet\" — an armed-but-idle state, not an error. */\nexport async function fetchProxyVersion(proxyBase: string): Promise<string | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n const version = (json as { version?: unknown }).version;\n return typeof version === \"string\" && version.length > 0 ? version : undefined;\n}\n","// Thin agent extension for pi and omp (\"内外呼应\", issue #1). Loaded by pi\n// via the package.json `pi` manifest (dist/agent/pi.js) or by omp via the\n// config.yml `extensions:` list (dist/agent/omp.js). pi and omp share the\n// ExtensionFactory API shape, so one factory serves both; types below are\n// minimal structural declarations — the bundled artifact imports NOTHING\n// from the host at runtime (the host duck-types us in).\n\nimport { detectProxyBase, fetchManifest, forwardTool, fetchStatus, fetchProxyVersion, type ManifestTool } from \"./shared.js\";\n\ntype Ctx = {\n sessionManager?: { getSessionId?: () => string } | undefined;\n model?: { contextWindow?: number; baseUrl?: string } | 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};\n\nfunction agentName(override: string | undefined): string {\n if (override) return override;\n return process.env.BILLION_CONTEXT_PLUGIN_AGENT === \"omp\" ? \"omp\" : \"pi\";\n}\n\nfunction proxyBaseForCtx(ctx: Ctx): string | undefined {\n return detectProxyBase(ctx.model?.baseUrl);\n}\n\nfunction sessionIdOf(ctx: Ctx): string | undefined {\n try {\n const sid = ctx.sessionManager?.getSessionId?.();\n return typeof sid === \"string\" ? sid : undefined;\n } catch {\n return undefined;\n }\n}\n\n// omp's chat-completions payloads carry NO conversation signal (no\n// prompt_cache_key / session / user, and no session header — verified by dump),\n// so the proxy's openai identity falls to a content fingerprint that never\n// matches the session id this plugin registered (the identity register is keyed\n// by the omp session uuid). The before_provider_request return value REPLACES\n// the whole outgoing payload (omp onPayload chain, verified in the omp 17.3.8\n// dist), so stamp prompt_cache_key with the omp session id: the proxy binds\n// pluginMode by that identity and /acp finds the session by it.\n// Chat shape = messages array, no responses `input`, no native prompt_cache_key.\n// max_tokens is NOT a discriminator: omp's openai-compat providers send it in\n// every chat-completions body (maxTokensField:\"max_tokens\") exactly like the\n// anthropic wire — excluding it meant the target shape was never stamped\n// (#268). The anthropic wire gets stamped too: the proxy records the mapping\n// from the body pck there as well and strips the field before forwarding to\n// the real Anthropic. pi is untouched (it stamps x-bili-plugin-conversation in\n// before_provider_headers, which outranks the body field).\nfunction stampPromptCacheKey(event: unknown, ctx: Ctx, agent: string): Record<string, unknown> | undefined {\n if (agent !== \"omp\") return undefined;\n const payload = (event as { payload?: unknown } | undefined)?.payload;\n if (payload === null || typeof payload !== \"object\" || Array.isArray(payload)) return undefined;\n const p = payload as Record<string, unknown>;\n if (!Array.isArray(p.messages)) return undefined;\n if (p.input !== undefined) return undefined;\n if (typeof p.prompt_cache_key === \"string\" && p.prompt_cache_key.trim().length > 0) return undefined;\n const sid = sessionIdOf(ctx);\n if (sid === undefined || sid.length === 0) return undefined;\n return { ...p, prompt_cache_key: sid };\n}\n\nfunction fmtTok(n: number): string {\n if (n < 1000) return String(n);\n if (n < 1_000_000) return `${(n / 1000).toFixed(1)}K`;\n return `${(n / 1_000_000).toFixed(2)}M`;\n}\n\nfunction renderAcpStatus(s: Record<string, unknown>): string {\n const num = (v: unknown): number | null => (typeof v === \"number\" && Number.isFinite(v) ? v : null);\n const contextTokens = num(s.contextTokens);\n const contextLimit = num(s.contextLimit);\n const inputTokens = num(s.inputTokens);\n const outputTokens = num(s.outputTokens);\n const cachedTokens = num(s.cachedTokens);\n const requests = num(s.requests);\n const blocks = Array.isArray(s.blocks) ? (s.blocks as Array<{ tier?: number; active?: boolean }>) : [];\n const activeBlocks = blocks.filter((b) => b.active === true).length;\n const lines: string[] = [\"📊 ACP status\"];\n if (contextTokens !== null) {\n const pct = contextLimit !== null && contextLimit > 0 ? ` (${((contextTokens / contextLimit) * 100).toFixed(1)}%)` : \"\";\n lines.push(` context: ${fmtTok(contextTokens)}${contextLimit !== null ? ` / ${fmtTok(contextLimit)}` : \"\"}${pct}`);\n }\n 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\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 if (typeof pi.registerCommand === \"function\") {\n pi.registerCommand(\"acp\", {\n description: \"Show ACP context-compression status for this session\",\n handler: async (_args, ctx) => {\n const notify = (message: string, type?: string): void => {\n try {\n ctx.ui?.notify?.(message, type);\n } catch {\n // host UI unavailable — the command is best-effort\n }\n };\n const proxyBase = detectProxyBase(ctx.model?.baseUrl);\n if (proxyBase === undefined) {\n notify(\"bili: no proxy detected (run via `bili <client>` or set a /bili/ baseURL)\", \"warning\");\n return;\n }\n const conversationId = sessionIdOf(ctx) ?? \"unknown\";\n let status: Record<string, unknown> | undefined;\n try {\n status = await fetchStatus(proxyBase, conversationId);\n } catch (err) {\n notify(`bili: status fetch failed: ${err instanceof Error ? err.message : String(err)}`, \"error\");\n return;\n }\n if (status === undefined) {\n // 404 from a live proxy = this conversation has sent no\n // model request yet (e.g. /acp right after startup).\n // Probe the manifest to confirm liveness + version and\n // show an armed/idle notice instead of a scary warning.\n let version: string | undefined;\n try {\n version = await fetchProxyVersion(proxyBase);\n } catch {\n version = undefined;\n }\n if (version !== undefined) {\n notify(\n `billion-context@${version} — proxy connected, compression armed. No model request in this conversation yet; send one, then run /acp again.`,\n \"info\",\n );\n } else {\n notify(\"bili: no ACP session yet (send a model request first, then run /acp)\", \"warning\");\n }\n return;\n }\n const panel = typeof status.panel === \"string\" ? status.panel : undefined;\n notify(panel ?? renderAcpStatus(status), \"info\");\n },\n });\n }\n pi.on(\"before_provider_headers\", (event, ctx) => {\n try {\n if (proxyBaseForCtx(ctx) === undefined) return;\n const headers = (event as unknown as { headers?: Record<string, string> }).headers;\n if (headers === undefined || typeof headers !== \"object\" || Array.isArray(headers)) return;\n // The x-bili-plugin marker tells the proxy \"the client owns the\n // ACP tools natively — skip wire-level injection\". Stamping it\n // before registerTools() finishes would send round 1 out with\n // NO ACP tools (the first provider request races the manifest\n // fetch). Claim ownership only once tools are registered;\n // until then the request rides the proxy's wire mode. A\n // permanently failing manifest fetch keeps us in wire mode —\n // a graceful fallback rather than a tool-less session.\n if (state.toolsReady === true) {\n const sid = sessionIdOf(ctx);\n if (sid !== undefined) headers[\"x-bili-plugin-conversation\"] = sid;\n headers[\"x-bili-plugin\"] = agent;\n const window = ctx.model?.contextWindow;\n if (typeof window === \"number\" && Number.isFinite(window) && window > 0) {\n headers[\"x-bili-plugin-context-window\"] = String(Math.floor(window));\n }\n }\n } catch (err) {\n console.error(`bili-plugin(${agent}): header stamp skipped (${err instanceof Error ? err.message : String(err)})`);\n }\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n });\n pi.on(\"before_provider_request\", (event, ctx) => {\n // omp emits this per model request (but never before_provider_headers);\n // it doubles as the retry driver when the session_start manifest\n // fetch raced the proxy startup. Cached by sid, throttled by retryAt.\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n return stampPromptCacheKey(event, ctx, agent);\n });\n pi.on(\"session_start\", (_event, ctx) => {\n state.sid = undefined;\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n });\n // omp fires session_compact on in-session native compaction (sid does\n // not rotate), so the proxy reuses stale state — notify it to archive\n // the now-unreachable blocks (#395). Fire-and-forget: a failed\n // notification must never break the agent's compaction.\n pi.on(\"session_compact\", (_event, ctx) => {\n const proxyBase = proxyBaseForCtx(ctx);\n if (proxyBase === undefined) return;\n const sid = sessionIdOf(ctx);\n if (sid === undefined || sid.length === 0) return;\n fetch(`${proxyBase}/__bili/plugin/compact`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId: sid }),\n signal: AbortSignal.timeout(5000),\n }).catch(() => {});\n });\n };\n}\n\nexport default createBiliPlugin();\n\nexport { fetchStatus };\n","import { createBiliPlugin } from \"./pi.js\";\n\nexport default createBiliPlugin(\"omp\");\n"],"mappings":";;;;AAaA,IAAM,sBAAsB;AAC5B,IAAM,kBAAkB;AACxB,IAAM,oBAAoB;AAOnB,SAAS,iBAAiB,SAAiD;AAC9E,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI;AACA,UAAM,MAAM,IAAI,IAAI,OAAO;AAC3B,QAAI,IAAI,aAAa,WAAW,IAAI,aAAa,SAAU,QAAO;AAClE,UAAM,WAAW,IAAI,SAAS,MAAM,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AACnE,QAAI,SAAS,CAAC,MAAM,OAAQ,QAAO;AACnC,UAAM,OAAO,IAAI,SAAS,MAAM,IAAI,SAAS,QAAQ,MAAM,IAAI,OAAO,MAAM;AAC5E,QAAI,CAAC,iBAAiB,KAAK,IAAI,EAAG,QAAO;AACzC,WAAO,GAAG,IAAI,QAAQ,KAAK,IAAI,IAAI;AAAA,EACvC,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAIO,SAAS,mBAAuC;AACnD,QAAM,MAAM,QAAQ,IAAI,uBAAuB,KAAK;AACpD,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AACA,UAAM,MAAM,IAAI,IAAI,GAAG;AACvB,WAAO,IAAI,aAAa,WAAW,IAAI,aAAa,WAAW,GAAG,IAAI,QAAQ,KAAK,IAAI,IAAI,KAAK;AAAA,EACpG,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEO,SAAS,gBAAgB,SAAiD;AAC7E,MAAI,QAAQ,IAAI,2BAA2B,IAAK,QAAO;AACvD,SAAO,iBAAiB,OAAO,KAAK,iBAAiB;AACzD;AAEA,eAAe,UAAU,KAAa,MAA+B,WAAmB,gBAAuF;AAC3K,QAAM,KAAK,IAAI,gBAAgB;AAI/B,MAAI,gBAAgB,QAAS,IAAG,MAAM;AACtC,QAAM,QAAQ,WAAW,MAAM,GAAG,MAAM,GAAG,SAAS;AACpD,QAAM,kBAAkB,MAAM,GAAG,MAAM;AACvC,kBAAgB,iBAAiB,SAAS,iBAAiB,EAAE,MAAM,KAAK,CAAC;AACzE,MAAI;AACA,QAAI;AACJ,QAAI;AACA,YAAM,MAAM,MAAM,KAAK,EAAE,GAAG,MAAM,QAAQ,GAAG,OAAO,CAAC;AAAA,IACzD,SAAS,KAAK;AACV,UAAI,GAAG,OAAO,WAAW,CAAC,gBAAgB,QAAS,OAAM,IAAI,MAAM,iBAAiB,SAAS,OAAO,GAAG,EAAE;AACzG,YAAM;AAAA,IACV;AACA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,OAAgB;AACpB,QAAI;AACA,aAAO,KAAK,MAAM,IAAI;AAAA,IAC1B,QAAQ;AACJ,aAAO;AAAA,IACX;AACA,WAAO,EAAE,IAAI,IAAI,IAAI,QAAQ,IAAI,QAAQ,KAAK;AAAA,EAClD,UAAE;AACE,iBAAa,KAAK;AAClB,oBAAgB,oBAAoB,SAAS,eAAe;AAAA,EAChE;AACJ;AAEA,eAAsB,cAAc,WAA4C;AAC5E,QAAM,EAAE,IAAI,QAAQ,KAAK,IAAI,MAAM,UAAU,GAAG,SAAS,2BAA2B,QAAW,mBAAmB;AAClH,MAAI,CAAC,MAAM,CAAC,QAAQ,OAAO,SAAS,SAAU,OAAM,IAAI,MAAM,0BAA0B,MAAM,EAAE;AAChG,QAAM,OAAO;AACb,QAAM,SAAS,KAAK,OAAO,aAAa,CAAC,GAAG,OAAO,CAAC,MAA2E,OAAO,EAAE,SAAS,QAAQ;AACzJ,MAAI,MAAM,WAAW,EAAG,OAAM,IAAI,MAAM,oCAAoC;AAC5E,SAAO,MAAM,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,EAAE,aAAa,aAAa,EAAE,gBAAgB,EAAE,MAAM,UAAU,YAAY,CAAC,EAAE,EAAE,EAAE;AAC7I;AAEA,eAAsB,YAAY,WAAmB,gBAAwB,MAAc,MAAe,QAAuC;AAC7I,QAAM,EAAE,IAAI,QAAQ,KAAK,IAAI,MAAM,UAAU,GAAG,SAAS,uBAAuB;AAAA,IAC5E,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,EAAE,gBAAgB,MAAM,MAAM,QAAQ,CAAC,EAAE,CAAC;AAAA,EACnE,GAAG,iBAAiB,MAAM;AAC1B,QAAM,OAAO;AACb,MAAI,CAAC,MAAM,CAAC,MAAM,IAAI;AAClB,UAAM,IAAI,MAAM,mBAAmB,IAAI,YAAY,MAAM,MAAM,MAAM,SAAS,eAAe,EAAE;AAAA,EACnG;AACA,SAAO,KAAK,UAAU;AAC1B;AAIA,eAAsB,YAAY,WAAmB,gBAAsE;AACvH,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,UAAU,GAAG,SAAS,wCAAwC,mBAAmB,cAAc,CAAC,IAAI,QAAW,iBAAiB;AAC3J,MAAI,CAAC,MAAM,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AACrD,SAAO;AACX;AAiBA,eAAsB,kBAAkB,WAAgD;AACpF,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,UAAU,GAAG,SAAS,2BAA2B,QAAW,iBAAiB;AACxG,MAAI,CAAC,MAAM,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AACrD,QAAM,UAAW,KAA+B;AAChD,SAAO,OAAO,YAAY,YAAY,QAAQ,SAAS,IAAI,UAAU;AACzE;;;AC9FA,SAAS,UAAU,UAAsC;AACrD,MAAI,SAAU,QAAO;AACrB,SAAO,QAAQ,IAAI,iCAAiC,QAAQ,QAAQ;AACxE;AAEA,SAAS,gBAAgB,KAA8B;AACnD,SAAO,gBAAgB,IAAI,OAAO,OAAO;AAC7C;AAEA,SAAS,YAAY,KAA8B;AAC/C,MAAI;AACA,UAAM,MAAM,IAAI,gBAAgB,eAAe;AAC/C,WAAO,OAAO,QAAQ,WAAW,MAAM;AAAA,EAC3C,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAkBA,SAAS,oBAAoB,OAAgB,KAAU,OAAoD;AACvG,MAAI,UAAU,MAAO,QAAO;AAC5B,QAAM,UAAW,OAA6C;AAC9D,MAAI,YAAY,QAAQ,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,EAAG,QAAO;AACtF,QAAM,IAAI;AACV,MAAI,CAAC,MAAM,QAAQ,EAAE,QAAQ,EAAG,QAAO;AACvC,MAAI,EAAE,UAAU,OAAW,QAAO;AAClC,MAAI,OAAO,EAAE,qBAAqB,YAAY,EAAE,iBAAiB,KAAK,EAAE,SAAS,EAAG,QAAO;AAC3F,QAAM,MAAM,YAAY,GAAG;AAC3B,MAAI,QAAQ,UAAa,IAAI,WAAW,EAAG,QAAO;AAClD,SAAO,EAAE,GAAG,GAAG,kBAAkB,IAAI;AACzC;AAEA,SAAS,OAAO,GAAmB;AAC/B,MAAI,IAAI,IAAM,QAAO,OAAO,CAAC;AAC7B,MAAI,IAAI,IAAW,QAAO,IAAI,IAAI,KAAM,QAAQ,CAAC,CAAC;AAClD,SAAO,IAAI,IAAI,KAAW,QAAQ,CAAC,CAAC;AACxC;AAEA,SAAS,gBAAgB,GAAoC;AACzD,QAAM,MAAM,CAAC,MAA+B,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,IAAI,IAAI;AAC9F,QAAM,gBAAgB,IAAI,EAAE,aAAa;AACzC,QAAM,eAAe,IAAI,EAAE,YAAY;AACvC,QAAM,cAAc,IAAI,EAAE,WAAW;AACrC,QAAM,eAAe,IAAI,EAAE,YAAY;AACvC,QAAM,eAAe,IAAI,EAAE,YAAY;AACvC,QAAM,WAAW,IAAI,EAAE,QAAQ;AAC/B,QAAM,SAAS,MAAM,QAAQ,EAAE,MAAM,IAAK,EAAE,SAAwD,CAAC;AACrG,QAAM,eAAe,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,IAAI,EAAE;AAC7D,QAAM,QAAkB,CAAC,sBAAe;AACxC,MAAI,kBAAkB,MAAM;AACxB,UAAM,MAAM,iBAAiB,QAAQ,eAAe,IAAI,MAAO,gBAAgB,eAAgB,KAAK,QAAQ,CAAC,CAAC,OAAO;AACrH,UAAM,KAAK,cAAc,OAAO,aAAa,CAAC,GAAG,iBAAiB,OAAO,MAAM,OAAO,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,EAAE;AAAA,EACtH;AACA,MAAI,gBAAgB,QAAQ,iBAAiB,QAAQ,iBAAiB,MAAM;AACxE,UAAM,KAAK,oBAAoB,OAAO,eAAe,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC,EAAE;AAAA,EAC3H;AACA,MAAI,aAAa,KAAM,OAAM,KAAK,eAAe,QAAQ,EAAE;AAC3D,MAAI,OAAO,SAAS,EAAG,OAAM,KAAK,aAAa,OAAO,MAAM,KAAK,YAAY,UAAU;AACvF,SAAO,MAAM,KAAK,IAAI;AAC1B;AAEA,SAAS,eAAe,WAAmB,MAAoB,OAA+B;AAC1F,SAAO;AAAA,IACH,MAAM,KAAK;AAAA,IACX,aAAa,KAAK;AAAA,IAClB,YAAY,KAAK;AAAA,IACjB,UAAU;AAAA,IACV,SAAS,OAAO,aAAa,QAAQ,QAAQ,WAAW,QAAQ;AAC5D,YAAM,iBAAiB,YAAY,GAAG,KAAK;AAC3C,UAAI;AACA,cAAM,SAAS,MAAM,YAAY,WAAW,gBAAgB,KAAK,MAAM,QAAQ,MAAM;AACrF,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,CAAC,EAAE;AAAA,MACvD,SAAS,KAAK;AACV,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,oBAAoB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,KAAK;AAAA,MACtI;AAAA,IACJ;AAAA,EACJ;AACJ;AAEA,IAAM,oBAAoB;AAQ1B,eAAe,qBAAqB,WAAmB,gBAAwB,OAA8B;AACzG,QAAM,MAAM,MAAM,MAAM,GAAG,SAAS,2BAA2B;AAAA,IAC3D,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,EAAE,gBAAgB,OAAO,UAAU,KAAK,CAAC;AAAA,IAC9D,QAAQ,YAAY,QAAQ,GAAI;AAAA,EACpC,CAAC;AACD,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,iBAAiB,IAAI,MAAM,EAAE;AAC9D;AAEA,eAAe,cAAc,IAAkB,KAAU,OAAsB,OAA8B;AACzG,QAAM,YAAY,gBAAgB,GAAG;AACrC,MAAI,cAAc,OAAW;AAI7B,QAAM,MAAM,YAAY,GAAG,KAAK;AAChC,MAAI,QAAQ,MAAM,IAAK;AACvB,MAAI,MAAM,YAAY,OAAW,QAAO,MAAM;AAC9C,MAAI,MAAM,YAAY,UAAa,KAAK,IAAI,IAAI,MAAM,QAAS;AAC/D,QAAM,OAAO,MAAM;AACnB,QAAM,WAAW,YAAY;AACzB,QAAI;AACJ,QAAI;AACA,cAAQ,MAAM,cAAc,SAAS;AAAA,IACzC,SAAS,KAAK;AACV,YAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,cAAQ,MAAM,eAAe,KAAK,6BAA6B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,uBAAkB,OAAO,GAAI,GAAG;AAC/I;AAAA,IACJ;AACA,QAAI;AAIA,UAAI,MAAM,aAAa,KAAK;AACxB,mBAAW,KAAK,MAAO,IAAG,aAAa,eAAe,WAAW,GAAG,KAAK,CAAC;AAC1E,cAAM,WAAW;AAAA,MACrB;AACA,YAAM,aAAa;AACnB,YAAM,UAAU;AAChB,UAAI,UAAU,SAAS,QAAQ,MAAM,MAAM,eAAe,KAAK;AAC3D,YAAI;AACA,gBAAM,qBAAqB,WAAW,KAAK,KAAK;AAChD,gBAAM,aAAa;AAAA,QACvB,SAAS,KAAK;AAMV,gBAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,kBAAQ,MAAM,eAAe,KAAK,gCAAgC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,wBAAmB,OAAO,GAAI,GAAG;AACnJ;AAAA,QACJ;AAAA,MACJ;AACA,YAAM,MAAM;AAAA,IAChB,SAAS,KAAK;AACV,YAAM,MAAM;AACZ,YAAM,WAAW;AACjB,YAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,cAAQ,MAAM,eAAe,KAAK,kCAAkC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,wBAAmB,OAAO,GAAI,GAAG;AAAA,IACzJ;AAAA,EACJ,GAAG;AACH,MAAI;AACA,UAAM,MAAM;AAAA,EAChB,UAAE;AACE,UAAM,UAAU;AAAA,EACpB;AACJ;AAEO,SAAS,iBAAiB,eAAwB,MAAiE;AACtH,SAAO,SAAS,WAAW,IAAwB;AAC/C,UAAM,QAAQ,UAAU,aAAa;AACrC,UAAM,QAAuB,EAAE,iBAAiB,MAAM,mBAAmB,kBAAkB;AAC3F,QAAI,OAAO,GAAG,oBAAoB,YAAY;AAC1C,SAAG,gBAAgB,OAAO;AAAA,QACtB,aAAa;AAAA,QACb,SAAS,OAAO,OAAO,QAAQ;AAC3B,gBAAM,SAAS,CAAC,SAAiB,SAAwB;AACrD,gBAAI;AACA,kBAAI,IAAI,SAAS,SAAS,IAAI;AAAA,YAClC,QAAQ;AAAA,YAER;AAAA,UACJ;AACA,gBAAM,YAAY,gBAAgB,IAAI,OAAO,OAAO;AACpD,cAAI,cAAc,QAAW;AACzB,mBAAO,6EAA6E,SAAS;AAC7F;AAAA,UACJ;AACA,gBAAM,iBAAiB,YAAY,GAAG,KAAK;AAC3C,cAAI;AACJ,cAAI;AACA,qBAAS,MAAM,YAAY,WAAW,cAAc;AAAA,UACxD,SAAS,KAAK;AACV,mBAAO,8BAA8B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,IAAI,OAAO;AAChG;AAAA,UACJ;AACA,cAAI,WAAW,QAAW;AAKtB,gBAAI;AACJ,gBAAI;AACA,wBAAU,MAAM,kBAAkB,SAAS;AAAA,YAC/C,QAAQ;AACJ,wBAAU;AAAA,YACd;AACA,gBAAI,YAAY,QAAW;AACvB;AAAA,gBACI,mBAAmB,OAAO;AAAA,gBAC1B;AAAA,cACJ;AAAA,YACJ,OAAO;AACH,qBAAO,wEAAwE,SAAS;AAAA,YAC5F;AACA;AAAA,UACJ;AACA,gBAAM,QAAQ,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAChE,iBAAO,SAAS,gBAAgB,MAAM,GAAG,MAAM;AAAA,QACnD;AAAA,MACJ,CAAC;AAAA,IACL;AACA,OAAG,GAAG,2BAA2B,CAAC,OAAO,QAAQ;AAC7C,UAAI;AACA,YAAI,gBAAgB,GAAG,MAAM,OAAW;AACxC,cAAM,UAAW,MAA0D;AAC3E,YAAI,YAAY,UAAa,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,EAAG;AASpF,YAAI,MAAM,eAAe,MAAM;AAC3B,gBAAM,MAAM,YAAY,GAAG;AAC3B,cAAI,QAAQ,OAAW,SAAQ,4BAA4B,IAAI;AAC/D,kBAAQ,eAAe,IAAI;AAC3B,gBAAM,SAAS,IAAI,OAAO;AAC1B,cAAI,OAAO,WAAW,YAAY,OAAO,SAAS,MAAM,KAAK,SAAS,GAAG;AACrE,oBAAQ,8BAA8B,IAAI,OAAO,KAAK,MAAM,MAAM,CAAC;AAAA,UACvE;AAAA,QACJ;AAAA,MACJ,SAAS,KAAK;AACV,gBAAQ,MAAM,eAAe,KAAK,4BAA4B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,GAAG;AAAA,MACrH;AACA,WAAK,cAAc,IAAI,KAAK,OAAO,KAAK,EAAE,MAAM,CAAC,QAAiB,QAAQ,MAAM,eAAe,KAAK,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE,CAAC;AAAA,IACjK,CAAC;AACD,OAAG,GAAG,2BAA2B,CAAC,OAAO,QAAQ;AAI7C,WAAK,cAAc,IAAI,KAAK,OAAO,KAAK,EAAE,MAAM,CAAC,QAAiB,QAAQ,MAAM,eAAe,KAAK,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE,CAAC;AAC7J,aAAO,oBAAoB,OAAO,KAAK,KAAK;AAAA,IAChD,CAAC;AACD,OAAG,GAAG,iBAAiB,CAAC,QAAQ,QAAQ;AACpC,YAAM,MAAM;AACZ,WAAK,cAAc,IAAI,KAAK,OAAO,KAAK,EAAE,MAAM,CAAC,QAAiB,QAAQ,MAAM,eAAe,KAAK,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE,CAAC;AAAA,IACjK,CAAC;AAKD,OAAG,GAAG,mBAAmB,CAAC,QAAQ,QAAQ;AACtC,YAAM,YAAY,gBAAgB,GAAG;AACrC,UAAI,cAAc,OAAW;AAC7B,YAAM,MAAM,YAAY,GAAG;AAC3B,UAAI,QAAQ,UAAa,IAAI,WAAW,EAAG;AAC3C,YAAM,GAAG,SAAS,0BAA0B;AAAA,QACxC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,gBAAgB,IAAI,CAAC;AAAA,QAC5C,QAAQ,YAAY,QAAQ,GAAI;AAAA,MACpC,CAAC,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACrB,CAAC;AAAA,EACL;AACJ;AAEA,IAAO,aAAQ,iBAAiB;;;ACnUhC,IAAO,cAAQ,iBAAiB,KAAK;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/agent/shared.ts","../../src/agent/pi.ts","../../src/agent/omp.ts"],"sourcesContent":["// Shared thin-plugin core for agent-side extensions (\"内外呼应\", issue #1).\n// The agent plugin is a PURE PROTOCOL CLIENT: no acp-kernel import, no\n// compression logic. The proxy stays the single compression authority; the\n// plugin only (1) detects the proxy, (2) fetches the tool manifest (single\n// source of truth), (3) forwards tool executes, (4) reads status. Same\n// package as the proxy ⇒ same version ⇒ no kernel-skew bug class.\n\nexport type ManifestTool = {\n name: string;\n description?: string;\n inputSchema: unknown;\n};\n\nconst MANIFEST_TIMEOUT_MS = 5000;\nconst TOOL_TIMEOUT_MS = 60000;\nconst STATUS_TIMEOUT_MS = 5000;\n\n/** Detect the proxy from a provider baseUrl's `/bili/` zero-config prefix.\n * The real prefix embeds the full upstream URL (`/bili/https://…`), so the\n * check requires `bili` as the first path segment followed by an http(s)\n * URL — a plain `/foo/bili/` path segment is NOT a bili proxy.\n * Returns the proxy origin (scheme//host) the request will actually hit. */\nexport function proxyBaseFromUrl(baseUrl: string | undefined): string | undefined {\n if (!baseUrl) return undefined;\n try {\n const url = new URL(baseUrl);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n const segments = url.pathname.split(\"/\").filter((s) => s.length > 0);\n if (segments[0] !== \"bili\") return undefined;\n const rest = url.pathname.slice(url.pathname.indexOf(\"bili\") + \"bili\".length);\n if (!/^\\/https?:\\/\\//.test(rest)) return undefined;\n return `${url.protocol}//${url.host}`;\n } catch {\n return undefined;\n }\n}\n\n/** MITM transparent mode has no `/bili/` prefix; the proxy's launcher exports\n * BILLION_CONTEXT_PROXY. A stale value surfaces as a tool-forward error. */\nexport function proxyBaseFromEnv(): string | undefined {\n const raw = process.env.BILLION_CONTEXT_PROXY?.trim();\n if (!raw) return undefined;\n try {\n const url = new URL(raw);\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? `${url.protocol}//${url.host}` : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function detectProxyBase(baseUrl: string | undefined): string | undefined {\n if (process.env.BILLION_CONTEXT_PLUGIN === \"0\") return undefined;\n return proxyBaseFromUrl(baseUrl) ?? proxyBaseFromEnv();\n}\n\nasync function fetchJson(url: string, init: RequestInit | undefined, timeoutMs: number, externalSignal?: AbortSignal): Promise<{ ok: boolean; status: number; json: unknown }> {\n const ac = new AbortController();\n // An already-aborted external signal never fires its \"abort\" event, so\n // forward the state directly — otherwise only the timeout could stop\n // the request, turning an instant cancel into a timeout wait.\n if (externalSignal?.aborted) ac.abort();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n const onExternalAbort = () => ac.abort();\n externalSignal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n try {\n let res: Response;\n try {\n res = await fetch(url, { ...init, signal: ac.signal });\n } catch (err) {\n if (ac.signal.aborted && !externalSignal?.aborted) throw new Error(`timeout after ${timeoutMs}ms: ${url}`);\n throw err;\n }\n const text = await res.text();\n let json: unknown = undefined;\n try {\n json = JSON.parse(text);\n } catch {\n json = undefined;\n }\n return { ok: res.ok, status: res.status, json };\n } finally {\n clearTimeout(timer);\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n }\n}\n\nexport async function fetchManifest(proxyBase: string): Promise<ManifestTool[]> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, MANIFEST_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") throw new Error(`manifest fetch failed: ${status}`);\n const data = json as { tools?: { anthropic?: { name?: string; description?: string; input_schema?: unknown }[] } };\n const tools = (data.tools?.anthropic ?? []).filter((t): t is { name: string; description?: string; input_schema?: unknown } => typeof t.name === \"string\");\n if (tools.length === 0) throw new Error(\"manifest served no anthropic tools\");\n return tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.input_schema ?? { type: \"object\", properties: {} } }));\n}\n\nexport async function forwardTool(proxyBase: string, conversationId: string, tool: string, args: unknown, signal?: AbortSignal): Promise<string> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/tool`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId, tool, args: args ?? {} }),\n }, TOOL_TIMEOUT_MS, signal);\n const data = json as { ok?: boolean; result?: string; error?: string } | undefined;\n if (!ok || !data?.ok) {\n throw new Error(`bili proxy tool ${tool} failed (${status}): ${data?.error ?? \"unknown error\"}`);\n }\n return data.result ?? \"\";\n}\n\n/** Soft-fail by design: the status read is best-effort UI data; undefined\n * means \"no data\" whether the proxy is down or the session is unknown. */\nexport async function fetchStatus(proxyBase: string, conversationId: string): Promise<Record<string, unknown> | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=${encodeURIComponent(conversationId)}`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Wire-mode status read (dsh): those clients carry no per-conversation id\n * the proxy could bind, so ask for the most recently active session instead\n * (fallback=latest). Same soft-fail contract as fetchStatus. */\nexport async function fetchStatusLatest(proxyBase: string): Promise<Record<string, unknown> | undefined> {\n // conversationId must be non-empty (server rejects the empty string), but\n // any unknown id is fine: fallback=latest then resolves the most recently\n // active session.\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=dsh&fallback=latest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Liveness + version probe for status UIs: same loopback origin as the\n * status endpoint, so a 404 status + a live manifest means \"proxy up,\n * conversation not seen yet\" — an armed-but-idle state, not an error. */\nexport async function fetchProxyVersion(proxyBase: string): Promise<string | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n const version = (json as { version?: unknown }).version;\n return typeof version === \"string\" && version.length > 0 ? version : undefined;\n}\n","// Thin agent extension for pi and omp (\"内外呼应\", issue #1). Loaded by pi\n// via the package.json `pi` manifest (dist/agent/pi.js) or by omp via the\n// config.yml `extensions:` list (dist/agent/omp.js). pi and omp share the\n// ExtensionFactory API shape, so one factory serves both; types below are\n// minimal structural declarations — the bundled artifact imports NOTHING\n// from the host at runtime (the host duck-types us in).\n\nimport { detectProxyBase, fetchManifest, forwardTool, fetchStatus, fetchProxyVersion, type ManifestTool } from \"./shared.js\";\n\ntype Ctx = {\n sessionManager?: { getSessionId?: () => string } | undefined;\n model?: { contextWindow?: number; baseUrl?: string } | 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 // Persistent transcript output (rendered by TUI and web hosts like\n // pi-web); notify() is a transient toast — only the fallback for hosts\n // without sendMessage (issue #359).\n sendMessage?: (message: { customType: string; content: string; display: boolean }) => void;\n};\n\nfunction agentName(override: string | undefined): string {\n if (override) return override;\n return process.env.BILLION_CONTEXT_PLUGIN_AGENT === \"omp\" ? \"omp\" : \"pi\";\n}\n\nfunction proxyBaseForCtx(ctx: Ctx): string | undefined {\n return detectProxyBase(ctx.model?.baseUrl);\n}\n\nfunction sessionIdOf(ctx: Ctx): string | undefined {\n try {\n const sid = ctx.sessionManager?.getSessionId?.();\n return typeof sid === \"string\" ? sid : undefined;\n } catch {\n return undefined;\n }\n}\n\n// omp's chat-completions payloads carry NO conversation signal (no\n// prompt_cache_key / session / user, and no session header — verified by dump),\n// so the proxy's openai identity falls to a content fingerprint that never\n// matches the session id this plugin registered (the identity register is keyed\n// by the omp session uuid). The before_provider_request return value REPLACES\n// the whole outgoing payload (omp onPayload chain, verified in the omp 17.3.8\n// dist), so stamp prompt_cache_key with the omp session id: the proxy binds\n// pluginMode by that identity and /acp finds the session by it.\n// Chat shape = messages array, no responses `input`, no native prompt_cache_key.\n// max_tokens is NOT a discriminator: omp's openai-compat providers send it in\n// every chat-completions body (maxTokensField:\"max_tokens\") exactly like the\n// anthropic wire — excluding it meant the target shape was never stamped\n// (#268). The anthropic wire gets stamped too: the proxy records the mapping\n// from the body pck there as well and strips the field before forwarding to\n// the real Anthropic. pi is untouched (it stamps x-bili-plugin-conversation in\n// before_provider_headers, which outranks the body field).\nfunction stampPromptCacheKey(event: unknown, ctx: Ctx, agent: string): Record<string, unknown> | undefined {\n if (agent !== \"omp\") return undefined;\n const payload = (event as { payload?: unknown } | undefined)?.payload;\n if (payload === null || typeof payload !== \"object\" || Array.isArray(payload)) return undefined;\n const p = payload as Record<string, unknown>;\n if (!Array.isArray(p.messages)) return undefined;\n if (p.input !== undefined) return undefined;\n if (typeof p.prompt_cache_key === \"string\" && p.prompt_cache_key.trim().length > 0) return undefined;\n const sid = sessionIdOf(ctx);\n if (sid === undefined || sid.length === 0) return undefined;\n return { ...p, prompt_cache_key: sid };\n}\n\nfunction fmtTok(n: number): string {\n if (n < 1000) return String(n);\n if (n < 1_000_000) return `${(n / 1000).toFixed(1)}K`;\n return `${(n / 1_000_000).toFixed(2)}M`;\n}\n\nfunction renderAcpStatus(s: Record<string, unknown>): string {\n const num = (v: unknown): number | null => (typeof v === \"number\" && Number.isFinite(v) ? v : null);\n const contextTokens = num(s.contextTokens);\n const contextLimit = num(s.contextLimit);\n const inputTokens = num(s.inputTokens);\n const outputTokens = num(s.outputTokens);\n const cachedTokens = num(s.cachedTokens);\n const requests = num(s.requests);\n const blocks = Array.isArray(s.blocks) ? (s.blocks as Array<{ tier?: number; active?: boolean }>) : [];\n const activeBlocks = blocks.filter((b) => b.active === true).length;\n const lines: string[] = [\"📊 ACP status\"];\n if (contextTokens !== null) {\n const pct = contextLimit !== null && contextLimit > 0 ? ` (${((contextTokens / contextLimit) * 100).toFixed(1)}%)` : \"\";\n lines.push(` context: ${fmtTok(contextTokens)}${contextLimit !== null ? ` / ${fmtTok(contextLimit)}` : \"\"}${pct}`);\n }\n 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\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 if (typeof pi.registerCommand === \"function\") {\n pi.registerCommand(\"acp\", {\n description: \"Show ACP context-compression status for this session\",\n handler: async (_args, ctx) => {\n const notify = (message: string, type?: string): void => {\n try {\n ctx.ui?.notify?.(message, type);\n } catch {\n // host UI unavailable — the command is best-effort\n }\n };\n const proxyBase = detectProxyBase(ctx.model?.baseUrl);\n if (proxyBase === undefined) {\n notify(\"bili: no proxy detected (run via `bili <client>` or set a /bili/ baseURL)\", \"warning\");\n return;\n }\n const conversationId = sessionIdOf(ctx) ?? \"unknown\";\n let status: Record<string, unknown> | undefined;\n try {\n status = await fetchStatus(proxyBase, conversationId);\n } catch (err) {\n notify(`bili: status fetch failed: ${err instanceof Error ? err.message : String(err)}`, \"error\");\n return;\n }\n if (status === undefined) {\n // 404 from a live proxy = this conversation has sent no\n // model request yet (e.g. /acp right after startup).\n // Probe the manifest to confirm liveness + version and\n // show an armed/idle notice instead of a scary warning.\n let version: string | undefined;\n try {\n version = await fetchProxyVersion(proxyBase);\n } catch {\n version = undefined;\n }\n if (version !== undefined) {\n notify(\n `billion-context@${version} — proxy connected, compression armed. No model request in this conversation yet; send one, then run /acp again.`,\n \"info\",\n );\n } else {\n notify(\"bili: no ACP session yet (send a model request first, then run /acp)\", \"warning\");\n }\n return;\n }\n const panel = typeof status.panel === \"string\" ? status.panel : undefined;\n const text = panel ?? renderAcpStatus(status);\n // Persistent transcript output (TUI + web hosts like pi-web).\n // The proxy strips this message from the model context by\n // content signature (src/acp-panel.ts), so it never reaches\n // the LLM; notify() is the fallback for hosts without\n // sendMessage (older pi).\n if (typeof pi.sendMessage === \"function\") {\n try {\n pi.sendMessage({ customType: \"bili-acp-status\", content: text, display: true });\n return;\n } catch (err) {\n console.error(`bili-plugin(${agent}): sendMessage failed (${err instanceof Error ? err.message : String(err)}) — falling back to notify`);\n }\n }\n notify(text, \"info\");\n },\n });\n }\n pi.on(\"before_provider_headers\", (event, ctx) => {\n try {\n if (proxyBaseForCtx(ctx) === undefined) return;\n const headers = (event as unknown as { headers?: Record<string, string> }).headers;\n if (headers === undefined || typeof headers !== \"object\" || Array.isArray(headers)) return;\n // The x-bili-plugin marker tells the proxy \"the client owns the\n // ACP tools natively — skip wire-level injection\". Stamping it\n // before registerTools() finishes would send round 1 out with\n // NO ACP tools (the first provider request races the manifest\n // fetch). Claim ownership only once tools are registered;\n // until then the request rides the proxy's wire mode. A\n // permanently failing manifest fetch keeps us in wire mode —\n // a graceful fallback rather than a tool-less session.\n if (state.toolsReady === true) {\n const sid = sessionIdOf(ctx);\n if (sid !== undefined) headers[\"x-bili-plugin-conversation\"] = sid;\n headers[\"x-bili-plugin\"] = agent;\n const window = ctx.model?.contextWindow;\n if (typeof window === \"number\" && Number.isFinite(window) && window > 0) {\n headers[\"x-bili-plugin-context-window\"] = String(Math.floor(window));\n }\n }\n } catch (err) {\n console.error(`bili-plugin(${agent}): header stamp skipped (${err instanceof Error ? err.message : String(err)})`);\n }\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n });\n pi.on(\"before_provider_request\", (event, ctx) => {\n // omp emits this per model request (but never before_provider_headers);\n // it doubles as the retry driver when the session_start manifest\n // fetch raced the proxy startup. Cached by sid, throttled by retryAt.\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n return stampPromptCacheKey(event, ctx, agent);\n });\n pi.on(\"session_start\", (_event, ctx) => {\n state.sid = undefined;\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n });\n // omp fires session_compact on in-session native compaction (sid does\n // not rotate), so the proxy reuses stale state — notify it to archive\n // the now-unreachable blocks (#395). Fire-and-forget: a failed\n // notification must never break the agent's compaction.\n pi.on(\"session_compact\", (_event, ctx) => {\n const proxyBase = proxyBaseForCtx(ctx);\n if (proxyBase === undefined) return;\n const sid = sessionIdOf(ctx);\n if (sid === undefined || sid.length === 0) return;\n fetch(`${proxyBase}/__bili/plugin/compact`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId: sid }),\n signal: AbortSignal.timeout(5000),\n }).catch(() => {});\n });\n };\n}\n\nexport default createBiliPlugin();\n\nexport { fetchStatus };\n","import { createBiliPlugin } from \"./pi.js\";\n\nexport default createBiliPlugin(\"omp\");\n"],"mappings":"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","names":[]}
|
package/dist/agent/pi.js
CHANGED
|
@@ -267,7 +267,16 @@ function createBiliPlugin(agentOverride, opts) {
|
|
|
267
267
|
return;
|
|
268
268
|
}
|
|
269
269
|
const panel = typeof status.panel === "string" ? status.panel : void 0;
|
|
270
|
-
|
|
270
|
+
const text = panel ?? renderAcpStatus(status);
|
|
271
|
+
if (typeof pi.sendMessage === "function") {
|
|
272
|
+
try {
|
|
273
|
+
pi.sendMessage({ customType: "bili-acp-status", content: text, display: true });
|
|
274
|
+
return;
|
|
275
|
+
} catch (err) {
|
|
276
|
+
console.error(`bili-plugin(${agent}): sendMessage failed (${err instanceof Error ? err.message : String(err)}) \u2014 falling back to notify`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
notify(text, "info");
|
|
271
280
|
}
|
|
272
281
|
});
|
|
273
282
|
}
|
package/dist/agent/pi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/agent/shared.ts","../../src/agent/pi.ts"],"sourcesContent":["// Shared thin-plugin core for agent-side extensions (\"内外呼应\", issue #1).\n// The agent plugin is a PURE PROTOCOL CLIENT: no acp-kernel import, no\n// compression logic. The proxy stays the single compression authority; the\n// plugin only (1) detects the proxy, (2) fetches the tool manifest (single\n// source of truth), (3) forwards tool executes, (4) reads status. Same\n// package as the proxy ⇒ same version ⇒ no kernel-skew bug class.\n\nexport type ManifestTool = {\n name: string;\n description?: string;\n inputSchema: unknown;\n};\n\nconst MANIFEST_TIMEOUT_MS = 5000;\nconst TOOL_TIMEOUT_MS = 60000;\nconst STATUS_TIMEOUT_MS = 5000;\n\n/** Detect the proxy from a provider baseUrl's `/bili/` zero-config prefix.\n * The real prefix embeds the full upstream URL (`/bili/https://…`), so the\n * check requires `bili` as the first path segment followed by an http(s)\n * URL — a plain `/foo/bili/` path segment is NOT a bili proxy.\n * Returns the proxy origin (scheme//host) the request will actually hit. */\nexport function proxyBaseFromUrl(baseUrl: string | undefined): string | undefined {\n if (!baseUrl) return undefined;\n try {\n const url = new URL(baseUrl);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n const segments = url.pathname.split(\"/\").filter((s) => s.length > 0);\n if (segments[0] !== \"bili\") return undefined;\n const rest = url.pathname.slice(url.pathname.indexOf(\"bili\") + \"bili\".length);\n if (!/^\\/https?:\\/\\//.test(rest)) return undefined;\n return `${url.protocol}//${url.host}`;\n } catch {\n return undefined;\n }\n}\n\n/** MITM transparent mode has no `/bili/` prefix; the proxy's launcher exports\n * BILLION_CONTEXT_PROXY. A stale value surfaces as a tool-forward error. */\nexport function proxyBaseFromEnv(): string | undefined {\n const raw = process.env.BILLION_CONTEXT_PROXY?.trim();\n if (!raw) return undefined;\n try {\n const url = new URL(raw);\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? `${url.protocol}//${url.host}` : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function detectProxyBase(baseUrl: string | undefined): string | undefined {\n if (process.env.BILLION_CONTEXT_PLUGIN === \"0\") return undefined;\n return proxyBaseFromUrl(baseUrl) ?? proxyBaseFromEnv();\n}\n\nasync function fetchJson(url: string, init: RequestInit | undefined, timeoutMs: number, externalSignal?: AbortSignal): Promise<{ ok: boolean; status: number; json: unknown }> {\n const ac = new AbortController();\n // An already-aborted external signal never fires its \"abort\" event, so\n // forward the state directly — otherwise only the timeout could stop\n // the request, turning an instant cancel into a timeout wait.\n if (externalSignal?.aborted) ac.abort();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n const onExternalAbort = () => ac.abort();\n externalSignal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n try {\n let res: Response;\n try {\n res = await fetch(url, { ...init, signal: ac.signal });\n } catch (err) {\n if (ac.signal.aborted && !externalSignal?.aborted) throw new Error(`timeout after ${timeoutMs}ms: ${url}`);\n throw err;\n }\n const text = await res.text();\n let json: unknown = undefined;\n try {\n json = JSON.parse(text);\n } catch {\n json = undefined;\n }\n return { ok: res.ok, status: res.status, json };\n } finally {\n clearTimeout(timer);\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n }\n}\n\nexport async function fetchManifest(proxyBase: string): Promise<ManifestTool[]> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, MANIFEST_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") throw new Error(`manifest fetch failed: ${status}`);\n const data = json as { tools?: { anthropic?: { name?: string; description?: string; input_schema?: unknown }[] } };\n const tools = (data.tools?.anthropic ?? []).filter((t): t is { name: string; description?: string; input_schema?: unknown } => typeof t.name === \"string\");\n if (tools.length === 0) throw new Error(\"manifest served no anthropic tools\");\n return tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.input_schema ?? { type: \"object\", properties: {} } }));\n}\n\nexport async function forwardTool(proxyBase: string, conversationId: string, tool: string, args: unknown, signal?: AbortSignal): Promise<string> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/tool`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId, tool, args: args ?? {} }),\n }, TOOL_TIMEOUT_MS, signal);\n const data = json as { ok?: boolean; result?: string; error?: string } | undefined;\n if (!ok || !data?.ok) {\n throw new Error(`bili proxy tool ${tool} failed (${status}): ${data?.error ?? \"unknown error\"}`);\n }\n return data.result ?? \"\";\n}\n\n/** Soft-fail by design: the status read is best-effort UI data; undefined\n * means \"no data\" whether the proxy is down or the session is unknown. */\nexport async function fetchStatus(proxyBase: string, conversationId: string): Promise<Record<string, unknown> | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=${encodeURIComponent(conversationId)}`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Wire-mode status read (dsh): those clients carry no per-conversation id\n * the proxy could bind, so ask for the most recently active session instead\n * (fallback=latest). Same soft-fail contract as fetchStatus. */\nexport async function fetchStatusLatest(proxyBase: string): Promise<Record<string, unknown> | undefined> {\n // conversationId must be non-empty (server rejects the empty string), but\n // any unknown id is fine: fallback=latest then resolves the most recently\n // active session.\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=dsh&fallback=latest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Liveness + version probe for status UIs: same loopback origin as the\n * status endpoint, so a 404 status + a live manifest means \"proxy up,\n * conversation not seen yet\" — an armed-but-idle state, not an error. */\nexport async function fetchProxyVersion(proxyBase: string): Promise<string | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n const version = (json as { version?: unknown }).version;\n return typeof version === \"string\" && version.length > 0 ? version : undefined;\n}\n","// Thin agent extension for pi and omp (\"内外呼应\", issue #1). Loaded by pi\n// via the package.json `pi` manifest (dist/agent/pi.js) or by omp via the\n// config.yml `extensions:` list (dist/agent/omp.js). pi and omp share the\n// ExtensionFactory API shape, so one factory serves both; types below are\n// minimal structural declarations — the bundled artifact imports NOTHING\n// from the host at runtime (the host duck-types us in).\n\nimport { detectProxyBase, fetchManifest, forwardTool, fetchStatus, fetchProxyVersion, type ManifestTool } from \"./shared.js\";\n\ntype Ctx = {\n sessionManager?: { getSessionId?: () => string } | undefined;\n model?: { contextWindow?: number; baseUrl?: string } | 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};\n\nfunction agentName(override: string | undefined): string {\n if (override) return override;\n return process.env.BILLION_CONTEXT_PLUGIN_AGENT === \"omp\" ? \"omp\" : \"pi\";\n}\n\nfunction proxyBaseForCtx(ctx: Ctx): string | undefined {\n return detectProxyBase(ctx.model?.baseUrl);\n}\n\nfunction sessionIdOf(ctx: Ctx): string | undefined {\n try {\n const sid = ctx.sessionManager?.getSessionId?.();\n return typeof sid === \"string\" ? sid : undefined;\n } catch {\n return undefined;\n }\n}\n\n// omp's chat-completions payloads carry NO conversation signal (no\n// prompt_cache_key / session / user, and no session header — verified by dump),\n// so the proxy's openai identity falls to a content fingerprint that never\n// matches the session id this plugin registered (the identity register is keyed\n// by the omp session uuid). The before_provider_request return value REPLACES\n// the whole outgoing payload (omp onPayload chain, verified in the omp 17.3.8\n// dist), so stamp prompt_cache_key with the omp session id: the proxy binds\n// pluginMode by that identity and /acp finds the session by it.\n// Chat shape = messages array, no responses `input`, no native prompt_cache_key.\n// max_tokens is NOT a discriminator: omp's openai-compat providers send it in\n// every chat-completions body (maxTokensField:\"max_tokens\") exactly like the\n// anthropic wire — excluding it meant the target shape was never stamped\n// (#268). The anthropic wire gets stamped too: the proxy records the mapping\n// from the body pck there as well and strips the field before forwarding to\n// the real Anthropic. pi is untouched (it stamps x-bili-plugin-conversation in\n// before_provider_headers, which outranks the body field).\nfunction stampPromptCacheKey(event: unknown, ctx: Ctx, agent: string): Record<string, unknown> | undefined {\n if (agent !== \"omp\") return undefined;\n const payload = (event as { payload?: unknown } | undefined)?.payload;\n if (payload === null || typeof payload !== \"object\" || Array.isArray(payload)) return undefined;\n const p = payload as Record<string, unknown>;\n if (!Array.isArray(p.messages)) return undefined;\n if (p.input !== undefined) return undefined;\n if (typeof p.prompt_cache_key === \"string\" && p.prompt_cache_key.trim().length > 0) return undefined;\n const sid = sessionIdOf(ctx);\n if (sid === undefined || sid.length === 0) return undefined;\n return { ...p, prompt_cache_key: sid };\n}\n\nfunction fmtTok(n: number): string {\n if (n < 1000) return String(n);\n if (n < 1_000_000) return `${(n / 1000).toFixed(1)}K`;\n return `${(n / 1_000_000).toFixed(2)}M`;\n}\n\nfunction renderAcpStatus(s: Record<string, unknown>): string {\n const num = (v: unknown): number | null => (typeof v === \"number\" && Number.isFinite(v) ? v : null);\n const contextTokens = num(s.contextTokens);\n const contextLimit = num(s.contextLimit);\n const inputTokens = num(s.inputTokens);\n const outputTokens = num(s.outputTokens);\n const cachedTokens = num(s.cachedTokens);\n const requests = num(s.requests);\n const blocks = Array.isArray(s.blocks) ? (s.blocks as Array<{ tier?: number; active?: boolean }>) : [];\n const activeBlocks = blocks.filter((b) => b.active === true).length;\n const lines: string[] = [\"📊 ACP status\"];\n if (contextTokens !== null) {\n const pct = contextLimit !== null && contextLimit > 0 ? ` (${((contextTokens / contextLimit) * 100).toFixed(1)}%)` : \"\";\n lines.push(` context: ${fmtTok(contextTokens)}${contextLimit !== null ? ` / ${fmtTok(contextLimit)}` : \"\"}${pct}`);\n }\n 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\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 if (typeof pi.registerCommand === \"function\") {\n pi.registerCommand(\"acp\", {\n description: \"Show ACP context-compression status for this session\",\n handler: async (_args, ctx) => {\n const notify = (message: string, type?: string): void => {\n try {\n ctx.ui?.notify?.(message, type);\n } catch {\n // host UI unavailable — the command is best-effort\n }\n };\n const proxyBase = detectProxyBase(ctx.model?.baseUrl);\n if (proxyBase === undefined) {\n notify(\"bili: no proxy detected (run via `bili <client>` or set a /bili/ baseURL)\", \"warning\");\n return;\n }\n const conversationId = sessionIdOf(ctx) ?? \"unknown\";\n let status: Record<string, unknown> | undefined;\n try {\n status = await fetchStatus(proxyBase, conversationId);\n } catch (err) {\n notify(`bili: status fetch failed: ${err instanceof Error ? err.message : String(err)}`, \"error\");\n return;\n }\n if (status === undefined) {\n // 404 from a live proxy = this conversation has sent no\n // model request yet (e.g. /acp right after startup).\n // Probe the manifest to confirm liveness + version and\n // show an armed/idle notice instead of a scary warning.\n let version: string | undefined;\n try {\n version = await fetchProxyVersion(proxyBase);\n } catch {\n version = undefined;\n }\n if (version !== undefined) {\n notify(\n `billion-context@${version} — proxy connected, compression armed. No model request in this conversation yet; send one, then run /acp again.`,\n \"info\",\n );\n } else {\n notify(\"bili: no ACP session yet (send a model request first, then run /acp)\", \"warning\");\n }\n return;\n }\n const panel = typeof status.panel === \"string\" ? status.panel : undefined;\n notify(panel ?? renderAcpStatus(status), \"info\");\n },\n });\n }\n pi.on(\"before_provider_headers\", (event, ctx) => {\n try {\n if (proxyBaseForCtx(ctx) === undefined) return;\n const headers = (event as unknown as { headers?: Record<string, string> }).headers;\n if (headers === undefined || typeof headers !== \"object\" || Array.isArray(headers)) return;\n // The x-bili-plugin marker tells the proxy \"the client owns the\n // ACP tools natively — skip wire-level injection\". Stamping it\n // before registerTools() finishes would send round 1 out with\n // NO ACP tools (the first provider request races the manifest\n // fetch). Claim ownership only once tools are registered;\n // until then the request rides the proxy's wire mode. A\n // permanently failing manifest fetch keeps us in wire mode —\n // a graceful fallback rather than a tool-less session.\n if (state.toolsReady === true) {\n const sid = sessionIdOf(ctx);\n if (sid !== undefined) headers[\"x-bili-plugin-conversation\"] = sid;\n headers[\"x-bili-plugin\"] = agent;\n const window = ctx.model?.contextWindow;\n if (typeof window === \"number\" && Number.isFinite(window) && window > 0) {\n headers[\"x-bili-plugin-context-window\"] = String(Math.floor(window));\n }\n }\n } catch (err) {\n console.error(`bili-plugin(${agent}): header stamp skipped (${err instanceof Error ? err.message : String(err)})`);\n }\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n });\n pi.on(\"before_provider_request\", (event, ctx) => {\n // omp emits this per model request (but never before_provider_headers);\n // it doubles as the retry driver when the session_start manifest\n // fetch raced the proxy startup. Cached by sid, throttled by retryAt.\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n return stampPromptCacheKey(event, ctx, agent);\n });\n pi.on(\"session_start\", (_event, ctx) => {\n state.sid = undefined;\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n });\n // omp fires session_compact on in-session native compaction (sid does\n // not rotate), so the proxy reuses stale state — notify it to archive\n // the now-unreachable blocks (#395). Fire-and-forget: a failed\n // notification must never break the agent's compaction.\n pi.on(\"session_compact\", (_event, ctx) => {\n const proxyBase = proxyBaseForCtx(ctx);\n if (proxyBase === undefined) return;\n const sid = sessionIdOf(ctx);\n if (sid === undefined || sid.length === 0) return;\n fetch(`${proxyBase}/__bili/plugin/compact`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId: sid }),\n signal: AbortSignal.timeout(5000),\n }).catch(() => {});\n });\n };\n}\n\nexport default createBiliPlugin();\n\nexport { fetchStatus };\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/agent/shared.ts","../../src/agent/pi.ts"],"sourcesContent":["// Shared thin-plugin core for agent-side extensions (\"内外呼应\", issue #1).\n// The agent plugin is a PURE PROTOCOL CLIENT: no acp-kernel import, no\n// compression logic. The proxy stays the single compression authority; the\n// plugin only (1) detects the proxy, (2) fetches the tool manifest (single\n// source of truth), (3) forwards tool executes, (4) reads status. Same\n// package as the proxy ⇒ same version ⇒ no kernel-skew bug class.\n\nexport type ManifestTool = {\n name: string;\n description?: string;\n inputSchema: unknown;\n};\n\nconst MANIFEST_TIMEOUT_MS = 5000;\nconst TOOL_TIMEOUT_MS = 60000;\nconst STATUS_TIMEOUT_MS = 5000;\n\n/** Detect the proxy from a provider baseUrl's `/bili/` zero-config prefix.\n * The real prefix embeds the full upstream URL (`/bili/https://…`), so the\n * check requires `bili` as the first path segment followed by an http(s)\n * URL — a plain `/foo/bili/` path segment is NOT a bili proxy.\n * Returns the proxy origin (scheme//host) the request will actually hit. */\nexport function proxyBaseFromUrl(baseUrl: string | undefined): string | undefined {\n if (!baseUrl) return undefined;\n try {\n const url = new URL(baseUrl);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n const segments = url.pathname.split(\"/\").filter((s) => s.length > 0);\n if (segments[0] !== \"bili\") return undefined;\n const rest = url.pathname.slice(url.pathname.indexOf(\"bili\") + \"bili\".length);\n if (!/^\\/https?:\\/\\//.test(rest)) return undefined;\n return `${url.protocol}//${url.host}`;\n } catch {\n return undefined;\n }\n}\n\n/** MITM transparent mode has no `/bili/` prefix; the proxy's launcher exports\n * BILLION_CONTEXT_PROXY. A stale value surfaces as a tool-forward error. */\nexport function proxyBaseFromEnv(): string | undefined {\n const raw = process.env.BILLION_CONTEXT_PROXY?.trim();\n if (!raw) return undefined;\n try {\n const url = new URL(raw);\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? `${url.protocol}//${url.host}` : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function detectProxyBase(baseUrl: string | undefined): string | undefined {\n if (process.env.BILLION_CONTEXT_PLUGIN === \"0\") return undefined;\n return proxyBaseFromUrl(baseUrl) ?? proxyBaseFromEnv();\n}\n\nasync function fetchJson(url: string, init: RequestInit | undefined, timeoutMs: number, externalSignal?: AbortSignal): Promise<{ ok: boolean; status: number; json: unknown }> {\n const ac = new AbortController();\n // An already-aborted external signal never fires its \"abort\" event, so\n // forward the state directly — otherwise only the timeout could stop\n // the request, turning an instant cancel into a timeout wait.\n if (externalSignal?.aborted) ac.abort();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n const onExternalAbort = () => ac.abort();\n externalSignal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n try {\n let res: Response;\n try {\n res = await fetch(url, { ...init, signal: ac.signal });\n } catch (err) {\n if (ac.signal.aborted && !externalSignal?.aborted) throw new Error(`timeout after ${timeoutMs}ms: ${url}`);\n throw err;\n }\n const text = await res.text();\n let json: unknown = undefined;\n try {\n json = JSON.parse(text);\n } catch {\n json = undefined;\n }\n return { ok: res.ok, status: res.status, json };\n } finally {\n clearTimeout(timer);\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n }\n}\n\nexport async function fetchManifest(proxyBase: string): Promise<ManifestTool[]> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, MANIFEST_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") throw new Error(`manifest fetch failed: ${status}`);\n const data = json as { tools?: { anthropic?: { name?: string; description?: string; input_schema?: unknown }[] } };\n const tools = (data.tools?.anthropic ?? []).filter((t): t is { name: string; description?: string; input_schema?: unknown } => typeof t.name === \"string\");\n if (tools.length === 0) throw new Error(\"manifest served no anthropic tools\");\n return tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.input_schema ?? { type: \"object\", properties: {} } }));\n}\n\nexport async function forwardTool(proxyBase: string, conversationId: string, tool: string, args: unknown, signal?: AbortSignal): Promise<string> {\n const { ok, status, json } = await fetchJson(`${proxyBase}/__bili/plugin/tool`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId, tool, args: args ?? {} }),\n }, TOOL_TIMEOUT_MS, signal);\n const data = json as { ok?: boolean; result?: string; error?: string } | undefined;\n if (!ok || !data?.ok) {\n throw new Error(`bili proxy tool ${tool} failed (${status}): ${data?.error ?? \"unknown error\"}`);\n }\n return data.result ?? \"\";\n}\n\n/** Soft-fail by design: the status read is best-effort UI data; undefined\n * means \"no data\" whether the proxy is down or the session is unknown. */\nexport async function fetchStatus(proxyBase: string, conversationId: string): Promise<Record<string, unknown> | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=${encodeURIComponent(conversationId)}`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Wire-mode status read (dsh): those clients carry no per-conversation id\n * the proxy could bind, so ask for the most recently active session instead\n * (fallback=latest). Same soft-fail contract as fetchStatus. */\nexport async function fetchStatusLatest(proxyBase: string): Promise<Record<string, unknown> | undefined> {\n // conversationId must be non-empty (server rejects the empty string), but\n // any unknown id is fine: fallback=latest then resolves the most recently\n // active session.\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/status?conversationId=dsh&fallback=latest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n return json as Record<string, unknown>;\n}\n\n/** Liveness + version probe for status UIs: same loopback origin as the\n * status endpoint, so a 404 status + a live manifest means \"proxy up,\n * conversation not seen yet\" — an armed-but-idle state, not an error. */\nexport async function fetchProxyVersion(proxyBase: string): Promise<string | undefined> {\n const { ok, json } = await fetchJson(`${proxyBase}/__bili/plugin/manifest`, undefined, STATUS_TIMEOUT_MS);\n if (!ok || !json || typeof json !== \"object\") return undefined;\n const version = (json as { version?: unknown }).version;\n return typeof version === \"string\" && version.length > 0 ? version : undefined;\n}\n","// Thin agent extension for pi and omp (\"内外呼应\", issue #1). Loaded by pi\n// via the package.json `pi` manifest (dist/agent/pi.js) or by omp via the\n// config.yml `extensions:` list (dist/agent/omp.js). pi and omp share the\n// ExtensionFactory API shape, so one factory serves both; types below are\n// minimal structural declarations — the bundled artifact imports NOTHING\n// from the host at runtime (the host duck-types us in).\n\nimport { detectProxyBase, fetchManifest, forwardTool, fetchStatus, fetchProxyVersion, type ManifestTool } from \"./shared.js\";\n\ntype Ctx = {\n sessionManager?: { getSessionId?: () => string } | undefined;\n model?: { contextWindow?: number; baseUrl?: string } | 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 // Persistent transcript output (rendered by TUI and web hosts like\n // pi-web); notify() is a transient toast — only the fallback for hosts\n // without sendMessage (issue #359).\n sendMessage?: (message: { customType: string; content: string; display: boolean }) => void;\n};\n\nfunction agentName(override: string | undefined): string {\n if (override) return override;\n return process.env.BILLION_CONTEXT_PLUGIN_AGENT === \"omp\" ? \"omp\" : \"pi\";\n}\n\nfunction proxyBaseForCtx(ctx: Ctx): string | undefined {\n return detectProxyBase(ctx.model?.baseUrl);\n}\n\nfunction sessionIdOf(ctx: Ctx): string | undefined {\n try {\n const sid = ctx.sessionManager?.getSessionId?.();\n return typeof sid === \"string\" ? sid : undefined;\n } catch {\n return undefined;\n }\n}\n\n// omp's chat-completions payloads carry NO conversation signal (no\n// prompt_cache_key / session / user, and no session header — verified by dump),\n// so the proxy's openai identity falls to a content fingerprint that never\n// matches the session id this plugin registered (the identity register is keyed\n// by the omp session uuid). The before_provider_request return value REPLACES\n// the whole outgoing payload (omp onPayload chain, verified in the omp 17.3.8\n// dist), so stamp prompt_cache_key with the omp session id: the proxy binds\n// pluginMode by that identity and /acp finds the session by it.\n// Chat shape = messages array, no responses `input`, no native prompt_cache_key.\n// max_tokens is NOT a discriminator: omp's openai-compat providers send it in\n// every chat-completions body (maxTokensField:\"max_tokens\") exactly like the\n// anthropic wire — excluding it meant the target shape was never stamped\n// (#268). The anthropic wire gets stamped too: the proxy records the mapping\n// from the body pck there as well and strips the field before forwarding to\n// the real Anthropic. pi is untouched (it stamps x-bili-plugin-conversation in\n// before_provider_headers, which outranks the body field).\nfunction stampPromptCacheKey(event: unknown, ctx: Ctx, agent: string): Record<string, unknown> | undefined {\n if (agent !== \"omp\") return undefined;\n const payload = (event as { payload?: unknown } | undefined)?.payload;\n if (payload === null || typeof payload !== \"object\" || Array.isArray(payload)) return undefined;\n const p = payload as Record<string, unknown>;\n if (!Array.isArray(p.messages)) return undefined;\n if (p.input !== undefined) return undefined;\n if (typeof p.prompt_cache_key === \"string\" && p.prompt_cache_key.trim().length > 0) return undefined;\n const sid = sessionIdOf(ctx);\n if (sid === undefined || sid.length === 0) return undefined;\n return { ...p, prompt_cache_key: sid };\n}\n\nfunction fmtTok(n: number): string {\n if (n < 1000) return String(n);\n if (n < 1_000_000) return `${(n / 1000).toFixed(1)}K`;\n return `${(n / 1_000_000).toFixed(2)}M`;\n}\n\nfunction renderAcpStatus(s: Record<string, unknown>): string {\n const num = (v: unknown): number | null => (typeof v === \"number\" && Number.isFinite(v) ? v : null);\n const contextTokens = num(s.contextTokens);\n const contextLimit = num(s.contextLimit);\n const inputTokens = num(s.inputTokens);\n const outputTokens = num(s.outputTokens);\n const cachedTokens = num(s.cachedTokens);\n const requests = num(s.requests);\n const blocks = Array.isArray(s.blocks) ? (s.blocks as Array<{ tier?: number; active?: boolean }>) : [];\n const activeBlocks = blocks.filter((b) => b.active === true).length;\n const lines: string[] = [\"📊 ACP status\"];\n if (contextTokens !== null) {\n const pct = contextLimit !== null && contextLimit > 0 ? ` (${((contextTokens / contextLimit) * 100).toFixed(1)}%)` : \"\";\n lines.push(` context: ${fmtTok(contextTokens)}${contextLimit !== null ? ` / ${fmtTok(contextLimit)}` : \"\"}${pct}`);\n }\n 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\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 if (typeof pi.registerCommand === \"function\") {\n pi.registerCommand(\"acp\", {\n description: \"Show ACP context-compression status for this session\",\n handler: async (_args, ctx) => {\n const notify = (message: string, type?: string): void => {\n try {\n ctx.ui?.notify?.(message, type);\n } catch {\n // host UI unavailable — the command is best-effort\n }\n };\n const proxyBase = detectProxyBase(ctx.model?.baseUrl);\n if (proxyBase === undefined) {\n notify(\"bili: no proxy detected (run via `bili <client>` or set a /bili/ baseURL)\", \"warning\");\n return;\n }\n const conversationId = sessionIdOf(ctx) ?? \"unknown\";\n let status: Record<string, unknown> | undefined;\n try {\n status = await fetchStatus(proxyBase, conversationId);\n } catch (err) {\n notify(`bili: status fetch failed: ${err instanceof Error ? err.message : String(err)}`, \"error\");\n return;\n }\n if (status === undefined) {\n // 404 from a live proxy = this conversation has sent no\n // model request yet (e.g. /acp right after startup).\n // Probe the manifest to confirm liveness + version and\n // show an armed/idle notice instead of a scary warning.\n let version: string | undefined;\n try {\n version = await fetchProxyVersion(proxyBase);\n } catch {\n version = undefined;\n }\n if (version !== undefined) {\n notify(\n `billion-context@${version} — proxy connected, compression armed. No model request in this conversation yet; send one, then run /acp again.`,\n \"info\",\n );\n } else {\n notify(\"bili: no ACP session yet (send a model request first, then run /acp)\", \"warning\");\n }\n return;\n }\n const panel = typeof status.panel === \"string\" ? status.panel : undefined;\n const text = panel ?? renderAcpStatus(status);\n // Persistent transcript output (TUI + web hosts like pi-web).\n // The proxy strips this message from the model context by\n // content signature (src/acp-panel.ts), so it never reaches\n // the LLM; notify() is the fallback for hosts without\n // sendMessage (older pi).\n if (typeof pi.sendMessage === \"function\") {\n try {\n pi.sendMessage({ customType: \"bili-acp-status\", content: text, display: true });\n return;\n } catch (err) {\n console.error(`bili-plugin(${agent}): sendMessage failed (${err instanceof Error ? err.message : String(err)}) — falling back to notify`);\n }\n }\n notify(text, \"info\");\n },\n });\n }\n pi.on(\"before_provider_headers\", (event, ctx) => {\n try {\n if (proxyBaseForCtx(ctx) === undefined) return;\n const headers = (event as unknown as { headers?: Record<string, string> }).headers;\n if (headers === undefined || typeof headers !== \"object\" || Array.isArray(headers)) return;\n // The x-bili-plugin marker tells the proxy \"the client owns the\n // ACP tools natively — skip wire-level injection\". Stamping it\n // before registerTools() finishes would send round 1 out with\n // NO ACP tools (the first provider request races the manifest\n // fetch). Claim ownership only once tools are registered;\n // until then the request rides the proxy's wire mode. A\n // permanently failing manifest fetch keeps us in wire mode —\n // a graceful fallback rather than a tool-less session.\n if (state.toolsReady === true) {\n const sid = sessionIdOf(ctx);\n if (sid !== undefined) headers[\"x-bili-plugin-conversation\"] = sid;\n headers[\"x-bili-plugin\"] = agent;\n const window = ctx.model?.contextWindow;\n if (typeof window === \"number\" && Number.isFinite(window) && window > 0) {\n headers[\"x-bili-plugin-context-window\"] = String(Math.floor(window));\n }\n }\n } catch (err) {\n console.error(`bili-plugin(${agent}): header stamp skipped (${err instanceof Error ? err.message : String(err)})`);\n }\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n });\n pi.on(\"before_provider_request\", (event, ctx) => {\n // omp emits this per model request (but never before_provider_headers);\n // it doubles as the retry driver when the session_start manifest\n // fetch raced the proxy startup. Cached by sid, throttled by retryAt.\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n return stampPromptCacheKey(event, ctx, agent);\n });\n pi.on(\"session_start\", (_event, ctx) => {\n state.sid = undefined;\n void registerTools(pi, ctx, state, agent).catch((err: unknown) => console.error(`bili-plugin(${agent}): ${err instanceof Error ? err.message : String(err)}`));\n });\n // omp fires session_compact on in-session native compaction (sid does\n // not rotate), so the proxy reuses stale state — notify it to archive\n // the now-unreachable blocks (#395). Fire-and-forget: a failed\n // notification must never break the agent's compaction.\n pi.on(\"session_compact\", (_event, ctx) => {\n const proxyBase = proxyBaseForCtx(ctx);\n if (proxyBase === undefined) return;\n const sid = sessionIdOf(ctx);\n if (sid === undefined || sid.length === 0) return;\n fetch(`${proxyBase}/__bili/plugin/compact`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ conversationId: sid }),\n signal: AbortSignal.timeout(5000),\n }).catch(() => {});\n });\n };\n}\n\nexport default createBiliPlugin();\n\nexport { fetchStatus };\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -52017,6 +52017,7 @@ var CONTEXT_OVERFLOW_PATTERNS = [
|
|
|
52017
52017
|
/maximum context length/i,
|
|
52018
52018
|
/max context length/i,
|
|
52019
52019
|
/maximum context size/i,
|
|
52020
|
+
/longer than the model'?s context length/i,
|
|
52020
52021
|
/exceeds the context window/i,
|
|
52021
52022
|
/out of room in the model/i,
|
|
52022
52023
|
/exceeded model token limit/i,
|
|
@@ -52033,7 +52034,7 @@ function toTokenNumber(s3) {
|
|
|
52033
52034
|
function parseOverflowWindow(text) {
|
|
52034
52035
|
let m2 = text.match(/>\s*(\d[\d,]*)\s*maximum/i);
|
|
52035
52036
|
if (m2) return toTokenNumber(m2[1]);
|
|
52036
|
-
m2 = text.match(/maximum context length is (\d[\d,]*)/i) ?? text.match(/maximum context length of (\d[\d,]*)/i) ?? text.match(/maximum context size (?:is|of) (\d[\d,]*)/i) ?? text.match(/(?:maximum|max)\s+(?:context\s+)?length\s+(?:is\s+)?(\d[\d,]*)/i) ?? text.match(/limit of (\d[\d,]*)\s*token/i) ?? text.match(/(\d[\d,]*)\s*maximum\b/i);
|
|
52037
|
+
m2 = text.match(/maximum context length is (\d[\d,]*)/i) ?? text.match(/maximum context length of (\d[\d,]*)/i) ?? text.match(/maximum context size (?:is|of) (\d[\d,]*)/i) ?? text.match(/(?:maximum|max)\s+(?:context\s+)?length\s+(?:is\s+)?(\d[\d,]*)/i) ?? text.match(/context length\s*\((\d[\d,]*)\s*token/i) ?? text.match(/limit of (\d[\d,]*)\s*token/i) ?? text.match(/(\d[\d,]*)\s*maximum\b/i);
|
|
52037
52038
|
if (m2) return toTokenNumber(m2[1]);
|
|
52038
52039
|
return void 0;
|
|
52039
52040
|
}
|
|
@@ -53640,6 +53641,76 @@ ${FAKE_COMPLETION_HINT}`;
|
|
|
53640
53641
|
return JSON.stringify(obj);
|
|
53641
53642
|
}
|
|
53642
53643
|
|
|
53644
|
+
// src/acp-panel.ts
|
|
53645
|
+
var PANEL_BOX_TOP = "\u256D";
|
|
53646
|
+
var PANEL_BOX_TITLE = "ACP Context Analysis";
|
|
53647
|
+
var PANEL_BOX_FOOTER = "Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.";
|
|
53648
|
+
var PANEL_FALLBACK_HEADER = "\u{1F4CA} ACP status";
|
|
53649
|
+
function isAcpPanelText(text) {
|
|
53650
|
+
const t = text.trim();
|
|
53651
|
+
if (t.length === 0) return false;
|
|
53652
|
+
return isBoxPanel(t) || isFallbackPanel(t);
|
|
53653
|
+
}
|
|
53654
|
+
function isBoxPanel(t) {
|
|
53655
|
+
return t.startsWith(PANEL_BOX_TOP) && t.includes(PANEL_BOX_TITLE) && t.endsWith(PANEL_BOX_FOOTER);
|
|
53656
|
+
}
|
|
53657
|
+
function isFallbackPanel(t) {
|
|
53658
|
+
const lines = t.split("\n");
|
|
53659
|
+
if (lines[0] !== PANEL_FALLBACK_HEADER) return false;
|
|
53660
|
+
for (let i = 1; i < lines.length; i++) {
|
|
53661
|
+
const line = lines[i];
|
|
53662
|
+
if (line.length > 0 && !line.startsWith(" ")) return false;
|
|
53663
|
+
}
|
|
53664
|
+
return true;
|
|
53665
|
+
}
|
|
53666
|
+
function messageText(content) {
|
|
53667
|
+
if (typeof content === "string") return content;
|
|
53668
|
+
if (Array.isArray(content)) {
|
|
53669
|
+
let joined = "";
|
|
53670
|
+
for (const part of content) {
|
|
53671
|
+
const p2 = part;
|
|
53672
|
+
if (p2 === null || typeof p2 !== "object") return void 0;
|
|
53673
|
+
const pt2 = p2.type;
|
|
53674
|
+
if (pt2 !== void 0 && pt2 !== "text" && pt2 !== "input_text") return void 0;
|
|
53675
|
+
if (typeof p2.text === "string") joined += p2.text;
|
|
53676
|
+
}
|
|
53677
|
+
return joined;
|
|
53678
|
+
}
|
|
53679
|
+
return void 0;
|
|
53680
|
+
}
|
|
53681
|
+
function stripAcpPanelMessages(messages) {
|
|
53682
|
+
if (!Array.isArray(messages)) return 0;
|
|
53683
|
+
let stripped = 0;
|
|
53684
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
53685
|
+
const rec = messages[i];
|
|
53686
|
+
if (rec === null || typeof rec !== "object") continue;
|
|
53687
|
+
if (rec.role !== "user") continue;
|
|
53688
|
+
const text = messageText(rec.content);
|
|
53689
|
+
if (text !== void 0 && isAcpPanelText(text)) {
|
|
53690
|
+
messages.splice(i, 1);
|
|
53691
|
+
stripped++;
|
|
53692
|
+
}
|
|
53693
|
+
}
|
|
53694
|
+
return stripped;
|
|
53695
|
+
}
|
|
53696
|
+
function stripAcpPanelResponsesInput(input) {
|
|
53697
|
+
if (!Array.isArray(input)) return 0;
|
|
53698
|
+
let stripped = 0;
|
|
53699
|
+
for (let i = input.length - 1; i >= 0; i--) {
|
|
53700
|
+
const rec = input[i];
|
|
53701
|
+
if (rec === null || typeof rec !== "object") continue;
|
|
53702
|
+
const type = rec.type;
|
|
53703
|
+
if (type !== "message" && type !== void 0) continue;
|
|
53704
|
+
if (rec.role !== "user") continue;
|
|
53705
|
+
const text = messageText(rec.content);
|
|
53706
|
+
if (text !== void 0 && isAcpPanelText(text)) {
|
|
53707
|
+
input.splice(i, 1);
|
|
53708
|
+
stripped++;
|
|
53709
|
+
}
|
|
53710
|
+
}
|
|
53711
|
+
return stripped;
|
|
53712
|
+
}
|
|
53713
|
+
|
|
53643
53714
|
// src/stream-openai.ts
|
|
53644
53715
|
function rewriteOpenaiJsonResponse(body, ctx) {
|
|
53645
53716
|
if (!body || typeof body !== "object") return body;
|
|
@@ -55416,13 +55487,20 @@ function loadClientConfig(env, cwd) {
|
|
|
55416
55487
|
config.dsh = readDshConfig(resolveDshHome(env));
|
|
55417
55488
|
return config;
|
|
55418
55489
|
}
|
|
55419
|
-
function collectModelWindows(config) {
|
|
55490
|
+
function collectModelWindows(config, scope) {
|
|
55420
55491
|
const out = {};
|
|
55421
55492
|
const add = (wins) => {
|
|
55422
55493
|
for (const w2 of wins ?? []) {
|
|
55423
55494
|
if (!out[w2.id] || w2.contextWindow > out[w2.id]) out[w2.id] = w2.contextWindow;
|
|
55424
55495
|
}
|
|
55425
55496
|
};
|
|
55497
|
+
if (scope) {
|
|
55498
|
+
if (scope === "codex") add(config.codex?.modelWindows);
|
|
55499
|
+
else if (scope === "pi") for (const p2 of Object.values(config.pi?.providers ?? {})) add(p2.models);
|
|
55500
|
+
else if (scope === "omp") for (const p2 of Object.values(config.omp?.providers ?? {})) add(p2.models);
|
|
55501
|
+
else if (scope === "opencode") for (const p2 of Object.values(config.opencode?.providers ?? {})) add(p2.models);
|
|
55502
|
+
return out;
|
|
55503
|
+
}
|
|
55426
55504
|
for (const p2 of Object.values(config.pi?.providers ?? {})) add(p2.models);
|
|
55427
55505
|
for (const p2 of Object.values(config.omp?.providers ?? {})) add(p2.models);
|
|
55428
55506
|
for (const p2 of Object.values(config.opencode?.providers ?? {})) add(p2.models);
|
|
@@ -57294,6 +57372,10 @@ function prepareAnthropic(parsed, req, opts, core, config, prompts, log2, sessio
|
|
|
57294
57372
|
let rebuiltMessages = parsed.messages;
|
|
57295
57373
|
let systemOut = parsed.system;
|
|
57296
57374
|
let toolsOut = parsed.tools;
|
|
57375
|
+
const strippedPanels = stripAcpPanelMessages(parsed.messages);
|
|
57376
|
+
if (strippedPanels > 0) {
|
|
57377
|
+
log2("info", `[${sessionId}] stripped ${strippedPanels} ACP panel message(s) before projection (UI-only, issue #359)`);
|
|
57378
|
+
}
|
|
57297
57379
|
try {
|
|
57298
57380
|
const { msgs, cacheControls } = anthropicToCore(parsed);
|
|
57299
57381
|
originalMessages = msgs;
|
|
@@ -57383,6 +57465,10 @@ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session,
|
|
|
57383
57465
|
const isTitleGen = maxTokens <= 200;
|
|
57384
57466
|
const shouldInject = opts.compress.injectTool && !isTitleGen;
|
|
57385
57467
|
const injectTools = shouldInject && !pluginMode;
|
|
57468
|
+
const strippedPanels = stripAcpPanelMessages(parsed.messages);
|
|
57469
|
+
if (strippedPanels > 0) {
|
|
57470
|
+
log2("info", `[${sessionId}] stripped ${strippedPanels} ACP panel message(s) before projection (UI-only, issue #359)`);
|
|
57471
|
+
}
|
|
57386
57472
|
try {
|
|
57387
57473
|
const { msgs, systemText } = openaiToCore(parsed);
|
|
57388
57474
|
openaiSystemText = systemText;
|
|
@@ -57468,6 +57554,10 @@ function prepareResponses(parsed, req, opts, core, config, prompts, log2, sessio
|
|
|
57468
57554
|
if (droppedEmpty > 0) {
|
|
57469
57555
|
log2("info", `[${sessionId}] dropped ${droppedEmpty} whitespace-only message item(s) before projection (flattened-turn artifact)`);
|
|
57470
57556
|
}
|
|
57557
|
+
const strippedPanels = stripAcpPanelResponsesInput(parsed.input);
|
|
57558
|
+
if (strippedPanels > 0) {
|
|
57559
|
+
log2("info", `[${sessionId}] stripped ${strippedPanels} ACP panel message(s) before projection (UI-only, issue #359)`);
|
|
57560
|
+
}
|
|
57471
57561
|
const shouldInject = opts.compress.injectTool;
|
|
57472
57562
|
const injectTools = shouldInject && !pluginMode;
|
|
57473
57563
|
const isCompactionTrigger = hasCompactionTrigger(parsed.input);
|
|
@@ -63718,7 +63808,7 @@ async function runLaunch(params, deps = {}) {
|
|
|
63718
63808
|
const routes = discoverRoutes(base, config);
|
|
63719
63809
|
const biliRoutes = loadRoutes(process.env);
|
|
63720
63810
|
const domains = dedupeInOrder([...routes.httpsDomains, ...params.mitmDomains ?? []]);
|
|
63721
|
-
const handle2 = await ensureProxyRunning({ host, port, passthrough, debug, mitmDomains: domains, modelWindows: collectModelWindows(config) }, deps);
|
|
63811
|
+
const handle2 = await ensureProxyRunning({ host, port, passthrough, debug, mitmDomains: domains, modelWindows: collectModelWindows(config, base) }, deps);
|
|
63722
63812
|
console.error(
|
|
63723
63813
|
`bili: started proxy at ${handle2.origin} (MITM domains: ${domains.length ? domains.join(", ") : "defaults"})` + (routes.httpRewrites.length > 0 ? ` (HTTP /bili/ rewrites: ${routes.httpRewrites.length})` : "") + (routes.httpsRewrites.length > 0 ? ` (HTTPS cert rewrites: ${routes.httpsRewrites.length})` : "") + (params.client === "pi-test" ? " (no extensions)" : "")
|
|
63724
63814
|
);
|
|
@@ -63893,7 +63983,7 @@ async function runTestPi(params, deps = {}) {
|
|
|
63893
63983
|
...discoverDomains("pi", config),
|
|
63894
63984
|
...params.mitmDomains ?? []
|
|
63895
63985
|
]);
|
|
63896
|
-
const handle2 = await ensureProxyRunning({ host, port, passthrough, debug, mitmDomains: domains, modelWindows: collectModelWindows(config) }, deps);
|
|
63986
|
+
const handle2 = await ensureProxyRunning({ host, port, passthrough, debug, mitmDomains: domains, modelWindows: collectModelWindows(config, "pi") }, deps);
|
|
63897
63987
|
console.error(
|
|
63898
63988
|
`bili: started proxy at ${handle2.origin} (MITM domains: ${domains.length ? domains.join(", ") : "defaults"})`
|
|
63899
63989
|
);
|