billion-context 0.1.68-pr.428.255 → 0.1.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -60
- package/dist/index.js +21 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,66 +50,6 @@ Agent (Claude Code / Codex / Cursor / Aider ...)
|
|
|
50
50
|
|
|
51
51
|
The proxy injects four context-management tools (`compress`, `decompress`, `search_context`, `acp_status`) into the conversation. The model calls `compress` when the conversation grows, and the proxy executes it server-side — the compressed ranges are folded into the conversation history before the next turn.
|
|
52
52
|
|
|
53
|
-
### Two compression modes — who executes `compress`
|
|
54
|
-
|
|
55
|
-
The proxy runs in one of two modes, and **the mode decides who executes
|
|
56
|
-
`compress`, which in turn decides how the summary travels to the model** (the
|
|
57
|
-
"carrier"). This distinction is the root of #377.
|
|
58
|
-
|
|
59
|
-
| | **Launcher / plugin mode** (`bili pi`, `bili codex`, …) | **Proxy mode** (plain client → `/bili/`) |
|
|
60
|
-
|---|---|---|
|
|
61
|
-
| Client | ACP-native agent with the bili extension (pi/omp) | Any OpenAI/Anthropic client, no extension |
|
|
62
|
-
| Who executes `compress` | **The agent** (pi runs it locally) | **The proxy** (server-side compress loop) |
|
|
63
|
-
| `compress` tool call in the re-sent history? | Yes — part of the agent's own conversation | No — ephemeral proxy-loop traffic |
|
|
64
|
-
| Preflight blocks (no tool call)? | No — overflow forces the model to call `compress` | Yes — `src/preflight.ts` compresses behind the client's back |
|
|
65
|
-
| **Summary carrier on the wire** | **the `compress` tool call** | **an `acp_summary` user message** |
|
|
66
|
-
| System messages on the wire | always exactly 1 (client + prompt) | always exactly 1 (client + prompt) — summaries ride on user messages |
|
|
67
|
-
| SGLang "single system" 400 (#377) | cannot happen | cannot happen (summaries are user messages, not system) |
|
|
68
|
-
|
|
69
|
-
**Why the carriers differ.** In plugin mode the agent owns compression: the
|
|
70
|
-
`compress` call + result live in the agent's own history and are re-sent every
|
|
71
|
-
turn, so the summary rides on the tool call and the agent's view never renders
|
|
72
|
-
the kernel's `acp_summary` fallback (`billion-context-pi` `src/messages.ts`
|
|
73
|
-
skips `acp_summary_*`). In proxy mode the client is not ACP-native, so the
|
|
74
|
-
proxy executes `compress` server-side; the tool call never enters the client's
|
|
75
|
-
history, and preflight blocks have no tool call at all — so the kernel's
|
|
76
|
-
`acp_summary` message is the only carrier. The kernel renders it as role
|
|
77
|
-
`system`, but strict OpenAI-compatible backends (SGLang) require exactly one
|
|
78
|
-
system message at index 0, so `systemToUser` (`src/util.ts`) re-voices it as a
|
|
79
|
-
`user` message, leaving it at its anchor position. This keeps the head system
|
|
80
|
-
message (the prefix-cache anchor) byte-stable across compress turns, so a new
|
|
81
|
-
block does not invalidate the whole-conversation prefix.
|
|
82
|
-
|
|
83
|
-
**Why `user`, not `system` or a forged tool call.** A mid-stream `system`
|
|
84
|
-
message is what SGLang rejects (#377). A forged `compress` tool call would be
|
|
85
|
-
the "pure" carrier, but in proxy mode it requires fabricating an
|
|
86
|
-
assistant `tool_calls` + `user` `tool_result` pair by id, declaring the tool in
|
|
87
|
-
the request, and handling preflight blocks that have no authentic call — far
|
|
88
|
-
more invasive than re-voicing a standalone note. A `user` message is allowed
|
|
89
|
-
anywhere in the conversation, so it is the minimal change that satisfies both
|
|
90
|
-
SGLang's one-system rule and prefix-cache stability. The accepted trade-off:
|
|
91
|
-
a summary is a stand-in for the folded history, and re-voicing it as a user
|
|
92
|
-
turn is a semantic mismatch the model tolerates (it is clearly marked
|
|
93
|
-
`[Compressed conversation section]`).
|
|
94
|
-
|
|
95
|
-
**Do the two modes coexist?**
|
|
96
|
-
|
|
97
|
-
- **Same proxy instance: yes, by design.** One proxy serves plugin and plain
|
|
98
|
-
clients at once; `pluginMode` is decided per request (`x-bili-plugin` header)
|
|
99
|
-
and bound per session (`session.metadata.pluginAgent`). The launcher reuses a
|
|
100
|
-
running proxy.
|
|
101
|
-
- **Same session: the mode is sticky.** A session created in plugin mode stays
|
|
102
|
-
plugin mode (metadata inheritance); a plain session can only be *upgraded* to
|
|
103
|
-
plugin mode if a plugin request arrives with a matching conversation id (the
|
|
104
|
-
header outranks) — and never downgraded. In practice a plain→plugin upgrade
|
|
105
|
-
requires the plugin client's conversation id to match an existing plain
|
|
106
|
-
session id, which doesn't happen (each client generates its own id).
|
|
107
|
-
- **Cross-mode block hazard: theoretical only.** It would require the same
|
|
108
|
-
conversation id to span a mode switch. plugin→proxy is safe (the tool call is
|
|
109
|
-
in the shared history); proxy→plugin could orphan proxy-created block
|
|
110
|
-
summaries (their tool call isn't in the agent's history and the agent's view
|
|
111
|
-
skips `acp_summary`) — but that needs the id match above, which doesn't occur.
|
|
112
|
-
|
|
113
53
|
## Install
|
|
114
54
|
|
|
115
55
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -51718,18 +51718,25 @@ function reserveOutputHeadroom(window2, maxOutput) {
|
|
|
51718
51718
|
}
|
|
51719
51719
|
return window2;
|
|
51720
51720
|
}
|
|
51721
|
-
function
|
|
51722
|
-
let
|
|
51723
|
-
|
|
51724
|
-
|
|
51725
|
-
|
|
51726
|
-
|
|
51727
|
-
|
|
51728
|
-
|
|
51729
|
-
|
|
51730
|
-
return messages
|
|
51731
|
-
|
|
51732
|
-
|
|
51721
|
+
function hoistMidSystemMessages(messages) {
|
|
51722
|
+
let lead = 0;
|
|
51723
|
+
while (lead < messages.length && (messages[lead].role === "system" || messages[lead].role === "developer")) lead++;
|
|
51724
|
+
if (lead === messages.length) return messages;
|
|
51725
|
+
let midCount = 0;
|
|
51726
|
+
for (let i = lead; i < messages.length; i++) {
|
|
51727
|
+
const r = messages[i].role;
|
|
51728
|
+
if (r === "system" || r === "developer") midCount++;
|
|
51729
|
+
}
|
|
51730
|
+
if (midCount === 0) return messages;
|
|
51731
|
+
const head = messages.slice(0, lead);
|
|
51732
|
+
const mid = [];
|
|
51733
|
+
const rest = [];
|
|
51734
|
+
for (let i = lead; i < messages.length; i++) {
|
|
51735
|
+
const r = messages[i].role;
|
|
51736
|
+
if (r === "system" || r === "developer") mid.push(messages[i]);
|
|
51737
|
+
else rest.push(messages[i]);
|
|
51738
|
+
}
|
|
51739
|
+
return [...head, ...mid, ...rest];
|
|
51733
51740
|
}
|
|
51734
51741
|
function shouldReserveOutputHeadroom(protocol) {
|
|
51735
51742
|
return protocol !== "anthropic";
|
|
@@ -52392,7 +52399,7 @@ function createOpenaiAdapter(requestBody, clientSystem) {
|
|
|
52392
52399
|
);
|
|
52393
52400
|
return {
|
|
52394
52401
|
buildRequest(coreMessages, systemPrompt, body) {
|
|
52395
|
-
const messages =
|
|
52402
|
+
const messages = hoistMidSystemMessages(coreToOpenai(coreMessages));
|
|
52396
52403
|
const withSys = injectOpenaiSystem(messages, [clientSystem, systemPrompt].filter((p2) => typeof p2 === "string" && p2.length > 0));
|
|
52397
52404
|
return { ...body, messages: withSys };
|
|
52398
52405
|
},
|
|
@@ -56638,7 +56645,7 @@ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session,
|
|
|
56638
56645
|
log2("info", diagNudge(turn, sessionId, tokenCount, config.modelContextLimit, parsed.model));
|
|
56639
56646
|
processedMessages = stripKernelSummaries(turn.messages, turn.state);
|
|
56640
56647
|
reapOrphanBlocks(session, msgs, deactivateBlock);
|
|
56641
|
-
rebuiltMessages =
|
|
56648
|
+
rebuiltMessages = hoistMidSystemMessages(coreToOpenai(processedMessages));
|
|
56642
56649
|
const sysParts = [];
|
|
56643
56650
|
if (systemText) sysParts.push(systemText);
|
|
56644
56651
|
if (shouldInject) sysParts.push(buildCompressSystemPrompt(prompts));
|